Merge "Add the systemOverlays() method"
diff --git a/GAME_MANAGER_OWNERS b/GAME_MANAGER_OWNERS
index 502a9e36..b65c43a 100644
--- a/GAME_MANAGER_OWNERS
+++ b/GAME_MANAGER_OWNERS
@@ -1,2 +1,3 @@
lpy@google.com
-timvp@google.com
+chingtangyu@google.com
+xwxw@google.com
diff --git a/apct-tests/perftests/multiuser/AndroidManifest.xml b/apct-tests/perftests/multiuser/AndroidManifest.xml
index 63e5983..5befa1f 100644
--- a/apct-tests/perftests/multiuser/AndroidManifest.xml
+++ b/apct-tests/perftests/multiuser/AndroidManifest.xml
@@ -35,4 +35,8 @@
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.perftests.multiuser"/>
+ <queries>
+ <package android:name="perftests.multiuser.apps.dummyapp" />
+ </queries>
+
</manifest>
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index 9ac3e41..9d363c8 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -80,6 +80,7 @@
import android.os.RemoteCallback;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.os.UserManager;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -619,7 +620,7 @@
return blobInfos;
}
- private void deleteBlobInternal(long blobId, int callingUid) {
+ private void deleteBlobInternal(long blobId) {
synchronized (mBlobsLock) {
mBlobsMap.entrySet().removeIf(entry -> {
final BlobMetadata blobMetadata = entry.getValue();
@@ -1612,10 +1613,7 @@
@Override
@NonNull
public List<BlobInfo> queryBlobsForUser(@UserIdInt int userId) {
- if (Binder.getCallingUid() != Process.SYSTEM_UID) {
- throw new SecurityException("Only system uid is allowed to call "
- + "queryBlobsForUser()");
- }
+ verifyCallerIsSystemUid("queryBlobsForUser");
final int resolvedUserId = userId == USER_CURRENT
? ActivityManager.getCurrentUser() : userId;
@@ -1629,13 +1627,9 @@
@Override
public void deleteBlob(long blobId) {
- final int callingUid = Binder.getCallingUid();
- if (callingUid != Process.SYSTEM_UID) {
- throw new SecurityException("Only system uid is allowed to call "
- + "deleteBlob()");
- }
+ verifyCallerIsSystemUid("deleteBlob");
- deleteBlobInternal(blobId, callingUid);
+ deleteBlobInternal(blobId);
}
@Override
@@ -1716,6 +1710,18 @@
return new BlobStoreManagerShellCommand(BlobStoreManagerService.this).exec(this,
in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(), args);
}
+
+ /**
+ * Verify if the caller is an admin user's app with system uid
+ */
+ private void verifyCallerIsSystemUid(final String operation) {
+ if (UserHandle.getCallingAppId() != Process.SYSTEM_UID
+ || !mContext.getSystemService(UserManager.class)
+ .isUserAdmin(UserHandle.getCallingUserId())) {
+ throw new SecurityException("Only admin user's app with system uid"
+ + "are allowed to call #" + operation);
+ }
+ }
}
static final class DumpArgs {
diff --git a/apex/jobscheduler/framework/java/android/app/job/IJobCallback.aidl b/apex/jobscheduler/framework/java/android/app/job/IJobCallback.aidl
index d281da0..a3390b7 100644
--- a/apex/jobscheduler/framework/java/android/app/job/IJobCallback.aidl
+++ b/apex/jobscheduler/framework/java/android/app/job/IJobCallback.aidl
@@ -30,6 +30,25 @@
*/
interface IJobCallback {
/**
+ * Immediate callback to the system after sending a data transfer download progress request
+ * signal; used to quickly detect ANR.
+ *
+ * @param jobId Unique integer used to identify this job.
+ * @param workId Unique integer used to identify a specific work item.
+ * @param transferredBytes How much data has been downloaded, in bytes.
+ */
+ void acknowledgeGetTransferredDownloadBytesMessage(int jobId, int workId,
+ long transferredBytes);
+ /**
+ * Immediate callback to the system after sending a data transfer upload progress request
+ * signal; used to quickly detect ANR.
+ *
+ * @param jobId Unique integer used to identify this job.
+ * @param workId Unique integer used to identify a specific work item.
+ * @param transferredBytes How much data has been uploaded, in bytes.
+ */
+ void acknowledgeGetTransferredUploadBytesMessage(int jobId, int workId, long transferredBytes);
+ /**
* Immediate callback to the system after sending a start signal, used to quickly detect ANR.
*
* @param jobId Unique integer used to identify this job.
@@ -65,4 +84,24 @@
*/
@UnsupportedAppUsage
void jobFinished(int jobId, boolean reschedule);
+ /*
+ * Inform JobScheduler of a change in the estimated transfer payload.
+ *
+ * @param jobId Unique integer used to identify this job.
+ * @param item The particular JobWorkItem this progress is associated with, if any.
+ * @param downloadBytes How many bytes the app expects to download.
+ * @param uploadBytes How many bytes the app expects to upload.
+ */
+ void updateEstimatedNetworkBytes(int jobId, in JobWorkItem item,
+ long downloadBytes, long uploadBytes);
+ /*
+ * Update JobScheduler of how much data the job has successfully transferred.
+ *
+ * @param jobId Unique integer used to identify this job.
+ * @param item The particular JobWorkItem this progress is associated with, if any.
+ * @param transferredDownloadBytes The number of bytes that have successfully been downloaded.
+ * @param transferredUploadBytes The number of bytes that have successfully been uploaded.
+ */
+ void updateTransferredNetworkBytes(int jobId, in JobWorkItem item,
+ long transferredDownloadBytes, long transferredUploadBytes);
}
diff --git a/apex/jobscheduler/framework/java/android/app/job/IJobService.aidl b/apex/jobscheduler/framework/java/android/app/job/IJobService.aidl
index 22ad252..2bb82bd 100644
--- a/apex/jobscheduler/framework/java/android/app/job/IJobService.aidl
+++ b/apex/jobscheduler/framework/java/android/app/job/IJobService.aidl
@@ -17,6 +17,7 @@
package android.app.job;
import android.app.job.JobParameters;
+import android.app.job.JobWorkItem;
/**
* Interface that the framework uses to communicate with application code that implements a
@@ -31,4 +32,8 @@
/** Stop execution of application's job. */
@UnsupportedAppUsage
void stopJob(in JobParameters jobParams);
+ /** Update JS of how much data has been downloaded. */
+ void getTransferredDownloadBytes(in JobParameters jobParams, in JobWorkItem jobWorkItem);
+ /** Update JS of how much data has been uploaded. */
+ void getTransferredUploadBytes(in JobParameters jobParams, in JobWorkItem jobWorkItem);
}
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
index dfdb290..7448686 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
@@ -22,8 +22,11 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.content.ClipData;
import android.content.Context;
+import android.os.Build;
import android.os.Bundle;
import android.os.PersistableBundle;
@@ -93,6 +96,16 @@
*/
@SystemService(Context.JOB_SCHEDULER_SERVICE)
public abstract class JobScheduler {
+ /**
+ * Whether to throw an exception when an app doesn't properly implement all the necessary
+ * data transfer APIs.
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ public static final long THROW_ON_INVALID_DATA_TRANSFER_IMPLEMENTATION = 255371817L;
+
/** @hide */
@IntDef(prefix = { "RESULT_" }, value = {
RESULT_FAILURE,
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobService.java b/apex/jobscheduler/framework/java/android/app/job/JobService.java
index d184d44..dabf728 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobService.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobService.java
@@ -16,7 +16,13 @@
package android.app.job;
+import static android.app.job.JobScheduler.THROW_ON_INVALID_DATA_TRANSFER_IMPLEMENTATION;
+
+import android.annotation.BytesLong;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.Service;
+import android.compat.Compatibility;
import android.content.Intent;
import android.os.IBinder;
@@ -72,6 +78,28 @@
public boolean onStopJob(JobParameters params) {
return JobService.this.onStopJob(params);
}
+
+ @Override
+ @BytesLong
+ public long getTransferredDownloadBytes(@NonNull JobParameters params,
+ @Nullable JobWorkItem item) {
+ if (item == null) {
+ return JobService.this.getTransferredDownloadBytes();
+ } else {
+ return JobService.this.getTransferredDownloadBytes(item);
+ }
+ }
+
+ @Override
+ @BytesLong
+ public long getTransferredUploadBytes(@NonNull JobParameters params,
+ @Nullable JobWorkItem item) {
+ if (item == null) {
+ return JobService.this.getTransferredUploadBytes();
+ } else {
+ return JobService.this.getTransferredUploadBytes(item);
+ }
+ }
};
}
return mEngine.getBinder();
@@ -171,4 +199,161 @@
* to end the job entirely. Regardless of the value returned, your job must stop executing.
*/
public abstract boolean onStopJob(JobParameters params);
+
+ /**
+ * Update how much data this job will transfer. This method can
+ * be called multiple times within the first 30 seconds after
+ * {@link #onStartJob(JobParameters)} has been called. Only
+ * one call will be heeded after that time has passed.
+ *
+ * This method (or an overload) must be called within the first
+ * 30 seconds for a data transfer job if a payload size estimate
+ * was not provided at the time of scheduling.
+ *
+ * @see JobInfo.Builder#setEstimatedNetworkBytes(long, long)
+ */
+ public final void updateEstimatedNetworkBytes(@NonNull JobParameters params,
+ @BytesLong long downloadBytes, @BytesLong long uploadBytes) {
+ mEngine.updateEstimatedNetworkBytes(params, null, downloadBytes, uploadBytes);
+ }
+
+ /**
+ * Update how much data will transfer for the JobWorkItem. This
+ * method can be called multiple times within the first 30 seconds
+ * after {@link #onStartJob(JobParameters)} has been called.
+ * Only one call will be heeded after that time has passed.
+ *
+ * This method (or an overload) must be called within the first
+ * 30 seconds for a data transfer job if a payload size estimate
+ * was not provided at the time of scheduling.
+ *
+ * @see JobInfo.Builder#setEstimatedNetworkBytes(long, long)
+ */
+ public final void updateEstimatedNetworkBytes(@NonNull JobParameters params,
+ @NonNull JobWorkItem jobWorkItem,
+ @BytesLong long downloadBytes, @BytesLong long uploadBytes) {
+ mEngine.updateEstimatedNetworkBytes(params, jobWorkItem, downloadBytes, uploadBytes);
+ }
+
+ /**
+ * Tell JobScheduler how much data has successfully been transferred for the data transfer job.
+ */
+ public final void updateTransferredNetworkBytes(@NonNull JobParameters params,
+ @BytesLong long transferredDownloadBytes, @BytesLong long transferredUploadBytes) {
+ mEngine.updateTransferredNetworkBytes(params, null,
+ transferredDownloadBytes, transferredUploadBytes);
+ }
+
+ /**
+ * Tell JobScheduler how much data has been transferred for the data transfer
+ * {@link JobWorkItem}.
+ */
+ public final void updateTransferredNetworkBytes(@NonNull JobParameters params,
+ @NonNull JobWorkItem item,
+ @BytesLong long transferredDownloadBytes, @BytesLong long transferredUploadBytes) {
+ mEngine.updateTransferredNetworkBytes(params, item,
+ transferredDownloadBytes, transferredUploadBytes);
+ }
+
+ /**
+ * Get the number of bytes the app has successfully downloaded for this job. JobScheduler
+ * will call this if the job has specified positive estimated download bytes and
+ * {@link #updateTransferredNetworkBytes(JobParameters, long, long)}
+ * hasn't been called recently.
+ *
+ * <p>
+ * This must be implemented for all data transfer jobs.
+ *
+ * @see JobInfo.Builder#setEstimatedNetworkBytes(long, long)
+ * @see JobInfo#NETWORK_BYTES_UNKNOWN
+ */
+ // TODO(255371817): specify the actual time JS will wait for progress before requesting
+ @BytesLong
+ public long getTransferredDownloadBytes() {
+ if (Compatibility.isChangeEnabled(THROW_ON_INVALID_DATA_TRANSFER_IMPLEMENTATION)) {
+ // Regular jobs don't have to implement this and JobScheduler won't call this API for
+ // non-data transfer jobs.
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
+ return 0;
+ }
+
+ /**
+ * Get the number of bytes the app has successfully downloaded for this job. JobScheduler
+ * will call this if the job has specified positive estimated upload bytes and
+ * {@link #updateTransferredNetworkBytes(JobParameters, long, long)}
+ * hasn't been called recently.
+ *
+ * <p>
+ * This must be implemented for all data transfer jobs.
+ *
+ * @see JobInfo.Builder#setEstimatedNetworkBytes(long, long)
+ * @see JobInfo#NETWORK_BYTES_UNKNOWN
+ */
+ // TODO(255371817): specify the actual time JS will wait for progress before requesting
+ @BytesLong
+ public long getTransferredUploadBytes() {
+ if (Compatibility.isChangeEnabled(THROW_ON_INVALID_DATA_TRANSFER_IMPLEMENTATION)) {
+ // Regular jobs don't have to implement this and JobScheduler won't call this API for
+ // non-data transfer jobs.
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
+ return 0;
+ }
+
+ /**
+ * Get the number of bytes the app has successfully downloaded for this job. JobScheduler
+ * will call this if the job has specified positive estimated download bytes and
+ * {@link #updateTransferredNetworkBytes(JobParameters, JobWorkItem, long, long)}
+ * hasn't been called recently and the job has
+ * {@link JobWorkItem JobWorkItems} that have been
+ * {@link JobParameters#dequeueWork dequeued} but not
+ * {@link JobParameters#completeWork(JobWorkItem) completed}.
+ *
+ * <p>
+ * This must be implemented for all data transfer jobs.
+ *
+ * @see JobInfo#NETWORK_BYTES_UNKNOWN
+ */
+ // TODO(255371817): specify the actual time JS will wait for progress before requesting
+ @BytesLong
+ public long getTransferredDownloadBytes(@NonNull JobWorkItem item) {
+ if (item == null) {
+ return getTransferredDownloadBytes();
+ }
+ if (Compatibility.isChangeEnabled(THROW_ON_INVALID_DATA_TRANSFER_IMPLEMENTATION)) {
+ // Regular jobs don't have to implement this and JobScheduler won't call this API for
+ // non-data transfer jobs.
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
+ return 0;
+ }
+
+ /**
+ * Get the number of bytes the app has successfully downloaded for this job. JobScheduler
+ * will call this if the job has specified positive estimated upload bytes and
+ * {@link #updateTransferredNetworkBytes(JobParameters, JobWorkItem, long, long)}
+ * hasn't been called recently and the job has
+ * {@link JobWorkItem JobWorkItems} that have been
+ * {@link JobParameters#dequeueWork dequeued} but not
+ * {@link JobParameters#completeWork(JobWorkItem) completed}.
+ *
+ * <p>
+ * This must be implemented for all data transfer jobs.
+ *
+ * @see JobInfo#NETWORK_BYTES_UNKNOWN
+ */
+ // TODO(255371817): specify the actual time JS will wait for progress before requesting
+ @BytesLong
+ public long getTransferredUploadBytes(@NonNull JobWorkItem item) {
+ if (item == null) {
+ return getTransferredUploadBytes();
+ }
+ if (Compatibility.isChangeEnabled(THROW_ON_INVALID_DATA_TRANSFER_IMPLEMENTATION)) {
+ // Regular jobs don't have to implement this and JobScheduler won't call this API for
+ // non-data transfer jobs.
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
+ return 0;
+ }
}
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java b/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java
index 3d43d20..6c4b686 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java
@@ -16,7 +16,13 @@
package android.app.job;
+import static android.app.job.JobScheduler.THROW_ON_INVALID_DATA_TRANSFER_IMPLEMENTATION;
+
+import android.annotation.BytesLong;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.Service;
+import android.compat.Compatibility;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
@@ -25,6 +31,8 @@
import android.os.RemoteException;
import android.util.Log;
+import com.android.internal.os.SomeArgs;
+
import java.lang.ref.WeakReference;
/**
@@ -51,6 +59,20 @@
* Message that the client has completed execution of this job.
*/
private static final int MSG_JOB_FINISHED = 2;
+ /**
+ * Message that will result in a call to
+ * {@link #getTransferredDownloadBytes(JobParameters, JobWorkItem)}.
+ */
+ private static final int MSG_GET_TRANSFERRED_DOWNLOAD_BYTES = 3;
+ /**
+ * Message that will result in a call to
+ * {@link #getTransferredUploadBytes(JobParameters, JobWorkItem)}.
+ */
+ private static final int MSG_GET_TRANSFERRED_UPLOAD_BYTES = 4;
+ /** Message that the client wants to update JobScheduler of the data transfer progress. */
+ private static final int MSG_UPDATE_TRANSFERRED_NETWORK_BYTES = 5;
+ /** Message that the client wants to update JobScheduler of the estimated transfer size. */
+ private static final int MSG_UPDATE_ESTIMATED_NETWORK_BYTES = 6;
private final IJobService mBinder;
@@ -68,6 +90,32 @@
}
@Override
+ public void getTransferredDownloadBytes(@NonNull JobParameters jobParams,
+ @Nullable JobWorkItem jobWorkItem) throws RemoteException {
+ JobServiceEngine service = mService.get();
+ if (service != null) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = jobParams;
+ args.arg2 = jobWorkItem;
+ service.mHandler.obtainMessage(MSG_GET_TRANSFERRED_DOWNLOAD_BYTES, args)
+ .sendToTarget();
+ }
+ }
+
+ @Override
+ public void getTransferredUploadBytes(@NonNull JobParameters jobParams,
+ @Nullable JobWorkItem jobWorkItem) throws RemoteException {
+ JobServiceEngine service = mService.get();
+ if (service != null) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = jobParams;
+ args.arg2 = jobWorkItem;
+ service.mHandler.obtainMessage(MSG_GET_TRANSFERRED_UPLOAD_BYTES, args)
+ .sendToTarget();
+ }
+ }
+
+ @Override
public void startJob(JobParameters jobParams) throws RemoteException {
JobServiceEngine service = mService.get();
if (service != null) {
@@ -98,9 +146,9 @@
@Override
public void handleMessage(Message msg) {
- final JobParameters params = (JobParameters) msg.obj;
switch (msg.what) {
- case MSG_EXECUTE_JOB:
+ case MSG_EXECUTE_JOB: {
+ final JobParameters params = (JobParameters) msg.obj;
try {
boolean workOngoing = JobServiceEngine.this.onStartJob(params);
ackStartMessage(params, workOngoing);
@@ -109,7 +157,9 @@
throw new RuntimeException(e);
}
break;
- case MSG_STOP_JOB:
+ }
+ case MSG_STOP_JOB: {
+ final JobParameters params = (JobParameters) msg.obj;
try {
boolean ret = JobServiceEngine.this.onStopJob(params);
ackStopMessage(params, ret);
@@ -118,7 +168,9 @@
throw new RuntimeException(e);
}
break;
- case MSG_JOB_FINISHED:
+ }
+ case MSG_JOB_FINISHED: {
+ final JobParameters params = (JobParameters) msg.obj;
final boolean needsReschedule = (msg.arg2 == 1);
IJobCallback callback = params.getCallback();
if (callback != null) {
@@ -132,19 +184,117 @@
Log.e(TAG, "finishJob() called for a nonexistent job id.");
}
break;
+ }
+ case MSG_GET_TRANSFERRED_DOWNLOAD_BYTES: {
+ final SomeArgs args = (SomeArgs) msg.obj;
+ final JobParameters params = (JobParameters) args.arg1;
+ final JobWorkItem item = (JobWorkItem) args.arg2;
+ try {
+ long ret = JobServiceEngine.this.getTransferredDownloadBytes(params, item);
+ ackGetTransferredDownloadBytesMessage(params, item, ret);
+ } catch (Exception e) {
+ Log.e(TAG, "Application unable to handle getTransferredDownloadBytes.", e);
+ throw new RuntimeException(e);
+ }
+ args.recycle();
+ break;
+ }
+ case MSG_GET_TRANSFERRED_UPLOAD_BYTES: {
+ final SomeArgs args = (SomeArgs) msg.obj;
+ final JobParameters params = (JobParameters) args.arg1;
+ final JobWorkItem item = (JobWorkItem) args.arg2;
+ try {
+ long ret = JobServiceEngine.this.getTransferredUploadBytes(params, item);
+ ackGetTransferredUploadBytesMessage(params, item, ret);
+ } catch (Exception e) {
+ Log.e(TAG, "Application unable to handle getTransferredUploadBytes.", e);
+ throw new RuntimeException(e);
+ }
+ args.recycle();
+ break;
+ }
+ case MSG_UPDATE_TRANSFERRED_NETWORK_BYTES: {
+ final SomeArgs args = (SomeArgs) msg.obj;
+ final JobParameters params = (JobParameters) args.arg1;
+ IJobCallback callback = params.getCallback();
+ if (callback != null) {
+ try {
+ callback.updateTransferredNetworkBytes(params.getJobId(),
+ (JobWorkItem) args.arg2, args.argl1, args.argl2);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error updating data transfer progress to system:"
+ + " binder has gone away.");
+ }
+ } else {
+ Log.e(TAG, "updateDataTransferProgress() called for a nonexistent job id.");
+ }
+ args.recycle();
+ break;
+ }
+ case MSG_UPDATE_ESTIMATED_NETWORK_BYTES: {
+ final SomeArgs args = (SomeArgs) msg.obj;
+ final JobParameters params = (JobParameters) args.arg1;
+ IJobCallback callback = params.getCallback();
+ if (callback != null) {
+ try {
+ callback.updateEstimatedNetworkBytes(params.getJobId(),
+ (JobWorkItem) args.arg2, args.argl1, args.argl2);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error updating estimated transfer size to system:"
+ + " binder has gone away.");
+ }
+ } else {
+ Log.e(TAG,
+ "updateEstimatedNetworkBytes() called for a nonexistent job id.");
+ }
+ args.recycle();
+ break;
+ }
default:
Log.e(TAG, "Unrecognised message received.");
break;
}
}
+ private void ackGetTransferredDownloadBytesMessage(@NonNull JobParameters params,
+ @Nullable JobWorkItem item, long progress) {
+ final IJobCallback callback = params.getCallback();
+ final int jobId = params.getJobId();
+ final int workId = item == null ? -1 : item.getWorkId();
+ if (callback != null) {
+ try {
+ callback.acknowledgeGetTransferredDownloadBytesMessage(jobId, workId, progress);
+ } catch (RemoteException e) {
+ Log.e(TAG, "System unreachable for returning progress.");
+ }
+ } else if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Attempting to ack a job that has already been processed.");
+ }
+ }
+
+ private void ackGetTransferredUploadBytesMessage(@NonNull JobParameters params,
+ @Nullable JobWorkItem item, long progress) {
+ final IJobCallback callback = params.getCallback();
+ final int jobId = params.getJobId();
+ final int workId = item == null ? -1 : item.getWorkId();
+ if (callback != null) {
+ try {
+ callback.acknowledgeGetTransferredUploadBytesMessage(jobId, workId, progress);
+ } catch (RemoteException e) {
+ Log.e(TAG, "System unreachable for returning progress.");
+ }
+ } else if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Attempting to ack a job that has already been processed.");
+ }
+ }
+
private void ackStartMessage(JobParameters params, boolean workOngoing) {
final IJobCallback callback = params.getCallback();
final int jobId = params.getJobId();
if (callback != null) {
try {
callback.acknowledgeStartMessage(jobId, workOngoing);
- } catch(RemoteException e) {
+ } catch (RemoteException e) {
Log.e(TAG, "System unreachable for starting job.");
}
} else {
@@ -213,4 +363,69 @@
m.arg2 = needsReschedule ? 1 : 0;
m.sendToTarget();
}
+
+ /**
+ * Engine's request to get how much data has been downloaded.
+ *
+ * @see JobService#getTransferredDownloadBytes()
+ */
+ @BytesLong
+ public long getTransferredDownloadBytes(@NonNull JobParameters params,
+ @Nullable JobWorkItem item) {
+ if (Compatibility.isChangeEnabled(THROW_ON_INVALID_DATA_TRANSFER_IMPLEMENTATION)) {
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
+ return 0;
+ }
+
+ /**
+ * Engine's request to get how much data has been uploaded.
+ *
+ * @see JobService#getTransferredUploadBytes()
+ */
+ @BytesLong
+ public long getTransferredUploadBytes(@NonNull JobParameters params,
+ @Nullable JobWorkItem item) {
+ if (Compatibility.isChangeEnabled(THROW_ON_INVALID_DATA_TRANSFER_IMPLEMENTATION)) {
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
+ return 0;
+ }
+
+ /**
+ * Call in to engine to report data transfer progress.
+ *
+ * @see JobService#updateTransferredNetworkBytes(JobParameters, long, long)
+ */
+ public void updateTransferredNetworkBytes(@NonNull JobParameters params,
+ @Nullable JobWorkItem item, long downloadBytes, long uploadBytes) {
+ if (params == null) {
+ throw new NullPointerException("params");
+ }
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = params;
+ args.arg2 = item;
+ args.argl1 = downloadBytes;
+ args.argl2 = uploadBytes;
+ mHandler.obtainMessage(MSG_UPDATE_TRANSFERRED_NETWORK_BYTES, args).sendToTarget();
+ }
+
+ /**
+ * Call in to engine to report data transfer progress.
+ *
+ * @see JobService#updateEstimatedNetworkBytes(JobParameters, JobWorkItem, long, long)
+ */
+ public void updateEstimatedNetworkBytes(@NonNull JobParameters params,
+ @NonNull JobWorkItem item,
+ @BytesLong long downloadBytes, @BytesLong long uploadBytes) {
+ if (params == null) {
+ throw new NullPointerException("params");
+ }
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = params;
+ args.arg2 = item;
+ args.argl1 = downloadBytes;
+ args.argl2 = uploadBytes;
+ mHandler.obtainMessage(MSG_UPDATE_ESTIMATED_NETWORK_BYTES, args).sendToTarget();
+ }
}
\ No newline at end of file
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index bd475e9..e3bd5ac 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -31,6 +31,7 @@
import android.app.AlarmManager;
import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
@@ -740,8 +741,10 @@
}
};
- private final BroadcastReceiver mIdleStartedDoneReceiver = new BroadcastReceiver() {
- @Override public void onReceive(Context context, Intent intent) {
+ private final IIntentReceiver mIdleStartedDoneReceiver = new IIntentReceiver.Stub() {
+ @Override
+ public void performReceive(Intent intent, int resultCode, String data, Bundle extras,
+ boolean ordered, boolean sticky, int sendingUser) {
// When coming out of a deep idle, we will add in some delay before we allow
// the system to settle down and finish the maintenance window. This is
// to give a chance for any pending work to be scheduled.
@@ -1816,13 +1819,15 @@
}
if (deepChanged) {
incActiveIdleOps();
- getContext().sendOrderedBroadcastAsUser(mIdleIntent, UserHandle.ALL,
- null, mIdleStartedDoneReceiver, null, 0, null, null);
+ mLocalActivityManager.broadcastIntentWithCallback(mIdleIntent,
+ mIdleStartedDoneReceiver, null, UserHandle.USER_ALL,
+ null, null, null);
}
if (lightChanged) {
incActiveIdleOps();
- getContext().sendOrderedBroadcastAsUser(mLightIdleIntent, UserHandle.ALL,
- null, mIdleStartedDoneReceiver, null, 0, null, null);
+ mLocalActivityManager.broadcastIntentWithCallback(mLightIdleIntent,
+ mIdleStartedDoneReceiver, null, UserHandle.USER_ALL,
+ null, null, null);
}
// Always start with one active op for the message being sent here.
// Now we are done!
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index bedfa7f..29e730d 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -84,6 +84,7 @@
import android.content.PermissionChecker;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.UserPackage;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.BatteryManager;
@@ -120,7 +121,6 @@
import android.util.IntArray;
import android.util.Log;
import android.util.LongArrayQueue;
-import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseArrayMap;
@@ -402,7 +402,7 @@
public long lastUsage;
}
/** Map of {package, user} -> {quotaInfo} */
- private final ArrayMap<Pair<String, Integer>, QuotaInfo> mQuotaBuffer = new ArrayMap<>();
+ private final ArrayMap<UserPackage, QuotaInfo> mQuotaBuffer = new ArrayMap<>();
private long mMaxDuration;
@@ -414,11 +414,11 @@
if (quota <= 0) {
return;
}
- final Pair<String, Integer> packageUser = Pair.create(packageName, userId);
- QuotaInfo currentQuotaInfo = mQuotaBuffer.get(packageUser);
+ final UserPackage userPackage = UserPackage.of(userId, packageName);
+ QuotaInfo currentQuotaInfo = mQuotaBuffer.get(userPackage);
if (currentQuotaInfo == null) {
currentQuotaInfo = new QuotaInfo();
- mQuotaBuffer.put(packageUser, currentQuotaInfo);
+ mQuotaBuffer.put(userPackage, currentQuotaInfo);
}
currentQuotaInfo.remainingQuota = quota;
currentQuotaInfo.expirationTime = nowElapsed + mMaxDuration;
@@ -426,8 +426,8 @@
/** Returns if the supplied package has reserve quota to fire at the given time. */
boolean hasQuota(String packageName, int userId, long triggerElapsed) {
- final Pair<String, Integer> packageUser = Pair.create(packageName, userId);
- final QuotaInfo quotaInfo = mQuotaBuffer.get(packageUser);
+ final UserPackage userPackage = UserPackage.of(userId, packageName);
+ final QuotaInfo quotaInfo = mQuotaBuffer.get(userPackage);
return quotaInfo != null && quotaInfo.remainingQuota > 0
&& triggerElapsed <= quotaInfo.expirationTime;
@@ -438,8 +438,8 @@
* required.
*/
void recordUsage(String packageName, int userId, long nowElapsed) {
- final Pair<String, Integer> packageUser = Pair.create(packageName, userId);
- final QuotaInfo quotaInfo = mQuotaBuffer.get(packageUser);
+ final UserPackage userPackage = UserPackage.of(userId, packageName);
+ final QuotaInfo quotaInfo = mQuotaBuffer.get(userPackage);
if (quotaInfo == null) {
Slog.wtf(TAG, "Temporary quota being consumed at " + nowElapsed
@@ -479,26 +479,26 @@
void removeForUser(int userId) {
for (int i = mQuotaBuffer.size() - 1; i >= 0; i--) {
- final Pair<String, Integer> packageUserKey = mQuotaBuffer.keyAt(i);
- if (packageUserKey.second == userId) {
+ final UserPackage userPackageKey = mQuotaBuffer.keyAt(i);
+ if (userPackageKey.userId == userId) {
mQuotaBuffer.removeAt(i);
}
}
}
void removeForPackage(String packageName, int userId) {
- final Pair<String, Integer> packageUser = Pair.create(packageName, userId);
- mQuotaBuffer.remove(packageUser);
+ final UserPackage userPackage = UserPackage.of(userId, packageName);
+ mQuotaBuffer.remove(userPackage);
}
void dump(IndentingPrintWriter pw, long nowElapsed) {
pw.increaseIndent();
for (int i = 0; i < mQuotaBuffer.size(); i++) {
- final Pair<String, Integer> packageUser = mQuotaBuffer.keyAt(i);
+ final UserPackage userPackage = mQuotaBuffer.keyAt(i);
final QuotaInfo quotaInfo = mQuotaBuffer.valueAt(i);
- pw.print(packageUser.first);
+ pw.print(userPackage.packageName);
pw.print(", u");
- pw.print(packageUser.second);
+ pw.print(userPackage.userId);
pw.print(": ");
if (quotaInfo == null) {
pw.print("--");
@@ -522,8 +522,7 @@
*/
@VisibleForTesting
static class AppWakeupHistory {
- private ArrayMap<Pair<String, Integer>, LongArrayQueue> mPackageHistory =
- new ArrayMap<>();
+ private final ArrayMap<UserPackage, LongArrayQueue> mPackageHistory = new ArrayMap<>();
private long mWindowSize;
AppWakeupHistory(long windowSize) {
@@ -531,11 +530,11 @@
}
void recordAlarmForPackage(String packageName, int userId, long nowElapsed) {
- final Pair<String, Integer> packageUser = Pair.create(packageName, userId);
- LongArrayQueue history = mPackageHistory.get(packageUser);
+ final UserPackage userPackage = UserPackage.of(userId, packageName);
+ LongArrayQueue history = mPackageHistory.get(userPackage);
if (history == null) {
history = new LongArrayQueue();
- mPackageHistory.put(packageUser, history);
+ mPackageHistory.put(userPackage, history);
}
if (history.size() == 0 || history.peekLast() < nowElapsed) {
history.addLast(nowElapsed);
@@ -545,16 +544,16 @@
void removeForUser(int userId) {
for (int i = mPackageHistory.size() - 1; i >= 0; i--) {
- final Pair<String, Integer> packageUserKey = mPackageHistory.keyAt(i);
- if (packageUserKey.second == userId) {
+ final UserPackage userPackageKey = mPackageHistory.keyAt(i);
+ if (userPackageKey.userId == userId) {
mPackageHistory.removeAt(i);
}
}
}
void removeForPackage(String packageName, int userId) {
- final Pair<String, Integer> packageUser = Pair.create(packageName, userId);
- mPackageHistory.remove(packageUser);
+ final UserPackage userPackage = UserPackage.of(userId, packageName);
+ mPackageHistory.remove(userPackage);
}
private void snapToWindow(LongArrayQueue history) {
@@ -564,7 +563,7 @@
}
int getTotalWakeupsInWindow(String packageName, int userId) {
- final LongArrayQueue history = mPackageHistory.get(Pair.create(packageName, userId));
+ final LongArrayQueue history = mPackageHistory.get(UserPackage.of(userId, packageName));
return (history == null) ? 0 : history.size();
}
@@ -573,7 +572,7 @@
* (1=1st-last=the ultimate wakeup and 2=2nd-last=the penultimate wakeup)
*/
long getNthLastWakeupForPackage(String packageName, int userId, int n) {
- final LongArrayQueue history = mPackageHistory.get(Pair.create(packageName, userId));
+ final LongArrayQueue history = mPackageHistory.get(UserPackage.of(userId, packageName));
if (history == null) {
return 0;
}
@@ -584,11 +583,11 @@
void dump(IndentingPrintWriter pw, long nowElapsed) {
pw.increaseIndent();
for (int i = 0; i < mPackageHistory.size(); i++) {
- final Pair<String, Integer> packageUser = mPackageHistory.keyAt(i);
+ final UserPackage userPackage = mPackageHistory.keyAt(i);
final LongArrayQueue timestamps = mPackageHistory.valueAt(i);
- pw.print(packageUser.first);
+ pw.print(userPackage.packageName);
pw.print(", u");
- pw.print(packageUser.second);
+ pw.print(userPackage.userId);
pw.print(": ");
// limit dumping to a max of 100 values
final int lastIdx = Math.max(0, timestamps.size() - 100);
@@ -1501,13 +1500,13 @@
* null indicates all
* @return True if there was any reordering done to the current list.
*/
- boolean reorderAlarmsBasedOnStandbyBuckets(ArraySet<Pair<String, Integer>> targetPackages) {
+ boolean reorderAlarmsBasedOnStandbyBuckets(ArraySet<UserPackage> targetPackages) {
final long start = mStatLogger.getTime();
final boolean changed = mAlarmStore.updateAlarmDeliveries(a -> {
- final Pair<String, Integer> packageUser =
- Pair.create(a.sourcePackage, UserHandle.getUserId(a.creatorUid));
- if (targetPackages != null && !targetPackages.contains(packageUser)) {
+ final UserPackage userPackage =
+ UserPackage.of(UserHandle.getUserId(a.creatorUid), a.sourcePackage);
+ if (targetPackages != null && !targetPackages.contains(userPackage)) {
return false;
}
return adjustDeliveryTimeBasedOnBucketLocked(a);
@@ -1524,13 +1523,13 @@
* null indicates all
* @return True if there was any reordering done to the current list.
*/
- boolean reorderAlarmsBasedOnTare(ArraySet<Pair<String, Integer>> targetPackages) {
+ boolean reorderAlarmsBasedOnTare(ArraySet<UserPackage> targetPackages) {
final long start = mStatLogger.getTime();
final boolean changed = mAlarmStore.updateAlarmDeliveries(a -> {
- final Pair<String, Integer> packageUser =
- Pair.create(a.sourcePackage, UserHandle.getUserId(a.creatorUid));
- if (targetPackages != null && !targetPackages.contains(packageUser)) {
+ final UserPackage userPackage =
+ UserPackage.of(UserHandle.getUserId(a.creatorUid), a.sourcePackage);
+ if (targetPackages != null && !targetPackages.contains(userPackage)) {
return false;
}
return adjustDeliveryTimeBasedOnTareLocked(a);
@@ -4786,8 +4785,7 @@
}
}
}
- final ArraySet<Pair<String, Integer>> triggerPackages =
- new ArraySet<>();
+ final ArraySet<UserPackage> triggerPackages = new ArraySet<>();
final IntArray wakeupUids = new IntArray();
for (int i = 0; i < triggerList.size(); i++) {
final Alarm a = triggerList.get(i);
@@ -4796,13 +4794,13 @@
}
if (mConstants.USE_TARE_POLICY) {
if (!isExemptFromTare(a)) {
- triggerPackages.add(Pair.create(
- a.sourcePackage,
- UserHandle.getUserId(a.creatorUid)));
+ triggerPackages.add(UserPackage.of(
+ UserHandle.getUserId(a.creatorUid),
+ a.sourcePackage));
}
} else if (!isExemptFromAppStandby(a)) {
- triggerPackages.add(Pair.create(
- a.sourcePackage, UserHandle.getUserId(a.creatorUid)));
+ triggerPackages.add(UserPackage.of(
+ UserHandle.getUserId(a.creatorUid), a.sourcePackage));
}
}
if (wakeupUids.size() > 0 && mBatteryStatsInternal != null) {
@@ -4990,8 +4988,8 @@
case TEMPORARY_QUOTA_CHANGED:
case APP_STANDBY_BUCKET_CHANGED:
synchronized (mLock) {
- final ArraySet<Pair<String, Integer>> filterPackages = new ArraySet<>();
- filterPackages.add(Pair.create((String) msg.obj, msg.arg1));
+ final ArraySet<UserPackage> filterPackages = new ArraySet<>();
+ filterPackages.add(UserPackage.of(msg.arg1, (String) msg.obj));
if (reorderAlarmsBasedOnStandbyBuckets(filterPackages)) {
rescheduleKernelAlarmsLocked();
updateNextAlarmClockLocked();
@@ -5004,8 +5002,8 @@
final int userId = msg.arg1;
final String packageName = (String) msg.obj;
- final ArraySet<Pair<String, Integer>> filterPackages = new ArraySet<>();
- filterPackages.add(Pair.create(packageName, userId));
+ final ArraySet<UserPackage> filterPackages = new ArraySet<>();
+ filterPackages.add(UserPackage.of(userId, packageName));
if (reorderAlarmsBasedOnTare(filterPackages)) {
rescheduleKernelAlarmsLocked();
updateNextAlarmClockLocked();
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 52dc01b..53f5c6e 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -16,6 +16,8 @@
package com.android.server.job;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+
import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
@@ -99,6 +101,18 @@
static final String KEY_PKG_CONCURRENCY_LIMIT_REGULAR =
CONFIG_KEY_PREFIX_CONCURRENCY + "pkg_concurrency_limit_regular";
private static final int DEFAULT_PKG_CONCURRENCY_LIMIT_REGULAR = STANDARD_CONCURRENCY_LIMIT / 2;
+ @VisibleForTesting
+ static final String KEY_ENABLE_MAX_WAIT_TIME_BYPASS =
+ CONFIG_KEY_PREFIX_CONCURRENCY + "enable_max_wait_time_bypass";
+ private static final boolean DEFAULT_ENABLE_MAX_WAIT_TIME_BYPASS = true;
+ private static final String KEY_MAX_WAIT_EJ_MS =
+ CONFIG_KEY_PREFIX_CONCURRENCY + "max_wait_ej_ms";
+ @VisibleForTesting
+ static final long DEFAULT_MAX_WAIT_EJ_MS = 5 * MINUTE_IN_MILLIS;
+ private static final String KEY_MAX_WAIT_REGULAR_MS =
+ CONFIG_KEY_PREFIX_CONCURRENCY + "max_wait_regular_ms";
+ @VisibleForTesting
+ static final long DEFAULT_MAX_WAIT_REGULAR_MS = 30 * MINUTE_IN_MILLIS;
/**
* Set of possible execution types that a job can have. The actual type(s) of a job are based
@@ -353,6 +367,20 @@
*/
private int mPkgConcurrencyLimitRegular = DEFAULT_PKG_CONCURRENCY_LIMIT_REGULAR;
+ private boolean mMaxWaitTimeBypassEnabled = DEFAULT_ENABLE_MAX_WAIT_TIME_BYPASS;
+
+ /**
+ * The maximum time an expedited job would have to be potentially waiting for an available
+ * slot before we would consider creating a new slot for it.
+ */
+ private long mMaxWaitEjMs = DEFAULT_MAX_WAIT_EJ_MS;
+
+ /**
+ * The maximum time a regular job would have to be potentially waiting for an available
+ * slot before we would consider creating a new slot for it.
+ */
+ private long mMaxWaitRegularMs = DEFAULT_MAX_WAIT_REGULAR_MS;
+
/** Current memory trim level. */
private int mLastMemoryTrimLevel;
@@ -665,7 +693,7 @@
return;
}
- prepareForAssignmentDeterminationLocked(
+ final long minPreferredUidOnlyWaitingTimeMs = prepareForAssignmentDeterminationLocked(
mRecycledIdle, mRecycledPreferredUidOnly, mRecycledStoppable);
if (DEBUG) {
@@ -674,7 +702,8 @@
}
determineAssignmentsLocked(
- mRecycledChanged, mRecycledIdle, mRecycledPreferredUidOnly, mRecycledStoppable);
+ mRecycledChanged, mRecycledIdle, mRecycledPreferredUidOnly, mRecycledStoppable,
+ minPreferredUidOnlyWaitingTimeMs);
if (DEBUG) {
Slog.d(TAG, printAssignments("running jobs final",
@@ -691,9 +720,10 @@
noteConcurrency();
}
+ /** @return the minimum remaining execution time for preferred UID only JobServiceContexts. */
@VisibleForTesting
@GuardedBy("mLock")
- void prepareForAssignmentDeterminationLocked(final ArraySet<ContextAssignment> idle,
+ long prepareForAssignmentDeterminationLocked(final ArraySet<ContextAssignment> idle,
final List<ContextAssignment> preferredUidOnly,
final List<ContextAssignment> stoppable) {
final PendingJobQueue pendingJobQueue = mService.getPendingJobQueue();
@@ -709,6 +739,8 @@
updateNonRunningPrioritiesLocked(pendingJobQueue, true);
final int numRunningJobs = activeServices.size();
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ long minPreferredUidOnlyWaitingTimeMs = Long.MAX_VALUE;
for (int i = 0; i < numRunningJobs; ++i) {
final JobServiceContext jsc = activeServices.get(i);
final JobStatus js = jsc.getRunningJobLocked();
@@ -729,6 +761,9 @@
if ((assignment.shouldStopJobReason = shouldStopRunningJobLocked(jsc)) != null) {
stoppable.add(assignment);
} else {
+ assignment.timeUntilStoppableMs = jsc.getRemainingGuaranteedTimeMs(nowElapsed);
+ minPreferredUidOnlyWaitingTimeMs =
+ Math.min(minPreferredUidOnlyWaitingTimeMs, assignment.timeUntilStoppableMs);
preferredUidOnly.add(assignment);
}
}
@@ -754,6 +789,10 @@
}
mWorkCountTracker.onCountDone();
+ // Return 0 if there were no preferred UID only contexts to indicate no waiting time due
+ // to such jobs.
+ return minPreferredUidOnlyWaitingTimeMs == Long.MAX_VALUE
+ ? 0 : minPreferredUidOnlyWaitingTimeMs;
}
@VisibleForTesting
@@ -761,12 +800,14 @@
void determineAssignmentsLocked(final ArraySet<ContextAssignment> changed,
final ArraySet<ContextAssignment> idle,
final List<ContextAssignment> preferredUidOnly,
- final List<ContextAssignment> stoppable) {
+ final List<ContextAssignment> stoppable,
+ long minPreferredUidOnlyWaitingTimeMs) {
final PendingJobQueue pendingJobQueue = mService.getPendingJobQueue();
final List<JobServiceContext> activeServices = mActiveServices;
pendingJobQueue.resetIterator();
JobStatus nextPending;
int projectedRunningCount = activeServices.size();
+ long minChangedWaitingTimeMs = Long.MAX_VALUE;
while ((nextPending = pendingJobQueue.next()) != null) {
if (mRunningJobs.contains(nextPending)) {
// Should never happen.
@@ -785,6 +826,14 @@
+ " to: " + nextPending);
}
+ // Factoring minChangedWaitingTimeMs into the min waiting time effectively limits
+ // the number of additional contexts that are created due to long waiting times.
+ // By factoring it in, we imply that the new slot will be available for other
+ // pending jobs that could be designated as waiting too long, and those other jobs
+ // would only have to wait for the new slots to become available.
+ final long minWaitingTimeMs =
+ Math.min(minPreferredUidOnlyWaitingTimeMs, minChangedWaitingTimeMs);
+
// Find an available slot for nextPending. The context should be one of the following:
// 1. Unused
// 2. Its job should have used up its minimum execution guarantee so it
@@ -833,6 +882,7 @@
// app was on TOP, the app is still TOP, but there are too many TOP+EJs
// running (because we don't want them to starve out other apps and the
// current job has already run for the minimum guaranteed time).
+ // 5. This new job could be waiting for too long for a slot to open up
boolean canReplace = isTopEj; // Case 1
if (!canReplace && !isInOverage) {
final int currentJobBias = mService.evaluateJobBiasLocked(runningJob);
@@ -840,6 +890,13 @@
|| currentJobBias < JobInfo.BIAS_TOP_APP // Case 3
|| topEjCount > .5 * mWorkTypeConfig.getMaxTotal(); // Case 4
}
+ if (!canReplace && mMaxWaitTimeBypassEnabled) { // Case 5
+ if (nextPending.shouldTreatAsExpeditedJob()) {
+ canReplace = minWaitingTimeMs >= mMaxWaitEjMs;
+ } else {
+ canReplace = minWaitingTimeMs >= mMaxWaitRegularMs;
+ }
+ }
if (canReplace) {
int replaceWorkType = mWorkCountTracker.canJobStart(allWorkTypes,
assignment.context.getRunningJobWorkType());
@@ -860,6 +917,7 @@
}
if (selectedContext == null && (!isInOverage || isTopEj)) {
int lowestBiasSeen = Integer.MAX_VALUE;
+ long newMinPreferredUidOnlyWaitingTimeMs = Long.MAX_VALUE;
for (int p = preferredUidOnly.size() - 1; p >= 0; --p) {
final ContextAssignment assignment = preferredUidOnly.get(p);
final JobStatus runningJob = assignment.context.getRunningJobLocked();
@@ -872,6 +930,13 @@
}
if (selectedContext == null || lowestBiasSeen > jobBias) {
+ if (selectedContext != null) {
+ // We're no longer using the previous context, so factor it into the
+ // calculation.
+ newMinPreferredUidOnlyWaitingTimeMs = Math.min(
+ newMinPreferredUidOnlyWaitingTimeMs,
+ selectedContext.timeUntilStoppableMs);
+ }
// Step down the preemption threshold - wind up replacing
// the lowest-bias running job
lowestBiasSeen = jobBias;
@@ -880,11 +945,17 @@
assignment.preemptReasonCode = JobParameters.STOP_REASON_PREEMPT;
// In this case, we're just going to preempt a low bias job, we're not
// actually starting a job, so don't set startingJob to true.
+ } else {
+ // We're not going to use this context, so factor it into the calculation.
+ newMinPreferredUidOnlyWaitingTimeMs = Math.min(
+ newMinPreferredUidOnlyWaitingTimeMs,
+ assignment.timeUntilStoppableMs);
}
}
if (selectedContext != null) {
selectedContext.newJob = nextPending;
preferredUidOnly.remove(selectedContext);
+ minPreferredUidOnlyWaitingTimeMs = newMinPreferredUidOnlyWaitingTimeMs;
}
}
// Make sure to run EJs for the TOP app immediately.
@@ -901,6 +972,9 @@
selectedContext = null;
}
if (selectedContext == null) {
+ if (DEBUG) {
+ Slog.d(TAG, "Allowing additional context because EJ would wait too long");
+ }
selectedContext = mContextAssignmentPool.acquire();
if (selectedContext == null) {
selectedContext = new ContextAssignment();
@@ -913,6 +987,35 @@
selectedContext.newWorkType =
(workType != WORK_TYPE_NONE) ? workType : WORK_TYPE_TOP;
}
+ } else if (selectedContext == null && mMaxWaitTimeBypassEnabled) {
+ final boolean wouldBeWaitingTooLong = nextPending.shouldTreatAsExpeditedJob()
+ ? minWaitingTimeMs >= mMaxWaitEjMs
+ : minWaitingTimeMs >= mMaxWaitRegularMs;
+ if (wouldBeWaitingTooLong) {
+ if (DEBUG) {
+ Slog.d(TAG, "Allowing additional context because job would wait too long");
+ }
+ selectedContext = mContextAssignmentPool.acquire();
+ if (selectedContext == null) {
+ selectedContext = new ContextAssignment();
+ }
+ selectedContext.context = mIdleContexts.size() > 0
+ ? mIdleContexts.removeAt(mIdleContexts.size() - 1)
+ : createNewJobServiceContext();
+ selectedContext.newJob = nextPending;
+ final int workType = mWorkCountTracker.canJobStart(allWorkTypes);
+ if (workType != WORK_TYPE_NONE) {
+ selectedContext.newWorkType = workType;
+ } else {
+ // Use the strongest work type possible for this job.
+ for (int type = 1; type <= ALL_WORK_TYPES; type = type << 1) {
+ if ((type & allWorkTypes) != 0) {
+ selectedContext.newWorkType = type;
+ break;
+ }
+ }
+ }
+ }
}
final PackageStats packageStats = getPkgStatsLocked(
nextPending.getSourceUserId(), nextPending.getSourcePackageName());
@@ -923,6 +1026,8 @@
}
if (selectedContext.newJob != null) {
projectedRunningCount++;
+ minChangedWaitingTimeMs = Math.min(minChangedWaitingTimeMs,
+ mService.getMinJobExecutionGuaranteeMs(selectedContext.newJob));
}
packageStats.adjustStagedCount(true, nextPending.shouldTreatAsExpeditedJob());
}
@@ -1251,13 +1356,37 @@
}
final PendingJobQueue pendingJobQueue = mService.getPendingJobQueue();
- if (mActiveServices.size() >= STANDARD_CONCURRENCY_LIMIT || pendingJobQueue.size() == 0) {
+ if (pendingJobQueue.size() == 0) {
worker.clearPreferredUid();
- // We're over the limit (because the TOP app scheduled a lot of EJs). Don't start
- // running anything new until we get back below the limit.
noteConcurrency();
return;
}
+ if (mActiveServices.size() >= STANDARD_CONCURRENCY_LIMIT) {
+ final boolean respectConcurrencyLimit;
+ if (!mMaxWaitTimeBypassEnabled) {
+ respectConcurrencyLimit = true;
+ } else {
+ long minWaitingTimeMs = Long.MAX_VALUE;
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ for (int i = mActiveServices.size() - 1; i >= 0; --i) {
+ minWaitingTimeMs = Math.min(minWaitingTimeMs,
+ mActiveServices.get(i).getRemainingGuaranteedTimeMs(nowElapsed));
+ }
+ final boolean wouldBeWaitingTooLong =
+ mWorkCountTracker.getPendingJobCount(WORK_TYPE_EJ) > 0
+ ? minWaitingTimeMs >= mMaxWaitEjMs
+ : minWaitingTimeMs >= mMaxWaitRegularMs;
+ respectConcurrencyLimit = !wouldBeWaitingTooLong;
+ }
+ if (respectConcurrencyLimit) {
+ worker.clearPreferredUid();
+ // We're over the limit (because the TOP app scheduled a lot of EJs), but we should
+ // be able to stop the other jobs soon so don't start running anything new until we
+ // get back below the limit.
+ noteConcurrency();
+ return;
+ }
+ }
if (worker.getPreferredUid() != JobServiceContext.NO_PREFERRED_UID) {
updateCounterConfigLocked();
@@ -1609,6 +1738,14 @@
mPkgConcurrencyLimitRegular = Math.max(1, Math.min(STANDARD_CONCURRENCY_LIMIT,
properties.getInt(
KEY_PKG_CONCURRENCY_LIMIT_REGULAR, DEFAULT_PKG_CONCURRENCY_LIMIT_REGULAR)));
+
+ mMaxWaitTimeBypassEnabled = properties.getBoolean(
+ KEY_ENABLE_MAX_WAIT_TIME_BYPASS, DEFAULT_ENABLE_MAX_WAIT_TIME_BYPASS);
+ // EJ max wait must be in the range [0, infinity).
+ mMaxWaitEjMs = Math.max(0, properties.getLong(KEY_MAX_WAIT_EJ_MS, DEFAULT_MAX_WAIT_EJ_MS));
+ // Regular max wait must be in the range [EJ max wait, infinity).
+ mMaxWaitRegularMs = Math.max(mMaxWaitEjMs,
+ properties.getLong(KEY_MAX_WAIT_REGULAR_MS, DEFAULT_MAX_WAIT_REGULAR_MS));
}
@GuardedBy("mLock")
@@ -1622,6 +1759,9 @@
pw.print(KEY_SCREEN_OFF_ADJUSTMENT_DELAY_MS, mScreenOffAdjustmentDelayMs).println();
pw.print(KEY_PKG_CONCURRENCY_LIMIT_EJ, mPkgConcurrencyLimitEj).println();
pw.print(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, mPkgConcurrencyLimitRegular).println();
+ pw.print(KEY_ENABLE_MAX_WAIT_TIME_BYPASS, mMaxWaitTimeBypassEnabled).println();
+ pw.print(KEY_MAX_WAIT_EJ_MS, mMaxWaitEjMs).println();
+ pw.print(KEY_MAX_WAIT_REGULAR_MS, mMaxWaitRegularMs).println();
pw.println();
CONFIG_LIMITS_SCREEN_ON.normal.dump(pw);
pw.println();
@@ -2382,6 +2522,7 @@
public int workType = WORK_TYPE_NONE;
public String preemptReason;
public int preemptReasonCode = JobParameters.STOP_REASON_UNDEFINED;
+ public long timeUntilStoppableMs;
public String shouldStopJobReason;
public JobStatus newJob;
public int newWorkType = WORK_TYPE_NONE;
@@ -2392,6 +2533,7 @@
workType = WORK_TYPE_NONE;
preemptReason = null;
preemptReasonCode = JobParameters.STOP_REASON_UNDEFINED;
+ timeUntilStoppableMs = 0;
shouldStopJobReason = null;
newJob = null;
newWorkType = WORK_TYPE_NONE;
@@ -2406,6 +2548,15 @@
final PackageStats packageStats =
getPackageStatsForTesting(job.getSourceUserId(), job.getSourcePackageName());
packageStats.adjustRunningCount(true, job.shouldTreatAsExpeditedJob());
+
+ final JobServiceContext context;
+ if (mIdleContexts.size() > 0) {
+ context = mIdleContexts.removeAt(mIdleContexts.size() - 1);
+ } else {
+ context = createNewJobServiceContext();
+ }
+ context.executeRunnableJob(job, mWorkCountTracker.canJobStart(getJobWorkTypes(job)));
+ mActiveServices.add(context);
}
@VisibleForTesting
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 d9fb318..d9fe30d 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -177,7 +177,7 @@
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
private static final long REQUIRE_NETWORK_CONSTRAINT_FOR_NETWORK_JOB_WORK_ITEMS = 241104082L;
- @VisibleForTesting
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public static Clock sSystemClock = Clock.systemUTC();
private abstract static class MySimpleClock extends Clock {
@@ -454,6 +454,10 @@
runtimeUpdated = true;
}
break;
+ case Constants.KEY_PERSIST_IN_SPLIT_FILES:
+ mConstants.updatePersistingConstantsLocked();
+ mJobs.setUseSplitFiles(mConstants.PERSIST_IN_SPLIT_FILES);
+ break;
default:
if (name.startsWith(JobConcurrencyManager.CONFIG_KEY_PREFIX_CONCURRENCY)
&& !concurrencyUpdated) {
@@ -537,6 +541,8 @@
private static final String KEY_RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS =
"runtime_min_high_priority_guarantee_ms";
+ private static final String KEY_PERSIST_IN_SPLIT_FILES = "persist_in_split_files";
+
private static final int DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT = 5;
private static final long DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = 31 * MINUTE_IN_MILLIS;
private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
@@ -563,6 +569,7 @@
public static final long DEFAULT_RUNTIME_MIN_EJ_GUARANTEE_MS = 3 * MINUTE_IN_MILLIS;
@VisibleForTesting
static final long DEFAULT_RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS = 5 * MINUTE_IN_MILLIS;
+ static final boolean DEFAULT_PERSIST_IN_SPLIT_FILES = false;
private static final boolean DEFAULT_USE_TARE_POLICY = false;
/**
@@ -678,6 +685,12 @@
DEFAULT_RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS;
/**
+ * Whether to persist jobs in split files (by UID). If false, all persisted jobs will be
+ * saved in a single file.
+ */
+ public boolean PERSIST_IN_SPLIT_FILES = DEFAULT_PERSIST_IN_SPLIT_FILES;
+
+ /**
* If true, use TARE policy for job limiting. If false, use quotas.
*/
public boolean USE_TARE_POLICY = DEFAULT_USE_TARE_POLICY;
@@ -735,6 +748,11 @@
DEFAULT_CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC);
}
+ private void updatePersistingConstantsLocked() {
+ PERSIST_IN_SPLIT_FILES = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+ KEY_PERSIST_IN_SPLIT_FILES, DEFAULT_PERSIST_IN_SPLIT_FILES);
+ }
+
private void updatePrefetchConstantsLocked() {
PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS = DeviceConfig.getLong(
DeviceConfig.NAMESPACE_JOB_SCHEDULER,
@@ -835,6 +853,8 @@
pw.print(KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, RUNTIME_FREE_QUOTA_MAX_LIMIT_MS)
.println();
+ pw.print(KEY_PERSIST_IN_SPLIT_FILES, PERSIST_IN_SPLIT_FILES).println();
+
pw.print(Settings.Global.ENABLE_TARE, USE_TARE_POLICY).println();
pw.decreaseIndent();
@@ -1290,7 +1310,16 @@
jobStatus.getJob().isPrefetch(),
jobStatus.getJob().getPriority(),
jobStatus.getEffectivePriority(),
- jobStatus.getNumPreviousAttempts());
+ jobStatus.getNumPreviousAttempts(),
+ jobStatus.getJob().getMaxExecutionDelayMillis(),
+ /* isDeadlineConstraintSatisfied */ false,
+ /* isCharging */ false,
+ /* batteryNotLow */ false,
+ /* storageNotLow */false,
+ /* timingDelayConstraintSatisfied */ false,
+ /* isDeviceIdle */ false,
+ /* hasConnectivityConstraintSatisfied */ false,
+ /* hasContentTriggerConstraintSatisfied */ false);
// If the job is immediately ready to run, then we can just immediately
// put it in the pending list and try to schedule it. This is especially
@@ -1489,7 +1518,16 @@
cancelled.getJob().isPrefetch(),
cancelled.getJob().getPriority(),
cancelled.getEffectivePriority(),
- cancelled.getNumPreviousAttempts());
+ cancelled.getNumPreviousAttempts(),
+ cancelled.getJob().getMaxExecutionDelayMillis(),
+ cancelled.isConstraintSatisfied(JobStatus.CONSTRAINT_DEADLINE),
+ cancelled.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_CHARGING),
+ cancelled.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW),
+ cancelled.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_STORAGE_NOT_LOW),
+ cancelled.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY),
+ cancelled.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE),
+ cancelled.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY),
+ cancelled.isConstraintSatisfied(JobStatus.CONSTRAINT_CONTENT_TRIGGER));
}
// If this is a replacement, bring in the new version of the job
if (incomingJob != null) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 9e3f19d..9aa6b1c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -21,6 +21,7 @@
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+import android.annotation.BytesLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.job.IJobCallback;
@@ -187,6 +188,18 @@
public long mStoppedTime;
@Override
+ public void acknowledgeGetTransferredDownloadBytesMessage(int jobId, int workId,
+ @BytesLong long transferredBytes) {
+ doAcknowledgeGetTransferredDownloadBytesMessage(this, jobId, workId, transferredBytes);
+ }
+
+ @Override
+ public void acknowledgeGetTransferredUploadBytesMessage(int jobId, int workId,
+ @BytesLong long transferredBytes) {
+ doAcknowledgeGetTransferredUploadBytesMessage(this, jobId, workId, transferredBytes);
+ }
+
+ @Override
public void acknowledgeStartMessage(int jobId, boolean ongoing) {
doAcknowledgeStartMessage(this, jobId, ongoing);
}
@@ -210,6 +223,18 @@
public void jobFinished(int jobId, boolean reschedule) {
doJobFinished(this, jobId, reschedule);
}
+
+ @Override
+ public void updateEstimatedNetworkBytes(int jobId, JobWorkItem item,
+ long downloadBytes, long uploadBytes) {
+ doUpdateEstimatedNetworkBytes(this, jobId, item, downloadBytes, uploadBytes);
+ }
+
+ @Override
+ public void updateTransferredNetworkBytes(int jobId, JobWorkItem item,
+ long downloadBytes, long uploadBytes) {
+ doUpdateTransferredNetworkBytes(this, jobId, item, downloadBytes, uploadBytes);
+ }
}
JobServiceContext(JobSchedulerService service, JobConcurrencyManager concurrencyManager,
@@ -363,7 +388,16 @@
job.getJob().isPrefetch(),
job.getJob().getPriority(),
job.getEffectivePriority(),
- job.getNumPreviousAttempts());
+ job.getNumPreviousAttempts(),
+ job.getJob().getMaxExecutionDelayMillis(),
+ isDeadlineExpired,
+ job.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_CHARGING),
+ job.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW),
+ job.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_STORAGE_NOT_LOW),
+ job.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY),
+ job.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE),
+ job.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY),
+ job.isConstraintSatisfied(JobStatus.CONSTRAINT_CONTENT_TRIGGER));
if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
// Use the context's ID to distinguish traces since there'll only be one job
// running per context.
@@ -454,6 +488,10 @@
return mTimeoutElapsed;
}
+ long getRemainingGuaranteedTimeMs(long nowElapsed) {
+ return Math.max(0, mExecutionStartTimeElapsed + mMinExecutionGuaranteeMillis - nowElapsed);
+ }
+
boolean isWithinExecutionGuaranteeTime() {
return sElapsedRealtimeClock.millis()
< mExecutionStartTimeElapsed + mMinExecutionGuaranteeMillis;
@@ -493,6 +531,16 @@
}
}
+ private void doAcknowledgeGetTransferredDownloadBytesMessage(JobCallback jobCallback, int jobId,
+ int workId, @BytesLong long transferredBytes) {
+ // TODO(255393346): Make sure apps call this appropriately and monitor for abuse
+ }
+
+ private void doAcknowledgeGetTransferredUploadBytesMessage(JobCallback jobCallback, int jobId,
+ int workId, @BytesLong long transferredBytes) {
+ // TODO(255393346): Make sure apps call this appropriately and monitor for abuse
+ }
+
void doAcknowledgeStopMessage(JobCallback cb, int jobId, boolean reschedule) {
doCallback(cb, reschedule, null);
}
@@ -545,6 +593,16 @@
}
}
+ private void doUpdateTransferredNetworkBytes(JobCallback jobCallback, int jobId,
+ @Nullable JobWorkItem item, long downloadBytes, long uploadBytes) {
+ // TODO(255393346): Make sure apps call this appropriately and monitor for abuse
+ }
+
+ private void doUpdateEstimatedNetworkBytes(JobCallback jobCallback, int jobId,
+ @Nullable JobWorkItem item, long downloadBytes, long uploadBytes) {
+ // TODO(255393346): Make sure apps call this appropriately and monitor for abuse
+ }
+
/**
* We acquire/release a wakelock on onServiceConnected/unbindService. This mirrors the work
* we intend to send to the client - we stop sending work when the service is unbound so until
@@ -1032,7 +1090,16 @@
completedJob.getJob().isPrefetch(),
completedJob.getJob().getPriority(),
completedJob.getEffectivePriority(),
- completedJob.getNumPreviousAttempts());
+ completedJob.getNumPreviousAttempts(),
+ completedJob.getJob().getMaxExecutionDelayMillis(),
+ mParams.isOverrideDeadlineExpired(),
+ completedJob.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_CHARGING),
+ completedJob.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW),
+ completedJob.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_STORAGE_NOT_LOW),
+ completedJob.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY),
+ completedJob.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE),
+ completedJob.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY),
+ completedJob.isConstraintSatisfied(JobStatus.CONSTRAINT_CONTENT_TRIGGER));
if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler",
completedJob.getTag(), getId());
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 68cb049..c2602f2 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
@@ -40,6 +40,7 @@
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.util.SystemConfigFileCommitEventLogger;
import android.util.Xml;
@@ -47,6 +48,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.BitUtils;
+import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.IoThread;
import com.android.server.job.JobSchedulerInternal.JobStorePersistStats;
@@ -89,6 +91,8 @@
/** Threshold to adjust how often we want to write to the db. */
private static final long JOB_PERSIST_DELAY = 2000L;
+ private static final String JOB_FILE_SPLIT_PREFIX = "jobs_";
+ private static final int ALL_UIDS = -1;
final Object mLock;
final Object mWriteScheduleLock; // used solely for invariants around write scheduling
@@ -105,13 +109,20 @@
@GuardedBy("mWriteScheduleLock")
private boolean mWriteInProgress;
+ @GuardedBy("mWriteScheduleLock")
+ private boolean mSplitFileMigrationNeeded;
+
private static final Object sSingletonLock = new Object();
private final SystemConfigFileCommitEventLogger mEventLogger;
private final AtomicFile mJobsFile;
+ private final File mJobFileDirectory;
+ private final SparseBooleanArray mPendingJobWriteUids = new SparseBooleanArray();
/** Handler backed by IoThread for writing to disk. */
private final Handler mIoHandler = IoThread.getHandler();
private static JobStore sSingleton;
+ private boolean mUseSplitFiles = JobSchedulerService.Constants.DEFAULT_PERSIST_IN_SPLIT_FILES;
+
private JobStorePersistStats mPersistInfo = new JobStorePersistStats();
/** Used by the {@link JobSchedulerService} to instantiate the JobStore. */
@@ -144,10 +155,10 @@
mContext = context;
File systemDir = new File(dataDir, "system");
- File jobDir = new File(systemDir, "job");
- jobDir.mkdirs();
+ mJobFileDirectory = new File(systemDir, "job");
+ mJobFileDirectory.mkdirs();
mEventLogger = new SystemConfigFileCommitEventLogger("jobs");
- mJobsFile = new AtomicFile(new File(jobDir, "jobs.xml"), mEventLogger);
+ mJobsFile = createJobFile(new File(mJobFileDirectory, "jobs.xml"));
mJobSet = new JobSet();
@@ -162,12 +173,21 @@
// an incorrect historical timestamp. That's fine; at worst we'll reboot with
// a *correct* timestamp, see a bunch of overdue jobs, and run them; then
// settle into normal operation.
- mXmlTimestamp = mJobsFile.getLastModifiedTime();
+ mXmlTimestamp = mJobsFile.exists()
+ ? mJobsFile.getLastModifiedTime() : mJobFileDirectory.lastModified();
mRtcGood = (sSystemClock.millis() > mXmlTimestamp);
readJobMapFromDisk(mJobSet, mRtcGood);
}
+ private AtomicFile createJobFile(String baseName) {
+ return createJobFile(new File(mJobFileDirectory, baseName + ".xml"));
+ }
+
+ private AtomicFile createJobFile(File file) {
+ return new AtomicFile(file, mEventLogger);
+ }
+
public boolean jobTimesInflatedValid() {
return mRtcGood;
}
@@ -211,6 +231,7 @@
public void add(JobStatus jobStatus) {
mJobSet.add(jobStatus);
if (jobStatus.isPersisted()) {
+ mPendingJobWriteUids.put(jobStatus.getUid(), true);
maybeWriteStatusToDiskAsync();
}
if (DEBUG) {
@@ -224,6 +245,9 @@
@VisibleForTesting
public void addForTesting(JobStatus jobStatus) {
mJobSet.add(jobStatus);
+ if (jobStatus.isPersisted()) {
+ mPendingJobWriteUids.put(jobStatus.getUid(), true);
+ }
}
boolean containsJob(JobStatus jobStatus) {
@@ -257,12 +281,24 @@
return false;
}
if (removeFromPersisted && jobStatus.isPersisted()) {
+ mPendingJobWriteUids.put(jobStatus.getUid(), true);
maybeWriteStatusToDiskAsync();
}
return removed;
}
/**
+ * Like {@link #remove(JobStatus, boolean)}, but doesn't schedule a disk write.
+ */
+ @VisibleForTesting
+ public void removeForTesting(JobStatus jobStatus) {
+ mJobSet.remove(jobStatus);
+ if (jobStatus.isPersisted()) {
+ mPendingJobWriteUids.put(jobStatus.getUid(), true);
+ }
+ }
+
+ /**
* Remove the jobs of users not specified in the keepUserIds.
* @param keepUserIds Array of User IDs whose jobs should be kept and not removed.
*/
@@ -273,6 +309,7 @@
@VisibleForTesting
public void clear() {
mJobSet.clear();
+ mPendingJobWriteUids.put(ALL_UIDS, true);
maybeWriteStatusToDiskAsync();
}
@@ -282,6 +319,36 @@
@VisibleForTesting
public void clearForTesting() {
mJobSet.clear();
+ mPendingJobWriteUids.put(ALL_UIDS, true);
+ }
+
+ void setUseSplitFiles(boolean useSplitFiles) {
+ synchronized (mLock) {
+ if (mUseSplitFiles != useSplitFiles) {
+ mUseSplitFiles = useSplitFiles;
+ migrateJobFilesAsync();
+ }
+ }
+ }
+
+ /**
+ * The same as above but does not schedule writing. This makes perf benchmarks more stable.
+ */
+ @VisibleForTesting
+ public void setUseSplitFilesForTesting(boolean useSplitFiles) {
+ final boolean changed;
+ synchronized (mLock) {
+ changed = mUseSplitFiles != useSplitFiles;
+ if (changed) {
+ mUseSplitFiles = useSplitFiles;
+ mPendingJobWriteUids.put(ALL_UIDS, true);
+ }
+ }
+ if (changed) {
+ synchronized (mWriteScheduleLock) {
+ mSplitFileMigrationNeeded = true;
+ }
+ }
}
/**
@@ -352,6 +419,16 @@
private static final String XML_TAG_ONEOFF = "one-off";
private static final String XML_TAG_EXTRAS = "extras";
+ private void migrateJobFilesAsync() {
+ synchronized (mLock) {
+ mPendingJobWriteUids.put(ALL_UIDS, true);
+ }
+ synchronized (mWriteScheduleLock) {
+ mSplitFileMigrationNeeded = true;
+ maybeWriteStatusToDiskAsync();
+ }
+ }
+
/**
* Every time the state changes we write all the jobs in one swath, instead of trying to
* track incremental changes.
@@ -449,10 +526,38 @@
* NOTE: This Runnable locks on mLock
*/
private final Runnable mWriteRunnable = new Runnable() {
+ private final SparseArray<AtomicFile> mJobFiles = new SparseArray<>();
+ private final CopyConsumer mPersistedJobCopier = new CopyConsumer();
+
+ class CopyConsumer implements Consumer<JobStatus> {
+ private final SparseArray<List<JobStatus>> mJobStoreCopy = new SparseArray<>();
+ private boolean mCopyAllJobs;
+
+ private void prepare() {
+ mCopyAllJobs = !mUseSplitFiles || mPendingJobWriteUids.get(ALL_UIDS);
+ }
+
+ @Override
+ public void accept(JobStatus jobStatus) {
+ final int uid = mUseSplitFiles ? jobStatus.getUid() : ALL_UIDS;
+ if (jobStatus.isPersisted() && (mCopyAllJobs || mPendingJobWriteUids.get(uid))) {
+ List<JobStatus> uidJobList = mJobStoreCopy.get(uid);
+ if (uidJobList == null) {
+ uidJobList = new ArrayList<>();
+ mJobStoreCopy.put(uid, uidJobList);
+ }
+ uidJobList.add(new JobStatus(jobStatus));
+ }
+ }
+
+ private void reset() {
+ mJobStoreCopy.clear();
+ }
+ }
+
@Override
public void run() {
final long startElapsed = sElapsedRealtimeClock.millis();
- final List<JobStatus> storeCopy = new ArrayList<JobStatus>();
// Intentionally allow new scheduling of a write operation *before* we clone
// the job set. If we reset it to false after cloning, there's a window in
// which no new write will be scheduled but mLock is not held, i.e. a new
@@ -469,31 +574,73 @@
}
mWriteInProgress = true;
}
+ final boolean useSplitFiles;
synchronized (mLock) {
// Clone the jobs so we can release the lock before writing.
- mJobSet.forEachJob(null, (job) -> {
- if (job.isPersisted()) {
- storeCopy.add(new JobStatus(job));
- }
- });
+ useSplitFiles = mUseSplitFiles;
+ mPersistedJobCopier.prepare();
+ mJobSet.forEachJob(null, mPersistedJobCopier);
+ mPendingJobWriteUids.clear();
}
- writeJobsMapImpl(storeCopy);
+ mPersistInfo.countAllJobsSaved = 0;
+ mPersistInfo.countSystemServerJobsSaved = 0;
+ mPersistInfo.countSystemSyncManagerJobsSaved = 0;
+ for (int i = mPersistedJobCopier.mJobStoreCopy.size() - 1; i >= 0; --i) {
+ AtomicFile file;
+ if (useSplitFiles) {
+ final int uid = mPersistedJobCopier.mJobStoreCopy.keyAt(i);
+ file = mJobFiles.get(uid);
+ if (file == null) {
+ file = createJobFile(JOB_FILE_SPLIT_PREFIX + uid);
+ mJobFiles.put(uid, file);
+ }
+ } else {
+ file = mJobsFile;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "Writing for " + mPersistedJobCopier.mJobStoreCopy.keyAt(i)
+ + " to " + file.getBaseFile().getName() + ": "
+ + mPersistedJobCopier.mJobStoreCopy.valueAt(i).size() + " jobs");
+ }
+ writeJobsMapImpl(file, mPersistedJobCopier.mJobStoreCopy.valueAt(i));
+ }
if (DEBUG) {
Slog.v(TAG, "Finished writing, took " + (sElapsedRealtimeClock.millis()
- startElapsed) + "ms");
}
+ mPersistedJobCopier.reset();
+ if (!useSplitFiles) {
+ mJobFiles.clear();
+ }
+ // Update the last modified time of the directory to aid in RTC time verification
+ // (see the JobStore constructor).
+ mJobFileDirectory.setLastModified(sSystemClock.millis());
synchronized (mWriteScheduleLock) {
+ if (mSplitFileMigrationNeeded) {
+ final File[] files = mJobFileDirectory.listFiles();
+ for (File file : files) {
+ if (useSplitFiles) {
+ if (!file.getName().startsWith(JOB_FILE_SPLIT_PREFIX)) {
+ // Delete the now unused file so there's no confusion in the future.
+ file.delete();
+ }
+ } else if (file.getName().startsWith(JOB_FILE_SPLIT_PREFIX)) {
+ // Delete the now unused file so there's no confusion in the future.
+ file.delete();
+ }
+ }
+ }
mWriteInProgress = false;
mWriteScheduleLock.notifyAll();
}
}
- private void writeJobsMapImpl(List<JobStatus> jobList) {
+ private void writeJobsMapImpl(@NonNull AtomicFile file, @NonNull List<JobStatus> jobList) {
int numJobs = 0;
int numSystemJobs = 0;
int numSyncJobs = 0;
mEventLogger.setStartTime(SystemClock.uptimeMillis());
- try (FileOutputStream fos = mJobsFile.startWrite()) {
+ try (FileOutputStream fos = file.startWrite()) {
TypedXmlSerializer out = Xml.resolveSerializer(fos);
out.startDocument(null, true);
out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
@@ -523,7 +670,7 @@
out.endTag(null, "job-info");
out.endDocument();
- mJobsFile.finishWrite(fos);
+ file.finishWrite(fos);
} catch (IOException e) {
if (DEBUG) {
Slog.v(TAG, "Error writing out job data.", e);
@@ -533,9 +680,9 @@
Slog.d(TAG, "Error persisting bundle.", e);
}
} finally {
- mPersistInfo.countAllJobsSaved = numJobs;
- mPersistInfo.countSystemServerJobsSaved = numSystemJobs;
- mPersistInfo.countSystemSyncManagerJobsSaved = numSyncJobs;
+ mPersistInfo.countAllJobsSaved += numJobs;
+ mPersistInfo.countSystemServerJobsSaved += numSystemJobs;
+ mPersistInfo.countSystemSyncManagerJobsSaved += numSyncJobs;
}
}
@@ -600,9 +747,11 @@
* because currently store is not including everything (like, UIDs, bandwidth,
* signal strength etc. are lost).
*/
- private void writeConstraintsToXml(XmlSerializer out, JobStatus jobStatus) throws IOException {
+ private void writeConstraintsToXml(TypedXmlSerializer out, JobStatus jobStatus)
+ throws IOException {
out.startTag(null, XML_TAG_PARAMS_CONSTRAINTS);
if (jobStatus.hasConnectivityConstraint()) {
+ final JobInfo job = jobStatus.getJob();
final NetworkRequest network = jobStatus.getJob().getRequiredNetwork();
out.attribute(null, "net-capabilities-csv", intArrayToString(
network.getCapabilities()));
@@ -610,6 +759,18 @@
network.getForbiddenCapabilities()));
out.attribute(null, "net-transport-types-csv", intArrayToString(
network.getTransportTypes()));
+ if (job.getEstimatedNetworkDownloadBytes() != JobInfo.NETWORK_BYTES_UNKNOWN) {
+ out.attributeLong(null, "estimated-download-bytes",
+ job.getEstimatedNetworkDownloadBytes());
+ }
+ if (job.getEstimatedNetworkUploadBytes() != JobInfo.NETWORK_BYTES_UNKNOWN) {
+ out.attributeLong(null, "estimated-upload-bytes",
+ job.getEstimatedNetworkUploadBytes());
+ }
+ if (job.getMinimumNetworkChunkBytes() != JobInfo.NETWORK_BYTES_UNKNOWN) {
+ out.attributeLong(null, "minimum-network-chunk-bytes",
+ job.getMinimumNetworkChunkBytes());
+ }
}
if (jobStatus.hasIdleConstraint()) {
out.attribute(null, "idle", Boolean.toString(true));
@@ -720,54 +881,87 @@
@Override
public void run() {
+ if (!mJobFileDirectory.isDirectory()) {
+ Slog.wtf(TAG, "jobs directory isn't a directory O.O");
+ mJobFileDirectory.mkdirs();
+ return;
+ }
+
int numJobs = 0;
int numSystemJobs = 0;
int numSyncJobs = 0;
List<JobStatus> jobs;
- try (FileInputStream fis = mJobsFile.openRead()) {
- synchronized (mLock) {
- jobs = readJobMapImpl(fis, rtcGood);
- if (jobs != null) {
- long now = sElapsedRealtimeClock.millis();
- for (int i=0; i<jobs.size(); i++) {
- JobStatus js = jobs.get(i);
- js.prepareLocked();
- js.enqueueTime = now;
- this.jobSet.add(js);
+ final File[] files;
+ try {
+ files = mJobFileDirectory.listFiles();
+ } catch (SecurityException e) {
+ Slog.wtf(TAG, "Not allowed to read job file directory", e);
+ return;
+ }
+ if (files == null) {
+ Slog.wtfStack(TAG, "Couldn't get job file list");
+ return;
+ }
+ boolean needFileMigration = false;
+ long now = sElapsedRealtimeClock.millis();
+ for (File file : files) {
+ final AtomicFile aFile = createJobFile(file);
+ try (FileInputStream fis = aFile.openRead()) {
+ synchronized (mLock) {
+ jobs = readJobMapImpl(fis, rtcGood);
+ if (jobs != null) {
+ for (int i = 0; i < jobs.size(); i++) {
+ JobStatus js = jobs.get(i);
+ js.prepareLocked();
+ js.enqueueTime = now;
+ this.jobSet.add(js);
- numJobs++;
- if (js.getUid() == Process.SYSTEM_UID) {
- numSystemJobs++;
- if (isSyncJob(js)) {
- numSyncJobs++;
+ numJobs++;
+ if (js.getUid() == Process.SYSTEM_UID) {
+ numSystemJobs++;
+ if (isSyncJob(js)) {
+ numSyncJobs++;
+ }
}
}
}
}
+ } catch (FileNotFoundException e) {
+ // mJobFileDirectory.listFiles() gave us this file...why can't we find it???
+ Slog.e(TAG, "Could not find jobs file: " + file.getName());
+ } catch (XmlPullParserException | IOException e) {
+ Slog.wtf(TAG, "Error in " + file.getName(), e);
+ } catch (Exception e) {
+ // Crashing at this point would result in a boot loop, so live with a general
+ // Exception for system stability's sake.
+ Slog.wtf(TAG, "Unexpected exception", e);
}
- } catch (FileNotFoundException e) {
- if (DEBUG) {
- Slog.d(TAG, "Could not find jobs file, probably there was nothing to load.");
- }
- } catch (XmlPullParserException | IOException e) {
- Slog.wtf(TAG, "Error jobstore xml.", e);
- } catch (Exception e) {
- // Crashing at this point would result in a boot loop, so live with a general
- // Exception for system stability's sake.
- Slog.wtf(TAG, "Unexpected exception", e);
- } finally {
- if (mPersistInfo.countAllJobsLoaded < 0) { // Only set them once.
- mPersistInfo.countAllJobsLoaded = numJobs;
- mPersistInfo.countSystemServerJobsLoaded = numSystemJobs;
- mPersistInfo.countSystemSyncManagerJobsLoaded = numSyncJobs;
+ if (mUseSplitFiles) {
+ if (!file.getName().startsWith(JOB_FILE_SPLIT_PREFIX)) {
+ // We're supposed to be using the split file architecture, but we still have
+ // the old job file around. Fully migrate and remove the old file.
+ needFileMigration = true;
+ }
+ } else if (file.getName().startsWith(JOB_FILE_SPLIT_PREFIX)) {
+ // We're supposed to be using the legacy single file architecture, but we still
+ // have some job split files around. Fully migrate and remove the split files.
+ needFileMigration = true;
}
}
+ if (mPersistInfo.countAllJobsLoaded < 0) { // Only set them once.
+ mPersistInfo.countAllJobsLoaded = numJobs;
+ mPersistInfo.countSystemServerJobsLoaded = numSystemJobs;
+ mPersistInfo.countSystemSyncManagerJobsLoaded = numSyncJobs;
+ }
Slog.i(TAG, "Read " + numJobs + " jobs");
+ if (needFileMigration) {
+ migrateJobFilesAsync();
+ }
}
private List<JobStatus> readJobMapImpl(InputStream fis, boolean rtcIsGood)
throws XmlPullParserException, IOException {
- XmlPullParser parser = Xml.resolvePullParser(fis);
+ TypedXmlPullParser parser = Xml.resolvePullParser(fis);
int eventType = parser.getEventType();
while (eventType != XmlPullParser.START_TAG &&
@@ -827,7 +1021,7 @@
* will take the parser into the body of the job tag.
* @return Newly instantiated job holding all the information we just read out of the xml tag.
*/
- private JobStatus restoreJobFromXml(boolean rtcIsGood, XmlPullParser parser,
+ private JobStatus restoreJobFromXml(boolean rtcIsGood, TypedXmlPullParser parser,
int schemaVersion) throws XmlPullParserException, IOException {
JobInfo.Builder jobBuilder;
int uid, sourceUserId;
@@ -1073,7 +1267,7 @@
* 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)
+ private void buildConstraintsFromXml(JobInfo.Builder jobBuilder, TypedXmlPullParser parser)
throws XmlPullParserException, IOException {
String val;
String netCapabilitiesLong = null;
@@ -1110,7 +1304,17 @@
for (int transport : stringToIntArray(netTransportTypesIntArray)) {
builder.addTransportType(transport);
}
- jobBuilder.setRequiredNetwork(builder.build());
+ jobBuilder
+ .setRequiredNetwork(builder.build())
+ .setEstimatedNetworkBytes(
+ parser.getAttributeLong(null,
+ "estimated-download-bytes", JobInfo.NETWORK_BYTES_UNKNOWN),
+ parser.getAttributeLong(null,
+ "estimated-upload-bytes", JobInfo.NETWORK_BYTES_UNKNOWN))
+ .setMinimumNetworkChunkBytes(
+ parser.getAttributeLong(null,
+ "minimum-network-chunk-bytes",
+ JobInfo.NETWORK_BYTES_UNKNOWN));
} else if (netCapabilitiesLong != null && netTransportTypesLong != null) {
// Format used on R- builds. Drop any unexpected capabilities and transports.
final NetworkRequest.Builder builder = new NetworkRequest.Builder()
@@ -1138,6 +1342,8 @@
}
}
jobBuilder.setRequiredNetwork(builder.build());
+ // Estimated bytes weren't persisted on R- builds, so no point querying for the
+ // attributes here.
} else {
// Read legacy values
val = parser.getAttributeValue(null, "connectivity");
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index f9fb0d0..f6410ff 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -95,11 +95,11 @@
static final int CONSTRAINT_IDLE = JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE; // 1 << 2
static final int CONSTRAINT_BATTERY_NOT_LOW = JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW; // 1 << 1
static final int CONSTRAINT_STORAGE_NOT_LOW = JobInfo.CONSTRAINT_FLAG_STORAGE_NOT_LOW; // 1 << 3
- static final int CONSTRAINT_TIMING_DELAY = 1<<31;
- static final int CONSTRAINT_DEADLINE = 1<<30;
- static final int CONSTRAINT_CONNECTIVITY = 1 << 28;
+ public static final int CONSTRAINT_TIMING_DELAY = 1 << 31;
+ public static final int CONSTRAINT_DEADLINE = 1 << 30;
+ public static final int CONSTRAINT_CONNECTIVITY = 1 << 28;
static final int CONSTRAINT_TARE_WEALTH = 1 << 27; // Implicit constraint
- static final int CONSTRAINT_CONTENT_TRIGGER = 1<<26;
+ public static final int CONSTRAINT_CONTENT_TRIGGER = 1 << 26;
static final int CONSTRAINT_DEVICE_NOT_DOZING = 1 << 25; // Implicit constraint
static final int CONSTRAINT_WITHIN_QUOTA = 1 << 24; // Implicit constraint
static final int CONSTRAINT_PREFETCH = 1 << 23;
@@ -1094,27 +1094,41 @@
private void updateNetworkBytesLocked() {
mTotalNetworkDownloadBytes = job.getEstimatedNetworkDownloadBytes();
+ if (mTotalNetworkDownloadBytes < 0) {
+ // Legacy apps may have provided invalid negative values. Ignore invalid values.
+ mTotalNetworkDownloadBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
+ }
mTotalNetworkUploadBytes = job.getEstimatedNetworkUploadBytes();
+ if (mTotalNetworkUploadBytes < 0) {
+ // Legacy apps may have provided invalid negative values. Ignore invalid values.
+ mTotalNetworkUploadBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
+ }
+ // Minimum network chunk bytes has had data validation since its introduction, so no
+ // need to do validation again.
mMinimumNetworkChunkBytes = job.getMinimumNetworkChunkBytes();
if (pendingWork != null) {
for (int i = 0; i < pendingWork.size(); i++) {
- if (mTotalNetworkDownloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
- // If any component of the job has unknown usage, we don't have a
- // complete picture of what data will be used, and we have to treat the
- // entire up/download as unknown.
- long downloadBytes = pendingWork.get(i).getEstimatedNetworkDownloadBytes();
- if (downloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
+ long downloadBytes = pendingWork.get(i).getEstimatedNetworkDownloadBytes();
+ if (downloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN && downloadBytes > 0) {
+ // If any component of the job has unknown usage, we won't have a
+ // complete picture of what data will be used. However, we use what we are given
+ // to get us as close to the complete picture as possible.
+ if (mTotalNetworkDownloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
mTotalNetworkDownloadBytes += downloadBytes;
+ } else {
+ mTotalNetworkDownloadBytes = downloadBytes;
}
}
- if (mTotalNetworkUploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
- // If any component of the job has unknown usage, we don't have a
- // complete picture of what data will be used, and we have to treat the
- // entire up/download as unknown.
- long uploadBytes = pendingWork.get(i).getEstimatedNetworkUploadBytes();
- if (uploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
+ long uploadBytes = pendingWork.get(i).getEstimatedNetworkUploadBytes();
+ if (uploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN && uploadBytes > 0) {
+ // If any component of the job has unknown usage, we won't have a
+ // complete picture of what data will be used. However, we use what we are given
+ // to get us as close to the complete picture as possible.
+ if (mTotalNetworkUploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
mTotalNetworkUploadBytes += uploadBytes;
+ } else {
+ mTotalNetworkUploadBytes = uploadBytes;
}
}
final long chunkBytes = pendingWork.get(i).getMinimumNetworkChunkBytes();
@@ -1599,7 +1613,8 @@
}
}
- boolean isConstraintSatisfied(int constraint) {
+ /** @return whether or not the @param constraint is satisfied */
+ public boolean isConstraintSatisfied(int constraint) {
return (satisfiedConstraints&constraint) != 0;
}
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 5195f28..fc60228 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
@@ -31,6 +31,7 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.expresslog.Counter;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.StateControllerProto;
@@ -88,6 +89,8 @@
// will never be unsatisfied (our time base can not go backwards).
final long nowElapsedMillis = sElapsedRealtimeClock.millis();
if (job.hasDeadlineConstraint() && evaluateDeadlineConstraint(job, nowElapsedMillis)) {
+ // We're intentionally excluding jobs whose deadlines have passed
+ // (mostly like deadlines of 0) when the job was scheduled.
return;
} else if (job.hasTimingDelayConstraint() && evaluateTimingDelayConstraint(job,
nowElapsedMillis)) {
@@ -158,6 +161,9 @@
// Scheduler.
mStateChangedListener.onRunJobNow(job);
}
+ mTrackedJobs.remove(job);
+ Counter.logIncrement(
+ "job_scheduler.value_job_scheduler_job_deadline_expired_counter");
} else if (wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_DEADLINE)) {
// This job's deadline is earlier than the current set alarm. Update the alarm.
setDeadlineExpiredAlarmLocked(job.getLatestRunTimeElapsed(),
@@ -169,8 +175,11 @@
&& job.getEarliestRunTime() <= mNextDelayExpiredElapsedMillis) {
// Since this is just the delay, we don't need to rush the Scheduler to run the job
// immediately if the constraint is satisfied here.
- if (!evaluateTimingDelayConstraint(job, nowElapsedMillis)
- && wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_TIMING_DELAY)) {
+ if (evaluateTimingDelayConstraint(job, nowElapsedMillis)) {
+ if (canStopTrackingJobLocked(job)) {
+ mTrackedJobs.remove(job);
+ }
+ } else if (wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_TIMING_DELAY)) {
// This job's delay is earlier than the current set alarm. Update the alarm.
setDelayExpiredAlarmLocked(job.getEarliestRunTime(),
mService.deriveWorkSource(job.getSourceUid(), job.getSourcePackageName()));
@@ -228,6 +237,8 @@
// Scheduler.
mStateChangedListener.onRunJobNow(job);
}
+ Counter.logIncrement(
+ "job_scheduler.value_job_scheduler_job_deadline_expired_counter");
it.remove();
} else { // Sorted by expiry time, so take the next one and stop.
if (!wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_DEADLINE)) {
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index 581a545..17b8746 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -701,6 +701,10 @@
return;
}
final long totalDischargeMah = mAnalyst.getBatteryScreenOffDischargeMah();
+ if (totalDischargeMah == 0) {
+ Slog.i(TAG, "Total discharge was 0");
+ return;
+ }
final long batteryCapacityMah = mBatteryManagerInternal.getBatteryFullCharge() / 1000;
final long estimatedLifeHours = batteryCapacityMah * totalScreenOffDurationMs
/ totalDischargeMah / HOUR_IN_MILLIS;
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 8be8cda..c4d90c6 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -130,14 +130,14 @@
uniform sampler2D uTexture;
uniform float uFade;
uniform float uColorProgress;
- uniform vec4 uStartColor0;
- uniform vec4 uStartColor1;
- uniform vec4 uStartColor2;
- uniform vec4 uStartColor3;
- uniform vec4 uEndColor0;
- uniform vec4 uEndColor1;
- uniform vec4 uEndColor2;
- uniform vec4 uEndColor3;
+ uniform vec3 uStartColor0;
+ uniform vec3 uStartColor1;
+ uniform vec3 uStartColor2;
+ uniform vec3 uStartColor3;
+ uniform vec3 uEndColor0;
+ uniform vec3 uEndColor1;
+ uniform vec3 uEndColor2;
+ uniform vec3 uEndColor3;
varying highp vec2 vUv;
void main() {
vec4 mask = texture2D(uTexture, vUv);
@@ -150,12 +150,12 @@
* step(cWhiteMaskThreshold, g)
* step(cWhiteMaskThreshold, b)
* step(cWhiteMaskThreshold, a);
- vec4 color = r * mix(uStartColor0, uEndColor0, uColorProgress)
+ vec3 color = r * mix(uStartColor0, uEndColor0, uColorProgress)
+ g * mix(uStartColor1, uEndColor1, uColorProgress)
+ b * mix(uStartColor2, uEndColor2, uColorProgress)
+ a * mix(uStartColor3, uEndColor3, uColorProgress);
- color = mix(color, vec4(vec3((r + g + b + a) * 0.25), 1.0), useWhiteMask);
- gl_FragColor = vec4(color.x, color.y, color.z, (1.0 - uFade)) * color.a;
+ color = mix(color, vec3((r + g + b + a) * 0.25), useWhiteMask);
+ gl_FragColor = vec4(color.x, color.y, color.z, (1.0 - uFade));
})";
static const char IMAGE_FRAG_SHADER_SOURCE[] = R"(
precision mediump float;
@@ -1439,12 +1439,12 @@
for (int i = 0; i < DYNAMIC_COLOR_COUNT; i++) {
float *startColor = mAnimation->startColors[i];
float *endColor = mAnimation->endColors[i];
- glUniform4f(glGetUniformLocation(mImageShader,
+ glUniform3f(glGetUniformLocation(mImageShader,
(U_START_COLOR_PREFIX + std::to_string(i)).c_str()),
- startColor[0], startColor[1], startColor[2], 1 /* alpha */);
- glUniform4f(glGetUniformLocation(mImageShader,
+ startColor[0], startColor[1], startColor[2]);
+ glUniform3f(glGetUniformLocation(mImageShader,
(U_END_COLOR_PREFIX + std::to_string(i)).c_str()),
- endColor[0], endColor[1], endColor[2], 1 /* alpha */);
+ endColor[0], endColor[1], endColor[2]);
}
mImageColorProgressLocation = glGetUniformLocation(mImageShader, U_COLOR_PROGRESS);
}
diff --git a/core/api/current.txt b/core/api/current.txt
index afa0453..53dbef8 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -84,6 +84,7 @@
field public static final String DELETE_CACHE_FILES = "android.permission.DELETE_CACHE_FILES";
field public static final String DELETE_PACKAGES = "android.permission.DELETE_PACKAGES";
field public static final String DELIVER_COMPANION_MESSAGES = "android.permission.DELIVER_COMPANION_MESSAGES";
+ field public static final String DETECT_SCREEN_CAPTURE = "android.permission.DETECT_SCREEN_CAPTURE";
field public static final String DIAGNOSTIC = "android.permission.DIAGNOSTIC";
field public static final String DISABLE_KEYGUARD = "android.permission.DISABLE_KEYGUARD";
field public static final String DUMP = "android.permission.DUMP";
@@ -8447,19 +8448,31 @@
public abstract class JobService extends android.app.Service {
ctor public JobService();
+ method public long getTransferredDownloadBytes();
+ method public long getTransferredDownloadBytes(@NonNull android.app.job.JobWorkItem);
+ method public long getTransferredUploadBytes();
+ method public long getTransferredUploadBytes(@NonNull android.app.job.JobWorkItem);
method public final void jobFinished(android.app.job.JobParameters, boolean);
method public final android.os.IBinder onBind(android.content.Intent);
method public abstract boolean onStartJob(android.app.job.JobParameters);
method public abstract boolean onStopJob(android.app.job.JobParameters);
+ method public final void updateEstimatedNetworkBytes(@NonNull android.app.job.JobParameters, long, long);
+ method public final void updateEstimatedNetworkBytes(@NonNull android.app.job.JobParameters, @NonNull android.app.job.JobWorkItem, long, long);
+ method public final void updateTransferredNetworkBytes(@NonNull android.app.job.JobParameters, long, long);
+ method public final void updateTransferredNetworkBytes(@NonNull android.app.job.JobParameters, @NonNull android.app.job.JobWorkItem, long, long);
field public static final String PERMISSION_BIND = "android.permission.BIND_JOB_SERVICE";
}
public abstract class JobServiceEngine {
ctor public JobServiceEngine(android.app.Service);
method public final android.os.IBinder getBinder();
+ method public long getTransferredDownloadBytes(@NonNull android.app.job.JobParameters, @Nullable android.app.job.JobWorkItem);
+ method public long getTransferredUploadBytes(@NonNull android.app.job.JobParameters, @Nullable android.app.job.JobWorkItem);
method public void jobFinished(android.app.job.JobParameters, boolean);
method public abstract boolean onStartJob(android.app.job.JobParameters);
method public abstract boolean onStopJob(android.app.job.JobParameters);
+ method public void updateEstimatedNetworkBytes(@NonNull android.app.job.JobParameters, @NonNull android.app.job.JobWorkItem, long, long);
+ method public void updateTransferredNetworkBytes(@NonNull android.app.job.JobParameters, @Nullable android.app.job.JobWorkItem, long, long);
}
public final class JobWorkItem implements android.os.Parcelable {
@@ -10392,6 +10405,7 @@
field public static final String ACTION_SEND_MULTIPLE = "android.intent.action.SEND_MULTIPLE";
field public static final String ACTION_SET_WALLPAPER = "android.intent.action.SET_WALLPAPER";
field public static final String ACTION_SHOW_APP_INFO = "android.intent.action.SHOW_APP_INFO";
+ field public static final String ACTION_SHOW_OUTPUT_SWITCHER = "android.intent.action.SHOW_OUTPUT_SWITCHER";
field public static final String ACTION_SHOW_WORK_APPS = "android.intent.action.SHOW_WORK_APPS";
field public static final String ACTION_SHUTDOWN = "android.intent.action.ACTION_SHUTDOWN";
field public static final String ACTION_SYNC = "android.intent.action.SYNC";
@@ -17770,6 +17784,7 @@
method @NonNull public <T> java.util.List<android.util.Size> getExtensionSupportedSizes(int, @NonNull Class<T>);
method @NonNull public java.util.List<android.util.Size> getExtensionSupportedSizes(int, int);
method @NonNull public java.util.List<java.lang.Integer> getSupportedExtensions();
+ method public boolean isCaptureProcessProgressAvailable(int);
field public static final int EXTENSION_AUTOMATIC = 0; // 0x0
field @Deprecated public static final int EXTENSION_BEAUTY = 1; // 0x1
field public static final int EXTENSION_BOKEH = 2; // 0x2
@@ -17789,6 +17804,7 @@
public abstract static class CameraExtensionSession.ExtensionCaptureCallback {
ctor public CameraExtensionSession.ExtensionCaptureCallback();
method public void onCaptureFailed(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest);
+ method public void onCaptureProcessProgressed(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest, @IntRange(from=0, to=100) int);
method public void onCaptureProcessStarted(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest);
method public void onCaptureResultAvailable(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest, @NonNull android.hardware.camera2.TotalCaptureResult);
method public void onCaptureSequenceAborted(@NonNull android.hardware.camera2.CameraExtensionSession, int);
@@ -26541,6 +26557,7 @@
method public boolean onKeyMultiple(int, int, @NonNull android.view.KeyEvent);
method public boolean onKeyUp(int, @NonNull android.view.KeyEvent);
method public void onMediaViewSizeChanged(@Px int, @Px int);
+ method public void onRecordingStarted(@NonNull String);
method public abstract void onRelease();
method public void onResetInteractiveApp();
method public abstract boolean onSetSurface(@Nullable android.view.Surface);
@@ -26566,6 +26583,7 @@
method @CallSuper public void requestCurrentChannelUri();
method @CallSuper public void requestCurrentTvInputId();
method @CallSuper public void requestSigning(@NonNull String, @NonNull String, @NonNull String, @NonNull byte[]);
+ method @CallSuper public void requestStartRecording(@Nullable android.net.Uri);
method @CallSuper public void requestStreamVolume();
method @CallSuper public void requestTrackInfoList();
method @CallSuper public void sendPlaybackCommandRequest(@NonNull String, @Nullable android.os.Bundle);
@@ -26597,6 +26615,7 @@
method public boolean dispatchUnhandledInputEvent(@NonNull android.view.InputEvent);
method @Nullable public android.media.tv.interactive.TvInteractiveAppView.OnUnhandledInputEventListener getOnUnhandledInputEventListener();
method public void notifyError(@NonNull String, @NonNull android.os.Bundle);
+ method public void notifyRecordingStarted(@NonNull String);
method public void onAttachedToWindow();
method public void onDetachedFromWindow();
method public void onLayout(boolean, int, int, int, int);
@@ -26638,6 +26657,7 @@
method public void onRequestCurrentChannelUri(@NonNull String);
method public void onRequestCurrentTvInputId(@NonNull String);
method public void onRequestSigning(@NonNull String, @NonNull String, @NonNull String, @NonNull String, @NonNull byte[]);
+ method public void onRequestStartRecording(@NonNull String, @Nullable android.net.Uri);
method public void onRequestStreamVolume(@NonNull String);
method public void onRequestTrackInfoList(@NonNull String);
method public void onSetVideoBounds(@NonNull String, @NonNull android.graphics.Rect);
@@ -32161,7 +32181,12 @@
public static class PerformanceHintManager.Session implements java.io.Closeable {
method public void close();
method public void reportActualWorkDuration(long);
+ method public void sendHint(int);
method public void updateTargetWorkDuration(long);
+ field public static final int CPU_LOAD_DOWN = 1; // 0x1
+ field public static final int CPU_LOAD_RESET = 2; // 0x2
+ field public static final int CPU_LOAD_RESUME = 3; // 0x3
+ field public static final int CPU_LOAD_UP = 0; // 0x0
}
public final class PersistableBundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -32534,7 +32559,7 @@
method @NonNull @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.QUERY_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.content.pm.UserProperties getUserProperties(@NonNull android.os.UserHandle);
method public android.os.Bundle getUserRestrictions();
method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.os.Bundle getUserRestrictions(android.os.UserHandle);
- method @NonNull @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}) public java.util.List<android.os.UserHandle> getVisibleUsers();
+ method @NonNull @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}) public java.util.Set<android.os.UserHandle> getVisibleUsers();
method public boolean hasUserRestriction(String);
method public boolean isDemoUser();
method public static boolean isHeadlessSystemUserMode();
@@ -32611,6 +32636,7 @@
field public static final String DISALLOW_SHARING_ADMIN_CONFIGURED_WIFI = "no_sharing_admin_configured_wifi";
field public static final String DISALLOW_SMS = "no_sms";
field public static final String DISALLOW_SYSTEM_ERROR_DIALOGS = "no_system_error_dialogs";
+ field public static final String DISALLOW_ULTRA_WIDEBAND_RADIO = "no_ultra_wideband_radio";
field public static final String DISALLOW_UNIFIED_PASSWORD = "no_unified_password";
field public static final String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
field public static final String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone";
@@ -35793,6 +35819,7 @@
field public static final String ACTION_MANAGE_UNKNOWN_APP_SOURCES = "android.settings.MANAGE_UNKNOWN_APP_SOURCES";
field public static final String ACTION_MANAGE_WRITE_SETTINGS = "android.settings.action.MANAGE_WRITE_SETTINGS";
field public static final String ACTION_MEMORY_CARD_SETTINGS = "android.settings.MEMORY_CARD_SETTINGS";
+ field public static final String ACTION_MEMTAG_SETTINGS = "android.settings.MEMTAG_SETTINGS";
field public static final String ACTION_NETWORK_OPERATOR_SETTINGS = "android.settings.NETWORK_OPERATOR_SETTINGS";
field public static final String ACTION_NFCSHARING_SETTINGS = "android.settings.NFCSHARING_SETTINGS";
field public static final String ACTION_NFC_PAYMENT_SETTINGS = "android.settings.NFC_PAYMENT_SETTINGS";
@@ -41714,7 +41741,7 @@
field public static final String KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY = "gsm_roaming_networks_string_array";
field public static final String KEY_HAS_IN_CALL_NOISE_SUPPRESSION_BOOL = "has_in_call_noise_suppression_bool";
field public static final String KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL = "hide_carrier_network_settings_bool";
- field public static final String KEY_HIDE_ENABLE_2G = "hide_enable_2g_bool";
+ field @Deprecated public static final String KEY_HIDE_ENABLE_2G = "hide_enable_2g_bool";
field public static final String KEY_HIDE_ENHANCED_4G_LTE_BOOL = "hide_enhanced_4g_lte_bool";
field public static final String KEY_HIDE_IMS_APN_BOOL = "hide_ims_apn_bool";
field public static final String KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL = "hide_lte_plus_data_icon_bool";
@@ -41783,7 +41810,8 @@
field public static final String KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG = "opportunistic_network_ping_pong_time_long";
field public static final String KEY_PING_TEST_BEFORE_DATA_SWITCH_BOOL = "ping_test_before_data_switch_bool";
field public static final String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
- field public static final String KEY_PREMIUM_CAPABILITY_MAXIMUM_NOTIFICATION_COUNT_INT_ARRAY = "premium_capability_maximum_notification_count_int_array";
+ field public static final String KEY_PREMIUM_CAPABILITY_MAXIMUM_DAILY_NOTIFICATION_COUNT_INT = "premium_capability_maximum_daily_notification_count_int";
+ field public static final String KEY_PREMIUM_CAPABILITY_MAXIMUM_MONTHLY_NOTIFICATION_COUNT_INT = "premium_capability_maximum_monthly_notification_count_int";
field public static final String KEY_PREMIUM_CAPABILITY_NETWORK_SETUP_TIME_MILLIS_LONG = "premium_capability_network_setup_time_millis_long";
field public static final String KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG = "premium_capability_notification_backoff_hysteresis_time_millis_long";
field public static final String KEY_PREMIUM_CAPABILITY_NOTIFICATION_DISPLAY_TIMEOUT_MILLIS_LONG = "premium_capability_notification_display_timeout_millis_long";
@@ -44051,7 +44079,6 @@
field public static final long NETWORK_TYPE_BITMASK_HSPA = 512L; // 0x200L
field public static final long NETWORK_TYPE_BITMASK_HSPAP = 16384L; // 0x4000L
field public static final long NETWORK_TYPE_BITMASK_HSUPA = 256L; // 0x100L
- field public static final long NETWORK_TYPE_BITMASK_IDEN = 1024L; // 0x400L
field public static final long NETWORK_TYPE_BITMASK_IWLAN = 131072L; // 0x20000L
field public static final long NETWORK_TYPE_BITMASK_LTE = 4096L; // 0x1000L
field @Deprecated public static final long NETWORK_TYPE_BITMASK_LTE_CA = 262144L; // 0x40000L
@@ -44071,7 +44098,7 @@
field public static final int NETWORK_TYPE_HSPA = 10; // 0xa
field public static final int NETWORK_TYPE_HSPAP = 15; // 0xf
field public static final int NETWORK_TYPE_HSUPA = 9; // 0x9
- field public static final int NETWORK_TYPE_IDEN = 11; // 0xb
+ field @Deprecated public static final int NETWORK_TYPE_IDEN = 11; // 0xb
field public static final int NETWORK_TYPE_IWLAN = 18; // 0x12
field public static final int NETWORK_TYPE_LTE = 13; // 0xd
field public static final int NETWORK_TYPE_NR = 20; // 0x14
@@ -44087,10 +44114,10 @@
field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED = 3; // 0x3
field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED = 7; // 0x7
field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR = 8; // 0x8
+ field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED = 13; // 0xd
field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_FEATURE_NOT_SUPPORTED = 10; // 0xa
- field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_CONGESTED = 13; // 0xd
field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE = 12; // 0xc
- field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA = 14; // 0xe
+ field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUB = 14; // 0xe
field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_OVERRIDDEN = 5; // 0x5
field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_PENDING_NETWORK_SETUP = 15; // 0xf
field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_REQUEST_FAILED = 11; // 0xb
@@ -52335,6 +52362,7 @@
method public CharSequence getClassName();
method public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo getCollectionInfo();
method public android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo getCollectionItemInfo();
+ method @Nullable public CharSequence getContainerTitle();
method public CharSequence getContentDescription();
method public int getDrawingOrder();
method public CharSequence getError();
@@ -52346,6 +52374,7 @@
method public android.view.accessibility.AccessibilityNodeInfo getLabeledBy();
method public int getLiveRegion();
method public int getMaxTextLength();
+ method public int getMinMillisBetweenContentChanges();
method public int getMovementGranularities();
method public CharSequence getPackageName();
method @Nullable public CharSequence getPaneTitle();
@@ -52411,6 +52440,7 @@
method public void setClickable(boolean);
method public void setCollectionInfo(android.view.accessibility.AccessibilityNodeInfo.CollectionInfo);
method public void setCollectionItemInfo(android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo);
+ method public void setContainerTitle(@Nullable CharSequence);
method public void setContentDescription(CharSequence);
method public void setContentInvalid(boolean);
method public void setContextClickable(boolean);
@@ -52432,6 +52462,7 @@
method public void setLiveRegion(int);
method public void setLongClickable(boolean);
method public void setMaxTextLength(int);
+ method public void setMinMillisBetweenContentChanges(int);
method public void setMovementGranularities(int);
method public void setMultiLine(boolean);
method public void setPackageName(CharSequence);
@@ -52511,11 +52542,13 @@
field public static final int FOCUS_ACCESSIBILITY = 2; // 0x2
field public static final int FOCUS_INPUT = 1; // 0x1
field public static final int MAX_NUMBER_OF_PREFETCHED_NODES = 50; // 0x32
+ field public static final int MINIMUM_MIN_MILLIS_BETWEEN_CONTENT_CHANGES = 100; // 0x64
field public static final int MOVEMENT_GRANULARITY_CHARACTER = 1; // 0x1
field public static final int MOVEMENT_GRANULARITY_LINE = 4; // 0x4
field public static final int MOVEMENT_GRANULARITY_PAGE = 16; // 0x10
field public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 8; // 0x8
field public static final int MOVEMENT_GRANULARITY_WORD = 2; // 0x2
+ field public static final int UNDEFINED_MIN_MILLIS_BETWEEN_CONTENT_CHANGES = -1; // 0xffffffff
}
public static final class AccessibilityNodeInfo.AccessibilityAction implements android.os.Parcelable {
@@ -53399,7 +53432,7 @@
method @NonNull public android.view.inputmethod.CursorAnchorInfo.Builder setTextAppearanceInfo(@Nullable android.view.inputmethod.TextAppearanceInfo);
}
- public final class DeleteGesture extends android.view.inputmethod.HandwritingGesture implements android.os.Parcelable {
+ public final class DeleteGesture extends android.view.inputmethod.PreviewableHandwritingGesture implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.graphics.RectF getDeletionArea();
method public int getGranularity();
@@ -53415,7 +53448,7 @@
method @NonNull public android.view.inputmethod.DeleteGesture.Builder setGranularity(int);
}
- public final class DeleteRangeGesture extends android.view.inputmethod.HandwritingGesture implements android.os.Parcelable {
+ public final class DeleteRangeGesture extends android.view.inputmethod.PreviewableHandwritingGesture implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.graphics.RectF getDeletionEndArea();
method @NonNull public android.graphics.RectF getDeletionStartArea();
@@ -53457,11 +53490,13 @@
method @Nullable public CharSequence getInitialTextAfterCursor(@IntRange(from=0) int, int);
method @Nullable public CharSequence getInitialTextBeforeCursor(@IntRange(from=0) int, int);
method public int getInitialToolType();
+ method @NonNull public java.util.Set<java.lang.Class<? extends android.view.inputmethod.PreviewableHandwritingGesture>> getSupportedHandwritingGesturePreviews();
method @NonNull public java.util.List<java.lang.Class<? extends android.view.inputmethod.HandwritingGesture>> getSupportedHandwritingGestures();
method public final void makeCompatible(int);
method public void setInitialSurroundingSubText(@NonNull CharSequence, int);
method public void setInitialSurroundingText(@NonNull CharSequence);
method public void setInitialToolType(int);
+ method public void setSupportedHandwritingGesturePreviews(@NonNull java.util.Set<java.lang.Class<? extends android.view.inputmethod.PreviewableHandwritingGesture>>);
method public void setSupportedHandwritingGestures(@NonNull java.util.List<java.lang.Class<? extends android.view.inputmethod.HandwritingGesture>>);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.EditorInfo> CREATOR;
@@ -53636,6 +53671,7 @@
method public default void performHandwritingGesture(@NonNull android.view.inputmethod.HandwritingGesture, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.IntConsumer);
method public boolean performPrivateCommand(String, android.os.Bundle);
method public default boolean performSpellCheck();
+ method public default boolean previewHandwritingGesture(@NonNull android.view.inputmethod.PreviewableHandwritingGesture, @Nullable android.os.CancellationSignal);
method public default boolean replaceText(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull CharSequence, int, @Nullable android.view.inputmethod.TextAttribute);
method public boolean reportFullscreenMode(boolean);
method public boolean requestCursorUpdates(int);
@@ -53843,6 +53879,7 @@
method @NonNull public String getLanguageTag();
method @Deprecated @NonNull public String getLocale();
method public String getMode();
+ method @NonNull public CharSequence getNameOverride();
method public int getNameResId();
method public boolean isAsciiCapable();
method public boolean isAuxiliary();
@@ -53863,6 +53900,7 @@
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeId(int);
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeLocale(String);
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeMode(String);
+ method @NonNull public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeNameOverride(@NonNull CharSequence);
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeNameResId(int);
}
@@ -53896,6 +53934,9 @@
method @NonNull public android.view.inputmethod.JoinOrSplitGesture.Builder setJoinOrSplitPoint(@NonNull android.graphics.PointF);
}
+ public abstract class PreviewableHandwritingGesture extends android.view.inputmethod.HandwritingGesture {
+ }
+
public final class RemoveSpaceGesture extends android.view.inputmethod.HandwritingGesture implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.graphics.PointF getEndPoint();
@@ -53911,7 +53952,7 @@
method @NonNull public android.view.inputmethod.RemoveSpaceGesture.Builder setPoints(@NonNull android.graphics.PointF, @NonNull android.graphics.PointF);
}
- public final class SelectGesture extends android.view.inputmethod.HandwritingGesture implements android.os.Parcelable {
+ public final class SelectGesture extends android.view.inputmethod.PreviewableHandwritingGesture implements android.os.Parcelable {
method public int describeContents();
method public int getGranularity();
method @NonNull public android.graphics.RectF getSelectionArea();
@@ -53927,7 +53968,7 @@
method @NonNull public android.view.inputmethod.SelectGesture.Builder setSelectionArea(@NonNull android.graphics.RectF);
}
- public final class SelectRangeGesture extends android.view.inputmethod.HandwritingGesture implements android.os.Parcelable {
+ public final class SelectRangeGesture extends android.view.inputmethod.PreviewableHandwritingGesture implements android.os.Parcelable {
method public int describeContents();
method public int getGranularity();
method @NonNull public android.graphics.RectF getSelectionEndArea();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 70b89b8..b5b87c1 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3617,6 +3617,7 @@
field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
field @Deprecated public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000
field @Nullable public final String backgroundPermission;
+ field @NonNull public java.util.Set<java.lang.String> knownCerts;
field @StringRes public int requestRes;
}
@@ -5555,6 +5556,28 @@
method @NonNull public android.location.CorrelationVector.Builder setSamplingWidthMeters(@FloatRange(from=0.0f, fromInclusive=false) double);
}
+ public final class Country implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public String getCountryIso();
+ method public int getSource();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int COUNTRY_SOURCE_LOCALE = 3; // 0x3
+ field public static final int COUNTRY_SOURCE_LOCATION = 1; // 0x1
+ field public static final int COUNTRY_SOURCE_NETWORK = 0; // 0x0
+ field public static final int COUNTRY_SOURCE_SIM = 2; // 0x2
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.Country> CREATOR;
+ }
+
+ public class CountryDetector {
+ method public void addCountryListener(@NonNull android.location.CountryListener, @Nullable android.os.Looper);
+ method @Nullable public android.location.Country detectCountry();
+ method public void removeCountryListener(@NonNull android.location.CountryListener);
+ }
+
+ public interface CountryListener {
+ method public void onCountryDetected(@NonNull android.location.Country);
+ }
+
public final class GnssCapabilities implements android.os.Parcelable {
method @Deprecated public boolean hasMeasurementCorrectionsReflectingPane();
method @Deprecated public boolean hasNavMessages();
@@ -13125,6 +13148,7 @@
field public static final int PRECISE_CALL_STATE_HOLDING = 2; // 0x2
field public static final int PRECISE_CALL_STATE_IDLE = 0; // 0x0
field public static final int PRECISE_CALL_STATE_INCOMING = 5; // 0x5
+ field public static final int PRECISE_CALL_STATE_INCOMING_SETUP = 9; // 0x9
field public static final int PRECISE_CALL_STATE_NOT_VALID = -1; // 0xffffffff
field public static final int PRECISE_CALL_STATE_WAITING = 6; // 0x6
}
@@ -14131,6 +14155,7 @@
ctor public QualifiedNetworksService.NetworkAvailabilityProvider(int);
method public abstract void close();
method public final int getSlotIndex();
+ method public void reportEmergencyDataNetworkPreferredTransportChanged(int);
method public void reportThrottleStatusChanged(@NonNull java.util.List<android.telephony.data.ThrottleStatus>);
method public final void updateQualifiedNetworkTypes(int, @NonNull java.util.List<java.lang.Integer>);
}
@@ -14213,7 +14238,8 @@
field public static final int RESULT_CALLER_NOT_ALLOWED = -3; // 0xfffffffd
field public static final int RESULT_EUICC_NOT_FOUND = -2; // 0xfffffffe
field public static final int RESULT_OK = 0; // 0x0
- field public static final int RESULT_PROFILE_NOT_FOUND = 1; // 0x1
+ field public static final int RESULT_PROFILE_DOES_NOT_EXIST = -4; // 0xfffffffc
+ field @Deprecated public static final int RESULT_PROFILE_NOT_FOUND = 1; // 0x1
field public static final int RESULT_UNKNOWN_ERROR = -1; // 0xffffffff
}
@@ -15380,6 +15406,16 @@
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipMessage> CREATOR;
}
+ public final class SrvccCall implements android.os.Parcelable {
+ ctor public SrvccCall(@NonNull String, int, @NonNull android.telephony.ims.ImsCallProfile);
+ method public int describeContents();
+ method @NonNull public String getCallId();
+ method @NonNull public android.telephony.ims.ImsCallProfile getImsCallProfile();
+ method public int getPreciseCallState();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SrvccCall> CREATOR;
+ }
+
}
package android.telephony.ims.feature {
@@ -15440,6 +15476,10 @@
method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.MmTelFeature.MmTelCapabilities);
method public final void notifyIncomingCall(@NonNull android.telephony.ims.stub.ImsCallSessionImplBase, @NonNull android.os.Bundle);
method public final void notifyRejectedCall(@NonNull android.telephony.ims.ImsCallProfile, @NonNull android.telephony.ims.ImsReasonInfo);
+ method public void notifySrvccCanceled();
+ method public void notifySrvccCompleted();
+ method public void notifySrvccFailed();
+ method public void notifySrvccStarted(@NonNull java.util.function.Consumer<java.util.List<android.telephony.ims.SrvccCall>>);
method public final void notifyVoiceMessageCountUpdate(int);
method public void onFeatureReady();
method public void onFeatureRemoved();
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index 025e862..47588d9 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -3,6 +3,10 @@
Method should return Collection<CharSequence> (or subclass) instead of raw array; was `java.lang.CharSequence[]`
+ExecutorRegistration: android.location.CountryDetector#addCountryListener(android.location.CountryListener, android.os.Looper):
+ Registration methods should have overload that accepts delivery Executor: `addCountryListener`
+
+
GenericException: android.app.prediction.AppPredictor#finalize():
Methods must not throw generic exceptions (`java.lang.Throwable`)
GenericException: android.hardware.location.ContextHubClient#finalize():
@@ -127,6 +131,8 @@
SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.PackageItemInfo.dumpFront) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.content.pm.ResolveInfo#dump(android.util.Printer, String):
SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.ResolveInfo.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.location.CountryDetector#addCountryListener(android.location.CountryListener, android.os.Looper):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.location.CountryDetector.addCountryListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.location.Location#dump(android.util.Printer, String):
SAM-compatible parameters (such as parameter 1, "pw", in android.location.Location.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.location.LocationManager#addNmeaListener(android.location.OnNmeaMessageListener, android.os.Handler):
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index e9f9136..d8419a8 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1252,6 +1252,7 @@
method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public boolean shouldAlwaysRespectAppRequestedMode();
field public static final int SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS = 2; // 0x2
field public static final int SWITCHING_TYPE_NONE = 0; // 0x0
+ field public static final int SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY = 3; // 0x3
field public static final int SWITCHING_TYPE_WITHIN_GROUPS = 1; // 0x1
field public static final int VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 512; // 0x200
}
@@ -1710,6 +1711,7 @@
public class Build {
method public static boolean is64BitAbi(String);
method public static boolean isDebuggable();
+ method public static boolean isSecure();
field public static final boolean IS_EMULATOR;
}
@@ -2943,6 +2945,7 @@
method public static int getHoverTooltipHideTimeout();
method public static int getHoverTooltipShowTimeout();
method public static int getLongPressTooltipHideTimeout();
+ method public static long getSendRecurringAccessibilityEventsInterval();
method public boolean isPreferKeepClearForFocusEnabled();
}
diff --git a/core/java/android/animation/AnimationHandler.java b/core/java/android/animation/AnimationHandler.java
index 1403ba2..dcabf57 100644
--- a/core/java/android/animation/AnimationHandler.java
+++ b/core/java/android/animation/AnimationHandler.java
@@ -219,12 +219,14 @@
return;
}
for (int i = 0; i < mAnimationCallbacks.size(); ++i) {
- Animator animator = ((Animator) mAnimationCallbacks.get(i));
- if (animator != null
- && animator.getTotalDuration() == Animator.DURATION_INFINITE
- && !animator.isPaused()) {
- mPausedAnimators.add(animator);
- animator.pause();
+ AnimationFrameCallback callback = mAnimationCallbacks.get(i);
+ if (callback instanceof Animator) {
+ Animator animator = ((Animator) callback);
+ if (animator.getTotalDuration() == Animator.DURATION_INFINITE
+ && !animator.isPaused()) {
+ mPausedAnimators.add(animator);
+ animator.pause();
+ }
}
}
};
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index baa3bd9..dc7331f 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -649,6 +649,8 @@
* using the rules of package visibility. Returns extras with legitimate package info that the
* receiver is able to access, or {@code null} if none of the packages is visible to the
* receiver.
+ * @param serialized Specifies whether or not the broadcast should be delivered to the
+ * receivers in a serial order.
*
* @see com.android.server.am.ActivityManagerService#broadcastIntentWithFeature(
* IApplicationThread, String, Intent, String, IIntentReceiver, int, String, Bundle,
@@ -662,6 +664,19 @@
@Nullable Bundle bOptions);
/**
+ * Variant of
+ * {@link #broadcastIntent(Intent, IIntentReceiver, String[], boolean, int, int[], BiFunction, Bundle)}
+ * that allows sender to receive a finish callback once the broadcast delivery is completed,
+ * but provides no ordering guarantee for how the broadcast is delivered to receivers.
+ */
+ public abstract int broadcastIntentWithCallback(Intent intent,
+ IIntentReceiver resultTo,
+ String[] requiredPermissions,
+ int userId, int[] appIdAllowList,
+ @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
+ @Nullable Bundle bOptions);
+
+ /**
* Add uid to the ActivityManagerService PendingStartActivityUids list.
* @param uid uid
* @param pid pid of the ProcessRecord that is pending top.
diff --git a/core/java/android/app/DisabledWallpaperManager.java b/core/java/android/app/DisabledWallpaperManager.java
index ae3a9e6..0d14c0b 100644
--- a/core/java/android/app/DisabledWallpaperManager.java
+++ b/core/java/android/app/DisabledWallpaperManager.java
@@ -193,6 +193,16 @@
}
@Override
+ public WallpaperInfo getWallpaperInfoWithFlags(@SetWallpaperFlags int which) {
+ return unsupported();
+ }
+
+ @Override
+ public WallpaperInfo getWallpaperInfoWithFlags(@SetWallpaperFlags int which, int userId) {
+ return unsupported();
+ }
+
+ @Override
public int getWallpaperId(int which) {
return unsupportedInt();
}
diff --git a/core/java/android/app/ILocaleManager.aidl b/core/java/android/app/ILocaleManager.aidl
index 3002c8b..c38b64f 100644
--- a/core/java/android/app/ILocaleManager.aidl
+++ b/core/java/android/app/ILocaleManager.aidl
@@ -33,7 +33,7 @@
/**
* Sets a specified app’s app-specific UI locales.
*/
- void setApplicationLocales(String packageName, int userId, in LocaleList locales);
+ void setApplicationLocales(String packageName, int userId, in LocaleList locales, boolean fromDelegate);
/**
* Returns the specified app's app-specific locales.
@@ -45,4 +45,4 @@
*/
LocaleList getSystemLocales();
- }
\ No newline at end of file
+ }
diff --git a/core/java/android/app/LocaleManager.java b/core/java/android/app/LocaleManager.java
index 0e2b098..70c014f 100644
--- a/core/java/android/app/LocaleManager.java
+++ b/core/java/android/app/LocaleManager.java
@@ -71,7 +71,7 @@
*/
@UserHandleAware
public void setApplicationLocales(@NonNull LocaleList locales) {
- setApplicationLocales(mContext.getPackageName(), locales);
+ setApplicationLocales(mContext.getPackageName(), locales, false);
}
/**
@@ -100,9 +100,14 @@
@RequiresPermission(Manifest.permission.CHANGE_CONFIGURATION)
@UserHandleAware
public void setApplicationLocales(@NonNull String appPackageName, @NonNull LocaleList locales) {
+ setApplicationLocales(appPackageName, locales, true);
+ }
+
+ private void setApplicationLocales(@NonNull String appPackageName, @NonNull LocaleList locales,
+ boolean fromDelegate) {
try {
mService.setApplicationLocales(appPackageName, mContext.getUser().getIdentifier(),
- locales);
+ locales, fromDelegate);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index f3fc468..8ec313ec 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -11,6 +11,7 @@
per-file ApplicationThreadConstants.java = file:/services/core/java/com/android/server/am/OWNERS
per-file BroadcastOptions.java = file:/services/core/java/com/android/server/am/OWNERS
per-file ContentProviderHolder* = file:/services/core/java/com/android/server/am/OWNERS
+per-file ForegroundService* = file:/services/core/java/com/android/server/am/OWNERS
per-file IActivityController.aidl = file:/services/core/java/com/android/server/am/OWNERS
per-file IActivityManager.aidl = file:/services/core/java/com/android/server/am/OWNERS
per-file IApplicationThread.aidl = file:/services/core/java/com/android/server/am/OWNERS
@@ -29,10 +30,12 @@
per-file Service* = file:/services/core/java/com/android/server/am/OWNERS
per-file SystemServiceRegistry.java = file:/services/core/java/com/android/server/am/OWNERS
per-file *UserSwitchObserver* = file:/services/core/java/com/android/server/am/OWNERS
-per-file UiAutomation* = file:/services/accessibility/OWNERS
+per-file *UiAutomation* = file:/services/accessibility/OWNERS
per-file GameManager* = file:/GAME_MANAGER_OWNERS
+per-file GameMode* = file:/GAME_MANAGER_OWNERS
per-file GameState* = file:/GAME_MANAGER_OWNERS
per-file IGameManager* = file:/GAME_MANAGER_OWNERS
+per-file IGameMode* = file:/GAME_MANAGER_OWNERS
# ActivityThread
per-file ActivityThread.java = file:/services/core/java/com/android/server/am/OWNERS
diff --git a/core/java/android/app/PictureInPictureParams.java b/core/java/android/app/PictureInPictureParams.java
index 3f1844e..96d874e 100644
--- a/core/java/android/app/PictureInPictureParams.java
+++ b/core/java/android/app/PictureInPictureParams.java
@@ -157,13 +157,18 @@
}
/**
- * Sets the source bounds hint. These bounds are only used when an activity first enters
- * picture-in-picture, and describe the bounds in window coordinates of activity entering
- * picture-in-picture that will be visible following the transition. For the best effect,
- * these bounds should also match the aspect ratio in the arguments.
+ * Sets the window-coordinate bounds of an activity transitioning to picture-in-picture.
+ * The bounds is the area of an activity that will be visible in the transition to
+ * picture-in-picture mode. For the best effect, these bounds should also match the
+ * aspect ratio in the arguments.
+ *
+ * In Android 12+ these bounds are also reused to improve the exit transition from
+ * picture-in-picture mode. See
+ * <a href="{@docRoot}develop/ui/views/picture-in-picture#smoother-exit">Support
+ * smoother animations when exiting out of PiP mode</a> for more details.
*
* @param launchBounds window-coordinate bounds indicating the area of the activity that
- * will still be visible following the transition into picture-in-picture (eg. the video
+ * will still be visible following the transition into picture-in-picture (e.g. the video
* view bounds in a video player)
*
* @return this builder instance.
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 5e15b0a..01f02fc 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -1318,18 +1318,16 @@
}
/**
- * Returns the information about the wallpaper if the current wallpaper is
- * a live wallpaper component. Otherwise, if the wallpaper is a static image,
- * this returns null.
+ * Returns the information about the home screen wallpaper if its current wallpaper is a live
+ * wallpaper component. Otherwise, if the wallpaper is a static image, this returns null.
*/
public WallpaperInfo getWallpaperInfo() {
return getWallpaperInfo(mContext.getUserId());
}
/**
- * Returns the information about the wallpaper if the current wallpaper is
- * a live wallpaper component. Otherwise, if the wallpaper is a static image,
- * this returns null.
+ * Returns the information about the home screen wallpaper if its current wallpaper is a live
+ * wallpaper component. Otherwise, if the wallpaper is a static image, this returns null.
*
* @param userId Owner of the wallpaper.
* @hide
@@ -1348,6 +1346,29 @@
}
/**
+ * Returns the information about the home screen wallpaper if its current wallpaper is a live
+ * wallpaper component. Otherwise, if the wallpaper is a static image, this returns null.
+ *
+ * @param which Specifies wallpaper destination (home or lock).
+ * @hide
+ */
+ public WallpaperInfo getWallpaperInfoWithFlags(@SetWallpaperFlags int which) {
+ return getWallpaperInfo();
+ }
+
+ /**
+ * Returns the information about the designated wallpaper if its current wallpaper is a live
+ * wallpaper component. Otherwise, if the wallpaper is a static image, this returns null.
+ *
+ * @param which Specifies wallpaper destination (home or lock).
+ * @param userId Owner of the wallpaper.
+ * @hide
+ */
+ public WallpaperInfo getWallpaperInfoWithFlags(@SetWallpaperFlags int which, int userId) {
+ return getWallpaperInfo(userId);
+ }
+
+ /**
* Get the ID of the current wallpaper of the given kind. If there is no
* such wallpaper configured, returns a negative number.
*
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index 30a6c31..ee14708 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -176,7 +176,6 @@
/** Write to Parcel. */
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeStrongBinder(mClient.asBinder());
final boolean writeActivityToken = mActivityToken != null;
dest.writeBoolean(writeActivityToken);
if (writeActivityToken) {
@@ -192,7 +191,6 @@
/** Read from Parcel. */
private ClientTransaction(Parcel in) {
- mClient = (IApplicationThread) in.readStrongBinder();
final boolean readActivityToken = in.readBoolean();
if (readActivityToken) {
mActivityToken = in.readStrongBinder();
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index f2ebec6..9d82274 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3561,6 +3561,17 @@
public static final String ACTION_MEDIA_BUTTON = "android.intent.action.MEDIA_BUTTON";
/**
+ * Broadcast action: Launch System output switcher. Includes a single extra field,
+ * {@link #EXTRA_PACKAGE_NAME}, which specifies the package name of the calling app
+ * so that the system can get the corresponding MediaSession for the output switcher.
+ *
+ * @see #EXTRA_PACKAGE_NAME
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_SHOW_OUTPUT_SWITCHER =
+ "android.intent.action.SHOW_OUTPUT_SWITCHER";
+
+ /**
* Broadcast Action: The "Camera Button" was pressed. Includes a single
* extra field, {@link #EXTRA_KEY_EVENT}, containing the key event that
* caused the broadcast.
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index bb88486..7c22c088 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringRes;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -33,6 +34,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
import java.util.Set;
/**
@@ -486,7 +488,10 @@
*
* @hide
*/
- public @Nullable Set<String> knownCerts;
+ // Already being used as mutable and most other fields in this class are also mutable.
+ @SuppressLint("MutableBareField")
+ @SystemApi
+ public @NonNull Set<String> knownCerts = Collections.emptySet();
/** @hide */
public static int fixProtectionLevel(int level) {
@@ -620,6 +625,8 @@
descriptionRes = orig.descriptionRes;
requestRes = orig.requestRes;
nonLocalizedDescription = orig.nonLocalizedDescription;
+ // Note that knownCerts wasn't properly copied before Android U.
+ knownCerts = orig.knownCerts;
}
/**
diff --git a/core/java/android/content/pm/UserPackage.java b/core/java/android/content/pm/UserPackage.java
index e75f551..7ca92c3 100644
--- a/core/java/android/content/pm/UserPackage.java
+++ b/core/java/android/content/pm/UserPackage.java
@@ -33,17 +33,19 @@
* @hide
*/
public final class UserPackage {
+ private static final boolean ENABLE_CACHING = true;
+
@UserIdInt
public final int userId;
public final String packageName;
- @GuardedBy("sCache")
+ private static final Object sCacheLock = new Object();
+ @GuardedBy("sCacheLock")
private static final SparseArrayMap<String, UserPackage> sCache = new SparseArrayMap<>();
- private static final Object sUserIdLock = new Object();
private static final class NoPreloadHolder {
/** Set of userIDs to cache objects for. */
- @GuardedBy("sUserIdLock")
+ @GuardedBy("sCacheLock")
private static int[] sUserIds = new int[]{UserHandle.getUserId(Process.myUid())};
}
@@ -80,13 +82,16 @@
/** Return an instance of this class representing the given userId + packageName combination. */
@NonNull
public static UserPackage of(@UserIdInt int userId, @NonNull String packageName) {
- synchronized (sUserIdLock) {
+ if (!ENABLE_CACHING) {
+ return new UserPackage(userId, packageName);
+ }
+
+ synchronized (sCacheLock) {
if (!ArrayUtils.contains(NoPreloadHolder.sUserIds, userId)) {
// Don't cache objects for invalid userIds.
return new UserPackage(userId, packageName);
}
- }
- synchronized (sCache) {
+
UserPackage up = sCache.get(userId, packageName);
if (up == null) {
packageName = packageName.intern();
@@ -99,23 +104,27 @@
/** Remove the specified app from the cache. */
public static void removeFromCache(@UserIdInt int userId, @NonNull String packageName) {
- synchronized (sCache) {
+ if (!ENABLE_CACHING) {
+ return;
+ }
+
+ synchronized (sCacheLock) {
sCache.delete(userId, packageName);
}
}
/** Indicate the list of valid user IDs on the device. */
public static void setValidUserIds(@NonNull int[] userIds) {
- userIds = userIds.clone();
- synchronized (sUserIdLock) {
- NoPreloadHolder.sUserIds = userIds;
+ if (!ENABLE_CACHING) {
+ return;
}
- synchronized (sCache) {
+
+ userIds = userIds.clone();
+ synchronized (sCacheLock) {
+ NoPreloadHolder.sUserIds = userIds;
+
for (int u = sCache.numMaps() - 1; u >= 0; --u) {
final int userId = sCache.keyAt(u);
- // Not holding sUserIdLock is intentional here. We don't modify the elements within
- // the array and so even if this method is called multiple times with different sets
- // of user IDs, we want to adjust the cache based on each new array.
if (!ArrayUtils.contains(userIds, userId)) {
sCache.deleteAt(u);
}
diff --git a/core/java/android/content/pm/UserProperties.java b/core/java/android/content/pm/UserProperties.java
index 80f3264..0d3c8db 100644
--- a/core/java/android/content/pm/UserProperties.java
+++ b/core/java/android/content/pm/UserProperties.java
@@ -44,12 +44,14 @@
private static final String ATTR_SHOW_IN_LAUNCHER = "showInLauncher";
private static final String ATTR_START_WITH_PARENT = "startWithParent";
private static final String ATTR_SHOW_IN_SETTINGS = "showInSettings";
+ private static final String ATTR_INHERIT_DEVICE_POLICY = "inheritDevicePolicy";
/** Index values of each property (to indicate whether they are present in this object). */
@IntDef(prefix = "INDEX_", value = {
INDEX_SHOW_IN_LAUNCHER,
INDEX_START_WITH_PARENT,
INDEX_SHOW_IN_SETTINGS,
+ INDEX_INHERIT_DEVICE_POLICY,
})
@Retention(RetentionPolicy.SOURCE)
private @interface PropertyIndex {
@@ -57,6 +59,7 @@
private static final int INDEX_SHOW_IN_LAUNCHER = 0;
private static final int INDEX_START_WITH_PARENT = 1;
private static final int INDEX_SHOW_IN_SETTINGS = 2;
+ private static final int INDEX_INHERIT_DEVICE_POLICY = 3;
/** A bit set, mapping each PropertyIndex to whether it is present (1) or absent (0). */
private long mPropertiesPresent = 0;
@@ -121,6 +124,37 @@
public static final int SHOW_IN_SETTINGS_NO = 2;
/**
+ * Possible values for whether (and from whom) to inherit select user restrictions
+ * or device policies.
+ *
+ * @hide
+ */
+ @IntDef(prefix = "INHERIT_DEVICE_POLICY", value = {
+ INHERIT_DEVICE_POLICY_NO,
+ INHERIT_DEVICE_POLICY_FROM_PARENT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface InheritDevicePolicy {
+ }
+ /**
+ * Suggests that the given user profile should not inherit user restriction or device policy
+ * from any other user. This is the default value for any new user type.
+ * @hide
+ */
+ public static final int INHERIT_DEVICE_POLICY_NO = 0;
+ /**
+ * Suggests that the given user profile should inherit select user restrictions or
+ * device policies from its parent profile.
+ *
+ *<p> All the user restrictions and device policies would be not propagated to the profile
+ * with this property value. The {(TODO:b/256978256) @link DevicePolicyEngine}
+ * uses this property to determine and propagate only select ones to the given profile.
+ *
+ * @hide
+ */
+ public static final int INHERIT_DEVICE_POLICY_FROM_PARENT = 1;
+
+ /**
* Reference to the default user properties for this user's user type.
* <li>If non-null, then any absent property will use the default property from here instead.
* <li>If null, then any absent property indicates that the caller lacks permission to see it,
@@ -161,6 +195,7 @@
if (exposeAllFields) {
// Add items that require exposeAllFields to be true (strictest permission level).
setStartWithParent(orig.getStartWithParent());
+ setInheritDevicePolicy(orig.getInheritDevicePolicy());
}
if (hasManagePermission) {
// Add items that require MANAGE_USERS or stronger.
@@ -261,6 +296,27 @@
}
private boolean mStartWithParent;
+ /**
+ * Return whether, and how, select user restrictions or device policies should be inherited
+ * from other user.
+ *
+ * Possible return values include
+ * {@link #INHERIT_DEVICE_POLICY_FROM_PARENT} or {@link #INHERIT_DEVICE_POLICY_NO}
+ *
+ * @hide
+ */
+ public @InheritDevicePolicy int getInheritDevicePolicy() {
+ if (isPresent(INDEX_INHERIT_DEVICE_POLICY)) return mInheritDevicePolicy;
+ if (mDefaultProperties != null) return mDefaultProperties.mInheritDevicePolicy;
+ throw new SecurityException("You don't have permission to query inheritDevicePolicy");
+ }
+ /** @hide */
+ public void setInheritDevicePolicy(@InheritDevicePolicy int val) {
+ this.mInheritDevicePolicy = val;
+ setPresent(INDEX_INHERIT_DEVICE_POLICY);
+ }
+ private @InheritDevicePolicy int mInheritDevicePolicy;
+
@Override
public String toString() {
// Please print in increasing order of PropertyIndex.
@@ -269,6 +325,7 @@
+ ", mShowInLauncher=" + getShowInLauncher()
+ ", mStartWithParent=" + getStartWithParent()
+ ", mShowInSettings=" + getShowInSettings()
+ + ", mInheritDevicePolicy=" + getInheritDevicePolicy()
+ "}";
}
@@ -283,6 +340,7 @@
pw.println(prefix + " mShowInLauncher=" + getShowInLauncher());
pw.println(prefix + " mStartWithParent=" + getStartWithParent());
pw.println(prefix + " mShowInSettings=" + getShowInSettings());
+ pw.println(prefix + " mInheritDevicePolicy=" + getInheritDevicePolicy());
}
/**
@@ -324,6 +382,10 @@
break;
case ATTR_SHOW_IN_SETTINGS:
setShowInSettings(parser.getAttributeInt(i));
+ break;
+ case ATTR_INHERIT_DEVICE_POLICY:
+ setInheritDevicePolicy(parser.getAttributeInt(i));
+ break;
default:
Slog.w(LOG_TAG, "Skipping unknown property " + attributeName);
}
@@ -350,6 +412,10 @@
if (isPresent(INDEX_SHOW_IN_SETTINGS)) {
serializer.attributeInt(null, ATTR_SHOW_IN_SETTINGS, mShowInSettings);
}
+ if (isPresent(INDEX_INHERIT_DEVICE_POLICY)) {
+ serializer.attributeInt(null, ATTR_INHERIT_DEVICE_POLICY,
+ mInheritDevicePolicy);
+ }
}
// For use only with an object that has already had any permission-lacking fields stripped out.
@@ -359,6 +425,7 @@
dest.writeInt(mShowInLauncher);
dest.writeBoolean(mStartWithParent);
dest.writeInt(mShowInSettings);
+ dest.writeInt(mInheritDevicePolicy);
}
/**
@@ -372,6 +439,7 @@
mShowInLauncher = source.readInt();
mStartWithParent = source.readBoolean();
mShowInSettings = source.readInt();
+ mInheritDevicePolicy = source.readInt();
}
@Override
@@ -399,6 +467,7 @@
private @ShowInLauncher int mShowInLauncher = SHOW_IN_LAUNCHER_WITH_PARENT;
private boolean mStartWithParent = false;
private @ShowInSettings int mShowInSettings = SHOW_IN_SETTINGS_WITH_PARENT;
+ private @InheritDevicePolicy int mInheritDevicePolicy = INHERIT_DEVICE_POLICY_NO;
public Builder setShowInLauncher(@ShowInLauncher int showInLauncher) {
mShowInLauncher = showInLauncher;
@@ -416,12 +485,20 @@
return this;
}
+ /** Sets the value for {@link #mInheritDevicePolicy}*/
+ public Builder setInheritDevicePolicy(
+ @InheritDevicePolicy int inheritRestrictionsDevicePolicy) {
+ mInheritDevicePolicy = inheritRestrictionsDevicePolicy;
+ return this;
+ }
+
/** Builds a UserProperties object with *all* values populated. */
public UserProperties build() {
return new UserProperties(
mShowInLauncher,
mStartWithParent,
- mShowInSettings);
+ mShowInSettings,
+ mInheritDevicePolicy);
}
} // end Builder
@@ -429,11 +506,13 @@
private UserProperties(
@ShowInLauncher int showInLauncher,
boolean startWithParent,
- @ShowInSettings int showInSettings) {
+ @ShowInSettings int showInSettings,
+ @InheritDevicePolicy int inheritDevicePolicy) {
mDefaultProperties = null;
setShowInLauncher(showInLauncher);
setStartWithParent(startWithParent);
setShowInSettings(showInSettings);
+ setInheritDevicePolicy(inheritDevicePolicy);
}
}
diff --git a/core/java/android/credentials/ui/CreateCredentialProviderData.java b/core/java/android/credentials/ui/CreateCredentialProviderData.java
index 9cc9c72..0444278 100644
--- a/core/java/android/credentials/ui/CreateCredentialProviderData.java
+++ b/core/java/android/credentials/ui/CreateCredentialProviderData.java
@@ -34,19 +34,15 @@
public class CreateCredentialProviderData extends ProviderData implements Parcelable {
@NonNull
private final List<Entry> mSaveEntries;
- @NonNull
- private final List<Entry> mActionChips;
private final boolean mIsDefaultProvider;
@Nullable
private final Entry mRemoteEntry;
public CreateCredentialProviderData(
@NonNull String providerFlattenedComponentName, @NonNull List<Entry> saveEntries,
- @NonNull List<Entry> actionChips, boolean isDefaultProvider,
- @Nullable Entry remoteEntry) {
+ boolean isDefaultProvider, @Nullable Entry remoteEntry) {
super(providerFlattenedComponentName);
mSaveEntries = saveEntries;
- mActionChips = actionChips;
mIsDefaultProvider = isDefaultProvider;
mRemoteEntry = remoteEntry;
}
@@ -56,11 +52,6 @@
return mSaveEntries;
}
- @NonNull
- public List<Entry> getActionChips() {
- return mActionChips;
- }
-
public boolean isDefaultProvider() {
return mIsDefaultProvider;
}
@@ -78,11 +69,6 @@
mSaveEntries = credentialEntries;
AnnotationValidations.validate(NonNull.class, null, mSaveEntries);
- List<Entry> actionChips = new ArrayList<>();
- in.readTypedList(actionChips, Entry.CREATOR);
- mActionChips = actionChips;
- AnnotationValidations.validate(NonNull.class, null, mActionChips);
-
mIsDefaultProvider = in.readBoolean();
Entry remoteEntry = in.readTypedObject(Entry.CREATOR);
@@ -93,7 +79,6 @@
public void writeToParcel(@NonNull Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeTypedList(mSaveEntries);
- dest.writeTypedList(mActionChips);
dest.writeBoolean(isDefaultProvider());
dest.writeTypedObject(mRemoteEntry, flags);
}
@@ -124,7 +109,6 @@
public static class Builder {
private @NonNull String mProviderFlattenedComponentName;
private @NonNull List<Entry> mSaveEntries = new ArrayList<>();
- private @NonNull List<Entry> mActionChips = new ArrayList<>();
private boolean mIsDefaultProvider = false;
private @Nullable Entry mRemoteEntry = null;
@@ -140,13 +124,6 @@
return this;
}
- /** Sets the list of action chips to be displayed to the user. */
- @NonNull
- public Builder setActionChips(@NonNull List<Entry> actionChips) {
- mActionChips = actionChips;
- return this;
- }
-
/** Sets whether this provider is the user's selected default provider. */
@NonNull
public Builder setIsDefaultProvider(boolean isDefaultProvider) {
@@ -154,11 +131,18 @@
return this;
}
+ /** Sets the remote entry of the provider. */
+ @NonNull
+ public Builder setRemoteEntry(@Nullable Entry remoteEntry) {
+ mRemoteEntry = remoteEntry;
+ return this;
+ }
+
/** Builds a {@link CreateCredentialProviderData}. */
@NonNull
public CreateCredentialProviderData build() {
return new CreateCredentialProviderData(mProviderFlattenedComponentName,
- mSaveEntries, mActionChips, mIsDefaultProvider, mRemoteEntry);
+ mSaveEntries, mIsDefaultProvider, mRemoteEntry);
}
}
}
diff --git a/core/java/android/credentials/ui/Entry.java b/core/java/android/credentials/ui/Entry.java
index 33427d3..6fa331b 100644
--- a/core/java/android/credentials/ui/Entry.java
+++ b/core/java/android/credentials/ui/Entry.java
@@ -53,7 +53,8 @@
/** Below are only available for get flows. */
public static final String HINT_NOTE = "HINT_NOTE";
public static final String HINT_USER_NAME = "HINT_USER_NAME";
- public static final String HINT_CREDENTIAL_TYPE = "HINT_CREDENTIAL_TYPE";
+ public static final String HINT_CREDENTIAL_TYPE_DISPLAY_NAME =
+ "HINT_CREDENTIAL_TYPE_DISPLAY_NAME";
public static final String HINT_PASSKEY_USER_DISPLAY_NAME = "HINT_PASSKEY_USER_DISPLAY_NAME";
public static final String HINT_PASSWORD_VALUE = "HINT_PASSWORD_VALUE";
diff --git a/core/java/android/credentials/ui/IntentFactory.java b/core/java/android/credentials/ui/IntentFactory.java
index 4751696..b608e65 100644
--- a/core/java/android/credentials/ui/IntentFactory.java
+++ b/core/java/android/credentials/ui/IntentFactory.java
@@ -18,6 +18,7 @@
import android.content.ComponentName;
import android.content.Intent;
+import android.content.res.Resources;
import android.os.Parcel;
import android.os.ResultReceiver;
@@ -36,9 +37,10 @@
ArrayList<DisabledProviderData> disabledProviderDataList,
ResultReceiver resultReceiver) {
Intent intent = new Intent();
- // TODO: define these as proper config strings.
- String activityName = "com.android.credentialmanager/.CredentialSelectorActivity";
- intent.setComponent(ComponentName.unflattenFromString(activityName));
+ ComponentName componentName = ComponentName.unflattenFromString(
+ Resources.getSystem().getString(
+ com.android.internal.R.string.config_credentialManagerDialogComponent));
+ intent.setComponent(componentName);
intent.putParcelableArrayListExtra(
ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST, enabledProviderDataList);
diff --git a/core/java/android/graphics/fonts/FontManager.java b/core/java/android/graphics/fonts/FontManager.java
index 24480e9..beb7f36 100644
--- a/core/java/android/graphics/fonts/FontManager.java
+++ b/core/java/android/graphics/fonts/FontManager.java
@@ -198,6 +198,15 @@
*/
public static final int RESULT_ERROR_INVALID_XML = -10007;
+ /**
+ * Indicates a failure due to invalid debug certificate file.
+ *
+ * This error code is only used with the shell command interaction.
+ *
+ * @hide
+ */
+ public static final int RESULT_ERROR_INVALID_DEBUG_CERTIFICATE = -10008;
+
private FontManager(@NonNull IFontManager iFontManager) {
mIFontManager = iFontManager;
}
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 2eb8cb9..6b044fc 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -639,5 +639,24 @@
}
}
}
+
+ /**
+ * Notifies AuthService that keyguard has been dismissed for the given userId.
+ *
+ * @param userId
+ * @param hardwareAuthToken
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void resetLockout(int userId, byte[] hardwareAuthToken) {
+ if (mService != null) {
+ try {
+ mService.resetLockout(userId, hardwareAuthToken);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ }
}
diff --git a/core/java/android/hardware/biometrics/BiometricStateListener.java b/core/java/android/hardware/biometrics/BiometricStateListener.java
index b167cc6..71b7850 100644
--- a/core/java/android/hardware/biometrics/BiometricStateListener.java
+++ b/core/java/android/hardware/biometrics/BiometricStateListener.java
@@ -73,4 +73,5 @@
*/
public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
}
+
}
diff --git a/core/java/android/hardware/biometrics/IAuthService.aidl b/core/java/android/hardware/biometrics/IAuthService.aidl
index 7c3cc10b..c2e5c0b 100644
--- a/core/java/android/hardware/biometrics/IAuthService.aidl
+++ b/core/java/android/hardware/biometrics/IAuthService.aidl
@@ -79,6 +79,9 @@
void resetLockoutTimeBound(IBinder token, String opPackageName, int fromSensorId, int userId,
in byte[] hardwareAuthToken);
+ // See documentation in BiometricManager.
+ void resetLockout(int userId, in byte[] hardwareAuthToken);
+
// Provides a localized string that may be used as the label for a button that invokes
// BiometricPrompt.
CharSequence getButtonLabel(int userId, String opPackageName, int authenticators);
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index 08f9ed6..c88af5a 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -91,6 +91,10 @@
void resetLockoutTimeBound(IBinder token, String opPackageName, int fromSensorId, int userId,
in byte[] hardwareAuthToken);
+ // See documentation in BiometricManager.
+ @EnforcePermission("USE_BIOMETRIC_INTERNAL")
+ void resetLockout(int userId, in byte[] hardwareAuthToken);
+
@EnforcePermission("USE_BIOMETRIC_INTERNAL")
int getCurrentStrength(int sensorId);
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index 2a47851..bbdb626 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -802,6 +802,46 @@
}
/**
+ * Retrieve support for capture progress callbacks via
+ * {@link CameraExtensionSession.ExtensionCaptureCallback#onCaptureProcessProgressed}.
+ *
+ * @param extension the extension type
+ * @return {@code true} in case progress callbacks are supported, {@code false} otherwise
+ *
+ * @throws IllegalArgumentException in case of an unsupported extension.
+ */
+ public boolean isCaptureProcessProgressAvailable(@Extension int extension) {
+ long clientId = registerClient(mContext);
+ if (clientId < 0) {
+ throw new IllegalArgumentException("Unsupported extensions");
+ }
+
+ try {
+ if (!isExtensionSupported(mCameraId, extension, mChars)) {
+ throw new IllegalArgumentException("Unsupported extension");
+ }
+
+ if (areAdvancedExtensionsSupported()) {
+ IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
+ extender.init(mCameraId);
+ return extender.isCaptureProcessProgressAvailable();
+ } else {
+ Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
+ initializeExtension(extension);
+ extenders.second.init(mCameraId, mChars.getNativeMetadata());
+ return extenders.second.isCaptureProcessProgressAvailable();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to query the extension progress callbacks! Extension service does"
+ + " not respond!");
+ } finally {
+ unregisterClient(clientId);
+ }
+
+ return false;
+ }
+
+ /**
* Returns the set of keys supported by a {@link CaptureRequest} submitted in a
* {@link CameraExtensionSession} with a given extension type.
*
diff --git a/core/java/android/hardware/camera2/CameraExtensionSession.java b/core/java/android/hardware/camera2/CameraExtensionSession.java
index 6ddaddf..6f895d5 100644
--- a/core/java/android/hardware/camera2/CameraExtensionSession.java
+++ b/core/java/android/hardware/camera2/CameraExtensionSession.java
@@ -16,6 +16,7 @@
package android.hardware.camera2;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import java.util.concurrent.Executor;
@@ -198,6 +199,41 @@
@NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
// default empty implementation
}
+
+ /**
+ * This method is called when image capture processing is ongoing between
+ * {@link #onCaptureProcessStarted} and the processed still capture frame returning
+ * to the client surface.
+ *
+ * <p>The value included in the arguments provides clients with an estimate
+ * of the post-processing progress which could take significantly more time
+ * relative to the rest of the {@link #capture} sequence.</p>
+ *
+ * <p>The callback will be triggered only by extensions that return {@code true}
+ * from calls
+ * {@link CameraExtensionCharacteristics#isCaptureProcessProgressAvailable}.</p>
+ *
+ * <p>If support for this callback is present, then clients will be notified at least once
+ * with progress value 100.</p>
+ *
+ * <p>The callback will be triggered only for still capture requests {@link #capture} and
+ * is not supported for repeating requests {@link #setRepeatingRequest}.</p>
+ *
+ * <p>The default implementation of this method does nothing.</p>
+ *
+ * @param session The session received during
+ * {@link StateCallback#onConfigured(CameraExtensionSession)}
+ * @param request The request that was given to the CameraDevice
+ * @param progress Value between 0 and 100 (inclusive) indicating the current
+ * post-processing progress
+ *
+ * @see CameraExtensionCharacteristics#isCaptureProcessProgressAvailable
+ *
+ */
+ public void onCaptureProcessProgressed(@NonNull CameraExtensionSession session,
+ @NonNull CaptureRequest request, @IntRange(from = 0, to = 100) int progress) {
+ // default empty implementation
+ }
}
/**
diff --git a/core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl b/core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl
index 935a542..fa2cbe7 100644
--- a/core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl
+++ b/core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl
@@ -33,4 +33,5 @@
ISessionProcessorImpl getSessionProcessor();
CameraMetadataNative getAvailableCaptureRequestKeys(in String cameraId);
CameraMetadataNative getAvailableCaptureResultKeys(in String cameraId);
+ boolean isCaptureProcessProgressAvailable();
}
diff --git a/core/java/android/hardware/camera2/extension/ICaptureCallback.aidl b/core/java/android/hardware/camera2/extension/ICaptureCallback.aidl
index f3062ad..02a4690 100644
--- a/core/java/android/hardware/camera2/extension/ICaptureCallback.aidl
+++ b/core/java/android/hardware/camera2/extension/ICaptureCallback.aidl
@@ -27,4 +27,5 @@
void onCaptureSequenceCompleted(int captureSequenceId);
void onCaptureSequenceAborted(int captureSequenceId);
void onCaptureCompleted(long shutterTimestamp, int requestId, in CameraMetadataNative results);
+ void onCaptureProcessProgressed(int progress);
}
diff --git a/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl b/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl
index 3a0c3a5..615536b 100644
--- a/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl
+++ b/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl
@@ -42,4 +42,5 @@
LatencyRange getEstimatedCaptureLatencyRange(in Size outputSize);
CameraMetadataNative getAvailableCaptureRequestKeys();
CameraMetadataNative getAvailableCaptureResultKeys();
+ boolean isCaptureProcessProgressAvailable();
}
diff --git a/core/java/android/hardware/camera2/extension/IProcessResultImpl.aidl b/core/java/android/hardware/camera2/extension/IProcessResultImpl.aidl
index 4114edb..57f94c0 100644
--- a/core/java/android/hardware/camera2/extension/IProcessResultImpl.aidl
+++ b/core/java/android/hardware/camera2/extension/IProcessResultImpl.aidl
@@ -21,4 +21,5 @@
interface IProcessResultImpl
{
void onCaptureCompleted(long shutterTimestamp, in CameraMetadataNative results);
+ void onCaptureProcessProgressed(int progress);
}
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
index 01c1ef4..3a8dc03 100644
--- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -663,6 +663,19 @@
Binder.restoreCallingIdentity(ident);
}
}
+
+ @Override
+ public void onCaptureProcessProgressed(int progress) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(
+ () -> mClientCallbacks.onCaptureProcessProgressed(
+ CameraAdvancedExtensionSessionImpl.this, mClientRequest,
+ progress));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
private final class CaptureCallbackHandler extends CameraCaptureSession.CaptureCallback {
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
index 1f9f3b8..389c214 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
@@ -1387,6 +1387,18 @@
Binder.restoreCallingIdentity(ident);
}
}
+
+ @Override
+ public void onCaptureProcessProgressed(int progress) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(
+ () -> mCallbacks.onCaptureProcessProgressed(CameraExtensionSessionImpl.this,
+ mClientRequest, progress));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
// This handler can operate in three modes:
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 8311190..9b07d3a 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -439,12 +439,13 @@
SWITCHING_TYPE_NONE,
SWITCHING_TYPE_WITHIN_GROUPS,
SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS,
+ SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY,
})
@Retention(RetentionPolicy.SOURCE)
public @interface SwitchingType {}
/**
- * No mode switching will happen.
+ * No display mode switching will happen.
* @hide
*/
@TestApi
@@ -467,6 +468,13 @@
public static final int SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS = 2;
/**
+ * Allow render frame rate switches, but not physical modes.
+ * @hide
+ */
+ @TestApi
+ public static final int SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY = 3;
+
+ /**
* @hide
*/
@LongDef(flag = true, prefix = {"EVENT_FLAG_"}, value = {
@@ -1308,6 +1316,7 @@
switch (switchingType) {
case SWITCHING_TYPE_NONE:
return MATCH_CONTENT_FRAMERATE_NEVER;
+ case SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY:
case SWITCHING_TYPE_WITHIN_GROUPS:
return MATCH_CONTENT_FRAMERATE_SEAMLESSS_ONLY;
case SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS:
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 49c0f92..fd3d1ac 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -168,4 +168,9 @@
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ "android.Manifest.permission.BLUETOOTH)")
String getInputDeviceBluetoothAddress(int deviceId);
+
+ @EnforcePermission("MONITOR_INPUT")
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ + "android.Manifest.permission.MONITOR_INPUT)")
+ void pilferPointers(IBinder inputChannelToken);
}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 0cf15f7..702b39b 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -1720,6 +1720,34 @@
}
/**
+ * Pilfer pointers from an input channel.
+ *
+ * Takes all the current pointer event streams that are currently being sent to the given
+ * input channel and generates appropriate cancellations for all other windows that are
+ * receiving these pointers.
+ *
+ * This API is intended to be used in conjunction with spy windows. When a spy window pilfers
+ * pointers, the foreground windows and all other spy windows that are receiving any of the
+ * pointers that are currently being dispatched to the pilfering window will have those pointers
+ * canceled. Only the pilfering window will continue to receive events for the affected pointers
+ * until the pointer is lifted.
+ *
+ * This method should be used with caution as unexpected pilfering can break fundamental user
+ * interactions.
+ *
+ * @see android.os.InputConfig#SPY
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MONITOR_INPUT)
+ public void pilferPointers(IBinder inputChannelToken) {
+ try {
+ mIm.pilferPointers(inputChannelToken);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Adds a battery listener to be notified about {@link BatteryState} changes for an input
* device. The same listener can be registered for multiple input devices.
* The listener will be notified of the initial battery state of the device after it is
diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java
index 8b71092..59465db 100644
--- a/core/java/android/hardware/radio/RadioManager.java
+++ b/core/java/android/hardware/radio/RadioManager.java
@@ -251,7 +251,8 @@
Objects.requireNonNull(entry.getValue());
}
}
- mDabFrequencyTable = dabFrequencyTable;
+ mDabFrequencyTable = (dabFrequencyTable == null || dabFrequencyTable.isEmpty())
+ ? null : dabFrequencyTable;
mVendorInfo = (vendorInfo == null) ? new HashMap<>() : vendorInfo;
}
@@ -446,7 +447,8 @@
mIsBgScanSupported = in.readInt() == 1;
mSupportedProgramTypes = arrayToSet(in.createIntArray());
mSupportedIdentifierTypes = arrayToSet(in.createIntArray());
- mDabFrequencyTable = Utils.readStringIntMap(in);
+ Map<String, Integer> dabFrequencyTableIn = Utils.readStringIntMap(in);
+ mDabFrequencyTable = (dabFrequencyTableIn.isEmpty()) ? null : dabFrequencyTableIn;
mVendorInfo = Utils.readStringMap(in);
}
diff --git a/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java b/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java
index 95aecfe..8759a6a 100644
--- a/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java
+++ b/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java
@@ -24,6 +24,7 @@
import android.annotation.Nullable;
import android.graphics.RectF;
import android.os.Bundle;
+import android.os.CancellationSignal;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.view.KeyEvent;
@@ -697,6 +698,27 @@
}
/**
+ * Invokes one of {@link IRemoteInputConnection#previewHandwritingGesture(
+ * InputConnectionCommandHeader, ParcelableHandwritingGesture, CancellationSignal)}
+ */
+ @AnyThread
+ public boolean previewHandwritingGesture(
+ @NonNull ParcelableHandwritingGesture gesture,
+ @Nullable CancellationSignal cancellationSignal) {
+ if (cancellationSignal != null && cancellationSignal.isCanceled()) {
+ return false; // cancelled.
+ }
+
+ // TODO(b/254727073): Implement CancellationSignal
+ try {
+ mConnection.previewHandwritingGesture(createHeader(), gesture, null);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
* Invokes {@link IRemoteInputConnection#requestCursorUpdates(InputConnectionCommandHeader, int,
* int, AndroidFuture)}.
*
@@ -806,8 +828,7 @@
}
/**
- * Invokes {@code IRemoteInputConnection#replaceText(InputConnectionCommandHeader, int, int,
- * CharSequence, TextAttribute)}.
+ * Replaces the specific range in the current input field with suggested text.
*
* @param start the character index where the replacement should start.
* @param end the character index where the replacement should end.
@@ -817,8 +838,9 @@
* that this means you can't position the cursor within the text.
* @param text the text to replace. This may include styles.
* @param textAttribute The extra information about the text. This value may be null.
- * @return {@code true} if the invocation is completed without {@link RemoteException}, {@code
- * false} otherwise.
+ * @return {@code true} if the specific range is replaced successfully, {@code false} otherwise.
+ * @see android.view.inputmethod.InputConnection#replaceText(int, int, CharSequence, int,
+ * TextAttribute)
*/
@AnyThread
public boolean replaceText(
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index cea46f3..d902486 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -2268,6 +2268,8 @@
* current input field.
*
* @param id Unique identifier of the new input method to start.
+ * @throws IllegalArgumentException if the input method is unknown or filtered
+ * by the rules of <a href="/training/basics/intents/package-visibility">package visibility</a>.
*/
public void switchInputMethod(String id) {
mPrivOps.setInputMethod(id);
@@ -2280,6 +2282,8 @@
*
* @param id Unique identifier of the new input method to start.
* @param subtype The new subtype of the new input method to be switched to.
+ * @throws IllegalArgumentException if the input method is unknown or filtered
+ * by the rules of <a href="/training/basics/intents/package-visibility">package visibility</a>.
*/
public final void switchInputMethod(String id, InputMethodSubtype subtype) {
mPrivOps.setInputMethodAndSubtype(id, subtype);
diff --git a/core/java/android/inputmethodservice/RemoteInputConnection.java b/core/java/android/inputmethodservice/RemoteInputConnection.java
index 976e71f..f93f9ab 100644
--- a/core/java/android/inputmethodservice/RemoteInputConnection.java
+++ b/core/java/android/inputmethodservice/RemoteInputConnection.java
@@ -23,6 +23,7 @@
import android.annotation.Nullable;
import android.graphics.RectF;
import android.os.Bundle;
+import android.os.CancellationSignal;
import android.os.Handler;
import android.util.Log;
import android.view.KeyEvent;
@@ -34,6 +35,7 @@
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.ParcelableHandwritingGesture;
+import android.view.inputmethod.PreviewableHandwritingGesture;
import android.view.inputmethod.SurroundingText;
import android.view.inputmethod.TextAttribute;
import android.view.inputmethod.TextBoundsInfoResult;
@@ -427,6 +429,14 @@
}
@AnyThread
+ public boolean previewHandwritingGesture(
+ @NonNull PreviewableHandwritingGesture gesture,
+ @Nullable CancellationSignal cancellationSignal) {
+ return mInvoker.previewHandwritingGesture(ParcelableHandwritingGesture.of(gesture),
+ cancellationSignal);
+ }
+
+ @AnyThread
public boolean requestCursorUpdates(int cursorUpdateMode) {
if (mCancellationGroup.isCanceled()) {
return false;
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 64c1211..3282d56 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -29,7 +29,6 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.IntentFilter;
-import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.nfc.tech.MifareClassic;
@@ -525,66 +524,6 @@
}
/**
- * Helper to check if this device has FEATURE_NFC_BEAM, but without using
- * a context.
- * Equivalent to
- * context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC_BEAM)
- */
- private static boolean hasBeamFeature() {
- IPackageManager pm = ActivityThread.getPackageManager();
- if (pm == null) {
- Log.e(TAG, "Cannot get package manager, assuming no Android Beam feature");
- return false;
- }
- try {
- return pm.hasSystemFeature(PackageManager.FEATURE_NFC_BEAM, 0);
- } catch (RemoteException e) {
- Log.e(TAG, "Package manager query failed, assuming no Android Beam feature", e);
- return false;
- }
- }
-
- /**
- * Helper to check if this device has FEATURE_NFC, but without using
- * a context.
- * Equivalent to
- * context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)
- */
- private static boolean hasNfcFeature() {
- IPackageManager pm = ActivityThread.getPackageManager();
- if (pm == null) {
- Log.e(TAG, "Cannot get package manager, assuming no NFC feature");
- return false;
- }
- try {
- return pm.hasSystemFeature(PackageManager.FEATURE_NFC, 0);
- } catch (RemoteException e) {
- Log.e(TAG, "Package manager query failed, assuming no NFC feature", e);
- return false;
- }
- }
-
- /**
- * Helper to check if this device is NFC HCE capable, by checking for
- * FEATURE_NFC_HOST_CARD_EMULATION and/or FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
- * but without using a context.
- */
- private static boolean hasNfcHceFeature() {
- IPackageManager pm = ActivityThread.getPackageManager();
- if (pm == null) {
- Log.e(TAG, "Cannot get package manager, assuming no NFC feature");
- return false;
- }
- try {
- return pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION, 0)
- || pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF, 0);
- } catch (RemoteException e) {
- Log.e(TAG, "Package manager query failed, assuming no NFC feature", e);
- return false;
- }
- }
-
- /**
* Return list of Secure Elements which support off host card emulation.
*
* @return List<String> containing secure elements on the device which supports
@@ -593,23 +532,21 @@
* @hide
*/
public @NonNull List<String> getSupportedOffHostSecureElements() {
+ if (mContext == null) {
+ throw new UnsupportedOperationException("You need a context on NfcAdapter to use the "
+ + " getSupportedOffHostSecureElements APIs");
+ }
List<String> offHostSE = new ArrayList<String>();
- IPackageManager pm = ActivityThread.getPackageManager();
+ PackageManager pm = mContext.getPackageManager();
if (pm == null) {
Log.e(TAG, "Cannot get package manager, assuming no off-host CE feature");
return offHostSE;
}
- try {
- if (pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC, 0)) {
- offHostSE.add("SIM");
- }
- if (pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE, 0)) {
- offHostSE.add("eSE");
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Package manager query failed, assuming no off-host CE feature", e);
- offHostSE.clear();
- return offHostSE;
+ if (pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC)) {
+ offHostSE.add("SIM");
+ }
+ if (pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE)) {
+ offHostSE.add("eSE");
}
return offHostSE;
}
@@ -621,10 +558,19 @@
*/
@UnsupportedAppUsage
public static synchronized NfcAdapter getNfcAdapter(Context context) {
+ if (context == null) {
+ if (sNullContextNfcAdapter == null) {
+ sNullContextNfcAdapter = new NfcAdapter(null);
+ }
+ return sNullContextNfcAdapter;
+ }
if (!sIsInitialized) {
- sHasNfcFeature = hasNfcFeature();
- sHasBeamFeature = hasBeamFeature();
- boolean hasHceFeature = hasNfcHceFeature();
+ PackageManager pm = context.getPackageManager();
+ sHasNfcFeature = pm.hasSystemFeature(PackageManager.FEATURE_NFC);
+ sHasBeamFeature = pm.hasSystemFeature(PackageManager.FEATURE_NFC_BEAM);
+ boolean hasHceFeature =
+ pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)
+ || pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF);
/* is this device meant to have NFC */
if (!sHasNfcFeature && !hasHceFeature) {
Log.v(TAG, "this device does not have NFC support");
@@ -660,12 +606,6 @@
sIsInitialized = true;
}
- if (context == null) {
- if (sNullContextNfcAdapter == null) {
- sNullContextNfcAdapter = new NfcAdapter(null);
- }
- return sNullContextNfcAdapter;
- }
NfcAdapter adapter = sNfcAdapters.get(context);
if (adapter == null) {
adapter = new NfcAdapter(context);
@@ -676,8 +616,12 @@
/** get handle to NFC service interface */
private static INfcAdapter getServiceInterface() {
+ if (!sHasNfcFeature) {
+ /* NFC is not supported */
+ return null;
+ }
/* get a handle to NFC service */
- IBinder b = ServiceManager.getService("nfc");
+ IBinder b = ServiceManager.waitForService("nfc");
if (b == null) {
return null;
}
@@ -707,6 +651,15 @@
"context not associated with any application (using a mock context?)");
}
+ synchronized (NfcAdapter.class) {
+ if (!sIsInitialized) {
+ PackageManager pm = context.getPackageManager();
+ sHasNfcFeature = pm.hasSystemFeature(PackageManager.FEATURE_NFC);
+ }
+ if (!sHasNfcFeature) {
+ return null;
+ }
+ }
if (getServiceInterface() == null) {
// NFC is not available
return null;
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index 0b56d19..6a42091 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -22,11 +22,9 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.Activity;
-import android.app.ActivityThread;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.nfc.INfcCardEmulation;
import android.nfc.NfcAdapter;
@@ -158,18 +156,13 @@
throw new UnsupportedOperationException();
}
if (!sIsInitialized) {
- IPackageManager pm = ActivityThread.getPackageManager();
+ PackageManager pm = context.getPackageManager();
if (pm == null) {
Log.e(TAG, "Cannot get PackageManager");
throw new UnsupportedOperationException();
}
- try {
- if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION, 0)) {
- Log.e(TAG, "This device does not support card emulation");
- throw new UnsupportedOperationException();
- }
- } catch (RemoteException e) {
- Log.e(TAG, "PackageManager query failed.");
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
+ Log.e(TAG, "This device does not support card emulation");
throw new UnsupportedOperationException();
}
sIsInitialized = true;
diff --git a/core/java/android/nfc/cardemulation/NfcFCardEmulation.java b/core/java/android/nfc/cardemulation/NfcFCardEmulation.java
index 3c92455..48bbf5b6 100644
--- a/core/java/android/nfc/cardemulation/NfcFCardEmulation.java
+++ b/core/java/android/nfc/cardemulation/NfcFCardEmulation.java
@@ -17,10 +17,8 @@
package android.nfc.cardemulation;
import android.app.Activity;
-import android.app.ActivityThread;
import android.content.ComponentName;
import android.content.Context;
-import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.nfc.INfcFCardEmulation;
import android.nfc.NfcAdapter;
@@ -70,18 +68,13 @@
throw new UnsupportedOperationException();
}
if (!sIsInitialized) {
- IPackageManager pm = ActivityThread.getPackageManager();
+ PackageManager pm = context.getPackageManager();
if (pm == null) {
Log.e(TAG, "Cannot get PackageManager");
throw new UnsupportedOperationException();
}
- try {
- if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF, 0)) {
- Log.e(TAG, "This device does not support NFC-F card emulation");
- throw new UnsupportedOperationException();
- }
- } catch (RemoteException e) {
- Log.e(TAG, "PackageManager query failed.");
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF)) {
+ Log.e(TAG, "This device does not support NFC-F card emulation");
throw new UnsupportedOperationException();
}
sIsInitialized = true;
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 3c4abab..8e55692 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -911,11 +911,15 @@
final String transactionName = getTransactionName(transactionCode);
final StringBuffer buf = new StringBuffer();
+ // Keep trace name consistent with cpp trace name in:
+ // system/tools/aidl/generate_cpp.cpp
+ buf.append("AIDL::java::");
if (transactionName != null) {
- buf.append(mSimpleDescriptor).append(":").append(transactionName);
+ buf.append(mSimpleDescriptor).append("::").append(transactionName);
} else {
- buf.append(mSimpleDescriptor).append("#").append(transactionCode);
+ buf.append(mSimpleDescriptor).append("::#").append(transactionCode);
}
+ buf.append("::server");
transactionTraceName = buf.toString();
mTransactionTraceNames.setRelease(index, transactionTraceName);
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 6330661..1929a4d 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -536,8 +536,8 @@
mWarnOnBlocking = false;
warnOnBlocking = false;
- if (Build.IS_USERDEBUG) {
- // Log this as a WTF on userdebug builds.
+ if (Build.IS_USERDEBUG || Build.IS_ENG) {
+ // Log this as a WTF on userdebug and eng builds.
Log.wtf(Binder.TAG,
"Outgoing transactions from this process must be FLAG_ONEWAY",
new Throwable());
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 249f486..44a1fa5 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1446,6 +1446,28 @@
return IS_DEBUGGABLE;
}
+
+ /**
+ * Returns true if the device is running a secure build, such as "user" or "userdebug".
+ *
+ * Secure builds drop adbd privileges by default, though debuggable builds still allow users
+ * to gain root access via local shell. See should_drop_privileges() in adb for details.
+ * @hide
+ */
+ private static final boolean IS_SECURE =
+ SystemProperties.getBoolean("ro.secure", true);
+ /**
+ * Returns true if the device is running a secure build, such as "user" or "userdebug".
+ *
+ * Secure builds drop adbd privileges by default, though debuggable builds still allow users
+ * to gain root access via local shell. See should_drop_privileges() in adb for details.
+ * @hide
+ */
+ @TestApi
+ public static boolean isSecure() {
+ return IS_SECURE;
+ }
+
/** {@hide} */
public static final boolean IS_ENG = "eng".equals(TYPE);
/** {@hide} */
diff --git a/core/java/android/os/IHintSession.aidl b/core/java/android/os/IHintSession.aidl
index 09bc4cc..0d1dde1 100644
--- a/core/java/android/os/IHintSession.aidl
+++ b/core/java/android/os/IHintSession.aidl
@@ -22,4 +22,5 @@
void updateTargetWorkDuration(long targetDurationNanos);
void reportActualWorkDuration(in long[] actualDurationNanos, in long[] timeStampNanos);
void close();
+ void sendHint(int hint);
}
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 8eaa5ad..a887f2a 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -130,7 +130,7 @@
boolean isUserRunning(int userId);
boolean isUserForeground(int userId);
boolean isUserVisible(int userId);
- List<UserHandle> getVisibleUsers();
+ int[] getVisibleUsers();
boolean isUserNameSet(int userId);
boolean hasRestrictedProfiles(int userId);
boolean requestQuietModeEnabled(String callingPackage, boolean enableQuietMode, int userId, in IntentSender target, int flags);
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 9d8df7e..1924dc6 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -73,4 +73,3 @@
# PermissionEnforcer
per-file PermissionEnforcer.java = tweek@google.com, brufino@google.com
-per-file PermissionEnforcer.java = file:/core/java/android/permission/OWNERS
diff --git a/core/java/android/os/PerformanceHintManager.java b/core/java/android/os/PerformanceHintManager.java
index a75b5ef..86135bc 100644
--- a/core/java/android/os/PerformanceHintManager.java
+++ b/core/java/android/os/PerformanceHintManager.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemService;
@@ -24,6 +25,10 @@
import com.android.internal.util.Preconditions;
import java.io.Closeable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.Reference;
+
/** The PerformanceHintManager allows apps to send performance hint to system. */
@SystemService(Context.PERFORMANCE_HINT_SERVICE)
@@ -104,6 +109,40 @@
mNativeSessionPtr = nativeSessionPtr;
}
+ /**
+ * This hint indicates a sudden increase in CPU workload intensity. It means
+ * that this hint session needs extra CPU resources immediately to meet the
+ * target duration for the current work cycle.
+ */
+ public static final int CPU_LOAD_UP = 0;
+ /**
+ * This hint indicates a decrease in CPU workload intensity. It means that
+ * this hint session can reduce CPU resources and still meet the target duration.
+ */
+ public static final int CPU_LOAD_DOWN = 1;
+ /*
+ * This hint indicates an upcoming CPU workload that is completely changed and
+ * unknown. It means that the hint session should reset CPU resources to a known
+ * baseline to prepare for an arbitrary load, and must wake up if inactive.
+ */
+ public static final int CPU_LOAD_RESET = 2;
+ /*
+ * This hint indicates that the most recent CPU workload is resuming after a
+ * period of inactivity. It means that the hint session should allocate similar
+ * CPU resources to what was used previously, and must wake up if inactive.
+ */
+ public static final int CPU_LOAD_RESUME = 3;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"CPU_LOAD_"}, value = {
+ CPU_LOAD_UP,
+ CPU_LOAD_DOWN,
+ CPU_LOAD_RESET,
+ CPU_LOAD_RESUME
+ })
+ public @interface Hint {}
+
/** @hide */
@Override
protected void finalize() throws Throwable {
@@ -152,6 +191,21 @@
mNativeSessionPtr = 0;
}
}
+
+ /**
+ * Sends performance hints to inform the hint session of changes in the workload.
+ *
+ * @param hint The hint to send to the session.
+ */
+ public void sendHint(@Hint int hint) {
+ Preconditions.checkArgumentNonNegative(hint, "the hint ID should be at least"
+ + " zero.");
+ try {
+ nativeSendHint(mNativeSessionPtr, hint);
+ } finally {
+ Reference.reachabilityFence(this);
+ }
+ }
}
private static native long nativeAcquireManager();
@@ -163,4 +217,5 @@
private static native void nativeReportActualWorkDuration(long nativeSessionPtr,
long actualDurationNanos);
private static native void nativeCloseSession(long nativeSessionPtr);
+ private static native void nativeSendHint(long nativeSessionPtr, int hint);
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 3d20d63..bda2a35 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1506,6 +1506,30 @@
public static final String DISALLOW_CELLULAR_2G = "no_cellular_2g";
/**
+ * This user restriction specifies if Ultra-wideband is disallowed on the device. If
+ * Ultra-wideband is disallowed it cannot be turned on via Settings.
+ *
+ * <p>This restriction can only be set by a device owner or a profile owner of an
+ * organization-owned managed profile on the parent profile.
+ * In both cases, the restriction applies globally on the device and will turn off the
+ * ultra-wideband radio if it's currently on and prevent the radio from being turned on in
+ * the future.
+ *
+ * <p>
+ * Ultra-wideband (UWB) is a radio technology that can use a very low energy level
+ * for short-range, high-bandwidth communications over a large portion of the radio spectrum.
+ *
+ * <p>Default is <code>false</code>.
+ *
+ * <p>Key for user restrictions.
+ * <p>Type: Boolean
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_ULTRA_WIDEBAND_RADIO = "no_ultra_wideband_radio";
+
+ /**
* List of key values that can be passed into the various user restriction related methods
* in {@link UserManager} & {@link DevicePolicyManager}.
* Note: This is slightly different from the real set of user restrictions listed in {@link
@@ -1587,6 +1611,7 @@
DISALLOW_WIFI_DIRECT,
DISALLOW_ADD_WIFI_CONFIG,
DISALLOW_CELLULAR_2G,
+ DISALLOW_ULTRA_WIDEBAND_RADIO,
})
@Retention(RetentionPolicy.SOURCE)
public @interface UserRestrictionKey {}
@@ -2904,12 +2929,19 @@
*/
@RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
Manifest.permission.INTERACT_ACROSS_USERS})
- public @NonNull List<UserHandle> getVisibleUsers() {
+ public @NonNull Set<UserHandle> getVisibleUsers() {
+ ArraySet<UserHandle> result = new ArraySet<>();
try {
- return mService.getVisibleUsers();
+ int[] visibleUserIds = mService.getVisibleUsers();
+ if (visibleUserIds != null) {
+ for (int userId : visibleUserIds) {
+ result.add(UserHandle.of(userId));
+ }
+ }
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
+ return result;
}
/**
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index 8534c66..16ae3bc 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -77,8 +77,7 @@
List<SplitPermissionInfoParcelable> getSplitPermissions();
void startOneTimePermissionSession(String packageName, int userId, long timeout,
- long revokeAfterKilledDelay, int importanceToResetTimer,
- int importanceToKeepSessionAlive);
+ long revokeAfterKilledDelay);
@EnforcePermission("MANAGE_ONE_TIME_PERMISSION_SESSIONS")
void stopOneTimePermissionSession(String packageName, int userId);
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 6b540d7..6769954 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -1371,8 +1371,7 @@
@ActivityManager.RunningAppProcessInfo.Importance int importanceToKeepSessionAlive) {
try {
mPermissionManager.startOneTimePermissionSession(packageName, mContext.getUserId(),
- timeoutMillis, revokeAfterKilledDelayMillis, importanceToResetTimer,
- importanceToKeepSessionAlive);
+ timeoutMillis, revokeAfterKilledDelayMillis);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index fab6f7b..ad39c0c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -192,6 +192,21 @@
"android.settings.LOCATION_SCANNING_SETTINGS";
/**
+ * Activity Action: Show settings to manage creation/deletion of cloned apps.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you
+ * safeguard against this.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_MANAGE_CLONED_APPS_SETTINGS =
+ "android.settings.MANAGE_CLONED_APPS_SETTINGS";
+
+ /**
* Activity Action: Show settings to allow configuration of users.
* <p>
* In some cases, a matching Activity may not exist, so ensure you
@@ -675,6 +690,22 @@
"android.settings.WIFI_SETTINGS";
/**
+ * Activity Action: Show settings to allow configuration of MTE.
+ * <p>
+ * Memory Tagging Extension (MTE) is a CPU extension that allows to protect against certain
+ * classes of security problems at a small runtime performance cost overhead.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you safeguard against this.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_MEMTAG_SETTINGS =
+ "android.settings.MEMTAG_SETTINGS";
+
+ /**
* Activity Action: Show settings to allow configuration of a static IP
* address for Wi-Fi.
* <p>
diff --git a/core/java/android/service/controls/ControlsProviderService.java b/core/java/android/service/controls/ControlsProviderService.java
index 47b16a3..d2a4ae2 100644
--- a/core/java/android/service/controls/ControlsProviderService.java
+++ b/core/java/android/service/controls/ControlsProviderService.java
@@ -55,6 +55,20 @@
"android.service.controls.ControlsProviderService";
/**
+ * Manifest metadata to show a custom embedded activity as part of device controls.
+ *
+ * The value of this metadata must be the {@link ComponentName} as a string of an activity in
+ * the same package that will be launched as part of a TaskView.
+ *
+ * The activity must be exported, enabled and protected by
+ * {@link Manifest.permission.BIND_CONTROLS}.
+ *
+ * @hide
+ */
+ public static final String META_DATA_PANEL_ACTIVITY =
+ "android.service.controls.META_DATA_PANEL_ACTIVITY";
+
+ /**
* @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
diff --git a/core/java/android/service/dreams/DreamManagerInternal.java b/core/java/android/service/dreams/DreamManagerInternal.java
index 5f30ad0..cd38e8a 100644
--- a/core/java/android/service/dreams/DreamManagerInternal.java
+++ b/core/java/android/service/dreams/DreamManagerInternal.java
@@ -16,7 +16,6 @@
package android.service.dreams;
-import android.content.ComponentName;
/**
* Dream manager local system service interface.
@@ -59,19 +58,4 @@
* @param isScreenOn True if the screen is currently on.
*/
public abstract boolean canStartDreaming(boolean isScreenOn);
-
- /**
- * Called by the ActivityTaskManagerService to verify that the startDreamActivity
- * request comes from the current active dream component.
- *
- * This function and its call path should not acquire the DreamManagerService lock
- * to avoid deadlock with the ActivityTaskManager lock.
- *
- * TODO: Make this interaction push-based - the DreamManager should inform the
- * ActivityTaskManager whenever the active dream component changes.
- *
- * @param doze If true returns the current active doze component. Otherwise, returns the
- * active dream component.
- */
- public abstract ComponentName getActiveDreamComponent(boolean doze);
}
diff --git a/core/java/android/service/timezone/TimeZoneProviderEvent.java b/core/java/android/service/timezone/TimeZoneProviderEvent.java
index 714afee..e64bdd6 100644
--- a/core/java/android/service/timezone/TimeZoneProviderEvent.java
+++ b/core/java/android/service/timezone/TimeZoneProviderEvent.java
@@ -74,39 +74,53 @@
@Nullable
private final String mFailureCause;
- // Populated when mType == EVENT_TYPE_SUGGESTION or EVENT_TYPE_UNCERTAIN
+ // May be populated when EVENT_TYPE_SUGGESTION or EVENT_TYPE_UNCERTAIN
@Nullable
private final TimeZoneProviderStatus mTimeZoneProviderStatus;
- private TimeZoneProviderEvent(int type,
+ private TimeZoneProviderEvent(@EventType int type,
@ElapsedRealtimeLong long creationElapsedMillis,
@Nullable TimeZoneProviderSuggestion suggestion,
@Nullable String failureCause,
@Nullable TimeZoneProviderStatus timeZoneProviderStatus) {
- mType = type;
+ mType = validateEventType(type);
mCreationElapsedMillis = creationElapsedMillis;
mSuggestion = suggestion;
mFailureCause = failureCause;
mTimeZoneProviderStatus = timeZoneProviderStatus;
+
+ // Confirm the type and the provider status agree.
+ if (mType == EVENT_TYPE_PERMANENT_FAILURE && mTimeZoneProviderStatus != null) {
+ throw new IllegalArgumentException(
+ "Unexpected status: mType=" + mType
+ + ", mTimeZoneProviderStatus=" + mTimeZoneProviderStatus);
+ }
+ }
+
+ private static @EventType int validateEventType(@EventType int eventType) {
+ if (eventType < EVENT_TYPE_PERMANENT_FAILURE || eventType > EVENT_TYPE_UNCERTAIN) {
+ throw new IllegalArgumentException(Integer.toString(eventType));
+ }
+ return eventType;
}
/** Returns an event of type {@link #EVENT_TYPE_SUGGESTION}. */
public static TimeZoneProviderEvent createSuggestionEvent(
@ElapsedRealtimeLong long creationElapsedMillis,
@NonNull TimeZoneProviderSuggestion suggestion,
- @NonNull TimeZoneProviderStatus providerStatus) {
+ @Nullable TimeZoneProviderStatus providerStatus) {
return new TimeZoneProviderEvent(EVENT_TYPE_SUGGESTION, creationElapsedMillis,
- Objects.requireNonNull(suggestion), null, Objects.requireNonNull(providerStatus));
+ Objects.requireNonNull(suggestion), null, providerStatus);
}
/** Returns an event of type {@link #EVENT_TYPE_UNCERTAIN}. */
public static TimeZoneProviderEvent createUncertainEvent(
@ElapsedRealtimeLong long creationElapsedMillis,
- @NonNull TimeZoneProviderStatus timeZoneProviderStatus) {
+ @Nullable TimeZoneProviderStatus timeZoneProviderStatus) {
return new TimeZoneProviderEvent(
EVENT_TYPE_UNCERTAIN, creationElapsedMillis, null, null,
- Objects.requireNonNull(timeZoneProviderStatus));
+ timeZoneProviderStatus);
}
/** Returns an event of type {@link #EVENT_TYPE_PERMANENT_FAILURE}. */
@@ -148,8 +162,8 @@
}
/**
- * Returns the status of the time zone provider. Populated when {@link #getType()} is {@link
- * #EVENT_TYPE_UNCERTAIN} or {@link #EVENT_TYPE_SUGGESTION}.
+ * Returns the status of the time zone provider. May be populated when {@link #getType()} is
+ * {@link #EVENT_TYPE_UNCERTAIN} or {@link #EVENT_TYPE_SUGGESTION}, otherwise {@code null}.
*/
@Nullable
public TimeZoneProviderStatus getTimeZoneProviderStatus() {
diff --git a/core/java/android/service/timezone/TimeZoneProviderService.java b/core/java/android/service/timezone/TimeZoneProviderService.java
index cd4a305..2cea95a 100644
--- a/core/java/android/service/timezone/TimeZoneProviderService.java
+++ b/core/java/android/service/timezone/TimeZoneProviderService.java
@@ -203,7 +203,8 @@
* details.
*/
public final void reportSuggestion(@NonNull TimeZoneProviderSuggestion suggestion) {
- reportSuggestion(suggestion, TimeZoneProviderStatus.UNKNOWN);
+ TimeZoneProviderStatus providerStatus = null;
+ reportSuggestionInternal(suggestion, providerStatus);
}
/**
@@ -217,6 +218,12 @@
*/
public final void reportSuggestion(@NonNull TimeZoneProviderSuggestion suggestion,
@NonNull TimeZoneProviderStatus providerStatus) {
+ Objects.requireNonNull(providerStatus);
+ reportSuggestionInternal(suggestion, providerStatus);
+ }
+
+ private void reportSuggestionInternal(@NonNull TimeZoneProviderSuggestion suggestion,
+ @Nullable TimeZoneProviderStatus providerStatus) {
Objects.requireNonNull(suggestion);
mHandler.post(() -> {
@@ -245,7 +252,8 @@
* to a time zone.
*/
public final void reportUncertain() {
- reportUncertain(TimeZoneProviderStatus.UNKNOWN);
+ TimeZoneProviderStatus providerStatus = null;
+ reportUncertainInternal(providerStatus);
}
/**
@@ -260,6 +268,11 @@
* @hide
*/
public final void reportUncertain(@NonNull TimeZoneProviderStatus providerStatus) {
+ Objects.requireNonNull(providerStatus);
+ reportUncertainInternal(providerStatus);
+ }
+
+ private void reportUncertainInternal(@Nullable TimeZoneProviderStatus providerStatus) {
mHandler.post(() -> {
synchronized (mLock) {
ITimeZoneProviderManager manager = mManager;
diff --git a/core/java/android/service/timezone/TimeZoneProviderStatus.java b/core/java/android/service/timezone/TimeZoneProviderStatus.java
index 87d7843..513068f 100644
--- a/core/java/android/service/timezone/TimeZoneProviderStatus.java
+++ b/core/java/android/service/timezone/TimeZoneProviderStatus.java
@@ -18,14 +18,18 @@
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 java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Information about the status of a {@link TimeZoneProviderService}.
@@ -71,7 +75,7 @@
@IntDef(prefix = "DEPENDENCY_STATUS_", value = {
DEPENDENCY_STATUS_UNKNOWN,
DEPENDENCY_STATUS_NOT_APPLICABLE,
- DEPENDENCY_STATUS_WORKING,
+ DEPENDENCY_STATUS_OK,
DEPENDENCY_STATUS_TEMPORARILY_UNAVAILABLE,
DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT,
DEPENDENCY_STATUS_DEGRADED_BY_SETTINGS,
@@ -81,14 +85,18 @@
@Retention(RetentionPolicy.SOURCE)
public @interface DependencyStatus {}
- /** The dependency's status is unknown. */
+ /**
+ * The dependency's status is unknown.
+ *
+ * @hide
+ */
public static final @DependencyStatus int DEPENDENCY_STATUS_UNKNOWN = 0;
/** The dependency is not used by the provider's implementation. */
public static final @DependencyStatus int DEPENDENCY_STATUS_NOT_APPLICABLE = 1;
- /** The dependency is applicable and working well. */
- public static final @DependencyStatus int DEPENDENCY_STATUS_WORKING = 2;
+ /** The dependency is applicable and there are no known problems. */
+ public static final @DependencyStatus int DEPENDENCY_STATUS_OK = 2;
/**
* The dependency is used but is temporarily unavailable, e.g. connectivity has been lost for an
@@ -136,76 +144,105 @@
@IntDef(prefix = "OPERATION_STATUS_", value = {
OPERATION_STATUS_UNKNOWN,
OPERATION_STATUS_NOT_APPLICABLE,
- OPERATION_STATUS_WORKING,
+ OPERATION_STATUS_OK,
OPERATION_STATUS_FAILED,
})
@Target(ElementType.TYPE_USE)
@Retention(RetentionPolicy.SOURCE)
public @interface OperationStatus {}
- /** The operation's status is unknown. */
+ /**
+ * The operation's status is unknown.
+ *
+ * @hide
+ */
public static final @OperationStatus int OPERATION_STATUS_UNKNOWN = 0;
/** The operation is not used by the provider's implementation. */
public static final @OperationStatus int OPERATION_STATUS_NOT_APPLICABLE = 1;
- /** The operation is applicable and working well. */
- public static final @OperationStatus int OPERATION_STATUS_WORKING = 2;
+ /** The operation is applicable and there are no known problems. */
+ public static final @OperationStatus int OPERATION_STATUS_OK = 2;
- /** The operation is applicable and failed. */
+ /** The operation is applicable and it recently failed. */
public static final @OperationStatus int OPERATION_STATUS_FAILED = 3;
- /**
- * An instance that provides no information about status. Effectively a "null" status.
- */
- @NonNull
- public static final TimeZoneProviderStatus UNKNOWN = new TimeZoneProviderStatus(
- DEPENDENCY_STATUS_UNKNOWN, DEPENDENCY_STATUS_UNKNOWN, OPERATION_STATUS_UNKNOWN);
-
- private final @DependencyStatus int mLocationDetectionStatus;
- private final @DependencyStatus int mConnectivityStatus;
- private final @OperationStatus int mTimeZoneResolutionStatus;
+ private final @DependencyStatus int mLocationDetectionDependencyStatus;
+ private final @DependencyStatus int mConnectivityDependencyStatus;
+ private final @OperationStatus int mTimeZoneResolutionOperationStatus;
private TimeZoneProviderStatus(
@DependencyStatus int locationDetectionStatus,
@DependencyStatus int connectivityStatus,
@OperationStatus int timeZoneResolutionStatus) {
- mLocationDetectionStatus = requireValidDependencyStatus(locationDetectionStatus);
- mConnectivityStatus = requireValidDependencyStatus(connectivityStatus);
- mTimeZoneResolutionStatus = requireValidOperationStatus(timeZoneResolutionStatus);
+ mLocationDetectionDependencyStatus = locationDetectionStatus;
+ mConnectivityDependencyStatus = connectivityStatus;
+ mTimeZoneResolutionOperationStatus = timeZoneResolutionStatus;
}
/**
* Returns the status of the location detection dependencies used by the provider (where
* applicable).
*/
- public @DependencyStatus int getLocationDetectionStatus() {
- return mLocationDetectionStatus;
+ public @DependencyStatus int getLocationDetectionDependencyStatus() {
+ return mLocationDetectionDependencyStatus;
}
/**
* Returns the status of the connectivity dependencies used by the provider (where applicable).
*/
- public @DependencyStatus int getConnectivityStatus() {
- return mConnectivityStatus;
+ public @DependencyStatus int getConnectivityDependencyStatus() {
+ return mConnectivityDependencyStatus;
}
/**
* Returns the status of the time zone resolution operation used by the provider.
*/
- public @OperationStatus int getTimeZoneResolutionStatus() {
- return mTimeZoneResolutionStatus;
+ public @OperationStatus int getTimeZoneResolutionOperationStatus() {
+ return mTimeZoneResolutionOperationStatus;
}
@Override
public String toString() {
return "TimeZoneProviderStatus{"
- + "mLocationDetectionStatus=" + mLocationDetectionStatus
- + ", mConnectivityStatus=" + mConnectivityStatus
- + ", mTimeZoneResolutionStatus=" + mTimeZoneResolutionStatus
+ + "mLocationDetectionDependencyStatus="
+ + dependencyStatusToString(mLocationDetectionDependencyStatus)
+ + ", mConnectivityDependencyStatus="
+ + dependencyStatusToString(mConnectivityDependencyStatus)
+ + ", mTimeZoneResolutionOperationStatus="
+ + operationStatusToString(mTimeZoneResolutionOperationStatus)
+ '}';
}
+ /**
+ * Parses a {@link TimeZoneProviderStatus} from a toString() string for manual command-line
+ * testing.
+ *
+ * @hide
+ */
+ @NonNull
+ public static TimeZoneProviderStatus parseProviderStatus(@NonNull String arg) {
+ // Note: "}" has to be escaped on Android with "\\}" because the regexp library is not based
+ // on OpenJDK code.
+ Pattern pattern = Pattern.compile("TimeZoneProviderStatus\\{"
+ + "mLocationDetectionDependencyStatus=([^,]+)"
+ + ", mConnectivityDependencyStatus=([^,]+)"
+ + ", mTimeZoneResolutionOperationStatus=([^\\}]+)"
+ + "\\}");
+ Matcher matcher = pattern.matcher(arg);
+ if (!matcher.matches()) {
+ throw new IllegalArgumentException("Unable to parse provider status: " + arg);
+ }
+ @DependencyStatus int locationDependencyStatus =
+ dependencyStatusFromString(matcher.group(1));
+ @DependencyStatus int connectivityDependencyStatus =
+ dependencyStatusFromString(matcher.group(2));
+ @OperationStatus int timeZoneResolutionOperationStatus =
+ operationStatusFromString(matcher.group(3));
+ return new TimeZoneProviderStatus(locationDependencyStatus, connectivityDependencyStatus,
+ timeZoneResolutionOperationStatus);
+ }
+
public static final @NonNull Creator<TimeZoneProviderStatus> CREATOR = new Creator<>() {
@Override
public TimeZoneProviderStatus createFromParcel(Parcel in) {
@@ -229,9 +266,9 @@
@Override
public void writeToParcel(@NonNull Parcel parcel, int flags) {
- parcel.writeInt(mLocationDetectionStatus);
- parcel.writeInt(mConnectivityStatus);
- parcel.writeInt(mTimeZoneResolutionStatus);
+ parcel.writeInt(mLocationDetectionDependencyStatus);
+ parcel.writeInt(mConnectivityDependencyStatus);
+ parcel.writeInt(mTimeZoneResolutionOperationStatus);
}
@Override
@@ -243,23 +280,33 @@
return false;
}
TimeZoneProviderStatus that = (TimeZoneProviderStatus) o;
- return mLocationDetectionStatus == that.mLocationDetectionStatus
- && mConnectivityStatus == that.mConnectivityStatus
- && mTimeZoneResolutionStatus == that.mTimeZoneResolutionStatus;
+ return mLocationDetectionDependencyStatus == that.mLocationDetectionDependencyStatus
+ && mConnectivityDependencyStatus == that.mConnectivityDependencyStatus
+ && mTimeZoneResolutionOperationStatus == that.mTimeZoneResolutionOperationStatus;
}
@Override
public int hashCode() {
return Objects.hash(
- mLocationDetectionStatus, mConnectivityStatus, mTimeZoneResolutionStatus);
+ mLocationDetectionDependencyStatus, mConnectivityDependencyStatus,
+ mTimeZoneResolutionOperationStatus);
+ }
+
+ /** @hide */
+ public boolean couldEnableTelephonyFallback() {
+ return mLocationDetectionDependencyStatus == DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT
+ || mLocationDetectionDependencyStatus == DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS
+ || mConnectivityDependencyStatus == DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT
+ || mConnectivityDependencyStatus == DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS;
}
/** A builder for {@link TimeZoneProviderStatus}. */
public static final class Builder {
- private @DependencyStatus int mLocationDetectionStatus = DEPENDENCY_STATUS_UNKNOWN;
- private @DependencyStatus int mConnectivityStatus = DEPENDENCY_STATUS_UNKNOWN;
- private @OperationStatus int mTimeZoneResolutionStatus = OPERATION_STATUS_UNKNOWN;
+ private @DependencyStatus int mLocationDetectionDependencyStatus =
+ DEPENDENCY_STATUS_UNKNOWN;
+ private @DependencyStatus int mConnectivityDependencyStatus = DEPENDENCY_STATUS_UNKNOWN;
+ private @OperationStatus int mTimeZoneResolutionOperationStatus = OPERATION_STATUS_UNKNOWN;
/**
* Creates a new builder instance. At creation time all status properties are set to
@@ -272,9 +319,9 @@
* @hide
*/
public Builder(TimeZoneProviderStatus toCopy) {
- mLocationDetectionStatus = toCopy.mLocationDetectionStatus;
- mConnectivityStatus = toCopy.mConnectivityStatus;
- mTimeZoneResolutionStatus = toCopy.mTimeZoneResolutionStatus;
+ mLocationDetectionDependencyStatus = toCopy.mLocationDetectionDependencyStatus;
+ mConnectivityDependencyStatus = toCopy.mConnectivityDependencyStatus;
+ mTimeZoneResolutionOperationStatus = toCopy.mTimeZoneResolutionOperationStatus;
}
/**
@@ -282,8 +329,9 @@
* See the {@code DEPENDENCY_STATUS_} constants for more information.
*/
@NonNull
- public Builder setLocationDetectionStatus(@DependencyStatus int locationDetectionStatus) {
- mLocationDetectionStatus = locationDetectionStatus;
+ public Builder setLocationDetectionDependencyStatus(
+ @DependencyStatus int locationDetectionStatus) {
+ mLocationDetectionDependencyStatus = locationDetectionStatus;
return this;
}
@@ -292,8 +340,8 @@
* See the {@code DEPENDENCY_STATUS_} constants for more information.
*/
@NonNull
- public Builder setConnectivityStatus(@DependencyStatus int connectivityStatus) {
- mConnectivityStatus = connectivityStatus;
+ public Builder setConnectivityDependencyStatus(@DependencyStatus int connectivityStatus) {
+ mConnectivityDependencyStatus = connectivityStatus;
return this;
}
@@ -302,8 +350,9 @@
* See the {@code OPERATION_STATUS_} constants for more information.
*/
@NonNull
- public Builder setTimeZoneResolutionStatus(@OperationStatus int timeZoneResolutionStatus) {
- mTimeZoneResolutionStatus = timeZoneResolutionStatus;
+ public Builder setTimeZoneResolutionOperationStatus(
+ @OperationStatus int timeZoneResolutionStatus) {
+ mTimeZoneResolutionOperationStatus = timeZoneResolutionStatus;
return this;
}
@@ -313,11 +362,14 @@
@NonNull
public TimeZoneProviderStatus build() {
return new TimeZoneProviderStatus(
- mLocationDetectionStatus, mConnectivityStatus, mTimeZoneResolutionStatus);
+ requireValidDependencyStatus(mLocationDetectionDependencyStatus),
+ requireValidDependencyStatus(mConnectivityDependencyStatus),
+ requireValidOperationStatus(mTimeZoneResolutionOperationStatus));
}
}
- private @OperationStatus int requireValidOperationStatus(@OperationStatus int operationStatus) {
+ private static @OperationStatus int requireValidOperationStatus(
+ @OperationStatus int operationStatus) {
if (operationStatus < OPERATION_STATUS_UNKNOWN
|| operationStatus > OPERATION_STATUS_FAILED) {
throw new IllegalArgumentException(Integer.toString(operationStatus));
@@ -325,6 +377,45 @@
return operationStatus;
}
+ /** @hide */
+ @NonNull
+ public static String operationStatusToString(@OperationStatus int operationStatus) {
+ switch (operationStatus) {
+ case OPERATION_STATUS_UNKNOWN:
+ return "UNKNOWN";
+ case OPERATION_STATUS_NOT_APPLICABLE:
+ return "NOT_APPLICABLE";
+ case OPERATION_STATUS_OK:
+ return "OK";
+ case OPERATION_STATUS_FAILED:
+ return "FAILED";
+ default:
+ throw new IllegalArgumentException("Unknown status: " + operationStatus);
+ }
+ }
+
+ /** @hide */
+ public static @OperationStatus int operationStatusFromString(
+ @Nullable String operationStatusString) {
+
+ if (TextUtils.isEmpty(operationStatusString)) {
+ throw new IllegalArgumentException("Empty status: " + operationStatusString);
+ }
+
+ switch (operationStatusString) {
+ case "UNKNOWN":
+ return OPERATION_STATUS_UNKNOWN;
+ case "NOT_APPLICABLE":
+ return OPERATION_STATUS_NOT_APPLICABLE;
+ case "OK":
+ return OPERATION_STATUS_OK;
+ case "FAILED":
+ return OPERATION_STATUS_FAILED;
+ default:
+ throw new IllegalArgumentException("Unknown status: " + operationStatusString);
+ }
+ }
+
private static @DependencyStatus int requireValidDependencyStatus(
@DependencyStatus int dependencyStatus) {
if (dependencyStatus < DEPENDENCY_STATUS_UNKNOWN
@@ -333,4 +424,56 @@
}
return dependencyStatus;
}
+
+ /** @hide */
+ @NonNull
+ public static String dependencyStatusToString(@DependencyStatus int dependencyStatus) {
+ switch (dependencyStatus) {
+ case DEPENDENCY_STATUS_UNKNOWN:
+ return "UNKNOWN";
+ case DEPENDENCY_STATUS_NOT_APPLICABLE:
+ return "NOT_APPLICABLE";
+ case DEPENDENCY_STATUS_OK:
+ return "OK";
+ case DEPENDENCY_STATUS_TEMPORARILY_UNAVAILABLE:
+ return "TEMPORARILY_UNAVAILABLE";
+ case DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT:
+ return "BLOCKED_BY_ENVIRONMENT";
+ case DEPENDENCY_STATUS_DEGRADED_BY_SETTINGS:
+ return "DEGRADED_BY_SETTINGS";
+ case DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS:
+ return "BLOCKED_BY_SETTINGS";
+ default:
+ throw new IllegalArgumentException("Unknown status: " + dependencyStatus);
+ }
+ }
+
+ /** @hide */
+ public static @DependencyStatus int dependencyStatusFromString(
+ @Nullable String dependencyStatusString) {
+
+ if (TextUtils.isEmpty(dependencyStatusString)) {
+ throw new IllegalArgumentException("Empty status: " + dependencyStatusString);
+ }
+
+ switch (dependencyStatusString) {
+ case "UNKNOWN":
+ return DEPENDENCY_STATUS_UNKNOWN;
+ case "NOT_APPLICABLE":
+ return DEPENDENCY_STATUS_NOT_APPLICABLE;
+ case "OK":
+ return DEPENDENCY_STATUS_OK;
+ case "TEMPORARILY_UNAVAILABLE":
+ return DEPENDENCY_STATUS_TEMPORARILY_UNAVAILABLE;
+ case "BLOCKED_BY_ENVIRONMENT":
+ return DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT;
+ case "DEGRADED_BY_SETTINGS":
+ return DEPENDENCY_STATUS_DEGRADED_BY_SETTINGS;
+ case "BLOCKED_BY_SETTINGS":
+ return DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS;
+ default:
+ throw new IllegalArgumentException(
+ "Unknown status: " + dependencyStatusString);
+ }
+ }
}
diff --git a/core/java/android/service/voice/HotwordAudioStream.java b/core/java/android/service/voice/HotwordAudioStream.java
index 1c4d14c91..bf8ee47 100644
--- a/core/java/android/service/voice/HotwordAudioStream.java
+++ b/core/java/android/service/voice/HotwordAudioStream.java
@@ -127,6 +127,16 @@
}
}
+ /**
+ * Provides an instance of {@link Builder} with state corresponding to this instance.
+ * @hide
+ */
+ public Builder buildUpon() {
+ return new Builder(mAudioFormat, mAudioStreamParcelFileDescriptor)
+ .setTimestamp(mTimestamp)
+ .setMetadata(mMetadata);
+ }
+
// Code below generated by codegen v1.0.23.
@@ -439,10 +449,10 @@
}
@DataClass.Generated(
- time = 1665976240224L,
+ time = 1666342101364L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/service/voice/HotwordAudioStream.java",
- inputSignatures = "private final @android.annotation.NonNull android.media.AudioFormat mAudioFormat\nprivate final @android.annotation.NonNull android.os.ParcelFileDescriptor mAudioStreamParcelFileDescriptor\nprivate final @android.annotation.Nullable android.media.AudioTimestamp mTimestamp\nprivate final @android.annotation.NonNull android.os.PersistableBundle mMetadata\nprivate static android.media.AudioTimestamp defaultTimestamp()\nprivate static android.os.PersistableBundle defaultMetadata()\nprivate java.lang.String timestampToString()\nprivate void parcelTimestamp(android.os.Parcel,int)\nprivate static @android.annotation.Nullable android.media.AudioTimestamp unparcelTimestamp(android.os.Parcel)\nclass HotwordAudioStream extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genParcelable=true, genToString=true)")
+ inputSignatures = "private final @android.annotation.NonNull android.media.AudioFormat mAudioFormat\nprivate final @android.annotation.NonNull android.os.ParcelFileDescriptor mAudioStreamParcelFileDescriptor\nprivate final @android.annotation.Nullable android.media.AudioTimestamp mTimestamp\nprivate final @android.annotation.NonNull android.os.PersistableBundle mMetadata\nprivate static android.media.AudioTimestamp defaultTimestamp()\nprivate static android.os.PersistableBundle defaultMetadata()\nprivate java.lang.String timestampToString()\nprivate void parcelTimestamp(android.os.Parcel,int)\nprivate static @android.annotation.Nullable android.media.AudioTimestamp unparcelTimestamp(android.os.Parcel)\npublic android.service.voice.HotwordAudioStream.Builder buildUpon()\nclass HotwordAudioStream extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genParcelable=true, genToString=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/service/voice/HotwordDetectedResult.java b/core/java/android/service/voice/HotwordDetectedResult.java
index e22bbd8..b4f20c9 100644
--- a/core/java/android/service/voice/HotwordDetectedResult.java
+++ b/core/java/android/service/voice/HotwordDetectedResult.java
@@ -388,6 +388,25 @@
}
}
+ /**
+ * Provides an instance of {@link Builder} with state corresponding to this instance.
+ * @hide
+ */
+ public Builder buildUpon() {
+ return new Builder()
+ .setConfidenceLevel(mConfidenceLevel)
+ .setMediaSyncEvent(mMediaSyncEvent)
+ .setHotwordOffsetMillis(mHotwordOffsetMillis)
+ .setHotwordDurationMillis(mHotwordDurationMillis)
+ .setAudioChannel(mAudioChannel)
+ .setHotwordDetectionPersonalized(mHotwordDetectionPersonalized)
+ .setScore(mScore)
+ .setPersonalizedScore(mPersonalizedScore)
+ .setHotwordPhraseId(mHotwordPhraseId)
+ .setAudioStreams(mAudioStreams)
+ .setExtras(mExtras);
+ }
+
// Code below generated by codegen v1.0.23.
@@ -984,10 +1003,10 @@
}
@DataClass.Generated(
- time = 1665995595979L,
+ time = 1666342044844L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/service/voice/HotwordDetectedResult.java",
- inputSignatures = "public static final int CONFIDENCE_LEVEL_NONE\npublic static final int CONFIDENCE_LEVEL_LOW\npublic static final int CONFIDENCE_LEVEL_LOW_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM_HIGH\npublic static final int CONFIDENCE_LEVEL_HIGH\npublic static final int CONFIDENCE_LEVEL_VERY_HIGH\npublic static final int HOTWORD_OFFSET_UNSET\npublic static final int AUDIO_CHANNEL_UNSET\nprivate static final int LIMIT_HOTWORD_OFFSET_MAX_VALUE\nprivate static final int LIMIT_AUDIO_CHANNEL_MAX_VALUE\npublic static final java.lang.String EXTRA_PROXIMITY_METERS\nprivate final @android.service.voice.HotwordDetectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate @android.annotation.Nullable android.media.MediaSyncEvent mMediaSyncEvent\nprivate int mHotwordOffsetMillis\nprivate int mHotwordDurationMillis\nprivate int mAudioChannel\nprivate boolean mHotwordDetectionPersonalized\nprivate final int mScore\nprivate final int mPersonalizedScore\nprivate final int mHotwordPhraseId\nprivate final @android.annotation.NonNull java.util.List<android.service.voice.HotwordAudioStream> mAudioStreams\nprivate final @android.annotation.NonNull android.os.PersistableBundle mExtras\nprivate static int sMaxBundleSize\nprivate static int defaultConfidenceLevel()\nprivate static int defaultScore()\nprivate static int defaultPersonalizedScore()\npublic static int getMaxScore()\nprivate static int defaultHotwordPhraseId()\npublic static int getMaxHotwordPhraseId()\nprivate static java.util.List<android.service.voice.HotwordAudioStream> defaultAudioStreams()\nprivate static android.os.PersistableBundle defaultExtras()\npublic static int getMaxBundleSize()\npublic @android.annotation.Nullable android.media.MediaSyncEvent getMediaSyncEvent()\npublic static int getParcelableSize(android.os.Parcelable)\npublic static int getUsageSize(android.service.voice.HotwordDetectedResult)\nprivate static int bitCount(long)\nprivate void onConstructed()\npublic @android.annotation.NonNull java.util.List<android.service.voice.HotwordAudioStream> getAudioStreams()\nclass HotwordDetectedResult extends java.lang.Object implements [android.os.Parcelable]\npublic @android.annotation.NonNull android.service.voice.HotwordDetectedResult.Builder setAudioStreams(java.util.List<android.service.voice.HotwordAudioStream>)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)\npublic @android.annotation.NonNull android.service.voice.HotwordDetectedResult.Builder setAudioStreams(java.util.List<android.service.voice.HotwordAudioStream>)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = "public static final int CONFIDENCE_LEVEL_NONE\npublic static final int CONFIDENCE_LEVEL_LOW\npublic static final int CONFIDENCE_LEVEL_LOW_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM_HIGH\npublic static final int CONFIDENCE_LEVEL_HIGH\npublic static final int CONFIDENCE_LEVEL_VERY_HIGH\npublic static final int HOTWORD_OFFSET_UNSET\npublic static final int AUDIO_CHANNEL_UNSET\nprivate static final int LIMIT_HOTWORD_OFFSET_MAX_VALUE\nprivate static final int LIMIT_AUDIO_CHANNEL_MAX_VALUE\npublic static final java.lang.String EXTRA_PROXIMITY_METERS\nprivate final @android.service.voice.HotwordDetectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate @android.annotation.Nullable android.media.MediaSyncEvent mMediaSyncEvent\nprivate int mHotwordOffsetMillis\nprivate int mHotwordDurationMillis\nprivate int mAudioChannel\nprivate boolean mHotwordDetectionPersonalized\nprivate final int mScore\nprivate final int mPersonalizedScore\nprivate final int mHotwordPhraseId\nprivate final @android.annotation.NonNull java.util.List<android.service.voice.HotwordAudioStream> mAudioStreams\nprivate final @android.annotation.NonNull android.os.PersistableBundle mExtras\nprivate static int sMaxBundleSize\nprivate static int defaultConfidenceLevel()\nprivate static int defaultScore()\nprivate static int defaultPersonalizedScore()\npublic static int getMaxScore()\nprivate static int defaultHotwordPhraseId()\npublic static int getMaxHotwordPhraseId()\nprivate static java.util.List<android.service.voice.HotwordAudioStream> defaultAudioStreams()\nprivate static android.os.PersistableBundle defaultExtras()\npublic static int getMaxBundleSize()\npublic @android.annotation.Nullable android.media.MediaSyncEvent getMediaSyncEvent()\npublic static int getParcelableSize(android.os.Parcelable)\npublic static int getUsageSize(android.service.voice.HotwordDetectedResult)\nprivate static int bitCount(long)\nprivate void onConstructed()\npublic @android.annotation.NonNull java.util.List<android.service.voice.HotwordAudioStream> getAudioStreams()\npublic android.service.voice.HotwordDetectedResult.Builder buildUpon()\nclass HotwordDetectedResult extends java.lang.Object implements [android.os.Parcelable]\npublic @android.annotation.NonNull android.service.voice.HotwordDetectedResult.Builder setAudioStreams(java.util.List<android.service.voice.HotwordAudioStream>)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)\npublic @android.annotation.NonNull android.service.voice.HotwordDetectedResult.Builder setAudioStreams(java.util.List<android.service.voice.HotwordAudioStream>)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 1285d1e..7c125c7 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -349,7 +349,7 @@
* {@link #createAlwaysOnHotwordDetector(String, Locale, PersistableBundle, SharedMemory,
* AlwaysOnHotwordDetector.Callback)} or {@link #createHotwordDetector(PersistableBundle,
* SharedMemory, HotwordDetector.Callback)}, call this will throw an
- * {@link IllegalArgumentException}.
+ * {@link IllegalStateException}.
*
* @param keyphrase The keyphrase that's being used, for example "Hello Android".
* @param locale The locale for which the enrollment needs to be performed.
@@ -385,7 +385,7 @@
*
* <p>Note: If there are any active detectors that are created by using
* {@link #createAlwaysOnHotwordDetector(String, Locale, AlwaysOnHotwordDetector.Callback)},
- * call this will throw an {@link IllegalArgumentException}.
+ * call this will throw an {@link IllegalStateException}.
*
* @param keyphrase The keyphrase that's being used, for example "Hello Android".
* @param locale The locale for which the enrollment needs to be performed.
@@ -428,13 +428,18 @@
if (!CompatChanges.isChangeEnabled(MULTIPLE_ACTIVE_HOTWORD_DETECTORS)) {
// Allow only one concurrent recognition via the APIs.
safelyShutdownAllHotwordDetectors();
- }
-
- for (HotwordDetector detector : mActiveHotwordDetectors) {
- if (detector.isUsingHotwordDetectionService() != supportHotwordDetectionService) {
- throw new IllegalArgumentException(
- "It disallows to create trusted and non-trusted detectors "
- + "at the same time.");
+ } else {
+ for (HotwordDetector detector : mActiveHotwordDetectors) {
+ if (detector.isUsingHotwordDetectionService()
+ != supportHotwordDetectionService) {
+ throw new IllegalStateException(
+ "It disallows to create trusted and non-trusted detectors "
+ + "at the same time.");
+ } else if (detector instanceof AlwaysOnHotwordDetector) {
+ throw new IllegalStateException(
+ "There is already an active AlwaysOnHotwordDetector. "
+ + "It must be destroyed to create a new one.");
+ }
}
}
@@ -442,11 +447,7 @@
callback, mKeyphraseEnrollmentInfo, mSystemService,
getApplicationContext().getApplicationInfo().targetSdkVersion,
supportHotwordDetectionService);
- if (!mActiveHotwordDetectors.add(dspDetector)) {
- throw new IllegalArgumentException(
- "the keyphrase=" + keyphrase + " and locale=" + locale
- + " are already used by another always-on detector");
- }
+ mActiveHotwordDetectors.add(dspDetector);
try {
dspDetector.registerOnDestroyListener(this::onHotwordDetectorDestroyed);
@@ -480,7 +481,7 @@
*
* <p>Note: If there are any active detectors that are created by using
* {@link #createAlwaysOnHotwordDetector(String, Locale, AlwaysOnHotwordDetector.Callback)},
- * call this will throw an {@link IllegalArgumentException}.
+ * call this will throw an {@link IllegalStateException}.
*
* @param options Application configuration data to be provided to the
* {@link HotwordDetectionService}. PersistableBundle does not allow any remotable objects or
@@ -513,11 +514,11 @@
} else {
for (HotwordDetector detector : mActiveHotwordDetectors) {
if (!detector.isUsingHotwordDetectionService()) {
- throw new IllegalArgumentException(
+ throw new IllegalStateException(
"It disallows to create trusted and non-trusted detectors "
+ "at the same time.");
} else if (detector instanceof SoftwareHotwordDetector) {
- throw new IllegalArgumentException(
+ throw new IllegalStateException(
"There is already an active SoftwareHotwordDetector. "
+ "It must be destroyed to create a new one.");
}
@@ -527,6 +528,7 @@
SoftwareHotwordDetector softwareHotwordDetector =
new SoftwareHotwordDetector(
mSystemService, null, callback);
+ mActiveHotwordDetectors.add(softwareHotwordDetector);
try {
softwareHotwordDetector.registerOnDestroyListener(
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 37fc9f2..104570d0 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -575,6 +575,7 @@
*/
public void reportEngineShown(boolean waitForEngineShown) {
if (mIWallpaperEngine.mShownReported) return;
+ Trace.beginSection("WPMS.reportEngineShown-" + waitForEngineShown);
Log.d(TAG, "reportEngineShown: shouldWait=" + waitForEngineShown);
if (!waitForEngineShown) {
Message message = mCaller.obtainMessage(MSG_REPORT_SHOWN);
@@ -587,6 +588,7 @@
mCaller.sendMessageDelayed(message, TimeUnit.SECONDS.toMillis(5));
}
}
+ Trace.endSection();
}
/**
@@ -1259,7 +1261,9 @@
didSurface = true;
if (DEBUG) Log.v(TAG, "onSurfaceCreated("
+ mSurfaceHolder + "): " + this);
+ Trace.beginSection("WPMS.Engine.onSurfaceCreated");
onSurfaceCreated(mSurfaceHolder);
+ Trace.endSection();
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
@@ -1285,8 +1289,10 @@
+ ", " + mCurWidth + ", " + mCurHeight
+ "): " + this);
didSurface = true;
+ Trace.beginSection("WPMS.Engine.onSurfaceChanged");
onSurfaceChanged(mSurfaceHolder, mFormat,
mCurWidth, mCurHeight);
+ Trace.endSection();
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
@@ -1303,11 +1309,15 @@
if (DEBUG) {
Log.v(TAG, "dispatching insets=" + windowInsets);
}
+ Trace.beginSection("WPMS.Engine.onApplyWindowInsets");
onApplyWindowInsets(windowInsets);
+ Trace.endSection();
}
if (redrawNeeded) {
+ Trace.beginSection("WPMS.Engine.onSurfaceRedrawNeeded");
onSurfaceRedrawNeeded(mSurfaceHolder);
+ Trace.endSection();
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
@@ -1332,11 +1342,15 @@
// the state to get them to notice.
if (DEBUG) Log.v(TAG, "onVisibilityChanged(true) at surface: "
+ this);
+ Trace.beginSection("WPMS.Engine.onVisibilityChanged-true");
onVisibilityChanged(true);
+ Trace.endSection();
}
if (DEBUG) Log.v(TAG, "onVisibilityChanged(false) at surface: "
+ this);
+ Trace.beginSection("WPMS.Engine.onVisibilityChanged-false");
onVisibilityChanged(false);
+ Trace.endSection();
}
} finally {
mIsCreating = false;
@@ -1421,12 +1435,16 @@
mDisplayState = mDisplay.getState();
if (DEBUG) Log.v(TAG, "onCreate(): " + this);
+ Trace.beginSection("WPMS.Engine.onCreate");
onCreate(mSurfaceHolder);
+ Trace.endSection();
mInitializing = false;
mReportedVisible = false;
+ Trace.beginSection("WPMS.Engine.updateSurface");
updateSurface(false, false, false);
+ Trace.endSection();
}
/**
@@ -2236,14 +2254,15 @@
public void reportShown() {
if (!mShownReported) {
mShownReported = true;
+ Trace.beginSection("WPMS.mConnection.engineShown");
try {
mConnection.engineShown(this);
Log.d(TAG, "Wallpaper has updated the surface:"
+ mWallpaperManager.getWallpaperInfo());
} catch (RemoteException e) {
Log.w(TAG, "Wallpaper host disappeared", e);
- return;
}
+ Trace.endSection();
}
}
@@ -2285,6 +2304,27 @@
return mEngine == null ? null : SurfaceControl.mirrorSurface(mEngine.mSurfaceControl);
}
+ private void doAttachEngine() {
+ Trace.beginSection("WPMS.onCreateEngine");
+ Engine engine = onCreateEngine();
+ Trace.endSection();
+ mEngine = engine;
+ Trace.beginSection("WPMS.mConnection.attachEngine-" + mDisplayId);
+ try {
+ mConnection.attachEngine(this, mDisplayId);
+ } catch (RemoteException e) {
+ engine.detach();
+ Log.w(TAG, "Wallpaper host disappeared", e);
+ return;
+ } finally {
+ Trace.endSection();
+ }
+ mActiveEngines.add(engine);
+ Trace.beginSection("WPMS.engine.attach");
+ engine.attach(this);
+ Trace.endSection();
+ }
+
private void doDetachEngine() {
mActiveEngines.remove(mEngine);
mEngine.detach();
@@ -2310,21 +2350,15 @@
}
switch (message.what) {
case DO_ATTACH: {
- Engine engine = onCreateEngine();
- mEngine = engine;
- try {
- mConnection.attachEngine(this, mDisplayId);
- } catch (RemoteException e) {
- engine.detach();
- Log.w(TAG, "Wallpaper host disappeared", e);
- return;
- }
- mActiveEngines.add(engine);
- engine.attach(this);
+ Trace.beginSection("WPMS.DO_ATTACH");
+ doAttachEngine();
+ Trace.endSection();
return;
}
case DO_DETACH: {
+ Trace.beginSection("WPMS.DO_DETACH");
doDetachEngine();
+ Trace.endSection();
return;
}
case DO_SET_DESIRED_SIZE: {
@@ -2405,7 +2439,9 @@
}
} break;
case MSG_REPORT_SHOWN: {
+ Trace.beginSection("WPMS.MSG_REPORT_SHOWN");
reportShown();
+ Trace.endSection();
} break;
default :
Log.w(TAG, "Unknown message type " + message.what);
@@ -2429,8 +2465,10 @@
public void attach(IWallpaperConnection conn, IBinder windowToken,
int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding,
int displayId, @SetWallpaperFlags int which) {
+ Trace.beginSection("WPMS.ServiceWrapper.attach");
mEngineWrapper = new IWallpaperEngineWrapper(mTarget, conn, windowToken,
windowType, isPreview, reqWidth, reqHeight, padding, displayId);
+ Trace.endSection();
}
@Override
@@ -2441,16 +2479,20 @@
@Override
public void onCreate() {
+ Trace.beginSection("WPMS.onCreate");
super.onCreate();
+ Trace.endSection();
}
@Override
public void onDestroy() {
+ Trace.beginSection("WPMS.onDestroy");
super.onDestroy();
for (int i=0; i<mActiveEngines.size(); i++) {
mActiveEngines.get(i).detach();
}
mActiveEngines.clear();
+ Trace.endSection();
}
/**
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index c8c1fd4..eb467e0 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -93,8 +93,9 @@
* associated with each signer.
*
* @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v2.
- * @throws SecurityException if a APK Signature Scheme v2 signature of this APK does not verify.
- * @throws IOException if an I/O error occurs while reading the APK file.
+ * @throws SecurityException if an APK Signature Scheme v2 signature of this APK does
+ * not verify.
+ * @throws IOException if an I/O error occurs while reading the APK file.
*/
public static X509Certificate[][] verify(String apkFile)
throws SignatureNotFoundException, SecurityException, IOException {
@@ -386,7 +387,6 @@
break;
}
}
- return;
}
static byte[] getVerityRootHash(String apkPath)
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index 332e97c..02e0fcc 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -65,7 +65,7 @@
public void onWindowFocusGained(boolean hasViewFocus) {
super.onWindowFocusGained(hasViewFocus);
getImm().registerImeConsumer(this);
- if (isRequestedVisible() && getControl() == null) {
+ if ((mController.getRequestedVisibleTypes() & getType()) != 0 && getControl() == null) {
mIsRequestedVisibleAwaitingControl = true;
}
}
@@ -125,7 +125,7 @@
// If we had a request before to show from IME (tracked with mImeRequestedShow), reaching
// this code here means that we now got control, so we can start the animation immediately.
// If client window is trying to control IME and IME is already visible, it is immediate.
- if (fromIme || mState.getSource(getType()).isVisible() && getControl() != null) {
+ if (fromIme || (mState.getSource(getInternalType()).isVisible() && getControl() != null)) {
return ShowResult.SHOW_IMMEDIATELY;
}
@@ -169,7 +169,7 @@
@Override
protected boolean isRequestedVisibleAwaitingControl() {
- return mIsRequestedVisibleAwaitingControl || isRequestedVisible();
+ return super.isRequestedVisibleAwaitingControl() || mIsRequestedVisibleAwaitingControl;
}
@Override
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 799955b..3d7843c 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -219,8 +219,9 @@
/**
* The input source is a mouse pointing device.
- * This code is also used for other mouse-like pointing devices such as trackpads
- * and trackpoints.
+ * This value is also used for other mouse-like pointing devices such as touchpads and pointing
+ * sticks. When used in combination with {@link #SOURCE_STYLUS}, it denotes an external drawing
+ * tablet.
*
* @see #SOURCE_CLASS_POINTER
*/
@@ -291,8 +292,8 @@
public static final int SOURCE_MOUSE_RELATIVE = 0x00020000 | SOURCE_CLASS_TRACKBALL;
/**
- * The input source is a touch pad or digitizer tablet that is not
- * associated with a display (unlike {@link #SOURCE_TOUCHSCREEN}).
+ * The input source is a touchpad (also known as a trackpad). Touchpads that are used to move
+ * the mouse cursor will also have {@link #SOURCE_MOUSE}.
*
* @see #SOURCE_CLASS_POSITION
*/
@@ -779,7 +780,7 @@
* same input device descriptor. This might happen in situations where a single
* human input device registers multiple {@link InputDevice} instances (HID collections)
* that describe separate features of the device, such as a keyboard that also
- * has a trackpad. Alternately, it may be that the input devices are simply
+ * has a touchpad. Alternately, it may be that the input devices are simply
* indistinguishable, such as two keyboards made by the same manufacturer.
* </p><p>
* The input device descriptor returned by {@link #getDescriptor} should only be
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 805727c..27b4d87 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -128,7 +128,7 @@
null /* typeSideMap */);
mShownInsets = calculateInsets(mInitialInsetsState, frame, controls, true /* shown */,
typeSideMap);
- mHasZeroInsetsIme = mShownInsets.bottom == 0 && controlsInternalType(ITYPE_IME);
+ mHasZeroInsetsIme = mShownInsets.bottom == 0 && controlsType(WindowInsets.Type.ime());
if (mHasZeroInsetsIme) {
// IME has shownInsets of ZERO, and can't map to a side by default.
// Map zero insets IME to bottom, making it a special case of bottom insets.
@@ -141,7 +141,7 @@
mCurrentInsets = calculateInsets(mInitialInsetsState, controls, true /* shown */);
mHiddenInsets = calculateInsets(null, controls, false /* shown */);
mShownInsets = calculateInsets(null, controls, true /* shown */);
- mHasZeroInsetsIme = mShownInsets.bottom == 0 && controlsInternalType(ITYPE_IME);
+ mHasZeroInsetsIme = mShownInsets.bottom == 0 && controlsType(WindowInsets.Type.ime());
buildSideControlsMap(mSideControlsMap, controls);
}
mPendingInsets = mCurrentInsets;
diff --git a/core/java/android/view/InsetsAnimationControlRunner.java b/core/java/android/view/InsetsAnimationControlRunner.java
index 1cb00e3..291351e 100644
--- a/core/java/android/view/InsetsAnimationControlRunner.java
+++ b/core/java/android/view/InsetsAnimationControlRunner.java
@@ -19,7 +19,6 @@
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import android.view.InsetsController.AnimationType;
-import android.view.InsetsState.InternalInsetsType;
import android.view.WindowInsets.Type.InsetsType;
/**
@@ -63,10 +62,10 @@
WindowInsetsAnimation getAnimation();
/**
- * @return Whether {@link #getTypes()} maps to a specific {@link InternalInsetsType}.
+ * @return Whether {@link #getTypes()} contains a specific {@link InsetsType}.
*/
- default boolean controlsInternalType(@InternalInsetsType int type) {
- return InsetsState.toInternalType(getTypes()).contains(type);
+ default boolean controlsType(@InsetsType int type) {
+ return (getTypes() & type) != 0;
}
/**
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 8b38e9e..35838a3 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -24,6 +24,8 @@
import static android.view.InsetsState.toInternalType;
import static android.view.InsetsState.toPublicType;
import static android.view.ViewRootImpl.CAPTION_ON_SHELL;
+import static android.view.WindowInsets.Type.FIRST;
+import static android.view.WindowInsets.Type.LAST;
import static android.view.WindowInsets.Type.all;
import static android.view.WindowInsets.Type.ime;
@@ -744,8 +746,8 @@
for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
InsetsSource source = newState.peekSource(type);
if (source == null) continue;
- @AnimationType int animationType = getAnimationType(type);
@InsetsType int insetsType = toPublicType(type);
+ @AnimationType int animationType = getAnimationType(insetsType);
if (!source.isUserControllable()) {
// The user animation is not allowed when visible frame is empty.
disabledUserAnimationTypes |= insetsType;
@@ -788,8 +790,7 @@
if (diff != 0) {
for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
- if (consumer.getControl() != null
- && (toPublicType(consumer.getType()) & diff) != 0) {
+ if (consumer.getControl() != null && (consumer.getType() & diff) != 0) {
mHandler.removeCallbacks(mInvokeControllableInsetsChangedListeners);
mHandler.post(mInvokeControllableInsetsChangedListeners);
break;
@@ -897,7 +898,7 @@
// Ensure to update all existing source consumers
for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
- final InsetsSourceControl control = mTmpControlArray.get(consumer.getType());
+ final InsetsSourceControl control = mTmpControlArray.get(consumer.getInternalType());
// control may be null, but we still need to update the control to null if it got
// revoked.
@@ -985,25 +986,26 @@
// TODO: Support a ResultReceiver for IME.
// TODO(b/123718661): Make show() work for multi-session IME.
int typesReady = 0;
- final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
- for (int i = internalTypes.size() - 1; i >= 0; i--) {
- @InternalInsetsType int internalType = internalTypes.valueAt(i);
- @AnimationType int animationType = getAnimationType(internalType);
- InsetsSourceConsumer consumer = getSourceConsumer(internalType);
- if (consumer.isRequestedVisible() && animationType == ANIMATION_TYPE_NONE
+ for (int type = FIRST; type <= LAST; type = type << 1) {
+ if ((types & type) == 0) {
+ continue;
+ }
+ final @AnimationType int animationType = getAnimationType(type);
+ final boolean requestedVisible = (type & mRequestedVisibleTypes) != 0;
+ if (requestedVisible && animationType == ANIMATION_TYPE_NONE
|| animationType == ANIMATION_TYPE_SHOW) {
// no-op: already shown or animating in (because window visibility is
// applied before starting animation).
if (DEBUG) Log.d(TAG, String.format(
"show ignored for type: %d animType: %d requestedVisible: %s",
- consumer.getType(), animationType, consumer.isRequestedVisible()));
+ type, animationType, requestedVisible));
continue;
}
if (fromIme && animationType == ANIMATION_TYPE_USER) {
// App is already controlling the IME, don't cancel it.
continue;
}
- typesReady |= InsetsState.toPublicType(consumer.getType());
+ typesReady |= type;
}
if (DEBUG) Log.d(TAG, "show typesReady: " + typesReady);
applyAnimation(typesReady, true /* show */, fromIme);
@@ -1024,17 +1026,18 @@
Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.hideRequestFromApi", 0);
}
int typesReady = 0;
- final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
- for (int i = internalTypes.size() - 1; i >= 0; i--) {
- @InternalInsetsType int internalType = internalTypes.valueAt(i);
- @AnimationType int animationType = getAnimationType(internalType);
- InsetsSourceConsumer consumer = getSourceConsumer(internalType);
- if (!consumer.isRequestedVisible() && animationType == ANIMATION_TYPE_NONE
+ for (int type = FIRST; type <= LAST; type = type << 1) {
+ if ((types & type) == 0) {
+ continue;
+ }
+ final @AnimationType int animationType = getAnimationType(type);
+ final boolean requestedVisible = (type & mRequestedVisibleTypes) != 0;
+ if (!requestedVisible && animationType == ANIMATION_TYPE_NONE
|| animationType == ANIMATION_TYPE_HIDE) {
// no-op: already hidden or animating out.
continue;
}
- typesReady |= InsetsState.toPublicType(consumer.getType());
+ typesReady |= type;
}
applyAnimation(typesReady, false /* show */, fromIme /* fromIme */);
}
@@ -1228,13 +1231,13 @@
if (!canRun) {
if (WARN) Log.w(TAG, String.format(
"collectSourceControls can't continue show for type: %s fromIme: %b",
- InsetsState.typeToString(consumer.getType()), fromIme));
+ InsetsState.typeToString(consumer.getInternalType()), fromIme));
continue;
}
final InsetsSourceControl control = consumer.getControl();
if (control != null && control.getLeash() != null) {
- controls.put(consumer.getType(), new InsetsSourceControl(control));
- typesReady |= toPublicType(consumer.getType());
+ controls.put(control.getType(), new InsetsSourceControl(control));
+ typesReady |= consumer.getType();
} else if (animationType == ANIMATION_TYPE_SHOW) {
if (DEBUG) Log.d(TAG, "collectSourceControls no control for show(). fromIme: "
+ fromIme);
@@ -1260,25 +1263,15 @@
private @LayoutInsetsDuringAnimation int getLayoutInsetsDuringAnimationMode(
@InsetsType int types) {
-
- final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
-
// Generally, we want to layout the opposite of the current state. This is to make animation
// callbacks easy to use: The can capture the layout values and then treat that as end-state
// during the animation.
//
// However, if controlling multiple sources, we want to treat it as shown if any of the
// types is currently hidden.
- for (int i = internalTypes.size() - 1; i >= 0; i--) {
- InsetsSourceConsumer consumer = mSourceConsumers.get(internalTypes.valueAt(i));
- if (consumer == null) {
- continue;
- }
- if (!consumer.isRequestedVisible()) {
- return LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
- }
- }
- return LAYOUT_INSETS_DURING_ANIMATION_HIDDEN;
+ return (mRequestedVisibleTypes & types) != types
+ ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN
+ : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN;
}
private void cancelExistingControllers(@InsetsType int types) {
@@ -1332,15 +1325,15 @@
}
void notifyControlRevoked(InsetsSourceConsumer consumer) {
- final @InsetsType int types = toPublicType(consumer.getType());
+ final @InsetsType int type = consumer.getType();
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner;
- control.notifyControlRevoked(types);
+ control.notifyControlRevoked(type);
if (control.getControllingTypes() == 0) {
cancelAnimation(control, true /* invokeCallback */);
}
}
- if (consumer.getType() == ITYPE_IME) {
+ if (type == ime()) {
abortPendingImeControlRequest();
}
}
@@ -1425,27 +1418,25 @@
}
@VisibleForTesting
- public @AnimationType int getAnimationType(@InternalInsetsType int type) {
+ public @AnimationType int getAnimationType(@InsetsType int type) {
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner;
- if (control.controlsInternalType(type)) {
+ if (control.controlsType(type)) {
return mRunningAnimations.get(i).type;
}
}
return ANIMATION_TYPE_NONE;
}
- @VisibleForTesting
- public void onRequestedVisibilityChanged(InsetsSourceConsumer consumer) {
- final @InsetsType int type = InsetsState.toPublicType(consumer.getType());
- final int requestedVisibleTypes = consumer.isRequestedVisible()
- ? mRequestedVisibleTypes | type
- : mRequestedVisibleTypes & ~type;
+ void setRequestedVisibleTypes(@InsetsType int visibleTypes, @InsetsType int mask) {
+ final @InsetsType int requestedVisibleTypes =
+ (mRequestedVisibleTypes & ~mask) | (visibleTypes & mask);
if (mRequestedVisibleTypes != requestedVisibleTypes) {
- mRequestedVisibleTypes = requestedVisibleTypes;
- if (WindowInsets.Type.hasCompatSystemBars(type)) {
+ if (WindowInsets.Type.hasCompatSystemBars(
+ mRequestedVisibleTypes ^ requestedVisibleTypes)) {
mCompatSysUiVisibilityStaled = true;
}
+ mRequestedVisibleTypes = requestedVisibleTypes;
}
}
@@ -1664,9 +1655,9 @@
@InsetsType int result = 0;
for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
- InsetsSource source = mState.peekSource(consumer.mType);
+ InsetsSource source = mState.peekSource(consumer.getInternalType());
if (consumer.getControl() != null && source != null && source.isUserControllable()) {
- result |= toPublicType(consumer.mType);
+ result |= consumer.getType();
}
}
return result & ~mState.calculateUncontrollableInsetsFromFrame(mFrame);
@@ -1707,12 +1698,11 @@
}
@Override
- public void reportPerceptible(int types, boolean perceptible) {
- final ArraySet<Integer> internalTypes = toInternalType(types);
+ public void reportPerceptible(@InsetsType int types, boolean perceptible) {
final int size = mSourceConsumers.size();
for (int i = 0; i < size; i++) {
final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
- if (internalTypes.contains(consumer.getType())) {
+ if ((consumer.getType() & types) != 0) {
consumer.onPerceptible(perceptible);
}
}
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 7a498ad..21c0395 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -27,7 +27,6 @@
import static android.view.InsetsSourceConsumerProto.SOURCE_CONTROL;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.getDefaultVisibility;
-import static android.view.InsetsState.toPublicType;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
@@ -74,9 +73,9 @@
}
protected final InsetsController mController;
- protected boolean mRequestedVisible;
protected final InsetsState mState;
- protected final @InternalInsetsType int mType;
+ private final @InternalInsetsType int mInternalType;
+ private final @InsetsType int mType;
private static final String TAG = "InsetsSourceConsumer";
private final Supplier<Transaction> mTransactionSupplier;
@@ -99,11 +98,11 @@
*/
public InsetsSourceConsumer(@InternalInsetsType int type, InsetsState state,
Supplier<Transaction> transactionSupplier, InsetsController controller) {
- mType = type;
+ mType = InsetsState.toPublicType(type);
+ mInternalType = type;
mState = state;
mTransactionSupplier = transactionSupplier;
mController = controller;
- mRequestedVisible = getDefaultVisibility(type);
}
/**
@@ -117,7 +116,7 @@
*/
public boolean setControl(@Nullable InsetsSourceControl control,
@InsetsType int[] showTypes, @InsetsType int[] hideTypes) {
- if (mType == ITYPE_IME) {
+ if (mInternalType == ITYPE_IME) {
ImeTracing.getInstance().triggerClientDump("InsetsSourceConsumer#setControl",
mController.getHost().getInputMethodManager(), null /* icProto */);
}
@@ -141,9 +140,10 @@
mController.notifyControlRevoked(this);
// Check if we need to restore server visibility.
- final InsetsSource source = mState.getSource(mType);
+ final InsetsSource source = mState.getSource(mInternalType);
final boolean serverVisibility =
- mController.getLastDispatchedState().getSourceOrDefaultVisibility(mType);
+ mController.getLastDispatchedState().getSourceOrDefaultVisibility(
+ mInternalType);
if (source.isVisible() != serverVisibility) {
source.setVisible(serverVisibility);
mController.notifyVisibilityChanged();
@@ -159,9 +159,9 @@
if (DEBUG) Log.d(TAG, String.format("Gaining leash in %s, requestedVisible: %b",
mController.getHost().getRootViewTitle(), requestedVisible));
if (requestedVisible) {
- showTypes[0] |= toPublicType(getType());
+ showTypes[0] |= mType;
} else {
- hideTypes[0] |= toPublicType(getType());
+ hideTypes[0] |= mType;
}
} else {
// We are gaining control, but don't need to run an animation.
@@ -172,7 +172,7 @@
// If we have a new leash, make sure visibility is up-to-date, even though we
// didn't want to run an animation above.
- if (mController.getAnimationType(control.getType()) == ANIMATION_TYPE_NONE) {
+ if (mController.getAnimationType(mType) == ANIMATION_TYPE_NONE) {
applyRequestedVisibilityToControl();
}
@@ -195,29 +195,32 @@
/**
* Determines if the consumer will be shown after control is available.
- * Note: for system bars this method is same as {@link #isRequestedVisible()}.
*
* @return {@code true} if consumer has a pending show.
*/
protected boolean isRequestedVisibleAwaitingControl() {
- return isRequestedVisible();
+ return (mController.getRequestedVisibleTypes() & mType) != 0;
}
- int getType() {
+ @InsetsType int getType() {
return mType;
}
+ @InternalInsetsType int getInternalType() {
+ return mInternalType;
+ }
+
@VisibleForTesting
public void show(boolean fromIme) {
if (DEBUG) Log.d(TAG, String.format("Call show() for type: %s fromIme: %b ",
- InsetsState.typeToString(mType), fromIme));
+ InsetsState.typeToString(mInternalType), fromIme));
setRequestedVisible(true);
}
@VisibleForTesting
public void hide() {
if (DEBUG) Log.d(TAG, String.format("Call hide for %s on %s",
- InsetsState.typeToString(mType), mController.getHost().getRootViewTitle()));
+ InsetsState.typeToString(mInternalType), mController.getHost().getRootViewTitle()));
setRequestedVisible(false);
}
@@ -245,11 +248,13 @@
}
boolean applyLocalVisibilityOverride() {
- final InsetsSource source = mState.peekSource(mType);
- final boolean isVisible = source != null ? source.isVisible() : getDefaultVisibility(mType);
+ final InsetsSource source = mState.peekSource(mInternalType);
+ final boolean isVisible = source != null ? source.isVisible() : getDefaultVisibility(
+ mInternalType);
final boolean hasControl = mSourceControl != null;
+ final boolean requestedVisible = (mController.getRequestedVisibleTypes() & mType) != 0;
- if (mType == ITYPE_IME) {
+ if (mInternalType == ITYPE_IME) {
ImeTracing.getInstance().triggerClientDump(
"InsetsSourceConsumer#applyLocalVisibilityOverride",
mController.getHost().getInputMethodManager(), null /* icProto */);
@@ -259,23 +264,18 @@
if (!hasControl) {
if (DEBUG) Log.d(TAG, "applyLocalVisibilityOverride: No control in "
+ mController.getHost().getRootViewTitle()
- + " requestedVisible " + mRequestedVisible);
+ + " requestedVisible=" + requestedVisible);
return false;
}
- if (isVisible == mRequestedVisible) {
+ if (isVisible == requestedVisible) {
return false;
}
if (DEBUG) Log.d(TAG, String.format("applyLocalVisibilityOverride: %s requestedVisible: %b",
- mController.getHost().getRootViewTitle(), mRequestedVisible));
- mState.getSource(mType).setVisible(mRequestedVisible);
+ mController.getHost().getRootViewTitle(), requestedVisible));
+ mState.getSource(mInternalType).setVisible(requestedVisible);
return true;
}
- @VisibleForTesting
- public boolean isRequestedVisible() {
- return mRequestedVisible;
- }
-
/**
* Request to show current window type.
*
@@ -314,7 +314,7 @@
@VisibleForTesting(visibility = PACKAGE)
public void updateSource(InsetsSource newSource, @AnimationType int animationType) {
- InsetsSource source = mState.peekSource(mType);
+ InsetsSource source = mState.peekSource(mInternalType);
if (source == null || animationType == ANIMATION_TYPE_NONE
|| source.getFrame().equals(newSource.getFrame())) {
mPendingFrame = null;
@@ -339,7 +339,7 @@
@VisibleForTesting(visibility = PACKAGE)
public boolean notifyAnimationFinished() {
if (mPendingFrame != null) {
- InsetsSource source = mState.getSource(mType);
+ InsetsSource source = mState.getSource(mInternalType);
source.setFrame(mPendingFrame);
source.setVisibleFrame(mPendingVisibleFrame);
mPendingFrame = null;
@@ -354,11 +354,8 @@
* the moment.
*/
protected void setRequestedVisible(boolean requestedVisible) {
- if (mRequestedVisible != requestedVisible) {
- mRequestedVisible = requestedVisible;
- mController.onRequestedVisibilityChanged(this);
- if (DEBUG) Log.d(TAG, "setRequestedVisible: " + requestedVisible);
- }
+ mController.setRequestedVisibleTypes(requestedVisible ? mType : 0, mType);
+ if (DEBUG) Log.d(TAG, "setRequestedVisible: " + requestedVisible);
if (applyLocalVisibilityOverride()) {
mController.notifyVisibilityChanged();
}
@@ -369,25 +366,26 @@
return;
}
+ final boolean requestedVisible = (mController.getRequestedVisibleTypes() & mType) != 0;
try (Transaction t = mTransactionSupplier.get()) {
- if (DEBUG) Log.d(TAG, "applyRequestedVisibilityToControl: " + mRequestedVisible);
- if (mRequestedVisible) {
+ if (DEBUG) Log.d(TAG, "applyRequestedVisibilityToControl: " + requestedVisible);
+ if (requestedVisible) {
t.show(mSourceControl.getLeash());
} else {
t.hide(mSourceControl.getLeash());
}
// Ensure the alpha value is aligned with the actual requested visibility.
- t.setAlpha(mSourceControl.getLeash(), mRequestedVisible ? 1 : 0);
+ t.setAlpha(mSourceControl.getLeash(), requestedVisible ? 1 : 0);
t.apply();
}
- onPerceptible(mRequestedVisible);
+ onPerceptible(requestedVisible);
}
void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
- proto.write(INTERNAL_INSETS_TYPE, InsetsState.typeToString(mType));
+ proto.write(INTERNAL_INSETS_TYPE, InsetsState.typeToString(mInternalType));
proto.write(HAS_WINDOW_FOCUS, mHasWindowFocus);
- proto.write(IS_REQUESTED_VISIBLE, mRequestedVisible);
+ proto.write(IS_REQUESTED_VISIBLE, (mController.getRequestedVisibleTypes() & mType) != 0);
if (mSourceControl != null) {
mSourceControl.dumpDebug(proto, SOURCE_CONTROL);
}
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index a08a5a8..b3e8fb6 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1757,7 +1757,7 @@
*
* @param downTime The time (in ms) when the user originally pressed down to start
* a stream of position events. This must be obtained from {@link SystemClock#uptimeMillis()}.
- * @param eventTime The the time (in ms) when this specific event was generated. This
+ * @param eventTime The time (in ms) when this specific event was generated. This
* must be obtained from {@link SystemClock#uptimeMillis()}.
* @param action The kind of action being performed, such as {@link #ACTION_DOWN}.
* @param pointerCount The number of pointers that will be in this event.
@@ -1771,7 +1771,7 @@
* @param buttonState The state of buttons that are pressed.
* @param xPrecision The precision of the X coordinate being reported.
* @param yPrecision The precision of the Y coordinate being reported.
- * @param deviceId The id for the device that this event came from. An id of
+ * @param deviceId The ID for the device that this event came from. An ID of
* zero indicates that the event didn't come from a physical device; other
* numbers are arbitrary and you shouldn't depend on the values.
* @param edgeFlags A bitfield indicating which edges, if any, were touched by this
@@ -1779,16 +1779,18 @@
* @param source The source of this event.
* @param displayId The display ID associated with this event.
* @param flags The motion event flags.
+ * @param classification The classification to give this event.
* @hide
*/
- static public MotionEvent obtain(long downTime, long eventTime,
+ public static MotionEvent obtain(long downTime, long eventTime,
int action, int pointerCount, PointerProperties[] pointerProperties,
PointerCoords[] pointerCoords, int metaState, int buttonState,
float xPrecision, float yPrecision, int deviceId,
- int edgeFlags, int source, int displayId, int flags) {
+ int edgeFlags, int source, int displayId, int flags,
+ @Classification int classification) {
MotionEvent ev = obtain();
final boolean success = ev.initialize(deviceId, source, displayId, action, flags, edgeFlags,
- metaState, buttonState, CLASSIFICATION_NONE, 0, 0, xPrecision, yPrecision,
+ metaState, buttonState, classification, 0, 0, xPrecision, yPrecision,
downTime * NS_PER_MS, eventTime * NS_PER_MS,
pointerCount, pointerProperties, pointerCoords);
if (!success) {
@@ -1805,7 +1807,7 @@
*
* @param downTime The time (in ms) when the user originally pressed down to start
* a stream of position events. This must be obtained from {@link SystemClock#uptimeMillis()}.
- * @param eventTime The the time (in ms) when this specific event was generated. This
+ * @param eventTime The time (in ms) when this specific event was generated. This
* must be obtained from {@link SystemClock#uptimeMillis()}.
* @param action The kind of action being performed, such as {@link #ACTION_DOWN}.
* @param pointerCount The number of pointers that will be in this event.
@@ -1819,7 +1821,47 @@
* @param buttonState The state of buttons that are pressed.
* @param xPrecision The precision of the X coordinate being reported.
* @param yPrecision The precision of the Y coordinate being reported.
- * @param deviceId The id for the device that this event came from. An id of
+ * @param deviceId The ID for the device that this event came from. An ID of
+ * zero indicates that the event didn't come from a physical device; other
+ * numbers are arbitrary and you shouldn't depend on the values.
+ * @param edgeFlags A bitfield indicating which edges, if any, were touched by this
+ * MotionEvent.
+ * @param source The source of this event.
+ * @param displayId The display ID associated with this event.
+ * @param flags The motion event flags.
+ * @hide
+ */
+ public static MotionEvent obtain(long downTime, long eventTime,
+ int action, int pointerCount, PointerProperties[] pointerProperties,
+ PointerCoords[] pointerCoords, int metaState, int buttonState,
+ float xPrecision, float yPrecision, int deviceId,
+ int edgeFlags, int source, int displayId, int flags) {
+ return obtain(downTime, eventTime, action, pointerCount, pointerProperties, pointerCoords,
+ metaState, buttonState, xPrecision, yPrecision, deviceId, edgeFlags, source,
+ displayId, flags, CLASSIFICATION_NONE);
+ }
+
+ /**
+ * Create a new MotionEvent, filling in all of the basic values that
+ * define the motion.
+ *
+ * @param downTime The time (in ms) when the user originally pressed down to start
+ * a stream of position events. This must be obtained from {@link SystemClock#uptimeMillis()}.
+ * @param eventTime The time (in ms) when this specific event was generated. This
+ * must be obtained from {@link SystemClock#uptimeMillis()}.
+ * @param action The kind of action being performed, such as {@link #ACTION_DOWN}.
+ * @param pointerCount The number of pointers that will be in this event.
+ * @param pointerProperties An array of <em>pointerCount</em> values providing
+ * a {@link PointerProperties} property object for each pointer, which must
+ * include the pointer identifier.
+ * @param pointerCoords An array of <em>pointerCount</em> values providing
+ * a {@link PointerCoords} coordinate object for each pointer.
+ * @param metaState The state of any meta / modifier keys that were in effect when
+ * the event was generated.
+ * @param buttonState The state of buttons that are pressed.
+ * @param xPrecision The precision of the X coordinate being reported.
+ * @param yPrecision The precision of the Y coordinate being reported.
+ * @param deviceId The ID for the device that this event came from. An ID of
* zero indicates that the event didn't come from a physical device; other
* numbers are arbitrary and you shouldn't depend on the values.
* @param edgeFlags A bitfield indicating which edges, if any, were touched by this
@@ -1843,7 +1885,7 @@
*
* @param downTime The time (in ms) when the user originally pressed down to start
* a stream of position events. This must be obtained from {@link SystemClock#uptimeMillis()}.
- * @param eventTime The the time (in ms) when this specific event was generated. This
+ * @param eventTime The time (in ms) when this specific event was generated. This
* must be obtained from {@link SystemClock#uptimeMillis()}.
* @param action The kind of action being performed, such as {@link #ACTION_DOWN}.
* @param pointerCount The number of pointers that will be in this event.
@@ -1855,7 +1897,7 @@
* the event was generated.
* @param xPrecision The precision of the X coordinate being reported.
* @param yPrecision The precision of the Y coordinate being reported.
- * @param deviceId The id for the device that this event came from. An id of
+ * @param deviceId The ID for the device that this event came from. An ID of
* zero indicates that the event didn't come from a physical device; other
* numbers are arbitrary and you shouldn't depend on the values.
* @param edgeFlags A bitfield indicating which edges, if any, were touched by this
@@ -1890,7 +1932,7 @@
*
* @param downTime The time (in ms) when the user originally pressed down to start
* a stream of position events. This must be obtained from {@link SystemClock#uptimeMillis()}.
- * @param eventTime The the time (in ms) when this specific event was generated. This
+ * @param eventTime The time (in ms) when this specific event was generated. This
* must be obtained from {@link SystemClock#uptimeMillis()}.
* @param action The kind of action being performed, such as {@link #ACTION_DOWN}.
* @param x The X coordinate of this event.
@@ -1907,7 +1949,7 @@
* the event was generated.
* @param xPrecision The precision of the X coordinate being reported.
* @param yPrecision The precision of the Y coordinate being reported.
- * @param deviceId The id for the device that this event came from. An id of
+ * @param deviceId The ID for the device that this event came from. An ID of
* zero indicates that the event didn't come from a physical device; other
* numbers are arbitrary and you shouldn't depend on the values.
* @param edgeFlags A bitfield indicating which edges, if any, were touched by this
@@ -1927,7 +1969,7 @@
*
* @param downTime The time (in ms) when the user originally pressed down to start
* a stream of position events. This must be obtained from {@link SystemClock#uptimeMillis()}.
- * @param eventTime The the time (in ms) when this specific event was generated. This
+ * @param eventTime The time (in ms) when this specific event was generated. This
* must be obtained from {@link SystemClock#uptimeMillis()}.
* @param action The kind of action being performed, such as {@link #ACTION_DOWN}.
* @param x The X coordinate of this event.
@@ -1944,7 +1986,7 @@
* the event was generated.
* @param xPrecision The precision of the X coordinate being reported.
* @param yPrecision The precision of the Y coordinate being reported.
- * @param deviceId The id for the device that this event came from. An id of
+ * @param deviceId The ID for the device that this event came from. An ID of
* zero indicates that the event didn't come from a physical device; other
* numbers are arbitrary and you shouldn't depend on the values.
* @param source The source of this event.
@@ -1986,7 +2028,7 @@
*
* @param downTime The time (in ms) when the user originally pressed down to start
* a stream of position events. This must be obtained from {@link SystemClock#uptimeMillis()}.
- * @param eventTime The the time (in ms) when this specific event was generated. This
+ * @param eventTime The time (in ms) when this specific event was generated. This
* must be obtained from {@link SystemClock#uptimeMillis()}.
* @param action The kind of action being performed, such as {@link #ACTION_DOWN}.
* @param pointerCount The number of pointers that are active in this event.
@@ -2004,7 +2046,7 @@
* the event was generated.
* @param xPrecision The precision of the X coordinate being reported.
* @param yPrecision The precision of the Y coordinate being reported.
- * @param deviceId The id for the device that this event came from. An id of
+ * @param deviceId The ID for the device that this event came from. An ID of
* zero indicates that the event didn't come from a physical device; other
* numbers are arbitrary and you shouldn't depend on the values.
* @param edgeFlags A bitfield indicating which edges, if any, were touched by this
@@ -2028,7 +2070,7 @@
*
* @param downTime The time (in ms) when the user originally pressed down to start
* a stream of position events. This must be obtained from {@link SystemClock#uptimeMillis()}.
- * @param eventTime The the time (in ms) when this specific event was generated. This
+ * @param eventTime The time (in ms) when this specific event was generated. This
* must be obtained from {@link SystemClock#uptimeMillis()}.
* @param action The kind of action being performed, such as {@link #ACTION_DOWN}.
* @param x The X coordinate of this event.
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index f51d9ba..58aee61 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -822,6 +822,7 @@
*
* @hide
*/
+ @TestApi
public static long getSendRecurringAccessibilityEventsInterval() {
return SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS;
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e664ebf..43f0947 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -711,7 +711,7 @@
private final InsetsState mTempInsets = new InsetsState();
private final InsetsSourceControl[] mTempControls = new InsetsSourceControl[SIZE];
private final WindowConfiguration mTempWinConfig = new WindowConfiguration();
- private float mInvSizeCompatScale = 1f;
+ private float mInvCompatScale = 1f;
final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
= new ViewTreeObserver.InternalInsetsInfo();
@@ -1105,11 +1105,11 @@
private WindowConfiguration getCompatWindowConfiguration() {
final WindowConfiguration winConfig = getConfiguration().windowConfiguration;
- if (mInvSizeCompatScale == 1f) {
+ if (mInvCompatScale == 1f) {
return winConfig;
}
mTempWinConfig.setTo(winConfig);
- mTempWinConfig.scale(mInvSizeCompatScale);
+ mTempWinConfig.scale(mInvCompatScale);
return mTempWinConfig;
}
@@ -1241,11 +1241,11 @@
controlInsetsForCompatibility(mWindowAttributes);
Rect attachedFrame = new Rect();
- final float[] sizeCompatScale = { 1f };
+ final float[] compatScale = { 1f };
res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId,
mInsetsController.getRequestedVisibleTypes(), inputChannel, mTempInsets,
- mTempControls, attachedFrame, sizeCompatScale);
+ mTempControls, attachedFrame, compatScale);
if (!attachedFrame.isValid()) {
attachedFrame = null;
}
@@ -1255,8 +1255,8 @@
mTranslator.translateRectInScreenToAppWindow(attachedFrame);
}
mTmpFrames.attachedFrame = attachedFrame;
- mTmpFrames.sizeCompatScale = sizeCompatScale[0];
- mInvSizeCompatScale = 1f / sizeCompatScale[0];
+ mTmpFrames.compatScale = compatScale[0];
+ mInvCompatScale = 1f / compatScale[0];
} catch (RemoteException | RuntimeException e) {
mAdded = false;
mView = null;
@@ -1787,24 +1787,24 @@
mTranslator.translateRectInScreenToAppWindow(displayFrame);
mTranslator.translateRectInScreenToAppWindow(attachedFrame);
}
- final float sizeCompatScale = frames.sizeCompatScale;
+ final float compatScale = frames.compatScale;
final boolean frameChanged = !mWinFrame.equals(frame);
final boolean configChanged = !mLastReportedMergedConfiguration.equals(mergedConfiguration);
final boolean attachedFrameChanged = LOCAL_LAYOUT
&& !Objects.equals(mTmpFrames.attachedFrame, attachedFrame);
final boolean displayChanged = mDisplay.getDisplayId() != displayId;
final boolean resizeModeChanged = mResizeMode != resizeMode;
- final boolean sizeCompatScaleChanged = mTmpFrames.sizeCompatScale != sizeCompatScale;
+ final boolean compatScaleChanged = mTmpFrames.compatScale != compatScale;
if (msg == MSG_RESIZED && !frameChanged && !configChanged && !attachedFrameChanged
&& !displayChanged && !resizeModeChanged && !forceNextWindowRelayout
- && !sizeCompatScaleChanged) {
+ && !compatScaleChanged) {
return;
}
mPendingDragResizing = resizeMode != RESIZE_MODE_INVALID;
mResizeMode = resizeMode;
- mTmpFrames.sizeCompatScale = sizeCompatScale;
- mInvSizeCompatScale = 1f / sizeCompatScale;
+ mTmpFrames.compatScale = compatScale;
+ mInvCompatScale = 1f / compatScale;
if (configChanged) {
// If configuration changed - notify about that and, maybe, about move to display.
@@ -8225,7 +8225,7 @@
mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets);
mTranslator.translateSourceControlsInScreenToAppWindow(mTempControls);
}
- mInvSizeCompatScale = 1f / mTmpFrames.sizeCompatScale;
+ mInvCompatScale = 1f / mTmpFrames.compatScale;
CompatibilityInfo.applyOverrideScaleIfNeeded(mPendingMergedConfiguration);
mInsetsController.onStateChanged(mTempInsets);
mInsetsController.onControlsChanged(mTempControls);
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index d07a797..88adb2e 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -127,6 +127,16 @@
/** @hide */
public static final long UNDEFINED_NODE_ID = makeNodeId(UNDEFINED_ITEM_ID, UNDEFINED_ITEM_ID);
+ /**
+ * The default value for {@link #getMinMillisBetweenContentChanges};
+ */
+ public static final int UNDEFINED_MIN_MILLIS_BETWEEN_CONTENT_CHANGES = -1;
+
+ /**
+ * The minimum value for {@link #setMinMillisBetweenContentChanges};
+ */
+ public static final int MINIMUM_MIN_MILLIS_BETWEEN_CONTENT_CHANGES = 100;
+
/** @hide */
public static final long ROOT_NODE_ID = makeNodeId(ROOT_ITEM_ID,
AccessibilityNodeProvider.HOST_VIEW_ID);
@@ -879,6 +889,9 @@
private long mTraversalBefore = UNDEFINED_NODE_ID;
private long mTraversalAfter = UNDEFINED_NODE_ID;
+ private int mMinMillisBetweenContentChanges =
+ UNDEFINED_MIN_MILLIS_BETWEEN_CONTENT_CHANGES;
+
private int mBooleanProperties;
private final Rect mBoundsInParent = new Rect();
private final Rect mBoundsInScreen = new Rect();
@@ -897,6 +910,7 @@
private CharSequence mTooltipText;
private String mViewIdResourceName;
private String mUniqueId;
+ private CharSequence mContainerTitle;
private ArrayList<String> mExtraDataKeys;
@UnsupportedAppUsage
@@ -1781,6 +1795,42 @@
}
/**
+ * Sets the minimum time duration between two content change events, which is used in throttling
+ * content change events in accessibility services.
+ *
+ * <p>
+ * <strong>Note:</strong>
+ * This value should not be smaller than {@link #MINIMUM_MIN_MILLIS_BETWEEN_CONTENT_CHANGES},
+ * otherwise it would be ignored by accessibility services.
+ * </p>
+ *
+ * <p>
+ * Example: An app can set MinMillisBetweenContentChanges as 1 min for a view which sends
+ * content change events to accessibility services one event per second.
+ * Accessibility service will throttle those content change events and only handle one event
+ * per minute for that view.
+ * </p>
+ *
+ * @see AccessibilityEvent#getContentChangeTypes for all content change types.
+ * @param minMillisBetweenContentChanges the minimum duration between content change events.
+ */
+ public void setMinMillisBetweenContentChanges(int minMillisBetweenContentChanges) {
+ enforceNotSealed();
+ mMinMillisBetweenContentChanges = minMillisBetweenContentChanges
+ >= MINIMUM_MIN_MILLIS_BETWEEN_CONTENT_CHANGES
+ ? minMillisBetweenContentChanges
+ : UNDEFINED_MIN_MILLIS_BETWEEN_CONTENT_CHANGES;
+ }
+
+ /**
+ * Gets the minimum time duration between two content change events. This method may return
+ * {@link #UNDEFINED_MIN_MILLIS_BETWEEN_CONTENT_CHANGES}
+ */
+ public int getMinMillisBetweenContentChanges() {
+ return mMinMillisBetweenContentChanges;
+ }
+
+ /**
* Performs an action on the node.
* <p>
* <strong>Note:</strong> An action can be performed only if the request is made
@@ -3631,6 +3681,47 @@
}
/**
+ * Sets the container title for app-developer-defined container which can be any type of
+ * ViewGroup or layout.
+ * Container title will be used to group together related controls, similar to HTML fieldset.
+ * Or container title may identify a large piece of the UI that is visibly grouped together,
+ * such as a toolbar or a card, etc.
+ * <p>
+ * Container title helps to assist in navigation across containers and other groups.
+ * For example, a screen reader may use this to determine where to put accessibility focus.
+ * </p>
+ * <p>
+ * Container title is different from pane title{@link #setPaneTitle} which indicates that the
+ * node represents a window or activity.
+ * </p>
+ *
+ * <p>
+ * Example: An app can set container titles on several non-modal menus, containing TextViews
+ * or ImageButtons that have content descriptions, text, etc. Screen readers can quickly
+ * switch accessibility focus among menus instead of child views. Other accessibility-services
+ * can easily find the menu.
+ * </p>
+ *
+ * @param containerTitle The container title that is associated with a ViewGroup/Layout on the
+ * screen.
+ */
+ public void setContainerTitle(@Nullable CharSequence containerTitle) {
+ enforceNotSealed();
+ mContainerTitle = (containerTitle == null) ? null
+ : containerTitle.subSequence(0, containerTitle.length());
+ }
+
+ /**
+ * Returns the container title.
+ *
+ * @see #setContainerTitle for details.
+ */
+ @Nullable
+ public CharSequence getContainerTitle() {
+ return mContainerTitle;
+ }
+
+ /**
* Sets the token and node id of the leashed parent.
*
* @param token The token.
@@ -3909,6 +4000,11 @@
fieldIndex++;
if (mTraversalAfter != DEFAULT.mTraversalAfter) nonDefaultFields |= bitAt(fieldIndex);
fieldIndex++;
+ if (mMinMillisBetweenContentChanges
+ != DEFAULT.mMinMillisBetweenContentChanges) {
+ nonDefaultFields |= bitAt(fieldIndex);
+ }
+ fieldIndex++;
if (mConnectionId != DEFAULT.mConnectionId) nonDefaultFields |= bitAt(fieldIndex);
fieldIndex++;
if (!LongArray.elementsEqual(mChildNodeIds, DEFAULT.mChildNodeIds)) {
@@ -3963,6 +4059,10 @@
nonDefaultFields |= bitAt(fieldIndex);
}
fieldIndex++;
+ if (!Objects.equals(mContainerTitle, DEFAULT.mContainerTitle)) {
+ nonDefaultFields |= bitAt(fieldIndex);
+ }
+ fieldIndex++;
if (!Objects.equals(mViewIdResourceName, DEFAULT.mViewIdResourceName)) {
nonDefaultFields |= bitAt(fieldIndex);
}
@@ -4034,6 +4134,9 @@
if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeLong(mLabeledById);
if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeLong(mTraversalBefore);
if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeLong(mTraversalAfter);
+ if (isBitSet(nonDefaultFields, fieldIndex++)) {
+ parcel.writeInt(mMinMillisBetweenContentChanges);
+ }
if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mConnectionId);
@@ -4108,10 +4211,10 @@
}
if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mPaneTitle);
if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mTooltipText);
+ if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mContainerTitle);
if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeString(mViewIdResourceName);
if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeString(mUniqueId);
-
if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mTextSelectionStart);
if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mTextSelectionEnd);
if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mInputType);
@@ -4189,6 +4292,7 @@
mLabeledById = other.mLabeledById;
mTraversalBefore = other.mTraversalBefore;
mTraversalAfter = other.mTraversalAfter;
+ mMinMillisBetweenContentChanges = other.mMinMillisBetweenContentChanges;
mWindowId = other.mWindowId;
mConnectionId = other.mConnectionId;
mUniqueId = other.mUniqueId;
@@ -4204,6 +4308,7 @@
mContentDescription = other.mContentDescription;
mPaneTitle = other.mPaneTitle;
mTooltipText = other.mTooltipText;
+ mContainerTitle = other.mContainerTitle;
mViewIdResourceName = other.mViewIdResourceName;
if (mActions != null) mActions.clear();
@@ -4291,6 +4396,9 @@
if (isBitSet(nonDefaultFields, fieldIndex++)) mLabeledById = parcel.readLong();
if (isBitSet(nonDefaultFields, fieldIndex++)) mTraversalBefore = parcel.readLong();
if (isBitSet(nonDefaultFields, fieldIndex++)) mTraversalAfter = parcel.readLong();
+ if (isBitSet(nonDefaultFields, fieldIndex++)) {
+ mMinMillisBetweenContentChanges = parcel.readInt();
+ }
if (isBitSet(nonDefaultFields, fieldIndex++)) mConnectionId = parcel.readInt();
@@ -4347,6 +4455,7 @@
}
if (isBitSet(nonDefaultFields, fieldIndex++)) mPaneTitle = parcel.readCharSequence();
if (isBitSet(nonDefaultFields, fieldIndex++)) mTooltipText = parcel.readCharSequence();
+ if (isBitSet(nonDefaultFields, fieldIndex++)) mContainerTitle = parcel.readCharSequence();
if (isBitSet(nonDefaultFields, fieldIndex++)) mViewIdResourceName = parcel.readString();
if (isBitSet(nonDefaultFields, fieldIndex++)) mUniqueId = parcel.readString();
@@ -4638,6 +4747,8 @@
builder.append("; mParentNodeId: 0x").append(Long.toHexString(mParentNodeId));
builder.append("; traversalBefore: 0x").append(Long.toHexString(mTraversalBefore));
builder.append("; traversalAfter: 0x").append(Long.toHexString(mTraversalAfter));
+ builder.append("; minMillisBetweenContentChanges: ")
+ .append(mMinMillisBetweenContentChanges);
int granularities = mMovementGranularities;
builder.append("; MovementGranularities: [");
@@ -4675,6 +4786,7 @@
builder.append("; stateDescription: ").append(mStateDescription);
builder.append("; contentDescription: ").append(mContentDescription);
builder.append("; tooltipText: ").append(mTooltipText);
+ builder.append("; containerTitle: ").append(mContainerTitle);
builder.append("; viewIdResName: ").append(mViewIdResourceName);
builder.append("; uniqueId: ").append(mUniqueId);
diff --git a/core/java/android/view/inputmethod/DeleteGesture.java b/core/java/android/view/inputmethod/DeleteGesture.java
index c88158f..b843594 100644
--- a/core/java/android/view/inputmethod/DeleteGesture.java
+++ b/core/java/android/view/inputmethod/DeleteGesture.java
@@ -33,7 +33,7 @@
* <p>Note: This deletes all text <em>within</em> the given area. To delete a range <em>between</em>
* two areas, use {@link DeleteRangeGesture}.</p>
*/
-public final class DeleteGesture extends HandwritingGesture implements Parcelable {
+public final class DeleteGesture extends PreviewableHandwritingGesture implements Parcelable {
private @Granularity int mGranularity;
private RectF mArea;
diff --git a/core/java/android/view/inputmethod/DeleteRangeGesture.java b/core/java/android/view/inputmethod/DeleteRangeGesture.java
index 53b4209..0bce15e 100644
--- a/core/java/android/view/inputmethod/DeleteRangeGesture.java
+++ b/core/java/android/view/inputmethod/DeleteRangeGesture.java
@@ -34,7 +34,7 @@
* <p>Note: this deletes text within a range <em>between</em> two given areas. To delete all text
* <em>within</em> a single area, use {@link DeleteGesture}.</p>
*/
-public final class DeleteRangeGesture extends HandwritingGesture implements Parcelable {
+public final class DeleteRangeGesture extends PreviewableHandwritingGesture implements Parcelable {
private @Granularity int mGranularity;
private RectF mStartArea;
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index febdac2..9ebaa67 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -55,8 +55,10 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* An EditorInfo describes several attributes of a text editing object
@@ -534,6 +536,8 @@
private @HandwritingGesture.GestureTypeFlags int mSupportedHandwritingGestureTypes;
+ private @HandwritingGesture.GestureTypeFlags int mSupportedHandwritingGesturePreviewTypes;
+
/**
* Set the Handwriting gestures supported by the current {@code Editor}.
* For an editor that supports Stylus Handwriting
@@ -541,8 +545,11 @@
* supported gestures.
* <p> If editor doesn't support one of the declared types, IME will not send those Gestures
* to the editor. Instead they will fallback to using normal text input. </p>
+ * <p>Note: A supported gesture may not have preview supported
+ * {@link #getSupportedHandwritingGesturePreviews()}.</p>
* @param gestures List of supported gesture classes including any of {@link SelectGesture},
* {@link InsertGesture}, {@link DeleteGesture}.
+ * @see #setSupportedHandwritingGesturePreviews(Set)
*/
public void setSupportedHandwritingGestures(
@NonNull List<Class<? extends HandwritingGesture>> gestures) {
@@ -584,6 +591,7 @@
* {@link InputMethodManager#startStylusHandwriting}, it also declares supported gestures.
* @return List of supported gesture classes including any of {@link SelectGesture},
* {@link InsertGesture}, {@link DeleteGesture}.
+ * @see #getSupportedHandwritingGesturePreviews()
*/
@NonNull
public List<Class<? extends HandwritingGesture>> getSupportedHandwritingGestures() {
@@ -623,6 +631,86 @@
}
/**
+ * Set the Handwriting gesture previews supported by the current {@code Editor}.
+ * For an editor that supports Stylus Handwriting
+ * {@link InputMethodManager#startStylusHandwriting}, it is also recommended that it declares
+ * supported gesture previews.
+ * <p>Note: A supported gesture {@link EditorInfo#getSupportedHandwritingGestures()} may not
+ * have preview supported {@link EditorInfo#getSupportedHandwritingGesturePreviews()}.</p>
+ * <p> If editor doesn't support one of the declared types, gesture preview will be ignored.</p>
+ * @param gestures Set of supported gesture classes. One of {@link SelectGesture},
+ * {@link SelectRangeGesture}, {@link DeleteGesture}, {@link DeleteRangeGesture}.
+ * @see #setSupportedHandwritingGestures(List)
+ */
+ public void setSupportedHandwritingGesturePreviews(
+ @NonNull Set<Class<? extends PreviewableHandwritingGesture>> gestures) {
+ Objects.requireNonNull(gestures);
+ if (gestures.isEmpty()) {
+ mSupportedHandwritingGesturePreviewTypes = 0;
+ return;
+ }
+
+ int supportedTypes = 0;
+ for (Class<? extends PreviewableHandwritingGesture> gesture : gestures) {
+ Objects.requireNonNull(gesture);
+ if (gesture.equals(SelectGesture.class)) {
+ supportedTypes |= HandwritingGesture.GESTURE_TYPE_SELECT;
+ } else if (gesture.equals(SelectRangeGesture.class)) {
+ supportedTypes |= HandwritingGesture.GESTURE_TYPE_SELECT_RANGE;
+ } else if (gesture.equals(DeleteGesture.class)) {
+ supportedTypes |= HandwritingGesture.GESTURE_TYPE_DELETE;
+ } else if (gesture.equals(DeleteRangeGesture.class)) {
+ supportedTypes |= HandwritingGesture.GESTURE_TYPE_DELETE_RANGE;
+ } else {
+ throw new IllegalArgumentException(
+ "Unsupported gesture type for preview: " + gesture);
+ }
+ }
+
+ mSupportedHandwritingGesturePreviewTypes = supportedTypes;
+ }
+
+ /**
+ * Returns the combination of Stylus handwriting gesture preview types
+ * supported by the current {@code Editor}.
+ * For an editor that supports Stylus Handwriting.
+ * {@link InputMethodManager#startStylusHandwriting}, it also declares supported gesture
+ * previews.
+ * <p>Note: A supported gesture {@link EditorInfo#getSupportedHandwritingGestures()} may not
+ * have preview supported {@link EditorInfo#getSupportedHandwritingGesturePreviews()}.</p>
+ * @return Set of supported gesture preview classes. One of {@link SelectGesture},
+ * {@link SelectRangeGesture}, {@link DeleteGesture}, {@link DeleteRangeGesture}.
+ * @see #getSupportedHandwritingGestures()
+ */
+ @NonNull
+ public Set<Class<? extends PreviewableHandwritingGesture>>
+ getSupportedHandwritingGesturePreviews() {
+ Set<Class<? extends PreviewableHandwritingGesture>> set = new HashSet<>();
+ if (mSupportedHandwritingGesturePreviewTypes == 0) {
+ return set;
+ }
+ if ((mSupportedHandwritingGesturePreviewTypes & HandwritingGesture.GESTURE_TYPE_SELECT)
+ == HandwritingGesture.GESTURE_TYPE_SELECT) {
+ set.add(SelectGesture.class);
+ }
+ if ((mSupportedHandwritingGesturePreviewTypes
+ & HandwritingGesture.GESTURE_TYPE_SELECT_RANGE)
+ == HandwritingGesture.GESTURE_TYPE_SELECT_RANGE) {
+ set.add(SelectRangeGesture.class);
+ }
+ if ((mSupportedHandwritingGesturePreviewTypes & HandwritingGesture.GESTURE_TYPE_DELETE)
+ == HandwritingGesture.GESTURE_TYPE_DELETE) {
+ set.add(DeleteGesture.class);
+ }
+ if ((mSupportedHandwritingGesturePreviewTypes
+ & HandwritingGesture.GESTURE_TYPE_DELETE_RANGE)
+ == HandwritingGesture.GESTURE_TYPE_DELETE_RANGE) {
+ set.add(DeleteRangeGesture.class);
+ }
+ return set;
+ }
+
+ /**
* If not {@code null}, this editor needs to talk to IMEs that run for the specified user, no
* matter what user ID the calling process has.
*
@@ -1114,6 +1202,9 @@
pw.println(prefix + "supportedHandwritingGestureTypes="
+ InputMethodDebug.handwritingGestureTypeFlagsToString(
mSupportedHandwritingGestureTypes));
+ pw.println(prefix + "supportedHandwritingGesturePreviewTypes="
+ + InputMethodDebug.handwritingGestureTypeFlagsToString(
+ mSupportedHandwritingGesturePreviewTypes));
pw.println(prefix + "contentMimeTypes=" + Arrays.toString(contentMimeTypes));
if (targetInputMethodUser != null) {
pw.println(prefix + "targetInputMethodUserId=" + targetInputMethodUser.getIdentifier());
@@ -1149,6 +1240,8 @@
newEditorInfo.contentMimeTypes = ArrayUtils.cloneOrNull(contentMimeTypes);
newEditorInfo.targetInputMethodUser = targetInputMethodUser;
newEditorInfo.mSupportedHandwritingGestureTypes = mSupportedHandwritingGestureTypes;
+ newEditorInfo.mSupportedHandwritingGesturePreviewTypes =
+ mSupportedHandwritingGesturePreviewTypes;
return newEditorInfo;
}
@@ -1177,6 +1270,7 @@
dest.writeString(fieldName);
dest.writeBundle(extras);
dest.writeInt(mSupportedHandwritingGestureTypes);
+ dest.writeInt(mSupportedHandwritingGesturePreviewTypes);
dest.writeBoolean(mInitialSurroundingText != null);
if (mInitialSurroundingText != null) {
mInitialSurroundingText.writeToParcel(dest, flags);
@@ -1215,6 +1309,7 @@
res.fieldName = source.readString();
res.extras = source.readBundle();
res.mSupportedHandwritingGestureTypes = source.readInt();
+ res.mSupportedHandwritingGesturePreviewTypes = source.readInt();
boolean hasInitialSurroundingText = source.readBoolean();
if (hasInitialSurroundingText) {
res.mInitialSurroundingText =
@@ -1258,6 +1353,8 @@
&& initialCapsMode == that.initialCapsMode
&& fieldId == that.fieldId
&& mSupportedHandwritingGestureTypes == that.mSupportedHandwritingGestureTypes
+ && mSupportedHandwritingGesturePreviewTypes
+ == that.mSupportedHandwritingGesturePreviewTypes
&& Objects.equals(autofillId, that.autofillId)
&& Objects.equals(privateImeOptions, that.privateImeOptions)
&& Objects.equals(packageName, that.packageName)
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 2099a31..9b519c3 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -26,6 +26,7 @@
import android.graphics.RectF;
import android.inputmethodservice.InputMethodService;
import android.os.Bundle;
+import android.os.CancellationSignal;
import android.os.Handler;
import android.text.TextUtils;
import android.view.KeyCharacterMap;
@@ -1031,6 +1032,8 @@
/**
* Perform a handwriting gesture on text.
*
+ * <p>Note: A supported gesture {@link EditorInfo#getSupportedHandwritingGestures()} may not
+ * have preview supported {@link EditorInfo#getSupportedHandwritingGesturePreviews()}.</p>
* @param gesture the gesture to perform
* @param executor The executor to run the callback on.
* @param consumer if the caller passes a non-null consumer, the editor must invoke this
@@ -1041,6 +1044,7 @@
* completed. Will be invoked on the given {@link Executor}.
* Default implementation provides a callback to {@link IntConsumer} with
* {@link #HANDWRITING_GESTURE_RESULT_UNSUPPORTED}.
+ * @see #previewHandwritingGesture(PreviewableHandwritingGesture, CancellationSignal)
*/
default void performHandwritingGesture(
@NonNull HandwritingGesture gesture, @Nullable @CallbackExecutor Executor executor,
@@ -1051,6 +1055,31 @@
}
/**
+ * Preview a handwriting gesture on text.
+ * Provides a real-time preview for a gesture to user for an ongoing gesture. e.g. as user
+ * begins to draw a circle around text, resulting selection {@link SelectGesture} is previewed
+ * while stylus is moving over applicable text.
+ *
+ * <p>Note: A supported gesture {@link EditorInfo#getSupportedHandwritingGestures()} might not
+ * have preview supported {@link EditorInfo#getSupportedHandwritingGesturePreviews()}.</p>
+ * @param gesture the gesture to preview. Preview support for a gesture (regardless of whether
+ * implemented by editor) can be determined if gesture subclasses
+ * {@link PreviewableHandwritingGesture}. Supported previewable gestures include
+ * {@link SelectGesture}, {@link SelectRangeGesture}, {@link DeleteGesture} and
+ * {@link DeleteRangeGesture}.
+ * @param cancellationSignal signal to cancel an ongoing preview.
+ * @return true on successfully sending command to Editor, false if not implemented by editor or
+ * the input connection is no longer valid or preview was cancelled with
+ * {@link CancellationSignal}.
+ * @see #performHandwritingGesture(HandwritingGesture, Executor, IntConsumer)
+ */
+ default boolean previewHandwritingGesture(
+ @NonNull PreviewableHandwritingGesture gesture,
+ @Nullable CancellationSignal cancellationSignal) {
+ return false;
+ }
+
+ /**
* The editor is requested to call
* {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)} at
* once, as soon as possible, regardless of cursor/anchor position changes. This flag can be
@@ -1411,6 +1440,8 @@
* that this means you can't position the cursor within the text.
* @param text the text to replace. This may include styles.
* @param textAttribute The extra information about the text. This value may be null.
+ * @return {@code true} if the replace command was sent to the associated editor (regardless of
+ * whether the replacement is success or not), {@code false} otherwise.
*/
default boolean replaceText(
@IntRange(from = 0) int start,
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index 7af96b6..4befd6f 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.graphics.RectF;
import android.os.Bundle;
+import android.os.CancellationSignal;
import android.os.Handler;
import android.view.KeyEvent;
@@ -340,6 +341,17 @@
* @throws NullPointerException if the target is {@code null}.
*/
@Override
+ public boolean previewHandwritingGesture(
+ @NonNull PreviewableHandwritingGesture gesture,
+ @Nullable CancellationSignal cancellationSignal) {
+ return mTarget.previewHandwritingGesture(gesture, cancellationSignal);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws NullPointerException if the target is {@code null}.
+ */
+ @Override
public boolean requestCursorUpdates(int cursorUpdateMode) {
return mTarget.requestCursorUpdates(cursorUpdateMode);
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index d08816b..74afced 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -3115,6 +3115,8 @@
* when it was started, which allows it to perform this operation on
* itself.
* @param id The unique identifier for the new input method to be switched to.
+ * @throws IllegalArgumentException if the input method is unknown or filtered by the rules of
+ * <a href="/training/basics/intents/package-visibility">package visibility</a>.
* @deprecated Use {@link InputMethodService#switchInputMethod(String)}
* instead. This method was intended for IME developers who should be accessing APIs through
* the service. APIs in this class are intended for app developers interacting with the IME.
@@ -3185,6 +3187,8 @@
* itself.
* @param id The unique identifier for the new input method to be switched to.
* @param subtype The new subtype of the new input method to be switched to.
+ * @throws IllegalArgumentException if the input method is unknown or filtered by the rules of
+ * <a href="/training/basics/intents/package-visibility">package visibility</a>.
* @deprecated Use
* {@link InputMethodService#switchInputMethod(String, InputMethodSubtype)}
* instead. This method was intended for IME developers who should be accessing APIs through
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java
index bdfcb03..6a02198 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtype.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.java
@@ -86,6 +86,7 @@
private final int mSubtypeHashCode;
private final int mSubtypeIconResId;
private final int mSubtypeNameResId;
+ private final CharSequence mSubtypeNameOverride;
private final int mSubtypeId;
private final String mSubtypeLocale;
private final String mSubtypeLanguageTag;
@@ -174,6 +175,21 @@
private int mSubtypeNameResId = 0;
/**
+ * Sets the untranslatable name of the subtype.
+ *
+ * This string is used as the subtype's display name if subtype's name res Id is 0.
+ *
+ * @param nameOverride is the name to set.
+ */
+ @NonNull
+ public InputMethodSubtypeBuilder setSubtypeNameOverride(
+ @NonNull CharSequence nameOverride) {
+ mSubtypeNameOverride = nameOverride;
+ return this;
+ }
+ private CharSequence mSubtypeNameOverride = "";
+
+ /**
* @param subtypeId is the unique ID for this subtype. The input method framework keeps
* track of enabled subtypes by ID. When the IME package gets upgraded, enabled IDs will
* stay enabled even if other attributes are different. If the ID is unspecified or 0,
@@ -229,23 +245,23 @@
public InputMethodSubtype build() {
return new InputMethodSubtype(this);
}
- }
+ }
- private static InputMethodSubtypeBuilder getBuilder(int nameId, int iconId, String locale,
- String mode, String extraValue, boolean isAuxiliary,
- boolean overridesImplicitlyEnabledSubtype, int id, boolean isAsciiCapable) {
- final InputMethodSubtypeBuilder builder = new InputMethodSubtypeBuilder();
- builder.mSubtypeNameResId = nameId;
- builder.mSubtypeIconResId = iconId;
- builder.mSubtypeLocale = locale;
- builder.mSubtypeMode = mode;
- builder.mSubtypeExtraValue = extraValue;
- builder.mIsAuxiliary = isAuxiliary;
- builder.mOverridesImplicitlyEnabledSubtype = overridesImplicitlyEnabledSubtype;
- builder.mSubtypeId = id;
- builder.mIsAsciiCapable = isAsciiCapable;
- return builder;
- }
+ private static InputMethodSubtypeBuilder getBuilder(int nameId, int iconId,
+ String locale, String mode, String extraValue, boolean isAuxiliary,
+ boolean overridesImplicitlyEnabledSubtype, int id, boolean isAsciiCapable) {
+ final InputMethodSubtypeBuilder builder = new InputMethodSubtypeBuilder();
+ builder.mSubtypeNameResId = nameId;
+ builder.mSubtypeIconResId = iconId;
+ builder.mSubtypeLocale = locale;
+ builder.mSubtypeMode = mode;
+ builder.mSubtypeExtraValue = extraValue;
+ builder.mIsAuxiliary = isAuxiliary;
+ builder.mOverridesImplicitlyEnabledSubtype = overridesImplicitlyEnabledSubtype;
+ builder.mSubtypeId = id;
+ builder.mIsAsciiCapable = isAsciiCapable;
+ return builder;
+ }
/**
* Constructor with no subtype ID specified.
@@ -305,6 +321,7 @@
*/
private InputMethodSubtype(InputMethodSubtypeBuilder builder) {
mSubtypeNameResId = builder.mSubtypeNameResId;
+ mSubtypeNameOverride = builder.mSubtypeNameOverride;
mSubtypeIconResId = builder.mSubtypeIconResId;
mSubtypeLocale = builder.mSubtypeLocale;
mSubtypeLanguageTag = builder.mSubtypeLanguageTag;
@@ -327,6 +344,8 @@
InputMethodSubtype(Parcel source) {
String s;
mSubtypeNameResId = source.readInt();
+ CharSequence cs = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+ mSubtypeNameOverride = cs != null ? cs : "";
mSubtypeIconResId = source.readInt();
s = source.readString();
mSubtypeLocale = s != null ? s : "";
@@ -351,6 +370,14 @@
}
/**
+ * @return The subtype's untranslatable name string.
+ */
+ @NonNull
+ public CharSequence getNameOverride() {
+ return mSubtypeNameOverride;
+ }
+
+ /**
* @return Resource ID of the subtype icon drawable.
*/
public int getIconResId() {
@@ -532,8 +559,11 @@
public CharSequence getDisplayName(
Context context, String packageName, ApplicationInfo appInfo) {
if (mSubtypeNameResId == 0) {
- return getLocaleDisplayName(getLocaleFromContext(context), getLocaleObject(),
- DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU);
+ return TextUtils.isEmpty(mSubtypeNameOverride)
+ ? getLocaleDisplayName(
+ getLocaleFromContext(context), getLocaleObject(),
+ DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU)
+ : mSubtypeNameOverride;
}
final CharSequence subtypeName = context.getPackageManager().getText(
@@ -698,6 +728,7 @@
@Override
public void writeToParcel(Parcel dest, int parcelableFlags) {
dest.writeInt(mSubtypeNameResId);
+ TextUtils.writeToParcel(mSubtypeNameOverride, dest, parcelableFlags);
dest.writeInt(mSubtypeIconResId);
dest.writeString(mSubtypeLocale);
dest.writeString(mSubtypeLanguageTag);
@@ -765,4 +796,4 @@
}
return sortedList;
}
-}
\ No newline at end of file
+}
diff --git a/core/java/android/view/inputmethod/PreviewableHandwritingGesture.java b/core/java/android/view/inputmethod/PreviewableHandwritingGesture.java
new file mode 100644
index 0000000..7683ece
--- /dev/null
+++ b/core/java/android/view/inputmethod/PreviewableHandwritingGesture.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+import android.os.CancellationSignal;
+
+import java.util.Set;
+
+/**
+ * A {@link HandwritingGesture} that can be
+ * {@link InputConnection#previewHandwritingGesture(
+ * PreviewableHandwritingGesture, CancellationSignal) previewed}.
+ *
+ * Note: An editor might only implement a subset of gesture previews and declares the supported
+ * ones via {@link EditorInfo#getSupportedHandwritingGesturePreviews}.
+ *
+ * @see EditorInfo#setSupportedHandwritingGesturePreviews(Set)
+ * @see EditorInfo#getSupportedHandwritingGesturePreviews()
+ */
+public abstract class PreviewableHandwritingGesture extends HandwritingGesture {
+ PreviewableHandwritingGesture() {
+ // intentionally empty.
+ }
+}
diff --git a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
index c01c310..7525d72 100644
--- a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
@@ -30,7 +30,9 @@
import android.annotation.Nullable;
import android.graphics.RectF;
import android.os.Bundle;
+import android.os.CancellationSignal;
import android.os.Handler;
+import android.os.ICancellationSignal;
import android.os.Looper;
import android.os.ResultReceiver;
import android.os.Trace;
@@ -1017,6 +1019,34 @@
@Dispatching(cancellable = true)
@Override
+ public void previewHandwritingGesture(
+ InputConnectionCommandHeader header, ParcelableHandwritingGesture gestureContainer,
+ ICancellationSignal transport) {
+
+ // TODO(b/254727073): Implement CancellationSignal receiver
+ final CancellationSignal cancellationSignal = CancellationSignal.fromTransport(transport);
+ // Previews always use PreviewableHandwritingGesture but if incorrectly wrong class is
+ // passed, ClassCastException will be sent back to caller.
+ final PreviewableHandwritingGesture gesture =
+ (PreviewableHandwritingGesture) gestureContainer.get();
+
+ dispatchWithTracing("previewHandwritingGesture", () -> {
+ if (header.mSessionId != mCurrentSessionId.get()
+ || (cancellationSignal != null && cancellationSignal.isCanceled())) {
+ return; // cancelled
+ }
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "previewHandwritingGesture on inactive InputConnection");
+ return; // cancelled
+ }
+
+ ic.previewHandwritingGesture(gesture, cancellationSignal);
+ });
+ }
+
+ @Dispatching(cancellable = true)
+ @Override
public void requestCursorUpdates(InputConnectionCommandHeader header, int cursorUpdateMode,
int imeDisplayId, AndroidFuture future /* T=Boolean */) {
dispatchWithTracing("requestCursorUpdates", future, () -> {
diff --git a/core/java/android/view/inputmethod/SelectGesture.java b/core/java/android/view/inputmethod/SelectGesture.java
index 6dc4ed2..ba600df 100644
--- a/core/java/android/view/inputmethod/SelectGesture.java
+++ b/core/java/android/view/inputmethod/SelectGesture.java
@@ -33,7 +33,7 @@
* <p>Note: This selects all text <em>within</em> the given area. To select a range <em>between</em>
* two areas, use {@link SelectRangeGesture}.</p>
*/
-public final class SelectGesture extends HandwritingGesture implements Parcelable {
+public final class SelectGesture extends PreviewableHandwritingGesture implements Parcelable {
private @Granularity int mGranularity;
private RectF mArea;
@@ -72,7 +72,6 @@
return mArea;
}
-
/**
* Builder for {@link SelectGesture}. This class is not designed to be thread-safe.
*/
diff --git a/core/java/android/view/inputmethod/SelectRangeGesture.java b/core/java/android/view/inputmethod/SelectRangeGesture.java
index 7cb6002..c31bc27 100644
--- a/core/java/android/view/inputmethod/SelectRangeGesture.java
+++ b/core/java/android/view/inputmethod/SelectRangeGesture.java
@@ -34,7 +34,7 @@
* <p>Note: this selects text within a range <em>between</em> two given areas. To select all text
* <em>within</em> a single area, use {@link SelectGesture}</p>
*/
-public final class SelectRangeGesture extends HandwritingGesture implements Parcelable {
+public final class SelectRangeGesture extends PreviewableHandwritingGesture implements Parcelable {
private @Granularity int mGranularity;
private RectF mStartArea;
@@ -87,7 +87,6 @@
return mEndArea;
}
-
/**
* Builder for {@link SelectRangeGesture}. This class is not designed to be thread-safe.
*/
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 1144b59..810fde2 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -982,8 +982,11 @@
/**
* The last input source on this TextView.
+ *
+ * Use the SOURCE_TOUCHSCREEN as the default value for backward compatibility. There could be a
+ * non UI event originated ActionMode initiation, e.g. API call, a11y events, etc.
*/
- private int mLastInputSource = InputDevice.SOURCE_UNKNOWN;
+ private int mLastInputSource = InputDevice.SOURCE_TOUCHSCREEN;
/**
* The TextView does not auto-size text (default).
diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java
index 9b91cf2..a25e035 100644
--- a/core/java/android/window/BackNavigationInfo.java
+++ b/core/java/android/window/BackNavigationInfo.java
@@ -89,8 +89,6 @@
@Nullable
private final IOnBackInvokedCallback mOnBackInvokedCallback;
private final boolean mPrepareRemoteAnimation;
- @Nullable
- private WindowContainerToken mDepartingWindowContainerToken;
/**
* Create a new {@link BackNavigationInfo} instance.
@@ -100,20 +98,15 @@
* back preview.
* @param onBackInvokedCallback The back callback registered by the current top level window.
* @param departingWindowContainerToken The {@link WindowContainerToken} of departing window.
- * @param isPrepareRemoteAnimation Return whether the core is preparing a back gesture
- * animation, if true, the caller of startBackNavigation should
- * be expected to receive an animation start callback.
*/
private BackNavigationInfo(@BackTargetType int type,
@Nullable RemoteCallback onBackNavigationDone,
@Nullable IOnBackInvokedCallback onBackInvokedCallback,
- boolean isPrepareRemoteAnimation,
- @Nullable WindowContainerToken departingWindowContainerToken) {
+ boolean isPrepareRemoteAnimation) {
mType = type;
mOnBackNavigationDone = onBackNavigationDone;
mOnBackInvokedCallback = onBackInvokedCallback;
mPrepareRemoteAnimation = isPrepareRemoteAnimation;
- mDepartingWindowContainerToken = departingWindowContainerToken;
}
private BackNavigationInfo(@NonNull Parcel in) {
@@ -121,7 +114,6 @@
mOnBackNavigationDone = in.readTypedObject(RemoteCallback.CREATOR);
mOnBackInvokedCallback = IOnBackInvokedCallback.Stub.asInterface(in.readStrongBinder());
mPrepareRemoteAnimation = in.readBoolean();
- mDepartingWindowContainerToken = in.readTypedObject(WindowContainerToken.CREATOR);
}
@Override
@@ -130,7 +122,6 @@
dest.writeTypedObject(mOnBackNavigationDone, flags);
dest.writeStrongInterface(mOnBackInvokedCallback);
dest.writeBoolean(mPrepareRemoteAnimation);
- dest.writeTypedObject(mDepartingWindowContainerToken, flags);
}
/**
@@ -164,18 +155,6 @@
}
/**
- * Returns the {@link WindowContainerToken} of the highest container in the hierarchy being
- * removed.
- * <p>
- * For example, if an Activity is the last one of its Task, the Task's token will be given.
- * Otherwise, it will be the Activity's token.
- */
- @Nullable
- public WindowContainerToken getDepartingWindowContainerToken() {
- return mDepartingWindowContainerToken;
- }
-
- /**
* Callback to be called when the back preview is finished in order to notify the server that
* it can clean up the resources created for the animation.
*
@@ -212,7 +191,6 @@
+ "mType=" + typeToString(mType) + " (" + mType + ")"
+ ", mOnBackNavigationDone=" + mOnBackNavigationDone
+ ", mOnBackInvokedCallback=" + mOnBackInvokedCallback
- + ", mWindowContainerToken=" + mDepartingWindowContainerToken
+ '}';
}
@@ -248,8 +226,6 @@
@Nullable
private IOnBackInvokedCallback mOnBackInvokedCallback = null;
private boolean mPrepareRemoteAnimation;
- @Nullable
- private WindowContainerToken mDepartingWindowContainerToken = null;
/**
* @see BackNavigationInfo#getType()
@@ -285,20 +261,12 @@
}
/**
- * @see BackNavigationInfo#getDepartingWindowContainerToken()
- */
- public void setDepartingWCT(@NonNull WindowContainerToken windowContainerToken) {
- mDepartingWindowContainerToken = windowContainerToken;
- }
-
- /**
* Builds and returns an instance of {@link BackNavigationInfo}
*/
public BackNavigationInfo build() {
return new BackNavigationInfo(mType, mOnBackNavigationDone,
mOnBackInvokedCallback,
- mPrepareRemoteAnimation,
- mDepartingWindowContainerToken);
+ mPrepareRemoteAnimation);
}
}
}
diff --git a/core/java/android/window/ClientWindowFrames.java b/core/java/android/window/ClientWindowFrames.java
index f274d1a..0ce076b6 100644
--- a/core/java/android/window/ClientWindowFrames.java
+++ b/core/java/android/window/ClientWindowFrames.java
@@ -49,7 +49,7 @@
public boolean isParentFrameClippedByDisplayCutout;
- public float sizeCompatScale = 1f;
+ public float compatScale = 1f;
public ClientWindowFrames() {
}
@@ -62,7 +62,7 @@
attachedFrame = new Rect(other.attachedFrame);
}
isParentFrameClippedByDisplayCutout = other.isParentFrameClippedByDisplayCutout;
- sizeCompatScale = other.sizeCompatScale;
+ compatScale = other.compatScale;
}
private ClientWindowFrames(Parcel in) {
@@ -76,7 +76,7 @@
parentFrame.readFromParcel(in);
attachedFrame = in.readTypedObject(Rect.CREATOR);
isParentFrameClippedByDisplayCutout = in.readBoolean();
- sizeCompatScale = in.readFloat();
+ compatScale = in.readFloat();
}
@Override
@@ -86,7 +86,7 @@
parentFrame.writeToParcel(dest, flags);
dest.writeTypedObject(attachedFrame, flags);
dest.writeBoolean(isParentFrameClippedByDisplayCutout);
- dest.writeFloat(sizeCompatScale);
+ dest.writeFloat(compatScale);
}
@Override
@@ -97,7 +97,7 @@
+ " parentFrame=" + parentFrame.toShortString(sb)
+ (attachedFrame != null ? " attachedFrame=" + attachedFrame.toShortString() : "")
+ (isParentFrameClippedByDisplayCutout ? " parentClippedByDisplayCutout" : "")
- + (sizeCompatScale != 1f ? " sizeCompatScale=" + sizeCompatScale : "") + "}";
+ + (compatScale != 1f ? " sizeCompatScale=" + compatScale : "") + "}";
}
@Override
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 0956a71..c2da638 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -138,8 +138,11 @@
/** The container is a system window, excluding wallpaper and input-method. */
public static final int FLAG_IS_SYSTEM_WINDOW = 1 << 16;
+ /** The window was animated by back gesture. */
+ public static final int FLAG_BACK_GESTURE_ANIMATED = 1 << 17;
+
/** The first unused bit. This can be used by remotes to attach custom flags to this change. */
- public static final int FLAG_FIRST_CUSTOM = 1 << 17;
+ public static final int FLAG_FIRST_CUSTOM = 1 << 18;
/** The change belongs to a window that won't contain activities. */
public static final int FLAGS_IS_NON_APP_WINDOW =
@@ -165,6 +168,7 @@
FLAG_IS_BEHIND_STARTING_WINDOW,
FLAG_IS_OCCLUDED,
FLAG_IS_SYSTEM_WINDOW,
+ FLAG_BACK_GESTURE_ANIMATED,
FLAG_FIRST_CUSTOM
})
public @interface ChangeFlags {}
@@ -380,6 +384,9 @@
if ((flags & FLAG_IS_SYSTEM_WINDOW) != 0) {
sb.append(sb.length() == 0 ? "" : "|").append("FLAG_IS_SYSTEM_WINDOW");
}
+ if ((flags & FLAG_BACK_GESTURE_ANIMATED) != 0) {
+ sb.append(sb.length() == 0 ? "" : "|").append("FLAG_BACK_GESTURE_ANIMATED");
+ }
if ((flags & FLAG_FIRST_CUSTOM) != 0) {
sb.append(sb.length() == 0 ? "" : "|").append("FIRST_CUSTOM");
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index c8eec7d..d37779b 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -2096,7 +2096,8 @@
return matchingShortcuts;
}
- private void sendShortcutManagerShareTargetResults(
+ @VisibleForTesting
+ protected void sendShortcutManagerShareTargetResults(
int shortcutType, ServiceResultInfo[] results) {
final Message msg = Message.obtain();
msg.what = ChooserHandler.SHORTCUT_MANAGER_ALL_SHARE_TARGET_RESULTS;
@@ -3873,7 +3874,11 @@
}
}
- static class ServiceResultInfo {
+ /**
+ * Shortcuts grouped by application.
+ */
+ @VisibleForTesting
+ public static class ServiceResultInfo {
public final DisplayResolveInfo originalTarget;
public final List<ChooserTarget> resultTargets;
public final UserHandle userHandle;
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 822393f..e926605 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1098,6 +1098,9 @@
@Override // ResolverListCommunicator
public final void onPostListReady(ResolverListAdapter listAdapter, boolean doPostProcessing,
boolean rebuildCompleted) {
+ if (isDestroyed()) {
+ return;
+ }
if (isAutolaunching()) {
return;
}
diff --git a/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl b/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl
index 81e060d..65016c2 100644
--- a/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl
+++ b/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl
@@ -18,6 +18,7 @@
import android.graphics.RectF;
import android.os.Bundle;
+import android.os.ICancellationSignal;
import android.os.ResultReceiver;
import android.view.KeyEvent;
import android.view.inputmethod.CompletionInfo;
@@ -92,6 +93,9 @@
void performHandwritingGesture(in InputConnectionCommandHeader header,
in ParcelableHandwritingGesture gesture, in ResultReceiver resultReceiver);
+ void previewHandwritingGesture(in InputConnectionCommandHeader header,
+ in ParcelableHandwritingGesture gesture, in ICancellationSignal transport);
+
void setComposingRegion(in InputConnectionCommandHeader header, int start, int end);
void setComposingRegionWithTextAttribute(in InputConnectionCommandHeader header, int start,
diff --git a/core/java/com/android/internal/inputmethod/ImeTracingServerImpl.java b/core/java/com/android/internal/inputmethod/ImeTracingServerImpl.java
index 2a242a5..edd74f6 100644
--- a/core/java/com/android/internal/inputmethod/ImeTracingServerImpl.java
+++ b/core/java/com/android/internal/inputmethod/ImeTracingServerImpl.java
@@ -138,7 +138,7 @@
private void writeTracesToFilesLocked() {
try {
long timeOffsetNs =
- TimeUnit.NANOSECONDS.convert(System.currentTimeMillis(), TimeUnit.NANOSECONDS)
+ TimeUnit.MILLISECONDS.toNanos(System.currentTimeMillis())
- SystemClock.elapsedRealtimeNanos();
ProtoOutputStream clientsProto = new ProtoOutputStream();
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index b0d5922..09f0991 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -27,6 +27,7 @@
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_ICON;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_RECENTS;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_WIDGET;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_SWIPE_TO_RECENTS;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNLOCK_ENTRANCE_ANIMATION;
@@ -226,6 +227,7 @@
public static final int CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION = 63;
public static final int CUJ_LOCKSCREEN_OCCLUSION = 64;
public static final int CUJ_RECENTS_SCROLLING = 65;
+ public static final int CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS = 66;
private static final int NO_STATSD_LOGGING = -1;
@@ -300,6 +302,7 @@
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNLOCK_ENTRANCE_ANIMATION,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_OCCLUSION,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__RECENTS_SCROLLING,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_SWIPE_TO_RECENTS,
};
private static class InstanceHolder {
@@ -389,7 +392,8 @@
CUJ_SHADE_CLEAR_ALL,
CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION,
CUJ_LOCKSCREEN_OCCLUSION,
- CUJ_RECENTS_SCROLLING
+ CUJ_RECENTS_SCROLLING,
+ CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
@@ -906,6 +910,8 @@
return "LOCKSCREEN_OCCLUSION";
case CUJ_RECENTS_SCROLLING:
return "RECENTS_SCROLLING";
+ case CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS:
+ return "LAUNCHER_APP_SWIPE_TO_RECENTS";
}
return "UNKNOWN";
}
diff --git a/core/java/com/android/internal/security/VerityUtils.java b/core/java/com/android/internal/security/VerityUtils.java
index 7f45c09..3ab11a8 100644
--- a/core/java/com/android/internal/security/VerityUtils.java
+++ b/core/java/com/android/internal/security/VerityUtils.java
@@ -17,6 +17,7 @@
package com.android.internal.security;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Build;
import android.os.SystemProperties;
import android.system.Os;
@@ -41,6 +42,7 @@
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
+import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
@@ -77,17 +79,23 @@
return filePath + FSVERITY_SIGNATURE_FILE_EXTENSION;
}
- /** Enables fs-verity for the file with a PKCS#7 detached signature file. */
- public static void setUpFsverity(@NonNull String filePath, @NonNull String signaturePath)
+ /** Enables fs-verity for the file with an optional PKCS#7 detached signature file. */
+ public static void setUpFsverity(@NonNull String filePath, @Nullable String signaturePath)
throws IOException {
- if (Files.size(Paths.get(signaturePath)) > MAX_SIGNATURE_FILE_SIZE_BYTES) {
- throw new SecurityException("Signature file is unexpectedly large: " + signaturePath);
+ byte[] rawSignature = null;
+ if (signaturePath != null) {
+ Path path = Paths.get(signaturePath);
+ if (Files.size(path) > MAX_SIGNATURE_FILE_SIZE_BYTES) {
+ throw new SecurityException("Signature file is unexpectedly large: "
+ + signaturePath);
+ }
+ rawSignature = Files.readAllBytes(path);
}
- setUpFsverity(filePath, Files.readAllBytes(Paths.get(signaturePath)));
+ setUpFsverity(filePath, rawSignature);
}
- /** Enables fs-verity for the file with a PKCS#7 detached signature bytes. */
- public static void setUpFsverity(@NonNull String filePath, @NonNull byte[] pkcs7Signature)
+ /** Enables fs-verity for the file with an optional PKCS#7 detached signature bytes. */
+ public static void setUpFsverity(@NonNull String filePath, @Nullable byte[] pkcs7Signature)
throws IOException {
// This will fail if the public key is not already in .fs-verity kernel keyring.
int errno = enableFsverityNative(filePath, pkcs7Signature);
@@ -227,7 +235,7 @@
}
private static native int enableFsverityNative(@NonNull String filePath,
- @NonNull byte[] pkcs7Signature);
+ @Nullable byte[] pkcs7Signature);
private static native int measureFsverityNative(@NonNull String filePath,
@NonNull byte[] digest);
private static native int statxForFsverityNative(@NonNull String filePath);
diff --git a/core/java/com/android/internal/widget/RemeasuringLinearLayout.java b/core/java/com/android/internal/widget/RemeasuringLinearLayout.java
index 7b154a5..80d1457 100644
--- a/core/java/com/android/internal/widget/RemeasuringLinearLayout.java
+++ b/core/java/com/android/internal/widget/RemeasuringLinearLayout.java
@@ -18,6 +18,7 @@
import android.annotation.Nullable;
import android.content.Context;
+import android.os.Trace;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;
@@ -54,6 +55,7 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ Trace.beginSection("RemeasuringLinearLayout#onMeasure");
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int count = getChildCount();
int height = 0;
@@ -86,5 +88,6 @@
}
mMatchParentViews.clear();
setMeasuredDimension(getMeasuredWidth(), height);
+ Trace.endSection();
}
}
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index d9ca16e..0798110 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -19,6 +19,7 @@
#define LOG_NDEBUG 1
#include <android-base/macros.h>
+#include <android-base/parsebool.h>
#include <android-base/properties.h>
#include <android/graphics/jni_runtime.h>
#include <android_runtime/AndroidRuntime.h>
@@ -52,6 +53,8 @@
using namespace android;
using android::base::GetBoolProperty;
using android::base::GetProperty;
+using android::base::ParseBool;
+using android::base::ParseBoolResult;
extern int register_android_os_Binder(JNIEnv* env);
extern int register_android_os_Process(JNIEnv* env);
@@ -703,17 +706,24 @@
// Read if we are using the profile configuration, do this at the start since the last ART args
// take precedence.
- property_get("dalvik.vm.profilebootclasspath", propBuf, "");
- std::string profile_boot_class_path_flag = propBuf;
- // Empty means the property is unset and we should default to the phenotype property.
- // The possible values are {"true", "false", ""}
- if (profile_boot_class_path_flag.empty()) {
- profile_boot_class_path_flag = server_configurable_flags::GetServerConfigurableFlag(
- RUNTIME_NATIVE_BOOT_NAMESPACE,
- PROFILE_BOOT_CLASS_PATH,
- /*default_value=*/ "");
+ std::string profile_boot_class_path_flag =
+ server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE,
+ PROFILE_BOOT_CLASS_PATH,
+ /*default_value=*/"");
+ bool profile_boot_class_path;
+ switch (ParseBool(profile_boot_class_path_flag)) {
+ case ParseBoolResult::kError:
+ // Default to the system property.
+ profile_boot_class_path =
+ GetBoolProperty("dalvik.vm.profilebootclasspath", /*default_value=*/false);
+ break;
+ case ParseBoolResult::kTrue:
+ profile_boot_class_path = true;
+ break;
+ case ParseBoolResult::kFalse:
+ profile_boot_class_path = false;
+ break;
}
- const bool profile_boot_class_path = (profile_boot_class_path_flag == "true");
if (profile_boot_class_path) {
addOption("-Xcompiler-option");
addOption("--count-hotness-in-compiled-code");
diff --git a/core/jni/android_os_PerformanceHintManager.cpp b/core/jni/android_os_PerformanceHintManager.cpp
index d05a24f..ac1401d 100644
--- a/core/jni/android_os_PerformanceHintManager.cpp
+++ b/core/jni/android_os_PerformanceHintManager.cpp
@@ -40,6 +40,7 @@
typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t);
typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t);
typedef void (*APH_closeSession)(APerformanceHintSession* session);
+typedef void (*APH_sendHint)(APerformanceHintSession*, int32_t);
bool gAPerformanceHintBindingInitialized = false;
APH_getManager gAPH_getManagerFn = nullptr;
@@ -48,6 +49,7 @@
APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr;
APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr;
APH_closeSession gAPH_closeSessionFn = nullptr;
+APH_sendHint gAPH_sendHintFn = nullptr;
void ensureAPerformanceHintBindingInitialized() {
if (gAPerformanceHintBindingInitialized) return;
@@ -88,6 +90,11 @@
LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr,
"Failed to find required symbol APerformanceHint_closeSession!");
+ gAPH_sendHintFn = (APH_sendHint)dlsym(handle_, "APerformanceHint_sendHint");
+ LOG_ALWAYS_FATAL_IF(gAPH_sendHintFn == nullptr,
+ "Failed to find required symbol "
+ "APerformanceHint_sendHint!");
+
gAPerformanceHintBindingInitialized = true;
}
@@ -138,6 +145,11 @@
gAPH_closeSessionFn(reinterpret_cast<APerformanceHintSession*>(nativeSessionPtr));
}
+static void nativeSendHint(JNIEnv* env, jclass clazz, jlong nativeSessionPtr, jint hint) {
+ ensureAPerformanceHintBindingInitialized();
+ gAPH_sendHintFn(reinterpret_cast<APerformanceHintSession*>(nativeSessionPtr), hint);
+}
+
static const JNINativeMethod gPerformanceHintMethods[] = {
{"nativeAcquireManager", "()J", (void*)nativeAcquireManager},
{"nativeGetPreferredUpdateRateNanos", "(J)J", (void*)nativeGetPreferredUpdateRateNanos},
@@ -145,6 +157,7 @@
{"nativeUpdateTargetWorkDuration", "(JJ)V", (void*)nativeUpdateTargetWorkDuration},
{"nativeReportActualWorkDuration", "(JJ)V", (void*)nativeReportActualWorkDuration},
{"nativeCloseSession", "(J)V", (void*)nativeCloseSession},
+ {"nativeSendHint", "(JI)V", (void*)nativeSendHint},
};
int register_android_os_PerformanceHintManager(JNIEnv* env) {
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 9f88f33..01837f4 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -25,6 +25,7 @@
#include <inttypes.h>
#include <mutex>
#include <stdio.h>
+#include <string>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
@@ -880,7 +881,7 @@
case FAILED_TRANSACTION: {
ALOGE("!!! FAILED BINDER TRANSACTION !!! (parcel size = %d)", parcelSize);
const char* exceptionToThrow;
- char msg[128];
+ std::string msg;
// TransactionTooLargeException is a checked exception, only throw from certain methods.
// TODO(b/28321379): Transaction size is the most common cause for FAILED_TRANSACTION
// but it is not the only one. The Binder driver can return BR_FAILED_REPLY
@@ -890,7 +891,7 @@
if (canThrowRemoteException && parcelSize > 200*1024) {
// bona fide large payload
exceptionToThrow = "android/os/TransactionTooLargeException";
- snprintf(msg, sizeof(msg)-1, "data parcel size %d bytes", parcelSize);
+ msg = base::StringPrintf("data parcel size %d bytes", parcelSize);
} else {
// Heuristic: a payload smaller than this threshold "shouldn't" be too
// big, so it's probably some other, more subtle problem. In practice
@@ -899,11 +900,10 @@
exceptionToThrow = (canThrowRemoteException)
? "android/os/DeadObjectException"
: "java/lang/RuntimeException";
- snprintf(msg, sizeof(msg) - 1,
- "Transaction failed on small parcel; remote process probably died, but "
- "this could also be caused by running out of binder buffer space");
+ msg = "Transaction failed on small parcel; remote process probably died, but "
+ "this could also be caused by running out of binder buffer space";
}
- jniThrowException(env, exceptionToThrow, msg);
+ jniThrowException(env, exceptionToThrow, msg.c_str());
} break;
case FDS_NOT_ALLOWED:
jniThrowException(env, "java/lang/RuntimeException",
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 8fd0401..eaec58b 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1202,66 +1202,40 @@
return object;
}
-struct RefreshRateRange {
- const float min;
- const float max;
-
- RefreshRateRange(float min, float max) : min(min), max(max) {}
-
- RefreshRateRange(JNIEnv* env, jobject obj)
- : min(env->GetFloatField(obj, gRefreshRateRangeClassInfo.min)),
- max(env->GetFloatField(obj, gRefreshRateRangeClassInfo.max)) {}
-
- jobject toJava(JNIEnv* env) const {
- return env->NewObject(gRefreshRateRangeClassInfo.clazz, gRefreshRateRangeClassInfo.ctor,
- min, max);
- }
-};
-
-struct RefreshRateRanges {
- const RefreshRateRange physical;
- const RefreshRateRange render;
-
- RefreshRateRanges(RefreshRateRange physical, RefreshRateRange render)
- : physical(physical), render(render) {}
-
- RefreshRateRanges(JNIEnv* env, jobject obj)
- : physical(env, env->GetObjectField(obj, gRefreshRateRangesClassInfo.physical)),
- render(env, env->GetObjectField(obj, gRefreshRateRangesClassInfo.render)) {}
-
- jobject toJava(JNIEnv* env) const {
- return env->NewObject(gRefreshRateRangesClassInfo.clazz, gRefreshRateRangesClassInfo.ctor,
- physical.toJava(env), render.toJava(env));
- }
-};
-
static jboolean nativeSetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobject tokenObj,
jobject DesiredDisplayModeSpecs) {
sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
if (token == nullptr) return JNI_FALSE;
- ui::DisplayModeId defaultMode = env->GetIntField(DesiredDisplayModeSpecs,
- gDesiredDisplayModeSpecsClassInfo.defaultMode);
- jboolean allowGroupSwitching =
+ const auto makeRanges = [env](jobject obj) {
+ const auto makeRange = [env](jobject obj) {
+ gui::DisplayModeSpecs::RefreshRateRanges::RefreshRateRange range;
+ range.min = env->GetFloatField(obj, gRefreshRateRangeClassInfo.min);
+ range.max = env->GetFloatField(obj, gRefreshRateRangeClassInfo.max);
+ return range;
+ };
+
+ gui::DisplayModeSpecs::RefreshRateRanges ranges;
+ ranges.physical = makeRange(env->GetObjectField(obj, gRefreshRateRangesClassInfo.physical));
+ ranges.render = makeRange(env->GetObjectField(obj, gRefreshRateRangesClassInfo.render));
+ return ranges;
+ };
+
+ gui::DisplayModeSpecs specs;
+ specs.defaultMode = env->GetIntField(DesiredDisplayModeSpecs,
+ gDesiredDisplayModeSpecsClassInfo.defaultMode);
+ specs.allowGroupSwitching =
env->GetBooleanField(DesiredDisplayModeSpecs,
gDesiredDisplayModeSpecsClassInfo.allowGroupSwitching);
- const jobject primaryRangesObject =
- env->GetObjectField(DesiredDisplayModeSpecs,
- gDesiredDisplayModeSpecsClassInfo.primaryRanges);
- const jobject appRequestRangesObject =
- env->GetObjectField(DesiredDisplayModeSpecs,
- gDesiredDisplayModeSpecsClassInfo.appRequestRanges);
- const RefreshRateRanges primaryRanges(env, primaryRangesObject);
- const RefreshRateRanges appRequestRanges(env, appRequestRangesObject);
+ specs.primaryRanges =
+ makeRanges(env->GetObjectField(DesiredDisplayModeSpecs,
+ gDesiredDisplayModeSpecsClassInfo.primaryRanges));
+ specs.appRequestRanges =
+ makeRanges(env->GetObjectField(DesiredDisplayModeSpecs,
+ gDesiredDisplayModeSpecsClassInfo.appRequestRanges));
- size_t result =
- SurfaceComposerClient::setDesiredDisplayModeSpecs(token, defaultMode,
- allowGroupSwitching,
- primaryRanges.physical.min,
- primaryRanges.physical.max,
- appRequestRanges.physical.min,
- appRequestRanges.physical.max);
+ size_t result = SurfaceComposerClient::setDesiredDisplayModeSpecs(token, specs);
return result == NO_ERROR ? JNI_TRUE : JNI_FALSE;
}
@@ -1269,33 +1243,26 @@
sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
if (token == nullptr) return nullptr;
- ui::DisplayModeId defaultMode;
- bool allowGroupSwitching;
- float primaryPhysicalRefreshRateMin;
- float primaryPhysicalRefreshRateMax;
- float appRequestPhysicalRefreshRateMin;
- float appRequestPhysicalRefreshRateMax;
- if (SurfaceComposerClient::getDesiredDisplayModeSpecs(token, &defaultMode, &allowGroupSwitching,
- &primaryPhysicalRefreshRateMin,
- &primaryPhysicalRefreshRateMax,
- &appRequestPhysicalRefreshRateMin,
- &appRequestPhysicalRefreshRateMax) !=
- NO_ERROR) {
+ const auto rangesToJava = [env](const gui::DisplayModeSpecs::RefreshRateRanges& ranges) {
+ const auto rangeToJava =
+ [env](const gui::DisplayModeSpecs::RefreshRateRanges::RefreshRateRange& range) {
+ return env->NewObject(gRefreshRateRangeClassInfo.clazz,
+ gRefreshRateRangeClassInfo.ctor, range.min, range.max);
+ };
+
+ return env->NewObject(gRefreshRateRangesClassInfo.clazz, gRefreshRateRangesClassInfo.ctor,
+ rangeToJava(ranges.physical), rangeToJava(ranges.render));
+ };
+
+ gui::DisplayModeSpecs specs;
+ if (SurfaceComposerClient::getDesiredDisplayModeSpecs(token, &specs) != NO_ERROR) {
return nullptr;
}
- const RefreshRateRange primaryPhysicalRange(primaryPhysicalRefreshRateMin,
- primaryPhysicalRefreshRateMax);
- const RefreshRateRange appRequestPhysicalRange(appRequestPhysicalRefreshRateMin,
- appRequestPhysicalRefreshRateMax);
-
- // TODO(b/241460058): populate the render ranges
- const RefreshRateRanges primaryRanges(primaryPhysicalRange, primaryPhysicalRange);
- const RefreshRateRanges appRequestRanges(appRequestPhysicalRange, appRequestPhysicalRange);
-
return env->NewObject(gDesiredDisplayModeSpecsClassInfo.clazz,
- gDesiredDisplayModeSpecsClassInfo.ctor, defaultMode, allowGroupSwitching,
- primaryRanges.toJava(env), appRequestRanges.toJava(env));
+ gDesiredDisplayModeSpecsClassInfo.ctor, specs.defaultMode,
+ specs.allowGroupSwitching, rangesToJava(specs.primaryRanges),
+ rangesToJava(specs.appRequestRanges));
}
static jobject nativeGetDisplayNativePrimaries(JNIEnv* env, jclass, jobject tokenObj) {
diff --git a/core/jni/com_android_internal_security_VerityUtils.cpp b/core/jni/com_android_internal_security_VerityUtils.cpp
index 8305bd0..dabee69 100644
--- a/core/jni/com_android_internal_security_VerityUtils.cpp
+++ b/core/jni/com_android_internal_security_VerityUtils.cpp
@@ -48,10 +48,6 @@
if (rfd.get() < 0) {
return errno;
}
- ScopedByteArrayRO signature_bytes(env, signature);
- if (signature_bytes.get() == nullptr) {
- return EINVAL;
- }
fsverity_enable_arg arg = {};
arg.version = 1;
@@ -59,8 +55,18 @@
arg.block_size = 4096;
arg.salt_size = 0;
arg.salt_ptr = reinterpret_cast<uintptr_t>(nullptr);
- arg.sig_size = signature_bytes.size();
- arg.sig_ptr = reinterpret_cast<uintptr_t>(signature_bytes.get());
+
+ if (signature != nullptr) {
+ ScopedByteArrayRO signature_bytes(env, signature);
+ if (signature_bytes.get() == nullptr) {
+ return EINVAL;
+ }
+ arg.sig_size = signature_bytes.size();
+ arg.sig_ptr = reinterpret_cast<uintptr_t>(signature_bytes.get());
+ } else {
+ arg.sig_size = 0;
+ arg.sig_ptr = reinterpret_cast<uintptr_t>(nullptr);
+ }
if (ioctl(rfd.get(), FS_IOC_ENABLE_VERITY, &arg) < 0) {
return errno;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f0fbff1..7ada548 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2533,6 +2533,19 @@
android:description="@string/permdesc_modifyAudioSettings"
android:protectionLevel="normal" />
+ <!-- ==================================================== -->
+ <!-- Permissions related to screen capture -->
+ <!-- ==================================================== -->
+ <eat-comment />
+
+ <!-- Allows an application to get notified when a screen capture of its windows is attempted.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.DETECT_SCREEN_CAPTURE"
+ android:label="@string/permlab_detectScreenCapture"
+ android:description="@string/permdesc_detectScreenCapture"
+ android:protectionLevel="normal" />
+
<!-- ======================================== -->
<!-- Permissions for factory reset protection -->
<!-- ======================================== -->
@@ -6908,13 +6921,6 @@
android:exported="false">
</activity>
- <activity android:name="com.android.internal.app.LogAccessDialogActivity"
- android:theme="@style/Theme.Translucent.NoTitleBar"
- android:process=":ui"
- android:excludeFromRecents="true"
- android:exported="false">
- </activity>
-
<activity android:name="com.android.server.notification.NASLearnMoreActivity"
android:theme="@style/Theme.Dialog.Confirmation"
android:excludeFromRecents="true"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index b20bfef..4a6d4b0 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Beller-ID se verstek is nie beperk nie. Volgende oproep: nie beperk nie"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Diens nie verskaf nie."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Jy kan nie die beller-ID-instelling verander nie."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Het data oorgeskakel na <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Jy kan dit enige tyd in instellings verander"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Geen mobiele datadiens nie"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Noodoproepe is onbeskikbaar"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Geen stemdiens nie"</string>
@@ -700,6 +698,8 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Laat die program toe om videolêers in jou gedeelde berging te lees."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"lees prentlêers in gedeelde berging"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Laat die program toe om prentlêers in jou gedeelde berging te lees."</string>
+ <string name="permlab_readVisualUserSelect" msgid="5516204215354667586">"lees prent- en videolêers wat gebruiker in gedeelde berging kies"</string>
+ <string name="permdesc_readVisualUserSelect" msgid="8027174717714968217">"Laat die app toe om prent- en videolêers te lees wat jy in jou gedeelde berging kies."</string>
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"verander of vee jou gedeelde berging se inhoud uit"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Laat die program toe om jou gedeelde berging se inhoud te skryf."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"maak en/of ontvang SIP-oproepe"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 2574fd8..1850cbe 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"የደዋይ ID ነባሪዎች ወደአልተከለከለም። ቀጥሎ ጥሪ፡አልተከለከለም"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"አገልግሎት አልቀረበም።"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"የደዋይ መታወቂያ ቅንብሮች መለወጥ አትችልም፡፡"</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"ውሂብ ወደ <xliff:g id="CARRIERDISPLAY">%s</xliff:g> ተቀይሯል"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"ይህን በማንኛውም ጊዜ በቅንብሮች ውስጥ መለወጥ ይችላሉ"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"ምንም የተንቀሳቃሽ ስልክ ውሂብ አገልግሎት የለም"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"የድንገተኛ አደጋ ጥሪ አይገኝም"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"ምንም የድምፅ ጥሪ አገልግሎት የለም"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"መተግበሪያው ከእርስዎ የተጋራ ማከማቻ የቪዲዮ ፋይሎችን እንዲያነብ ይፈቅድለታል።"</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"ከጋራ ማከማቻ የምስል ፋይሎችን አንብብ"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"መተግበሪያው ከእርስዎ የተጋራ ማከማቻ የምስል ፋይሎችን እንዲያነብ ይፈቅድለታል።"</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"የተጋራ ማከማቻዎን ይዘቶች ይቀይሩ ወይም ይሰርዙ"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"መተግበሪያው የእርስዎን የተጋራ ማከማቻ ይዘቶችን እንዲጽፍ ያስችለዋል።"</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"የSIP ጥሪዎችን ያድርጉ/ይቀበሉ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index ba3ebb1..f113413 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -76,10 +76,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"الإعداد التلقائي لمعرف المتصل هو غير محظور . الاتصال التالي: غير محظور"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"الخدمة غير متوفرة."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"لا يمكنك تغيير إعداد معرّف المتصل."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"تم تبديل البيانات إلى <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"يمكنك تغيير هذه الميزة في أي وقت في الإعدادات."</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"لا تتوفّر خدمة بيانات جوّال."</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"لا تتوفّر مكالمات طوارئ."</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"لا تتوفر خدمة صوتية"</string>
@@ -704,6 +702,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"يسمح للتطبيق بقراءة ملفات الفيديو من مساحة التخزين المشتركة."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"قراءة ملفات الصور من مساحة التخزين المشتركة"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"يسمح هذا الإذن للتطبيق بقراءة ملفات الصور من مساحة التخزين المشتركة."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"تعديل محتوى مساحة التخزين المشتركة أو حذفه"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"للسماح للتطبيق بالكتابة إلى محتوى مساحة التخزين المشتركة."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"إجراء/تلقي مكالمات SIP"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index d49d3b1..687fc99 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"কলাৰ আইডি সীমিত নকৰিবলৈ পূর্বনির্ধাৰণ কৰা হৈছে। পৰৱৰ্তী কল: সীমিত কৰা হোৱা নাই"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"সুবিধা যোগান ধৰা হোৱা নাই।"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"আপুনি কলাৰ আইডি ছেটিং সলনি কৰিব নোৱাৰে।"</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"ডেটা <xliff:g id="CARRIERDISPLAY">%s</xliff:g>লৈ সলনি কৰা হৈছে"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"আপুনি ছেটিঙত এইটো যিকোনো সময়তে সলনি কৰিব পাৰে"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"কোনো ম’বাইল ডেটা সেৱা নাই"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"জৰুৰীকালীন কল কৰাৰ সুবিধা উপলব্ধ নহয়"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"কোনো ভইচ সেৱা নাই"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"আপোনাৰ শ্বেয়াৰ কৰি ৰখা ষ্ট’ৰেজৰ পৰা ভিডিঅ’ ফাইল পঢ়িবলৈ এপ্টোক অনুমতি দিয়ে।"</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"শ্বেয়াৰ কৰি ৰখা ষ্ট’ৰেজৰ পৰা প্ৰতিচ্ছবিৰ ফাইল পঢ়ক"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"আপোনাৰ শ্বেয়াৰ কৰি ৰখা ষ্ট’ৰেজৰ পৰা প্ৰতিচ্ছবিৰ ফাইল পঢ়িবলৈ এপ্টোক অনুমতি দিয়ে।"</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"আপোনাৰ শ্বেয়াৰ কৰি ৰখা ষ্ট’ৰেজৰ সমল সংশোধন কৰিব বা মচিব পাৰে"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"আপোনাৰ শ্বেয়াৰ কৰি ৰখা ষ্ট’ৰেজৰ সমল লিখিবলৈ এপ্টোক অনুমতি দিয়ে।"</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"SIP কল কৰা/পোৱা"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index ffd1480..f82a007 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Zəng edənin kimliyi defolt olaraq qadağan deyil. Növbəti zəng: Qadağan deyil"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Xidmət təmin edilməyib."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Çağrı kimliyi ayarını dəyişə bilməzsiniz."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Data <xliff:g id="CARRIERDISPLAY">%s</xliff:g> operatoruna keçirilib"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Bunu istənilən zaman Ayarlarda dəyişə bilərsiniz"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Mobil data xidməti yoxdur"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Təcili zəng əlçatan deyil"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Səsli xidmət yoxdur"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Tətbiqə paylaşılan yaddaşdakı video fayllarını oxumaq imkanı verir."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"paylaşılan yaddaşdakı şəkil fayllarını oxumaq"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Tətbiqə paylaşılan yaddaşdakı şəkil fayllarını oxumaq imkanı verir."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"paylaşılan yaddaşdakı kontenti dəyişmək və ya silmək"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Tətbiqə paylaşılan yaddaşdakı kontenti yazmaq imkanı verir."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"SIP çağrıları göndərin/qəbul edin"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index b1ec940b..40b126f 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -73,10 +73,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID pozivaoca podrazumevano nije ograničen. Sledeći poziv: Nije ograničen."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Usluga nije dobavljena."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Ne možete da promenite podešavanje ID-a korisnika."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Mobilni podaci su prebačeni na operatera <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Ovo možete u svakom trenutku da promenite u Podešavanjima"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Nema usluge mobilnih podataka"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Hitni pozivi nisu dostupni"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Nema glasovne usluge"</string>
@@ -701,6 +699,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Omogućava aplikaciji da čita video fajlove iz deljenog memorijskog prostora."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"čitanje fajlova slika iz deljenog memorijskog prostora"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Omogućava aplikaciji da čita fajlove slika iz deljenog memorijskog prostora."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"menjanje ili brisanje sadržaja deljenog memorijskog prostora"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Dozvoljava aplikaciji da upisuje sadržaj deljenog memorijskog prostora."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"upućivanje/prijem SIP poziva"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 40605c5..bf1302c 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -74,10 +74,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Налады ідэнтыфікатару АВН па змаўчанні: не абмяжавана. Наступны выклік: не абмежавана"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Служба не прадастаўляецца."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Вы не можаце змяніць налады ідэнтыфікатара абанента, якi тэлефануе."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Мабільны трафік пераключаны на аператара \"<xliff:g id="CARRIERDISPLAY">%s</xliff:g>\""</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Вы можаце змяніць гэта ў любы час у Наладах"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Мабільная перадача даных недаступная"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Экстранныя выклікі недаступныя"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Няма сэрвісу галасавых выклікаў"</string>
@@ -702,6 +700,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Праграма зможа счытваць відэафайлы з абагуленага сховішча."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"счытваць файлы відарысаў з абагуленага сховішча"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Праграма зможа счытваць файлы відарысаў з абагуленага сховішча."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"змяненне або выдаленне змесціва абагуленага сховішча"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Дазваляе праграме запісваць змесціва абагуленага сховішча."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"ажыццяўленне/прыманне выклікаў SIP"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index a1c9d1c..b472930 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Стандартната идентификация на повикванията е „разрешено“. За следващото обаждане тя е разрешена."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Услугата не е обезпечена."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Не можете да променяте настройката за идентификация на обажданията."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Преминахте към мобилни данни от <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Можете да промените това по всяко време в „Настройки“"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Няма достъп до мобилната услуга за данни"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Няма достъп до спешните обаждания"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Няма услуга за гласови обаждания"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Разрешава на приложението да чете видеофайлове от споделеното ви хранилище."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"да чете графични файлове от споделеното хранилище"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Разрешава на приложението да чете графични файлове от споделеното ви хранилище."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"промяна или изтрив. на съдърж. от сподел. ви хранил."</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Разрешава на прил. да записва съдърж. от сподел. ви хранил."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"извършване/получаване на обаждания чрез SIP"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index fd69acd..aba5e7d 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ডিফল্টরূপে কলার আইডি সীমাবদ্ধ করা থাকে না৷ পরবর্তী কল: সীমাবদ্ধ নয়"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"পরিষেবা প্রস্তুত নয়৷"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"আপনি কলার আইডি এর সেটিংস পরিবর্তন করতে পারবেন না৷"</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"<xliff:g id="CARRIERDISPLAY">%s</xliff:g>-এর ডেটা ব্যবহার করা হয়েছে"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"আপনি যে কোনও সময় সেটিংস থেকে এটি পরিবর্তন করতে পারবেন"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"মোবাইল ডেটা পরিষেবা নেই"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"জরুরি কল করা যাবে না"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"ভয়েস পরিষেবা নেই"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"আপনার শেয়ার করা স্টোরেজ থেকে ভিডিও ফাইল রিড করতে অ্যাপকে অনুমতি দিন।"</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"শেয়ার করা স্টোরেজ থেকে ছবির ফাইল রিড করা"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"আপনার শেয়ার করা স্টোরেজ থেকে ছবির ফাইল রিড করার জন্য অ্যাপকে অনুমতি দেয়।"</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"শেয়ার করা স্টোরেজের কন্টেন্ট মুছে ফেলুন বা পরিবর্তন করুন"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"শেয়ার করা স্টোরেজের কন্টেন্ট লেখার জন্য অ্যাপটিকে অনুমতি দিন।"</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"SIP কল করুন/গ্রহণ করুন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 455c9e9..815bca8 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -73,8 +73,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Prikaz ID-a pozivaoca u zadanim postavkama nije zabranjen. Sljedeći poziv: nije zabranjen"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Uslugu nije moguće koristiti."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Ne možete promijeniti postavke ID-a pozivaoca."</string>
- <string name="auto_data_switch_title" msgid="3286350716870518297">"Podaci su prebačeni na <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
- <string name="auto_data_switch_content" msgid="803557715007110959">"To uvijek možete promijeniti u postavkama"</string>
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Prijenos podataka usmjeravanjem na <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Ove postavke možete uvijek promijeniti u Postavkama"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Nema usluge prijenosa podataka na mobilnoj mreži"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Hitni pozivi su nedostupni"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Nema usluge govornih poziva"</string>
@@ -699,6 +699,8 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Omogućava aplikaciji da čita fajlove videozapisa iz vaše dijeljene pohrane."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"čitanje fajlova slika iz dijeljene pohrane"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Omogućava aplikaciji da čita fajlove slika iz vaše dijeljene pohrane."</string>
+ <string name="permlab_readVisualUserSelect" msgid="5516204215354667586">"čitati slikovne i videodatoteke koje je korisnik odabrao iz dijeljene pohrane"</string>
+ <string name="permdesc_readVisualUserSelect" msgid="8027174717714968217">"Aplikaciji omogućuje čitanje slikovnih i videodatoteka koje odaberete iz dijeljene pohrane."</string>
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"mijenja ili briše sadržaj vaše dijeljene pohrane"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Omogućava aplikaciji da piše sadržaj vaše dijeljene pohrane."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"Uputi/primi SIP pozive"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 43dc676..b423b65 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"El valor predeterminat de l\'identificador de trucada és no restringit. Trucada següent: no restringit"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"No s\'ha proveït el servei."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"No pots canviar la configuració de l\'identificador de trucada."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Les dades s\'han canviat a <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Pots canviar aquesta opció en qualsevol moment a Configuració"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"No hi ha servei de dades mòbils"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Les trucades d\'emergència no estan disponibles"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Sense servei de veu"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Permet que l\'aplicació llegeixi fitxers de vídeo de l\'emmagatzematge compartit."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"llegir fitxers d\'imatge de l\'emmagatzematge compartit"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Permet que l\'aplicació llegeixi fitxers d\'imatge de l\'emmagatzematge compartit."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"editar o suprimir cont. d\'emmagatzematge compartit"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"L\'app pot editar contingut de l\'emmagatzematge compartit."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"Fer i rebre trucades de SIP"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 132a9f5..c22628c 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -74,10 +74,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Ve výchozím nastavení není funkce ID volajícího omezena. Příští hovor: Neomezeno"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Služba není zřízena."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Nastavení ID volajícího nesmíte měnit."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Datové připojení bylo přepnuto na operátora <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Volbu můžete kdykoli změnit v nastavení"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Není k dispozici žádná mobilní datová služba"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Tísňová volání jsou nedostupná"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Hlasová volání nejsou k dispozici"</string>
@@ -702,6 +700,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Umožňuje aplikaci čtení videosouborů ze sdíleného úložiště."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"čtení obrázkových souborů ze sdíleného úložiště"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Umožňuje aplikaci číst obrázkové soubory ze sdíleného úložiště."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"úprava nebo mazání obsahu sdíleného úložiště"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Umožňuje aplikaci zápis obsahu do sdíleného úložiště."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"uskutečňování/příjem volání SIP"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 9dd4334..59c18bd 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -334,7 +334,7 @@
<string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Udføre bevægelser"</string>
<string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Kan trykke, stryge, knibe sammen og udføre andre bevægelser."</string>
<string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Fingeraftryksbevægelser"</string>
- <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Kan registrere bevægelser, der foretages på enhedens fingeraftrykslæser."</string>
+ <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Kan registrere bevægelser, der foretages på enhedens fingeraftrykssensor."</string>
<string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Tag screenshot"</string>
<string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Kan tage et screenshot af skærmen."</string>
<string name="permlab_statusBar" msgid="8798267849526214017">"deaktivere eller redigere statuslinje"</string>
@@ -583,11 +583,11 @@
<string name="biometric_error_generic" msgid="6784371929985434439">"Der opstod fejl i forbindelse med godkendelse"</string>
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Brug skærmlås"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Angiv din skærmlås for at fortsætte"</string>
- <string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Hold fingeren nede på læseren"</string>
+ <string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Hold fingeren nede på sensoren"</string>
<string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Fingeraftrykket kan ikke genkendes. Prøv igen."</string>
- <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Rengør fingeraftrykslæseren, og prøv igen"</string>
- <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Rengør læseren, og prøv igen"</string>
- <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Hold fingeren nede på læseren"</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Rengør fingeraftrykssensoren, og prøv igen"</string>
+ <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Rengør sensoren, og prøv igen"</string>
+ <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Hold fingeren nede på sensoren"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Du bevægede fingeren for langsomt. Prøv igen."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Prøv med et andet fingeraftryk"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Der er for lyst"</string>
@@ -610,9 +610,9 @@
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Du har brugt for mange forsøg. Brug skærmlåsen i stedet."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Fingeraftrykket kan ikke behandles. Prøv igen."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Der er ikke registreret nogen fingeraftryk."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Denne enhed har ingen fingeraftrykslæser."</string>
+ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Denne enhed har ingen fingeraftrykssensor."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensoren er midlertidigt deaktiveret."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Fingeraftrykslæseren kan ikke bruges. Få den repareret"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Fingeraftrykssensoren kan ikke bruges. Få den repareret"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Der blev trykket på afbryderknappen"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Fingeraftryk <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Brug fingeraftryk"</string>
@@ -632,7 +632,7 @@
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Konfigurer flere måder at låse op på"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tryk for at tilføje et fingeraftryk"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Oplåsning med fingeraftryk"</string>
- <string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Fingeraftrykslæseren kan ikke bruges"</string>
+ <string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Fingeraftrykssensoren kan ikke bruges"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Få den repareret."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Din ansigtsmodel kan ikke oprettes. Prøv igen."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Der er for lyst. Prøv en mere dæmpet belysning."</string>
@@ -698,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Tillader, at appen læser videofiler fra din fælles lagerplads."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"Læse billedfiler fra den delte lagerplads"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Tillader, at appen læser billedfiler fra din delte lagerplads."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"ændre eller slette indholdet af din delte lagerplads"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Tillader, at appen kan skrive indholdet af din delte lagerplads."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"foretage/modtage SIP-opkald"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 661c2cd..e14d3e9 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Anrufer-ID ist standardmäßig nicht beschränkt. Nächster Anruf: Nicht beschränkt"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Dienst nicht eingerichtet."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Du kannst die Einstellung für die Anrufer-ID nicht ändern."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Mobile Daten wurden auf <xliff:g id="CARRIERDISPLAY">%s</xliff:g> umgestellt"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Du kannst dies jederzeit in den Einstellungen ändern"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Kein mobiler Datendienst"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Notrufe nicht möglich"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Keine Anrufe"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Gewährt der App Lesezugriff auf Videodateien in deinem freigegebenen Speicher."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"Lesezugriff auf Bilddateien im freigegebenen Speicher"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Gewährt der App Lesezugriff auf Bilddateien in deinem freigegebenen Speicher."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"Inhalte deines freigegebenen Speichers ändern oder löschen"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"So kann die App Inhalte deines freigegebenen Speichers erstellen."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"SIP-Anrufe tätigen/empfangen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 8cb3989b..47421cd 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -698,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Επιτρέπει στην εφαρμογή την ανάγνωση αρχείων βίντεο από τον κοινόχρηστο αποθηκευτικό σας χώρο."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"ανάγνωση αρχείων εικόνας από τον κοινόχρηστο αποθηκευτικό χώρο"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Επιτρέπει στην εφαρμογή την ανάγνωση αρχείων εικόνας από τον κοινόχρηστο αποθηκευτικό σας χώρο."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"τροποποιεί ή διαγράφει το περιεχόμενο του κοινόχρηστου αποθηκευτικού χώρου σας"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Επιτρέπει στην εφαρμογή την εγγραφή του περιεχομένου του κοινόχρηστου αποθηκευτικού χώρου σας."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"πραγματοποιεί/λαμβάνει κλήσεις SIP"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 75db3826..e37dbcd 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -698,6 +698,8 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Allows the app to read video files from your shared storage."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"read image files from shared storage"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Allows the app to read image files from your shared storage."</string>
+ <string name="permlab_readVisualUserSelect" msgid="5516204215354667586">"read user selected image and video files from shared storage"</string>
+ <string name="permdesc_readVisualUserSelect" msgid="8027174717714968217">"Allows the app to read image and video files that you select from your shared storage."</string>
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"modify or delete the contents of your shared storage"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Allows the app to write the contents of your shared storage."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"make/receive SIP calls"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 883cd55..3b6bf27 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -698,6 +698,8 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Allows the app to read video files from your shared storage."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"read image files from shared storage"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Allows the app to read image files from your shared storage."</string>
+ <string name="permlab_readVisualUserSelect" msgid="5516204215354667586">"read user selected image and video files from shared storage"</string>
+ <string name="permdesc_readVisualUserSelect" msgid="8027174717714968217">"Allows the app to read image and video files that you select from your shared storage."</string>
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"modify or delete the contents of your shared storage"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Allows the app to write the contents of your shared storage."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"make/receive SIP calls"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index c4c19da..6faf78b 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -698,6 +698,8 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Allows the app to read video files from your shared storage."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"read image files from shared storage"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Allows the app to read image files from your shared storage."</string>
+ <string name="permlab_readVisualUserSelect" msgid="5516204215354667586">"read user selected image and video files from shared storage"</string>
+ <string name="permdesc_readVisualUserSelect" msgid="8027174717714968217">"Allows the app to read image and video files that you select from your shared storage."</string>
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"modify or delete the contents of your shared storage"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Allows the app to write the contents of your shared storage."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"make/receive SIP calls"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index d19199d..1f5b6a1 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -698,6 +698,8 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Allows the app to read video files from your shared storage."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"read image files from shared storage"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Allows the app to read image files from your shared storage."</string>
+ <string name="permlab_readVisualUserSelect" msgid="5516204215354667586">"read user selected image and video files from shared storage"</string>
+ <string name="permdesc_readVisualUserSelect" msgid="8027174717714968217">"Allows the app to read image and video files that you select from your shared storage."</string>
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"modify or delete the contents of your shared storage"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Allows the app to write the contents of your shared storage."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"make/receive SIP calls"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 9037d70..5e6afc3 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -698,6 +698,8 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Allows the app to read video files from your shared storage."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"read image files from shared storage"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Allows the app to read image files from your shared storage."</string>
+ <string name="permlab_readVisualUserSelect" msgid="5516204215354667586">"read user selected image and video files from shared storage"</string>
+ <string name="permdesc_readVisualUserSelect" msgid="8027174717714968217">"Allows the app to read image and video files that you select from your shared storage."</string>
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"modify or delete the contents of your shared storage"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Allows the app to write the contents of your shared storage."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"make/receive SIP calls"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 9441f27..247aed6 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -73,10 +73,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"El Identificador de llamadas está predeterminado en no restringido. Llamada siguiente: no restringido"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Servicio no suministrado."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"No puedes cambiar la configuración del identificador de llamadas."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Se cambiaron los datos a <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Puedes cambiar esto cuando quieras en Configuración"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"No hay ningún servicio de datos móviles"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Servicio de llamadas de emergencia no disponible"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Sin servicio de voz"</string>
@@ -701,6 +699,8 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Permite que la app lea los archivos de video del almacenamiento compartido."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"leer los archivos de imagen del almacenamiento compartido"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Permite que la app lea los archivos de imagen del almacenamiento compartido."</string>
+ <string name="permlab_readVisualUserSelect" msgid="5516204215354667586">"leer los archivos de imagen y video que el usuario haya seleccionado del almacenamiento compartido"</string>
+ <string name="permdesc_readVisualUserSelect" msgid="8027174717714968217">"Permite que la app lea los archivos de imagen y video que selecciones del almacenamiento compartido."</string>
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"cambiar o borrar contenido de almacenamiento compartido"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Editar almacen. compartido"</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"realizar/recibir llamadas SIP"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 753aa8b..63383fc 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -73,10 +73,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"La identificación del emisor presenta el valor predeterminado de no restringido. Siguiente llamada: No restringido"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"El servicio no se suministra."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"No puedes modificar la identificación de emisor."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Datos cambiados a <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Puedes cambiar esta opción en cualquier momento en Ajustes"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"No hay ningún servicio de datos móviles"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Servicio de llamadas de emergencia no disponible"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Sin servicio de voz"</string>
@@ -701,6 +699,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Permite que la aplicación lea archivos de vídeo desde tu almacenamiento compartido."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"leer archivos de imagen desde el almacenamiento compartido"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Permite que la aplicación lea archivos de imagen desde tu almacenamiento compartido."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"editar/eliminar contenido de almacenamiento compartido"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Permite que app edite contenido de almacenamiento compartido."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"hacer/recibir llamadas SIP"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 1789793..6c0b4ff 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Helistaja ID pole vaikimisi piiratud. Järgmine kõne: pole piiratud"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Teenus pole ette valmistatud."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Helistaja ID seadet ei saa muuta."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Mobiilne andmeside lülitati operaatorile <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Saate seda igal ajal seadetes muuta"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Mobiilne andmesideteenus puudub"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Hädaabikõned pole saadaval"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Häälkõned pole saadaval"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Võimaldab rakendusel lugeda teie jagatud salvestusruumis olevaid videofaile."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"lugeda teie jagatud salvestusruumis olevaid pildifaile"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Võimaldab rakendusel lugeda teie jagatud salvestusruumis olevaid pildifaile."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"Jagatud salvestusruumi sisu muutmine või kustutamine"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Lubab rakendusel kirjutada jagatud salvestusruumi sisu."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"SIP-kõnede tegemine/vastuvõtmine"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 8a7d073..ff12661 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Deitzailearen identitatea zerbitzuaren balio lehenetsiak ez du murriztapenik ezartzen. Hurrengo deia: murriztapenik gabe."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Zerbitzua ez da hornitu."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Ezin duzu aldatu deitzailearen identitatearen ezarpena."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"<xliff:g id="CARRIERDISPLAY">%s</xliff:g> operadorearen datu-konexiora aldatu zara"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Ezarpenak atalean alda dezakezu aukera hori"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Ez dago mugikorreko datu-zerbitzurik"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Ezin da egin larrialdi-deirik"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Ez dago ahots-deien zerbitzurik"</string>
@@ -179,7 +177,7 @@
<string name="low_memory" product="watch" msgid="3479447988234030194">"Erlojuaren memoria beteta dago. Tokia egiteko, ezabatu fitxategi batzuk."</string>
<string name="low_memory" product="tv" msgid="6663680413790323318">"Android TV gailuaren memoria beteta dago. Tokia egiteko, ezabatu fitxategi batzuk."</string>
<string name="low_memory" product="default" msgid="2539532364144025569">"Telefonoaren memoria beteta dago. Tokia egiteko, ezabatu fitxategi batzuk."</string>
- <string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{Ziurtagiri-emaile bat dago instalatuta}other{Ziurtagiri-emaile bat baino gehiago daude instalatuta}}"</string>
+ <string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{Autoritate ziurtagiri-emaile bat dago instalatuta}other{Autoritate ziurtagiri-emaile bat baino gehiago daude instalatuta}}"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"Hirugarren alderdi ezezagun baten arabera"</string>
<string name="ssl_ca_cert_noti_by_administrator" msgid="4564941950768783879">"Laneko profilen administratzaileak"</string>
<string name="ssl_ca_cert_noti_managed" msgid="217337232273211674">"<xliff:g id="MANAGING_DOMAIN">%s</xliff:g> da arduraduna"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Biltegi partekatuko bideo-fitxategiak irakurtzeko baimena ematen die aplikazioei."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"irakurri biltegi partekatuko irudi-fitxategiak"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Biltegi partekatuko irudi-fitxategiak irakurtzeko baimena ematen die aplikazioei."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"aldatu edo ezabatu biltegi partekatuko edukia"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Biltegi partekatuko edukian idazteko baimena ematen die aplikazioei."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"egin/jaso SIP deiak"</string>
@@ -1458,7 +1460,7 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Paketeak ezabatzeko eskatzea baimentzen die aplikazioei."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"eskatu bateria-optimizazioei ez ikusi egitea"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Bateriaren optimizazioei ez ikusi egiteko baimena eskatzea baimentzen die aplikazioei."</string>
- <string name="permlab_queryAllPackages" msgid="2928450604653281650">"Kontsultatu pakete guztiak"</string>
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"kontsultatu pakete guztiak"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Instalatutako pakete guztiak ikusteko baimena ematen dio aplikazioari."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Sakatu birritan zooma kontrolatzeko"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Ezin izan da widgeta gehitu."</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 88bbcc6..029d9d1 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"پیشفرض شناسه تماسگیرنده روی غیرمحدود است. تماس بعدی: بدون محدودیت"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"سرویس دارای مجوز نیست."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"شما میتوانید تنظیم شناسه تماسگیرنده را تغییر دهید."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"داده به <xliff:g id="CARRIERDISPLAY">%s</xliff:g> تغییر کرد"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"هرزمان بخواهید میتوانید این را در «تنظیمات» تغییر دهید"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"بدون سرویس داده تلفن همراه"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"تماس اضطراری دردسترس نیست"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"سرویس صوتی دردسترس نیست"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"به برنامه اجازه میدهد فایلهای ویدیویی موجود در فضای ذخیرهسازی همرسانیشده را بخواند."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"خواندن فایلهای تصویری موجود در فضای ذخیرهسازی مشترک"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"به برنامه اجازه میدهد فایلهای تصویری موجود در فضای ذخیرهسازی مشترک را بخواند."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"تغییر یا حذف محتوای فضای ذخیرهسازی مشترک"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"به برنامه اجازه میدهد محتوای فضای ذخیرهسازی مشترکتان را بنویسد."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"تماس گرفتن/دریافت تماس از طریق SIP"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index da3a91e..487d199 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Soittajan tunnukseksi muutetaan rajoittamaton. Seuraava puhelu: ei rajoitettu"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Palvelua ei tarjota."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Et voi muuttaa soittajan tunnuksen asetusta."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Data vaihdettu: <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Voit vaihtaa valintasi milloin tahansa asetuksista."</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Ei mobiilidatapalvelua"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Hätäpuhelut eivät ole käytettävissä"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Ei äänipuheluja"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Sallii sovelluksen lukea jaetun tallennustilan videotiedostoja."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"lue jaetun tallennustilan kuvatiedostoja"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Sallii sovelluksen lukea jaetun tallennustilan kuvatiedostoja."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"muokata tai poistaa jaetun tallennustilan sisältöä"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Antaa sovelluksen kirjoittaa jaetun tallennustilan sisällön."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"soita/vastaanota SIP-puheluja"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index d05d97b..722a66e 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -73,10 +73,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Par défaut, les numéros des appelants ne sont pas restreints. Appel suivant : non restreint"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Ce service n\'est pas pris en charge."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Impossible de modifier le paramètre relatif au numéro de l\'appelant."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Données changées à <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Vous pouvez modifier cette option en tout temps dans les paramètres"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Aucun service de données cellulaires"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Le service d\'appel d\'urgence n\'est pas accessible"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Aucun service vocal"</string>
@@ -701,6 +699,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Permet à l\'application de lire les fichiers vidéo de votre espace de stockage partagé."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"lire des fichiers d\'image à partir de l\'espace de stockage partagé"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Permet à l\'application de lire les fichiers d\'image de votre espace de stockage partagé."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"modifier ou supprimer le contenu de votre espace de stockage partagé"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Autorise l\'application à écrire le contenu de votre espace de stockage partagé."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"faire et recevoir des appels SIP"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index cb2e201..e20fb5f 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -73,10 +73,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Par défaut, les numéros des appelants ne sont pas restreints. Appel suivant : non restreint"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Ce service n\'est pas pris en charge."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Impossible de modifier le paramètre relatif au numéro de l\'appelant."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Données transférées vers <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Vous pouvez modifier cette option à tout moment dans les paramètres."</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Aucun service de données mobiles"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Appels d\'urgence non disponibles"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Aucun service vocal"</string>
@@ -701,6 +699,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Permettre à l\'application de lire les fichiers vidéo de votre espace de stockage partagé."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"lire les fichiers image de l\'espace de stockage partagé"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Permettre à l\'application de lire les fichiers image de votre espace de stockage partagé."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"modifier/supprimer contenu mémoire stockage partagée"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Permet de modifier le contenu mémoire de stockage partagée."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"effectuer/recevoir des appels SIP"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index a3aed41..9a3bfd7 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"O valor predeterminado do identificador de chamada é restrinxido. Próxima chamada: non restrinxido"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Servizo non ofrecido."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Non podes cambiar a configuración do identificador de chamada."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Cambiouse a conexión de datos a <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Podes cambiar esta opción en calquera momento en Configuración"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Non hai servizo de datos para móbiles"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"As chamadas de emerxencia non están dispoñibles"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Non hai servizo de chamadas de voz"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Permite que a aplicación acceda a ficheiros de vídeo do almacenamento compartido."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"acceder a ficheiros de imaxe do almacenamento compartido"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Permite que a aplicación acceda a ficheiros de imaxe do almacenamento compartido."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"modificar ou eliminar o almacenamento compartido"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Permite á aplicación escribir no almacenamento compartido."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"facer/recibir chamadas SIP"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 2d9210b..f312d2b 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"કૉલર ID પ્રતિબંધિત નહીં પર ડિફોલ્ટ છે. આગલો કૉલ: પ્રતિબંધિત નહીં"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"સેવાની જોગવાઈ કરી નથી."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"તમે કૉલર ID સેટિંગ બદલી શકતાં નથી."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"ડેટા <xliff:g id="CARRIERDISPLAY">%s</xliff:g> પર સ્વિચ કર્યો"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"સેટિંગમાં તમે આને કોઈપણ સમયે બદલી શકો છો"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"કોઈ મોબાઇલ ડેટા સેવા નથી"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"કોટોકટીની કૉલિંગ સેવા અનુપલબ્ધ"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"કોઈ વૉઇસ સેવા નથી"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"ઍપને તમારા શેર કરાયેલા સ્ટોરેજમાંથી વીડિયો ફાઇલો વાંચવાની મંજૂરી આપે છે."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"શેર કરાયેલા સ્ટોરેજમાંથી છબી ફાઇલો વાંચવા માટે"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"ઍપને તમારા શેર કરાયેલા સ્ટોરેજમાંથી છબી ફાઇલો વાંચવાની મંજૂરી આપે છે."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"શેર કરેલા સ્ટોરેજ કન્ટેન્ટમાં ફેરફાર કરો/ડિલીટ કરો"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"શેર કરેલા સ્ટોરેજ કન્ટેન્ટમાં લખવાની મંજૂરી આપે છે."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"SIP કૉલ્સ કરો/પ્રાપ્ત કરો"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 9f0e936..b103619 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"कॉलर आईडी डिफ़ॉल्ट रूप से सीमित नहीं है. अगली कॉल: सीमित नहीं"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"सेवा प्रावधान की हुई नहीं है."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"आप कॉलर आईडी सेटिंग नहीं बदल सकते."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"डेटा को <xliff:g id="CARRIERDISPLAY">%s</xliff:g> पर स्विच किया गया"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"सेटिंग में जाकर, इसे कभी भी बंद किया जा सकता है"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"मोबाइल डेटा सेवा पर रोक लगा दी गई है"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"आपातकालीन कॉल पर रोक लगा दी गई है"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"कोई वॉइस सेवा नहीं है"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"अपने डिवाइस के शेयर किए गए स्टोरेज से, ऐप्लिकेशन को वीडियो फ़ाइलें पढ़ने की अनुमति दें."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"डिवाइस के शेयर किए गए स्टोरेज से, इमेज फ़ाइलें देखने की अनुमति"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"अपने डिवाइस के शेयर किए गए स्टोरेज से, ऐप्लिकेशन को इमेज फ़ाइलें देखने की अनुमति दें."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"आपकी शेयर की गई मेमोरी की सामग्री में बदलाव करना या उसे मिटाना"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"ऐप्लिकेशन को आपकी शेयर की गई मेमोरी की सामग्री लिखने देती है."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"SIP कॉल करें/पाएं"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 40a49db..346ea39 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -699,6 +699,8 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Aplikaciji omogućuje čitanje videodatoteka iz dijeljene pohrane."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"čitati slikovne datoteke iz dijeljene pohrane"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Aplikaciji omogućuje čitanje slikovnih datoteka iz dijeljene pohrane."</string>
+ <string name="permlab_readVisualUserSelect" msgid="5516204215354667586">"čitati slikovne i videodatoteke koje je korisnik odabrao iz dijeljene pohrane"</string>
+ <string name="permdesc_readVisualUserSelect" msgid="8027174717714968217">"Aplikaciji omogućuje čitanje slikovnih i videodatoteka koje odaberete iz dijeljene pohrane."</string>
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"izmjena ili brisanje sadržaja dijeljene pohrane"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Aplikaciji omogućuje pisanje sadržaja u dijeljenu pohranu."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"upućivanje/primanje SIP poziva"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index cf6132f..291ae7d 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"A hívóazonosító alapértelmezett értéke nem korlátozott. Következő hívás: nem korlátozott"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"A szolgáltatás nincs biztosítva."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Nem tudja módosítani a hívó fél azonosítója beállítást."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Adatforgalom átváltva a következőre: <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"A Beállításokban ezt bármikor módosíthatja"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Nincs mobiladat-szolgáltatás"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Segélyhívás nem lehetséges"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Hangszolgáltatás letiltva"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Engedélyezi az alkalmazásnak a megosztott tárhelyen található videófájlok olvasását."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"a megosztott tárhelyen található képfájlok olvasása"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Engedélyezi az alkalmazásnak a megosztott tárhelyen található képfájlok olvasását."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"a közös tárhely tartalmainak törlése és módosítása"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Engedélyezi az alkalmazás számára a közös tárhely tartalmainak felülírását."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"SIP-hívások indítása/fogadása"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 4da9e3b..ce62e20 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Զանգողի ID-ն լռելյայն չսահմանափակված է: Հաջորդ զանգը` չսահմանափակված"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Ծառայությունը չի տրամադրվում:"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Դուք չեք կարող փոխել զանգողի ID-ի կարգավորումները:"</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Օգտագործվում է <xliff:g id="CARRIERDISPLAY">%s</xliff:g>-ի բջջային ինտերնետը"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Այս գործառույթը կարող եք փոփոխել Կարգավորումներում"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Բջջային ինտերնետի ծառայությունն արգելափակված է"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Շտապ կանչերը հասանելի չեն"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Ձայնային ծառայությունն անհասանելի է"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Հավելվածին թույլ է տալիս կարդալ ձեր սարքի ընդհանուր հիշողության մեջ պահված վիդեո ֆայլերը։"</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"կարդալ ձեր սարքի ընդհանուր հիշողության մեջ պահված գրաֆիկական ֆայլերը"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Հավելվածին թույլ է տալիս կարդալ ձեր սարքի ընդհանուր հիշողության մեջ պահված գրաֆիկական ֆայլերը։"</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"փոփոխել կամ ջնջել ձեր ընդհանուր հիշողության բովանդակությունը"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Հավելվածին թույլ է տալիս փոփոխել ձեր ընդհանուր հիշողության պարունակությունը:"</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"կատարել կամ ստանալ SIP զանգեր"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 2f2e524..2be19a6 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID penelepon diatur default ke tidak dibatasi. Panggilan selanjutnya: Tidak dibatasi"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Layanan tidak diperlengkapi."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Anda tidak dapat mengubah setelan ID penelepon."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Mengalihkan data seluler ke <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Anda dapat mengubahnya kapan saja di Setelan"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Tidak ada layanan data seluler"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Panggilan darurat tidak tersedia"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Tidak ada layanan panggilan suara"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Mengizinkan aplikasi membaca file video dari penyimpanan bersama."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"membaca file gambar dari penyimpanan bersama"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Mengizinkan aplikasi membaca file gambar dari penyimpanan bersama."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"memodifikasi atau menghapus konten penyimpanan bersama Anda"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Mengizinkan aplikasi menulis konten penyimpanan bersama Anda."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"lakukan/terima panggilan SIP"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 82a1b66..f5473a2 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Númerabirting er sjálfgefið án takmarkana. Næsta símtal: Án takmarkana"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Þjónustu ekki útdeilt."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Þú getur ekki breytt stillingu númerabirtingar."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Skipt yfir í farsímagögn hjá <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Þú getur breytt þessu hvenær sem er í stillingum"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Engin gagnaþjónusta fyrir farsíma"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Neyðarsímtöl eru ekki í boði"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Símtöl eru ekki í boði"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Leyfir forritinu að lesa myndskeiðaskrár úr samnýtta geymslurýminu þínu."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"lesa myndskrár úr samnýttu geymslurými"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Leyfir forritinu að lesa myndskrár úr samnýtta geymslurýminu þínu."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"breyta eða eyða innihaldi samnýtta geymslurýmisins"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Leyfir forriti að skrifa í innihald samnýtta geymslurýmisins."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"hringja/svara SIP-símtölum"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index b499eea..f88ad40 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -73,10 +73,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID chiamante generalmente non limitato. Prossima chiamata: non limitato"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Servizio non fornito."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Non è possibile modificare l\'impostazione ID chiamante."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"I dati sono stati trasferiti a <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Puoi modificare questa opzione in qualsiasi momento in Impostazioni"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Nessun servizio dati mobile"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Chiamate di emergenza non disponibili"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Nessun servizio di telefonia"</string>
@@ -701,6 +699,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Consente all\'app di leggere i file video dal tuo spazio di archiviazione condiviso."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"Lettura dei file immagine dallo spazio di archiviazione condiviso"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Consente all\'app di leggere i file immagine dal tuo spazio di archiviazione condiviso."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"Modifica/eliminazione dei contenuti dell\'archivio condiviso"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Consente all\'app di modificare i contenuti del tuo archivio condiviso."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"invio/ricezione di chiamate SIP"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 2ef4934..29e33dff 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -700,6 +700,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"מאפשרת לאפליקציה לקרוא קובצי וידאו מתוך האחסון המשותף."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"קריאה של קובצי תמונה מתוך האחסון המשותף"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"מאפשרת לאפליקציה לקרוא קובצי תמונה מתוך האחסון המשותף."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"שינוי או מחיקה של תוכן האחסון המשותף שלך"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"מאפשרת לאפליקציה לכתוב את התוכן של האחסון המשותף."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"ביצוע/קבלה של שיחות SIP"</string>
@@ -1252,7 +1256,7 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"מתבצעת הפעלה של אפליקציות."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"תהליך האתחול בשלבי סיום."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"לחצת על לחצן ההפעלה – בדרך כלל הפעולה הזו מכבה את המסך.\n\nעליך לנסות להקיש בעדינות במהלך ההגדרה של טביעת האצבע שלך."</string>
- <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"כדי לסיים את ההגדרה צריך לכבות את המסך"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"לסיום ההגדרה, יש לכבות את המסך"</string>
<string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"השבתה"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"להמשיך לאמת את טביעת האצבע שלך?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"לחצת על לחצן ההפעלה – בדרך כלל הפעולה הזו מכבה את המסך.\n\nעליך לנסות להקיש בעדינות כדי לאמת את טביעת האצבע שלך."</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 53fa057..eb5cc2f 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"既定: 発信者番号通知、次の発信: 通知"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"提供可能なサービスがありません。"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"発信者番号の設定は変更できません。"</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"データが <xliff:g id="CARRIERDISPLAY">%s</xliff:g> に切り替わりました"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"これは [設定] でいつでも変更できます"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"モバイルデータ サービスのブロック"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"緊急通報のブロック"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"音声通話サービス停止"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"共有ストレージからの動画ファイルの読み取りをアプリに許可します。"</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"共有ストレージからの画像ファイルの読み取り"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"共有ストレージからの画像ファイルの読み取りをアプリに許可します。"</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"共有ストレージのコンテンツの変更または削除"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"共有ストレージのコンテンツの書き込みをアプリに許可します。"</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"SIP通話の発着信"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 57ff75a..634da8e 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ნაგულისხმებად დაყენებულია ნომრის დაფარვის გამორთვა. შემდეგი ზარი: არ არის დაფარული."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"სერვისი არ არის მიწოდებული."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"არ შეგიძლიათ აბონენტის ID პარამეტრების შეცვლა."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"მონაცემები გადართულია <xliff:g id="CARRIERDISPLAY">%s</xliff:g>-ზე"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"ამის შეცვლა ნებისმიერ დროს შეგიძლიათ პარამეტრებში"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"მობილური ინტერნეტის სერვისი არ არის"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"გადაუდებელი ზარი მიუწვდომელია"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"ხმოვანი ზარების სერვისი არ არის"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"საშუალებას აძლევს აპს, წაიკითხოს ვიდეო ფაილები თქვენი ზიარი მეხსიერებიდან."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"სურათების ფაილების წაკითხვა ზიარი მეხსიერებიდან"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"საშუალებას აძლევს აპს, წაიკითხოს სურათის ფაილები თქვენი ზიარი მეხსიერებიდან."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"თქვენი ზიარი მეხსიერების შიგთავსის შეცვლა ან წაშლა"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"საშუალებას აძლევს აპს, ჩაწეროს თქვენი ზიარი მეხსიერების შიგთავსი."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"SIP ზარების წამოწყება/მიღება"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 8f5b1c0..20e7d3e 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Қоңырау шалушының жеке анықтағышы бастапқы бойынша шектелмеген. Келесі қоңырау: Шектелмеген"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Қызмет ұсынылмаған."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Қоңырау шалушы идентификаторы параметрін өзгерту мүмкін емес."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Деректер <xliff:g id="CARRIERDISPLAY">%s</xliff:g> операторына ауыстырылды"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Бұны кез келген уақытта \"Параметрлер\" бөлімінен өзгертуге болады."</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Мобильдік интернет қызметі жоқ"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Жедел қызметке қоңырау шалу қолжетімді емес"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Дауыстық қоңыраулар қызметі жоқ"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Қолданбаға ортақ жадтың бейнефайлдарын оқуға мүмкіндік береді."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"ортақ жадтың кескін файлдарын оқу"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Қолданбаға ортақ жадтың кескін файлдарын оқуға мүмкіндік береді."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"ортақ жадтың мазмұнын өзгерту немесе жою"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Қолданбаға ортақ жадтың мазмұнын жазуға мүмкіндік береді."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"SIP қоңырауларын шалу/қабылдау"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 1eae9db..22bb80f 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"មិនបានដាក់កម្រិតលំនាំដើមលេខសម្គាល់អ្នកហៅ។ ការហៅបន្ទាប់៖ មិនបានដាក់កម្រិត។"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"មិនបានផ្ដល់សេវាកម្ម។"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"អ្នកមិនអាចប្ដូរការកំណត់លេខសម្គាល់អ្នកហៅបានទេ។"</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"បានប្ដូរទិន្នន័យទៅ <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"អ្នកអាចផ្លាស់ប្ដូរលក្ខណៈនេះនៅពេលណាក៏បាននៅក្នុងការកំណត់"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"គ្មានសេវាកម្មទិន្នន័យចល័តទេ"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"ការហៅបន្ទាន់មិនអាចប្រើបានទេ"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"គ្មានសេវាកម្មជាសំឡេងទេ"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"អនុញ្ញាតឱ្យកម្មវិធីអានឯកសារវីដេអូពីទំហំផ្ទុករួមរបស់អ្នក។"</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"អានឯកសាររូបភាពពីទំហំផ្ទុករួម"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"អនុញ្ញាតឱ្យកម្មវិធីអានឯកសាររូបភាពពីទំហំផ្ទុករួមរបស់អ្នក។"</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"កែប្រែ ឬលុបខ្លឹមសារនៃទំហំផ្ទុករួមរបស់អ្នក"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"អនុញ្ញាតឱ្យកម្មវិធីសរសេរខ្លឹមសារនៃទំហំផ្ទុករួមរបស់អ្នក។"</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"បង្កើត/ទទួល ការហៅ SIP"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 821138a..953dd60 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ಕರೆಮಾಡುವವರ ID ಅನ್ನು ನಿರ್ಬಂಧಿಸದಿರುವಂತೆ ಡಿಫಾಲ್ಟ್ ಮಾಡಲಾಗಿದೆ. ಮುಂದಿನ ಕರೆ: ನಿರ್ಬಂಧಿಸಲಾಗಿಲ್ಲ"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"ಸೇವೆಯನ್ನು ಪೂರೈಸಲಾಗಿಲ್ಲ."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"ನೀವು ಕಾಲರ್ ID ಸೆಟ್ಟಿಂಗ್ ಬದಲಾಯಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"<xliff:g id="CARRIERDISPLAY">%s</xliff:g> ಗೆ ಡೇಟಾವನ್ನು ಬದಲಾಯಿಸಲಾಗಿದೆ"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"ನೀವು ಇದನ್ನು ಯಾವುದೇ ಸಮಯದಲ್ಲಿಯೂ ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಬದಲಿಸಬಹುದು"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"ಮೊಬೈಲ್ ಡೇಟಾ ಸೇವೆಯಿಲ್ಲ"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"ತುರ್ತು ಕರೆ ಲಭ್ಯವಿಲ್ಲ"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"ಧ್ವನಿ ಸೇವೆಯಿಲ್ಲ"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"ನಿಮ್ಮ ಹಂಚಿಕೊಂಡ ಸಂಗ್ರಹಣೆಯಿಂದ ವೀಡಿಯೊ ಫೈಲ್ಗಳನ್ನು ಓದಲು ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"ಹಂಚಿಕೊಂಡ ಸಂಗ್ರಹಣೆಯಿಂದ ಚಿತ್ರದ ಫೈಲ್ಗಳನ್ನು ಓದಿ"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"ನಿಮ್ಮ ಹಂಚಿಕೊಂಡ ಸಂಗ್ರಹಣೆಯಿಂದ ಚಿತ್ರದ ಫೈಲ್ಗಳನ್ನು ಓದಲು ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"ನಿಮ್ಮ ಹಂಚಿಕೊಂಡ ಸಂಗ್ರಹಣೆಯ ವಿಷಯಗಳನ್ನು ಮಾರ್ಪಡಿಸಿ ಅಥವಾ ಅಳಿಸಿ"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"ನಿಮ್ಮ ಹಂಚಿಕೊಂಡ ಸಂಗ್ರಹಣೆಯ ವಿಷಯಗಳನ್ನು ಬರೆಯಲು ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"ಎಸ್ಐಪಿ ಕರೆಗಳನ್ನು ಮಾಡಿ/ಸ್ವೀಕರಿಸಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 5f3c7ca..a591b43 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"발신자 번호가 기본적으로 제한되지 않음으로 설정됩니다. 다음 통화: 제한되지 않음"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"서비스가 준비되지 않았습니다."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"발신자 번호 설정을 변경할 수 없습니다."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"<xliff:g id="CARRIERDISPLAY">%s</xliff:g> 이동통신사로 데이터가 변경됨"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"언제든지 설정에서 변경할 수 있습니다."</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"모바일 데이터 서비스가 차단됨"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"긴급 전화를 사용할 수 없음"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"음성 서비스를 이용할 수 없음"</string>
@@ -598,7 +596,7 @@
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"지문을 등록할 때마다 손가락을 조금씩 이동하세요"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_error_not_match" msgid="4599441812893438961">"지문이 인식되지 않습니다."</string>
+ <string name="fingerprint_error_not_match" msgid="4599441812893438961">"지문이 인식되지 않았습니다."</string>
<string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"지문을 인식할 수 없습니다."</string>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"지문이 인증됨"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"얼굴이 인증되었습니다"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"앱이 공유 저장소에서 동영상 파일을 읽도록 허용합니다."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"공유 저장소에서 이미지 파일 읽기"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"앱이 공유 저장소에서 이미지 파일을 읽도록 허용합니다."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"공유 저장공간의 콘텐츠 수정 또는 삭제"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"앱이 공유 저장공간의 콘텐츠에 쓰도록 허용합니다."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"SIP 통화 발신/수신"</string>
@@ -1253,7 +1255,7 @@
<string name="android_upgrading_complete" msgid="409800058018374746">"부팅 완료"</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"전원 버튼을 눌렀습니다. 이러면 보통 화면이 꺼집니다.\n\n지문 설정 중에 가볍게 탭하세요."</string>
<string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"설정을 완료하려면 화면을 끄세요"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"사용 중지"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"화면 끄기"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"지문 인증을 계속할까요?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"전원 버튼을 눌렀습니다. 이러면 보통 화면이 꺼집니다.\n\n지문을 인식하려면 화면을 가볍게 탭하세요."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"화면 끄기"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 6e7b355..61d64c1 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Номурду аныктоонун демейки абалы \"чектелбейт\" деп коюлган. Кийинки чалуу: Чектелбейт"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Кызмат камсыздалган эмес."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Чалуучунун далдаштырма дайындары жөндөөлөрүн өзгөртө албайсыз."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Мобилдик Интернет <xliff:g id="CARRIERDISPLAY">%s</xliff:g> которулду"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Бул функциянын параметрлерин \"Тууралоо\" бөлүмүнөн өзгөртө аласыз"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Мобилдик Интернет кызматы жок"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Шашылыш чалуу бөгөттөлгөн"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Аудио чалуу кызматы бөгөттөлгөн"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Колдонмого жалпы сактагычыңыздагы видеолорду окуу мүмкүнчүлүгүн берет."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"жалпы сактагычтагы сүрөт файлдарды окуу"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Колдонмого жалпы сактагычыңыздагы сүрөт файлдарды окуу мүмкүнчүлүгүн берет."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"жалпы сактагычыңыздын мазмунун өзгөртүү же жок кылуу"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Колдонмого жалпы сактагычыңыздын мазмунун жазуу мүмкүнчүлүгүн берет."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"SIP чалуу/чалууну кабыл алуу"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 7a76c2c..1263a83 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ໝາຍເລກຜູ່ໂທ ໄດ້ຮັບການຕັ້ງຄ່າເລີ່ມຕົ້ນເປັນ ບໍ່ຖືກຈຳກັດ. ການໂທຄັ້ງຕໍ່ໄປ: ບໍ່ຖືກຈຳກັດ."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"ບໍ່ໄດ້ເປີດໃຊ້ບໍລິການ."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"ທ່ານບໍ່ສາມາດປ່ຽນແປງການຕັ້ງຄ່າ Caller ID"</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"ປ່ຽນໄປໃຊ້ອິນເຕີເນັດມືຖືຂອງ <xliff:g id="CARRIERDISPLAY">%s</xliff:g> ແລ້ວ"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"ທ່ານສາມາດປ່ຽນສິ່ງນີ້ຕອນໃດກໍໄດ້ໃນການຕັ້ງຄ່າ"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"ບໍ່ມີບໍລິການອິນເຕີເນັດມືຖື"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"ບໍ່ສາມາດໃຊ້ການໂທສຸກເສີນໄດ້"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"ບໍ່ມີບໍລິການໂທສຽງ"</string>
@@ -700,6 +698,8 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"ອະນຸຍາດໃຫ້ແອັບອ່ານໄຟລ໌ວິດີໂອຈາກບ່ອນຈັດເກັບຂໍ້ມູນທີ່ແບ່ງປັນຂອງທ່ານ."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"ອ່ານໄຟລ໌ຮູບຈາກບ່ອນຈັດເກັບຂໍ້ມູນທີ່ແບ່ງປັນ"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"ອະນຸຍາດໃຫ້ແອັບອ່ານໄຟລ໌ຮູບຈາກບ່ອນຈັດເກັບຂໍ້ມູນທີ່ແບ່ງປັນຂອງທ່ານ."</string>
+ <string name="permlab_readVisualUserSelect" msgid="5516204215354667586">"ອ່ານໄຟລ໌ຮູບ ແລະ ວິດີໂອທີ່ຜູ້ໃຊ້ເລືອກຈາກບ່ອນຈັດເກັບຂໍ້ມູນທີ່ແບ່ງປັນ"</string>
+ <string name="permdesc_readVisualUserSelect" msgid="8027174717714968217">"ອະນຸຍາດໃຫ້ແອັບອ່ານໄຟລ໌ຮູບ ແລະ ວິດີໂອທີ່ທ່ານເລືອກຈາກບ່ອນຈັດເກັບຂໍ້ມູນທີ່ແບ່ງປັນຂອງທ່ານ."</string>
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"ແກ້ໄຂ ຫຼືລຶບເນື້ອຫາໃນບ່ອນຈັດເກັບຂໍ້ມູນທີ່ແບ່ງປັນຂອງທ່ານ"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"ອະນຸຍາດໃຫ້ແອັບຂຽນເນື້ອຫາຕ່າງໆຂອງບ່ອນຈັດເກັບຂໍ້ມູນທີ່ແບ່ງປັນຂອງທ່ານ."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"ຮັບສາຍ/ໂທອອກ ຜ່ານ SIP"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index e0d1fc7..8a07e05 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -74,10 +74,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Skambintojo ID pagal numatytuosius nustatymus yra neapribotas. Kitas skambutis: neapribotas"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Paslauga neteikiama."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Negalima pakeisti skambinančiojo ID nustatymo."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Duomenys perjungti į „<xliff:g id="CARRIERDISPLAY">%s</xliff:g>“"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Galite tai bet kada pakeisti nustatymuose"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Duomenų paslaugos mobiliesiems nėra"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Skambučių pagalbos numeriu paslaugos nėra"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Balso skambučių paslauga neteikiama"</string>
@@ -702,6 +700,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Leidžiama programai nuskaityti vaizdo įrašo failus iš bendrinamos saugyklos."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"nuskaityti vaizdo failus iš bendrinamos saugyklos"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Leidžiama programai nuskaityti vaizdo failus iš bendrinamos saugyklos."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"keisti / trinti bendr. atm. t."</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Pr. leidž. raš. bendr. atm. t."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"skambinti / priimti SIP skambučius"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index ec8cb7d..2a2f9e7 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -73,10 +73,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Zvanītāja ID noklusējumi ir iestatīti uz Nav ierobežots. Nākamais zvans: nav ierobežots"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Pakalpojums netiek nodrošināts."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Zvanītāja ID iestatījumu nevar mainīt."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Tiek izmantots operatora <xliff:g id="CARRIERDISPLAY">%s</xliff:g> datu savienojums"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Šo opciju jebkurā brīdī var mainīt iestatījumos"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Nav pieejams neviens datu pakalpojums mobilajām ierīcēm"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Nav pieejami ārkārtas izsaukumi"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Balss izsaukumu pakalpojums nedarbojas"</string>
@@ -701,6 +699,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Ļauj lietotnei lasīt video failus jūsu koplietotajā krātuvē."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"lasīt attēlu failus koplietotajā krātuvē"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Ļauj lietotnei lasīt attēlu failus jūsu koplietotajā krātuvē."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"Jūsu kopīgotās krātuves satura pārveidošana vai dzēšana"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Ļauj lietotnei rakstīt jūsu kopīgotās krātuves saturu."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"SIP zvanu veikšana/saņemšana"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 442047d..b9ce23b 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Стандардно, ID на повикувач не е скриен. Следен повик: не е скриен"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Услугата не е предвидена."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Не може да го промените поставувањето за ID на повикувач."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Мобилниот интернет се префрли на <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Ова може да го промените во секое време во „Поставки“"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Нема услуга за мобилен интернет"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Итните повици се недостапни"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Нема услуга за говорни повици"</string>
@@ -700,6 +698,8 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Дозволува апликацијата да ги чита видеодатотеките од споделениот капацитет."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"да чита датотеки со слики од споделениот капацитет"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Дозволува апликацијата да ги чита датотеките со слики од споделениот капацитет."</string>
+ <string name="permlab_readVisualUserSelect" msgid="5516204215354667586">"да чита датотеки со слики и видеа избрани од корисникот од споделениот простор."</string>
+ <string name="permdesc_readVisualUserSelect" msgid="8027174717714968217">"Дозволува апликацијата да чита датотеки со слики и видеа што ќе ги изберете од вашиот споделен простор."</string>
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"ги менува или брише содржините на заедничкото место за складирање"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Дозволува апликацијата да ги пишува содржините на заедничкото место за складирање."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"остварува/прима повици преку SIP"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 5b59e66..026d8bd 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -698,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"നിങ്ങളുടെ പങ്കിട്ട സ്റ്റോറേജിൽ നിന്നുള്ള വീഡിയോ ഫയലുകൾ വായിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"പങ്കിട്ട സ്റ്റോറേജിൽ നിന്നുള്ള ചിത്ര ഫയലുകൾ വായിക്കുക"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"നിങ്ങളുടെ പങ്കിട്ട സ്റ്റോറേജിൽ നിന്നുള്ള ചിത്ര ഫയലുകൾ വായിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"നിങ്ങൾ പങ്കിടുന്ന സ്റ്റോറേജിലെ ഉള്ളടക്കങ്ങൾ പരിഷ്ക്കരിക്കുക അല്ലെങ്കിൽ ഇല്ലാതാക്കുക"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"നിങ്ങൾ പങ്കിടുന്ന സ്റ്റോറേജിലെ ഉള്ളടക്കങ്ങൾ എഴുതാൻ ആപ്പിനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"SIP കോളുകൾ വിളിക്കുക/സ്വീകരിക്കുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 03deebb..45d2f31 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Дуудлага хийгчийн ID хязгаарлагдсан. Дараагийн дуудлага: Хязгаарлагдсан"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Үйлчилгээ провишн хийгдээгүй ."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Та дуудлага хийгчийн ID тохиргоог солиж чадахгүй."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Өгөгдлийг <xliff:g id="CARRIERDISPLAY">%s</xliff:g> руу шилжүүлсэн"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Та үүнийг Тохиргооноос хэдийд ч өөрчлөх боломжтой."</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Мобайл дата үйлчилгээ алга"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Яаралтай дуудлага боломжтой"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Дуу хоолойны үйлчилгээ алга"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Аппад таны дундын хадгалах сангаас видео файлыг унших боломжийг олгодог."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"дундын хадгалах сангаас зургийн файл унших"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Аппад таны дундын хадгалах сангаас зургийн файлыг унших зөвшөөрөл олгодог."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"дундын хадгалах сангийнхаа контентыг өөрчлөх эсвэл устгах"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Аппад таны дундын хадгалах сангийн контентыг бичихийг зөвшөөрдөг."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"SIP дуудлага хийх/хүлээн авах"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 1d25e67..360221c 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"कॉलर आयडी डीफॉल्ट रूपात प्रतिबंधित नाही वर सेट असतो. पुढील कॉल: प्रतिबंधित नाही"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"सेवेची तरतूद केलेली नाही."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"तुम्ही कॉलर आयडी सेटिंग बदलू शकत नाही."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"डेटा <xliff:g id="CARRIERDISPLAY">%s</xliff:g> वर स्विच केला"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"तुम्ही हे सेटिंग्जमध्ये कधीही बदलू शकता"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"मोबाइल डेटा सेवा नाही"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"आणीबाणी कॉलिंग अनुपलब्ध आहे"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"व्हॉइस सेवा नाही"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"ॲपला तुमच्या शेअर केलेल्या स्टोरेजमधून व्हिडिओ फाइल वाचण्याची अनुमती देते."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"शेअर केलेल्या स्टोरेजमधून इमेज फाइल वाचा"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"ॲपला तुमच्या शेअर केलेल्या स्टोरेजमधून इमेज फाइल वाचण्याची अनुमती देते."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"तुमच्या शेअर केलेल्या स्टोरेजच्या आशयांमध्ये सुधारणा करा किंवा हटवा"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"ॲपला तुमच्या शेअर केलेल्या स्टोरेजचे आशय लिहिण्याची अनमती देते."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"SIP कॉल करा/मिळवा"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 72d3f2f..9162690 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID pemanggil secara lalainya ditetapkan kepada tidak dihadkan. Panggilan seterusnya: Tidak terhad"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Perkhidmatan yang tidak diuntukkan."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Anda tidak boleh mengubah tetapan ID pemanggil."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Data ditukar kepada <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Anda boleh menukar pilihan ini pada bila-bila masa dalam Tetapan"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Tiada perkhidmatan data mudah alih"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Panggilan kecemasan tidak tersedia"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Tiada perkhidmatan suara"</string>
@@ -700,6 +698,8 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Membenarkan apl membaca fail video daripada storan kongsi anda."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"baca fail imej daripada storan kongsi"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Membenarkan apl membaca fail imej daripada storan kongsi anda."</string>
+ <string name="permlab_readVisualUserSelect" msgid="5516204215354667586">"baca fail imej dan video yang dipilih oleh pengguna daripada storan kongsi"</string>
+ <string name="permdesc_readVisualUserSelect" msgid="8027174717714968217">"Membenarkan apl membaca fail imej dan video yang anda pilih daripada storan kongsi anda."</string>
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"mengubah suai atau memadamkan kandungan storan kongsi anda"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Membenarkan apl menulis kandungan storan kongsi anda."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"buat/terima panggilan SIP"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 5ff033d..e4f937b 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ပုံသေအားဖြင့် ခေါ်ဆိုသူအိုင်ဒီ(Caller ID)အား ကန့်သတ်မထားပါ။ နောက်ထပ်အဝင်ခေါ်ဆိုမှု-ကန့်သတ်မထားပါ။"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"ဝန်ဆောင်မှုအား ကန့်သတ်မထားပါ"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"သင်သည် ခေါ်ဆိုသူ ID ဆက်တင်ကို မပြောင်းလဲနိုင်ပါ။"</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"ဒေတာကို <xliff:g id="CARRIERDISPLAY">%s</xliff:g> သို့ ပြောင်းထားသည်"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"၎င်းကို ဆက်တင်များတွင် အချိန်မရွေး ပြောင်းနိုင်သည်"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"မိုဘိုင်း ဒေတာဝန်ဆောင်မှု မရှိပါ"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"အရေးပေါ်ခေါ်ဆိုမှု မရနိုင်ပါ"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"ဖုန်းဝန်ဆောင်မှု မရှိပါ"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"သင်၏မျှဝေထားသော သိုလှောင်ခန်းမှ ဗီဒီယိုဖိုင်များဖတ်ရန် အက်ပ်ကိုခွင့်ပြုသည်။"</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"မျှဝေထားသည့် သိုလှောင်ခန်းမှ ပုံပါဝင်သောဖိုင်များဖတ်ရန်"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"သင်၏မျှဝေထားသည့် သိုလှောင်ခန်းမှ ပုံပါဝင်သောဖိုင်များဖတ်ရန် အက်ပ်ကို ခွင့်ပြုသည်။"</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"မျှဝေသိုလှောင်ခန်းမှ အရာများ ပြုပြင်/ဖျက်ခြင်း"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"မျှဝေသိုလှောင်ခန်းမှ အရာများ ရေးခွင့်ပြုသည်။"</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"SIP ခေါ်ဆိုမှုများ ခေါ်ရန်/လက်ခံရန်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index bb4fbe4..ba55359 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Nummervisning er ikke begrenset som standard. Neste anrop: Ikke begrenset"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"SIM-kortet er ikke tilrettelagt for tjenesten."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Du kan ikke endre innstillingen for anrops-ID."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Byttet data til <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Du kan endre dette når som helst i innstillingene"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Ingen mobildatatjeneste"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Nødanrop er utilgjengelig"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Ingen taletjeneste"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Lar appen lese videofiler fra den delte lagringsplassen din."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"lese bildefiler fra delt lagringsplass"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Lar appen lese bildefiler fra den delte lagringsplassen din."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"endre eller slette innholdet i den delte lagringen din"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Lar appen skrive innholdet i den delte lagringen din."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"foreta/motta SIP-anrop"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index ea96df0..ae833bf 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"कलर ID पूर्वनिर्धारितको लागि रोकावट छैन। अर्को कल: रोकावट छैन"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"सेवाको व्यवस्था छैन।"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"तपाईं कलर ID सेटिङ परिवर्तन गर्न सक्नुहुन्न।"</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"<xliff:g id="CARRIERDISPLAY">%s</xliff:g> को डेटा प्रयोग गर्न थालिएको छ"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"तपाईं जुनसुकै बेला सेटिङमा गई यो कुरा बदल्न सक्नुहुन्छ"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"कुनै पनि मोबाइल डेटा सेवा उपलब्ध छैन"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"आपत्कालीन कल सेवा उपलब्ध छैन"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"कुनै पनि भ्वाइस सेवा उपलब्ध छैन"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"एपलाई तपाईंको साझा भण्डारणमा भएका भिडियो फाइलहरू पढ्ने अनुमति दिन्छ।"</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"साझा भण्डारणमा भएका फोटो फाइलहरू पढ्ने"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"एपलाई तपाईंको साझा भण्डारणमा भएका फोटो फाइलहरू पढ्ने अनुमति दिन्छ।"</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"तपाईंको आदान प्रदान गरिएको भण्डारणको विषयवस्तुहरूलाई परिमार्जन गर्नहोस् वा मेटाउनुहोस्"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"एपलाई तपाईंको आदान प्रदान गरिएको भण्डारणको सामग्री लेख्न अनुमति दिन्छ।"</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"SIP कलहरू प्राप्त/बनाउन"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 24877dd..37d9798 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Beller-ID standaard ingesteld op \'onbeperkt\'. Volgend gesprek: onbeperkt."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Service niet voorzien."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"U kunt de instelling voor de beller-ID niet wijzigen."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Mobiele data overgeschakeld naar <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Je kunt dit altijd wijzigen via Instellingen"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Geen service voor mobiele data"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Noodoproepen niet beschikbaar"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Geen belservice"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Hiermee kan de app videobestanden in je gedeelde opslag lezen."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"afbeeldingsbestanden in gedeelde opslag lezen"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Hiermee kan de app afbeeldingsbestanden in je gedeelde opslag lezen."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"de content van je gedeelde opslag aanpassen of verwijderen"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Hiermee kan de app de content van je gedeelde opslag schrijven."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"Bellen of gebeld worden via SIP"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 456fc83..5ee80ce 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"କଲର୍ ଆଇଡି ଡିଫଲ୍ଟ ଭାବରେ ପ୍ରତିବନ୍ଧିତ ନୁହେଁ। ପରବର୍ତ୍ତୀ କଲ୍: ପ୍ରତିବନ୍ଧିତ ନୁହେଁ"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"ସେବାର ସୁବିଧା ନାହିଁ।"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"ଆପଣ କଲର୍ ID ସେଟିଙ୍ଗ ବଦଳାଇପାରିବେ ନାହିଁ।"</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"<xliff:g id="CARRIERDISPLAY">%s</xliff:g>କୁ ଡାଟା ସ୍ୱିଚ କରାଯାଇଛି"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"ଆପଣ ଯେ କୌଣସି ସମୟରେ ସେଟିଂସରେ ଏହାକୁ ପରିବର୍ତ୍ତନ କରିପାରିବେ"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"କୌଣସି ମୋବାଇଲ୍ ଡାଟା ସେବା ନାହିଁ"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"ଜରୁରୀକାଳୀନ କଲ୍ ଉପଲବ୍ଧ ନାହିଁ"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"କୌଣସି ଭଏସ୍ ସେବା ନାହିଁ"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"ଆପଣଙ୍କ ସେୟାର କରାଯାଇଥିବା ଷ୍ଟୋରେଜରୁ ଭିଡିଓ ଫାଇଲଗୁଡ଼ିକୁ ପଢ଼ିବା ପାଇଁ ଆପକୁ ଅନୁମତି ଦିଏ।"</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"ସେୟାର କରାଯାଇଥିବା ଷ୍ଟୋରେଜରୁ ଇମେଜ ଫାଇଲଗୁଡ଼ିକୁ ପଢ଼ନ୍ତୁ"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"ଆପଣଙ୍କ ସେୟାର କରାଯାଇଥିବା ଷ୍ଟୋରେଜରୁ ଇମେଜ ଫାଇଲଗୁଡ଼ିକୁ ପଢ଼ିବା ପାଇଁ ଆପକୁ ଅନୁମତି ଦିଏ।"</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"ଆପଣଙ୍କତ ସେୟାର୍ ହୋଇଥିବା ଷ୍ଟୋରେଜ୍ର ବିଷୟବସ୍ତୁ ସଂଶୋଧନ କିମ୍ବା ଡିଲିଟ୍ କରନ୍ତୁ"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"ଆପଣଙ୍କର ସେୟାର୍ ହୋଇଥିବା ଷ୍ଟୋରେଜ୍ର ବିଷୟବସ୍ତୁ ଲେଖିବାକୁ ଅନୁମତି କରିଥାଏ।"</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"SIP କଲ୍ କରନ୍ତୁ ଏବଂ ଗ୍ରହଣ କରନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 79ab897..62f2da5 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ਪ੍ਰਤਿਬੰਧਿਤ ਨਾ ਕਰਨ ਲਈ ਕਾਲਰ ਆਈ.ਡੀ. ਪੂਰਵ-ਨਿਰਧਾਰਤ। ਅਗਲੀ ਕਾਲ: ਪ੍ਰਤਿਬੰਧਿਤ ਨਹੀਂ"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"ਸੇਵਾ ਪ੍ਰਬੰਧਿਤ ਨਹੀਂ ਹੈ।"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"ਤੁਸੀਂ ਕਾਲਰ ਆਈ.ਡੀ. ਸੈਟਿੰਗ ਨਹੀਂ ਬਦਲ ਸਕਦੇ।"</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"ਡਾਟੇ ਨੂੰ <xliff:g id="CARRIERDISPLAY">%s</xliff:g> \'ਤੇ ਸਵਿੱਚ ਕੀਤਾ ਗਿਆ"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"ਤੁਸੀਂ ਕਿਸੇ ਵੇਲੇ ਵੀ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਜਾ ਕੇ ਇਸਨੂੰ ਬਦਲ ਸਕਦੇ ਹੋ"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"ਕੋਈ ਮੋਬਾਈਲ ਡਾਟਾ ਸੇਵਾ ਨਹੀਂ"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"ਸੰਕਟਕਾਲੀਨ ਕਾਲਿੰਗ ਉਪਲਬਧ ਨਹੀਂ"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"ਕੋਈ ਆਵਾਜ਼ੀ ਸੇਵਾ ਨਹੀਂ"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"ਐਪ ਨੂੰ ਤੁਹਾਡੀ ਸਾਂਝੀ ਕੀਤੀ ਸਟੋਰੇਜ ਤੋਂ ਵੀਡੀਓ ਫ਼ਾਈਲਾਂ ਪੜ੍ਹਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"ਸਾਂਝੀ ਕੀਤੀ ਸਟੋਰੇਜ ਤੋਂ ਚਿੱਤਰ ਫ਼ਾਈਲਾਂ ਪੜ੍ਹੋ"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"ਐਪ ਨੂੰ ਤੁਹਾਡੀ ਸਾਂਝੀ ਕੀਤੀ ਸਟੋਰੇਜ ਤੋਂ ਚਿੱਤਰ ਫ਼ਾਈਲਾਂ ਪੜ੍ਹਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"ਸਮੱਗਰੀਆਂ ਦਾ ਸੰਸ਼ੋਧਨ ਕਰੋ ਜਾਂ ਮਿਟਾਓ"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"ਐਪ ਨੂੰ ਸਮੱਗਰੀਆਂ ਲਿਖਣ ਦਿੰਦੀ ਹੈ।"</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"SIP ਕਾਲਾਂ ਕਰੋ/ਪ੍ਰਾਪਤ ਕਰੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 5e3a64a..76bd69b 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -74,10 +74,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID rozmówcy ustawiony jest domyślnie na „nie zastrzeżony”. Następne połączenie: nie zastrzeżony"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Usługa nie jest świadczona."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Nie możesz zmienić ustawienia ID rozmówcy."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Przełączono mobilną transmisję danych na: <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Możesz to zmienić w dowolnym momencie w Ustawieniach"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Brak komórkowej usługi transmisji danych"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Połączenia alarmowe są niedostępne"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Brak usługi połączeń głosowych"</string>
@@ -702,6 +700,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Zezwala na odczyt przez aplikację plików wideo w pamięci współdzielonej."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"odczyt plików graficznych z pamięci współdzielonej"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Zezwala na odczyt przez aplikację plików graficznych w pamięci współdzielonej."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"modyfikowanie i usuwanie zawartości pamięci współdzielonej"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Zezwala aplikacji na zapis zawartości pamięci współdzielonej."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"wykonywanie/odbieranie połączeń SIP"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 34c0f0c..82f23f2 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -73,10 +73,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"O identificador de chamadas assume o padrão de não restrito. Próxima chamada: Não restrita"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"O serviço não foi habilitado."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Não é possível alterar a configuração do identificador de chamadas."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Dados da <xliff:g id="CARRIERDISPLAY">%s</xliff:g> ativados"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"É possível mudar essa opção a qualquer momento nas Configurações"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Nenhum serviço móvel de dados"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Chamadas de emergência indisponíveis"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Sem serviço de voz"</string>
@@ -701,6 +699,8 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Permite que o app leia arquivos de vídeo do armazenamento compartilhado."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"ler arquivos de imagem do armazenamento compartilhado"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Permite que o app leia arquivos de imagem do armazenamento compartilhado."</string>
+ <string name="permlab_readVisualUserSelect" msgid="5516204215354667586">"ler arquivos de imagem e vídeo selecionados pelo usuário no armazenamento compartilhado"</string>
+ <string name="permdesc_readVisualUserSelect" msgid="8027174717714968217">"Permite que o app leia arquivos de imagem e vídeo que você selecionar no armazenamento compartilhado."</string>
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"alterar ou excluir conteúdo do armaz. compartilhado"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Permite que o app grave o conteúdo do armaz. compartilhado."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"fazer/receber chamadas SIP"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 0fb835b..7efd2a1 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -699,6 +699,8 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Permite que a app leia ficheiros de vídeo do armazenamento partilhado."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"ler ficheiros de imagem do armazenamento partilhado"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Permite que a app leia ficheiros de imagem do armazenamento partilhado."</string>
+ <string name="permlab_readVisualUserSelect" msgid="5516204215354667586">"ler ficheiros de imagem e vídeo do armazenamento partilhado selecionados pelo utilizador"</string>
+ <string name="permdesc_readVisualUserSelect" msgid="8027174717714968217">"Permite que a app leia ficheiros de imagem e vídeo que selecionar no seu armazenamento partilhado."</string>
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"modif./elim. os conteúdos do armazenam. partilhado"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Permite que a apl. escreva conteúd. do armazen. partilhado."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"efetuar/receber chamadas SIP"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 34c0f0c..82f23f2 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -73,10 +73,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"O identificador de chamadas assume o padrão de não restrito. Próxima chamada: Não restrita"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"O serviço não foi habilitado."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Não é possível alterar a configuração do identificador de chamadas."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Dados da <xliff:g id="CARRIERDISPLAY">%s</xliff:g> ativados"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"É possível mudar essa opção a qualquer momento nas Configurações"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Nenhum serviço móvel de dados"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Chamadas de emergência indisponíveis"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Sem serviço de voz"</string>
@@ -701,6 +699,8 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Permite que o app leia arquivos de vídeo do armazenamento compartilhado."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"ler arquivos de imagem do armazenamento compartilhado"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Permite que o app leia arquivos de imagem do armazenamento compartilhado."</string>
+ <string name="permlab_readVisualUserSelect" msgid="5516204215354667586">"ler arquivos de imagem e vídeo selecionados pelo usuário no armazenamento compartilhado"</string>
+ <string name="permdesc_readVisualUserSelect" msgid="8027174717714968217">"Permite que o app leia arquivos de imagem e vídeo que você selecionar no armazenamento compartilhado."</string>
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"alterar ou excluir conteúdo do armaz. compartilhado"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Permite que o app grave o conteúdo do armaz. compartilhado."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"fazer/receber chamadas SIP"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 88aab00..8d8059c 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -73,10 +73,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID-ul apelantului este nerestricționat în mod prestabilit. Apelul următor: nerestricționat"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Nu se asigură accesul la acest serviciu."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Nu poți modifica setarea pentru ID-ul apelantului."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"S-a trecut la datele mobile <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Poți modifica oricând opțiunea din Setări"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Fără serviciu de date mobile"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Apelurile de urgență nu sunt disponibile"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Fără servicii vocale"</string>
@@ -701,6 +699,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Permite aplicației să citească fișiere video din spațiul de stocare comun."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"să citească fișiere imagine din spațiul de stocare comun"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Permite aplicației să citească fișiere imagine din spațiul de stocare comun."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"să modifice sau să șteargă conținutul spațiului de stocare comun"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Permite aplicației scrierea conținutul spațiului de stocare comun."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"efectuarea/primirea apelurilor SIP"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 33bfe77..5cf8eff 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -74,10 +74,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Идентификация абонента по умолчанию не запрещена. След. вызов: разрешена"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Услуга не предоставляется."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Невозможно изменить параметр идентификатора вызывающего абонента."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Используется мобильный интернет <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Этот параметр можно в любой момент изменить в настройках."</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Мобильный Интернет недоступен"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Экстренные вызовы недоступны"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Голосовые вызовы недоступны"</string>
@@ -702,6 +700,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Приложение сможет считывать видеофайлы из общего хранилища."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"считывание изображений из общего хранилища"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Приложение сможет считывать изображения из общего хранилища."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"Изменение или удаление данных на общем накопителе"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Приложение сможет записывать данные на общий накопитель."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"Входящие и исходящие вызовы SIP"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index dd5b817..8d329fc 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"අමතන්නාගේ ID සුපුරුදු අනුව සීමා වී නැත. මීළඟ ඇමතුම: සීමා කර ඇත"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"සේවාවන් සපයා නැත."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"අමතන්නාගේ ID සැකසීම ඔබට වෙනස්කල නොහැක."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"දත්ත <xliff:g id="CARRIERDISPLAY">%s</xliff:g> වෙත මාරු කරන ලදි"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"ඔබට සැකසීම් තුළ මෙය ඕනෑම වේලාවක වෙනස් කළ හැක"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"ජංගම දත්ත සේවාව නැත"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"හදිසි ඇමතුම් ලබා ගත නොහැකිය"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"හඬ සේවාව නැත"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"ඔබගේ බෙදා ගත් ගබඩාවෙන් වීඩියෝ ගොනු කියවීමට යෙදුමට ඉඩ දෙයි."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"බෙදා ගත් ගබඩාවෙන් රූප ගොනු කියවන්න"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"ඔබගේ බෙදා ගත් ගබඩාවෙන් රූප ගොනු කියවීමට යෙදුමට ඉඩ දෙයි."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"ඔබේ බෙදා ගත් ගබඩාවේ අන්තර්ගත වෙනස් කරන්න නැතහොත් මකන්න"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"යෙදුමට ඔබේ බෙදා ගත් ගබඩාවේ අන්තර්ගත කියවීමට ඉඩ දෙයි."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"SIP ඇමතුම් සිදුකිරීමට/ලබාගැනීමට"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 40abf79..2d058e8 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -74,10 +74,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"V predvolenom nastavení nie je identifikácia volajúceho obmedzená. Ďalší hovor: Bez obmedzenia"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Služba nie je poskytovaná."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Nemôžete meniť nastavenie identifikácie volajúcich."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Dátové pripojenie bolo prepnuté na operátora <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Toto môžete kedykoľvek zmeniť v Nastaveniach"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Žiadna mobilná dátová služba"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Tiesňové volania nie sú k dispozícii"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Žiadne hlasové hovory"</string>
@@ -702,6 +700,8 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Umožňuje aplikácii čítať videosúbory z vášho zdieľaného priestoru."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"čítať súbory obrázka zo zdieľaného priestoru"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Umožňuje aplikácii čítať súbory obrázka z vášho zdieľaného priestoru."</string>
+ <string name="permlab_readVisualUserSelect" msgid="5516204215354667586">"čítanie obrázkových súborov a videosúborov vybraných používateľom v zdieľanom ukladacom priestore"</string>
+ <string name="permdesc_readVisualUserSelect" msgid="8027174717714968217">"Umožňuje aplikácii čítať obrázkové súbory a videosúbory, ktoré vyberiete vo svojom zdieľanom ukladacom priestore."</string>
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"upravovanie alebo odstraňovanie obsahu zdieľaného úložiska"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Umožňuje aplikácii zapisovať obsah zdieľaného úložiska."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"uskutočňovanie/príjem hovorov SIP"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 53a0321..883beb1 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -74,10 +74,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID klicatelja je ponastavljen na neomejeno. Naslednji klic: ni omejeno"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Storitev ni nastavljena in omogočena."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Ne morete spremeniti nastavitve ID-ja klicatelja."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Prenos podatkov je preklopljen na operaterja <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"To lahko kadar koli spremenite v nastavitvah."</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Ni mobilne podatkovne storitve"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Klicanje v sili ni na voljo"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Ni storitve za glasovne klice"</string>
@@ -702,6 +700,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Aplikaciji omogoča branje videodatotek v deljeni shrambi."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"branje slikovnih datotek v deljeni shrambi"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Aplikaciji omogoča branje slikovnih datotek v deljeni shrambi."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"spreminjanje ali brisanje vsebine skupne shrambe"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Aplikaciji omogoča zapisovanje vsebine skupne shrambe."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"opravljanje/sprejemanje klicev SIP"</string>
@@ -1254,7 +1256,7 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Zagon aplikacij."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Dokončevanje zagona."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Pritisnili ste gumb za vklop, s čimer običajno izklopite zaslon.\n\nPoskusite se narahlo dotakniti med nastavljanjem prstnega odtisa."</string>
- <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Za končanje nastavitve izklopite zaslon"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Za končanje nastavitve izklopite zaslon."</string>
<string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Izklopi"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Želite nadaljevati preverjanje prstnega odtisa?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Pritisnili ste gumb za vklop, s čimer običajno izklopite zaslon.\n\nZa preverjanje prstnega odtisa se poskusite narahlo dotakniti."</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 6c3f145..9380bf4 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID-ja e telefonuesit kalon me paracaktim në listën e të telefonuesve të pakufizuar. Telefonata e radhës: e pakufizuar!"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Shërbimi nuk është përgatitur."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Nuk mund ta ndryshosh cilësimin e ID-së së telefonuesit."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Të dhënat u kaluan te <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Mund ta ndryshosh këtë në çdo kohë te \"Cilësimet\""</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Nuk ka shërbim të të dhënave celulare"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Telefonatat e urgjencës nuk ofrohen"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Nuk ka shërbim zanor"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Lejon që aplikacioni të lexojë skedarët e videove nga hapësira ruajtëse e ndarë."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"të lexojë skedarët e imazheve nga hapësira ruajtëse e ndarë"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Lejon që aplikacioni të lexojë skedarët e imazheve nga hapësira ruajtëse e ndarë."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"modifiko ose fshi përmbajtjet e hapësirës ruajtëse të ndarë"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Lejon që aplikacioni të shkruajë përmbajtjet e hapësirës ruajtëse të ndarë."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"bëj/merr telefonata SIP"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index d23b056..ff9ab54 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -73,10 +73,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ИД позиваоца подразумевано није ограничен. Следећи позив: Није ограничен."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Услуга није добављена."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Не можете да промените подешавање ИД-а корисника."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Мобилни подаци су пребачени на оператера <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Ово можете у сваком тренутку да промените у Подешавањима"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Нема услуге мобилних података"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Хитни позиви нису доступни"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Нема гласовне услуге"</string>
@@ -701,6 +699,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Омогућава апликацији да чита видео фајлове из дељеног меморијског простора."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"читање фајлова слика из дељеног меморијског простора"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Омогућава апликацији да чита фајлове слика из дељеног меморијског простора."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"мењање или брисање садржаја дељеног меморијског простора"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Дозвољава апликацији да уписује садржај дељеног меморијског простора."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"упућивање/пријем SIP позива"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index ee6a8ac..ee528db 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Nummerpresentatörens standardinställning är inte blockerad. Nästa samtal: Inte blockerad"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Tjänsten är inte etablerad."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Det går inte att ändra inställningen för nummerpresentatör."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Du har bytt mobildata till <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Du kan ändra det här när som helst i inställningarna"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Ingen mobildatatjänst"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Det går inte att ringa nödsamtal"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Tjänsten för röstsamtal har blockerats"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Tillåter att appen läser videofiler från delad lagring."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"läsa bildfiler från delad lagring"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Tillåter att appen läser bildfiler från delad lagring."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"ändra eller ta bort innehåll på delat lagringsutrymme"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Tillåter att appen skriver innehåll på ditt delade lagringsutrymme."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"gör/ta emot SIP-anrop"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 7b9d89f..1fb9f1e 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Chaguomsingi za ID ya mpigaji simu za kutozuia. Simu ifuatayo: Haijazuiliwa"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Huduma haitathminiwi."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Hauwezi kubadilisha mpangilio wa kitambulisho cha anayepiga."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Sasa unatumia data ya mtandao wa <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Unaweza kubadilisha hali hii wakati wowote kwenye Mipangilio"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Hakuna huduma ya data kwa vifaa vya mkononi"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Huduma ya kupiga simu za dharura haipatikani"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Hakuna huduma za simu za sauti"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Huruhusu programu kusoma faili za video kutoka kwenye hifadhi unayoshiriki."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"soma faili za picha kutoka kwenye hifadhi iliyoshirikiwa"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Huruhusu programu kusoma faili za picha kutoka kwenye hifadhi yako iliyoshirikiwa."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"irekebishe au ifute maudhui ya hifadhi unayoshiriki"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Huruhusu programu iandike maudhui ya hifadhi unayoshiriki."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"piga/pokea simu za SIP"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 2a0fc2c..5021c23 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"அழைப்பாளர் ஐடி ஆனது வரையறுக்கப்படவில்லை என்பதற்கு இயல்பாக அமைக்கப்பட்டது. அடுத்த அழைப்பு: வரையறுக்கப்படவில்லை"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"சேவை ஒதுக்கப்படவில்லை."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"அழைப்பாளர் ஐடி அமைப்பை மாற்ற முடியாது."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"டேட்டா <xliff:g id="CARRIERDISPLAY">%s</xliff:g>க்கு மாற்றப்பட்டது"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"அமைப்புகளில் இதை எப்போது வேண்டுமானாலும் மாற்றிக்கொள்ளலாம்"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"மொபைல் டேட்டா சேவையைப் பயன்படுத்த முடியாது"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"அவசர அழைப்பைச் செய்ய முடியாது"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"குரல் சேவை இல்லை"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"உங்கள் பகிர்ந்த சேமிப்பகத்திலுள்ள வீடியோ ஃபைல்களைப் படிக்க ஆப்ஸை அனுமதிக்கும்."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"பகிர்ந்த சேமிப்பகத்திலுள்ள பட ஃபைல்களைப் படித்தல்"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"உங்கள் பகிர்ந்த சேமிப்பகத்திலுள்ள பட ஃபைல்களைப் படிக்க ஆப்ஸை அனுமதிக்கும்."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"பகிர்ந்த சேமிப்பகத்தின் உள்ளடக்கங்களை மாற்றும் அல்லது நீக்கும்"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"பகிர்ந்த சேமிப்பகத்தின் உள்ளடக்கத்தில் மாற்றங்களைச் செய்ய அனுமதிக்கும்."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"SIP அழைப்புகளைச் செய்தல்/பெறுதல்"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index a7c2d4e..aa8e4db 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"కాలర్ ID ఆటోమేటిక్లపై పరిమితి లేదు. తర్వాత కాల్: పరిమితి లేదు"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"సేవ కేటాయించబడలేదు."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"మీరు కాలర్ ID సెట్టింగ్ను మార్చలేరు."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"డేటాను <xliff:g id="CARRIERDISPLAY">%s</xliff:g>కు స్విచ్ చేశారు"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"మీరు ఎప్పుడైనా సెట్టింగ్లలో దీనిని మార్చవచ్చు"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"మొబైల్ డేటా సేవ లేదు"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"అత్యవసర కాలింగ్ అందుబాటులో లేదు"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"వాయిస్ సర్వీస్ లేదు"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"మీ షేర్ చేయబడిన స్టోరేజ్ నుండి వీడియో ఫైల్లను చదవడానికి యాప్ను అనుమతిస్తుంది."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"షేర్ చేయబడిన స్టోరేజ్ నుండి ఇమేజ్ ఫైల్లను చదవండి"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"మీ షేర్ చేయబడిన స్టోరేజ్ నుండి ఇమేజ్ ఫైల్లను చదవడానికి యాప్ను అనుమతిస్తుంది."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"మీ షేర్ చేసిన నిల్వ యొక్క కంటెంట్లను ఎడిట్ చేయండి లేదా తొలగించండి"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"మీ షేర్ చేసిన నిల్వ యొక్క కంటెంట్లను రాయడానికి యాప్ను అనుమతిస్తుంది."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"SIP కాల్స్ను చేయడానికి/స్వీకరించడానికి"</string>
diff --git a/core/res/res/values-television/styles.xml b/core/res/res/values-television/styles.xml
deleted file mode 100644
index ad9140c..0000000
--- a/core/res/res/values-television/styles.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<!--
- ~ Copyright (C) 2022 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<resources>
- <style name="PermissionGrantButtonTop"
- parent="@style/Widget.Leanback.Button.ButtonBarGravityStart" />
- <style name="PermissionGrantButtonBottom"
- parent="@style/Widget.Leanback.Button.ButtonBarGravityStart" />
-</resources>
\ No newline at end of file
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index c0ab275..4c96aa1 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -698,6 +698,8 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"อนุญาตให้แอปอ่านไฟล์วิดีโอจากพื้นที่เก็บข้อมูลที่แชร์"</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"อ่านไฟล์ภาพจากพื้นที่เก็บข้อมูลที่แชร์"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"อนุญาตให้แอปอ่านไฟล์ภาพจากพื้นที่เก็บข้อมูลที่แชร์"</string>
+ <string name="permlab_readVisualUserSelect" msgid="5516204215354667586">"อ่านไฟล์รูปภาพและวิดีโอที่ผู้ใช้เลือกจากพื้นที่เก็บข้อมูลที่ใช้ร่วมกัน"</string>
+ <string name="permdesc_readVisualUserSelect" msgid="8027174717714968217">"อนุญาตให้แอปอ่านไฟล์รูปภาพและวิดีโอที่คุณเลือกจากพื้นที่เก็บข้อมูลที่ใช้ร่วมกัน"</string>
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"แก้ไขหรือลบเนื้อหาในพื้นที่จัดเก็บข้อมูลที่ใช้ร่วมกัน"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"อนุญาตให้แอปเขียนเนื้อหาในพื้นที่จัดเก็บข้อมูลที่ใช้ร่วมกัน"</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"โทร/รับสาย SIP"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 7b9807a..93495d2 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -698,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Nagbibigay-daan sa app na magbasa ng mga video file mula sa iyong nakabahaging storage."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"magbasa ng mga image file mula sa nakabahaging storage"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Nagbibigay-daan sa app na magbasa ng mga image file mula sa iyong nakabahaging storage."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"baguhin o i-delete ang content ng nakabahagi mong storage"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Pinapayagan ang app na mag-write sa content ng nakabahagi mong storage."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"magsagawa/tumanggap ng mga tawag sa SIP"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 3459cb0..ca6ff7f 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Arayan kimliği varsayılanları kısıtlanmamıştır. Sonraki çağrı: Kısıtlanmamış"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Hizmet sağlanamadı."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Arayanın kimliği ayarını değiştiremezsiniz."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Veriler şuraya aktarıldı: <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Bunu istediğiniz zaman Ayarlar\'da değiştirebilirsiniz"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Mobil veri hizmeti yok"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Acil durum çağrısı kullanılamaz"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Sesli çağrı hizmeti yok"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Uygulamaya, paylaşılan depolama alanınızdaki video dosyalarını okuma izni verir."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"paylaşılan depolama alanınızdaki resim dosyalarını okuma"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Uygulamaya, paylaşılan depolama alanınızdaki resim dosyalarını okuma izni verir."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"paylaşılan depolama alanımın içeriğini değiştir veya sil"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Uygulamanın paylaşılan depolama alanınıza içerik yazmasına izin verir."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"SIP aramaları yapma/alma"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 2483a30..459f9f0 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -74,10 +74,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Ідентиф. абонента за умовч. не обмеж. Наст. дзвінок: не обмежений"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Службу не ініціалізовано."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Ви не можете змінювати налаштування ідентифікатора абонента."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Мобільний Інтернет переключено на <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Цей параметр можна будь-коли змінити в налаштуваннях"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Службу передавання мобільних даних заблоковано"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Екстрені виклики недоступні"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Немає голосової служби"</string>
@@ -702,6 +700,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Дозволяє додатку зчитувати відеофайли з вашого спільного сховища."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"зчитувати файли зображень зі спільного сховища"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Дозволяє додатку зчитувати файли зображень із вашого спільного сховища."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"змінювати чи видаляти вміст у спільній пам’яті"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Додаток може писати вміст у спільній пам’яті."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"здійснювати й отримувати дзвінки через протокол SIP"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 7de793c..91b0efd 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"کالر ID کی ڈیفالٹ ترتیب غیر محدود کردہ ہے۔ اگلی کال: غیر محدود کردہ"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"سروس فراہم نہیں کی گئی۔"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"آپ کالر ID کی ترتیبات تبدیل نہیں کر سکتے ہیں۔"</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"ڈیٹا <xliff:g id="CARRIERDISPLAY">%s</xliff:g> پر سوئچ کیا گیا"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"آپ اسے ترتیبات میں کسی بھی وقت تبدیل کر سکتے ہیں"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"کوئی موبائل ڈیٹا سروس دستیاب نہیں ہے"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"ہنگامی کالنگ دستیاب نہیں ہے"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"کوئی صوتی سروس نہیں"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"ایپ کو آپ کی اشتراک کردہ اسٹوریج سے ویڈیو فائلز کو پڑھنے کی اجازت دیتا ہے۔"</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"اشتراک کردہ اسٹوریج سے تصویری فائلز کو پڑھیں"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"ایپ کو آپ کی اشتراک کردہ اسٹوریج سے تصویری فائلز کو پڑھنے کی اجازت دیتا ہے۔"</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"اپنے اشتراک کردہ اسٹوریج کے مواد میں ترمیم کریں یا اسے حذف کریں"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"ایپ کو آپ کے اشتراک کردہ اسٹوریج کے مواد کو لکھنے کی اجازت دیتا ہے۔"</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"SIP کالز کریں/موصول کریں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 8c88585..0a114fe 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Qo‘ng‘iroq qiluvchi ma’lumotlari cheklanmagan. Keyingi qo‘ng‘iroq: cheklanmagan"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Xizmat ishalamaydi."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Qo‘ng‘iroq qiluvchining ID raqami sozlamasini o‘zgartirib bo‘lmaydi."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Internet <xliff:g id="CARRIERDISPLAY">%s</xliff:g> operatoriga almashtirildi"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Buni istalgan vaqtda sozlamalardan o‘zgartirish mumkin"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Mobil internet ishlamayapti"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Favqulodda chaqiruv ishlamayapti"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Ovozli chaqiruvlar ishlamaydi"</string>
@@ -700,6 +698,8 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Ilovaga video fayllarni umumiy xotiradan oʻqish imkonini beradi."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"umumiy xotiradan rasmli fayllarni oʻqish"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Ilovaga rasm fayllarini umumiy xotiradan oʻqish imkonini beradi."</string>
+ <string name="permlab_readVisualUserSelect" msgid="5516204215354667586">"umumiy xotiradan siz tanlagan rasm va video fayllarni oʻqish"</string>
+ <string name="permdesc_readVisualUserSelect" msgid="8027174717714968217">"Ilovaga siz tanlagan rasm va video fayllarni umumiy xotiradan oʻqish imkonini beradi."</string>
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"umumiy xotiradagi kontentlarni tahrirlash yoki oʻchirib tashlash"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Ilovaga umumiy xotiradagi kontentlarga yozish imkonini beradi."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"SIP qo‘ng‘iroqlarini amalga oshirish/qabul qilish"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 68bb062..3842634 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Số gọi đến mặc định thành không bị giới hạn. Cuộc gọi tiếp theo. Không bị giới hạn"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Dịch vụ không được cấp phép."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Bạn không thể thay đổi cài đặt ID người gọi."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Đã chuyển dữ liệu sang <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Bạn có thể thay đổi chế độ này bất cứ lúc nào trong phần Cài đặt"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Không có dịch vụ dữ liệu di động"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Không có dịch vụ gọi khẩn cấp"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Không có dịch vụ thoại"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Cho phép ứng dụng đọc tệp video trong bộ nhớ dùng chung."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"đọc tệp hình ảnh trong bộ nhớ dùng chung"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Cho phép ứng dụng đọc tệp hình ảnh trong bộ nhớ dùng chung."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"sửa đổi hoặc xóa nội dung của bộ nhớ dùng chung"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Cho phép ứng dụng ghi nội dung của bộ nhớ dùng chung."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"thực hiện/nhận các cuộc gọi qua SIP"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index f2b033e..1a84172 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"默认显示本机号码,在下一次通话中也显示"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"未提供服务。"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"您无法更改来电显示设置。"</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"移动数据网络已切换至“<xliff:g id="CARRIERDISPLAY">%s</xliff:g>”"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"您可以随时在“设置”中更改这项设置"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"无法使用移动数据服务"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"无法使用紧急呼救服务"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"无法使用语音通话服务"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"允许应用读取您共享存储空间中的视频文件。"</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"读取共享存储空间中的图片文件"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"允许应用读取您共享存储空间中的图片文件。"</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"修改或删除您共享存储空间中的内容"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"允许该应用写入您共享存储空间中的内容。"</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"拨打/接听SIP电话"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 63995bb..7af19ce 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"預設顯示來電號碼,下一通電話也繼續顯示。"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"未提供此服務。"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"您無法更改來電顯示設定。"</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"流動數據已切換至「<xliff:g id="CARRIERDISPLAY">%s</xliff:g>」"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"您隨時可在「設定」中變更此設定"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"無法使用流動數據服務"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"無法撥打緊急電話"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"沒有語音服務"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"允許應用程式讀取共用儲存空間中的影片檔案。"</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"讀取共用儲存空間中的圖片檔案"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"允許應用程式讀取共用儲存空間中的圖片檔案。"</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"修改或刪除您共用儲存空間的內容"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"允許應用程式寫入您共用儲存空間的內容。"</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"撥打/接聽 SIP 電話"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 55f9321..09b6f90 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"預設顯示本機號碼,下一通電話也繼續顯示。"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"無法提供此服務。"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"你無法變更來電顯示設定。"</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"行動數據已切換至「<xliff:g id="CARRIERDISPLAY">%s</xliff:g>」"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"你隨時可以前往「設定」變更這項設定"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"沒有行動數據傳輸服務"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"無法撥打緊急電話"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"無法使用語音通話服務"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"允許應用程式讀取共用儲存空間中的影片檔案。"</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"讀取共用儲存空間中的圖片檔案"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"允許應用程式讀取共用儲存空間中的圖片檔案。"</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"修改或刪除共用儲存空間中的內容"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"允許這個應用程式寫入共用儲存空間中的內容。"</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"撥打/接聽 SIP 通話"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 2218b76..1e20552 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -72,10 +72,8 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"I-ID Yomshayeli ishintshela kokungavinjelwe. Ucingo olulandelayo: Aluvinjelwe"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Isevisi ayilungiselelwe."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Ngeke ukwazi ukuguqul izilungiselelo zemininingwane yoshayayo."</string>
- <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
- <skip />
- <!-- no translation found for auto_data_switch_content (803557715007110959) -->
- <skip />
+ <string name="auto_data_switch_title" msgid="3286350716870518297">"Ushintshele idatha ku-<xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+ <string name="auto_data_switch_content" msgid="803557715007110959">"Ungashintsha lokhu noma nini kumasethingi"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Ayikho isevisi yedatha yeselula"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Ukushaya okuphuthumayo akutholakali"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Ayikho isevisi yezwi"</string>
@@ -700,6 +698,10 @@
<string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Ivumela i-app ukuthi ifunde amafayela amavidiyo kwisitoreji sakho owabelane ngaso."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"funda amafayela womfanekiso wesitoreji okwabelenwe ngaso"</string>
<string name="permdesc_readMediaImages" msgid="5836219373138469259">"Ivumela i-app ukuthi ifunde amafayela ezithombe kwisitoreji sakho owabelane ngaso."</string>
+ <!-- no translation found for permlab_readVisualUserSelect (5516204215354667586) -->
+ <skip />
+ <!-- no translation found for permdesc_readVisualUserSelect (8027174717714968217) -->
+ <skip />
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"guqula noma susa okuqukethwe kwesitoreji sakho esabiwe"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Ivumela uhlelo lokusebenza ukuthi lubhale okuqukethwe kwesitoreji sakho esabiwe."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"yenza/thola amakholi we-SIP"</string>
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index dff7751..9e735d0 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -166,12 +166,12 @@
</string-array>
<array name="sim_colors">
- <item>@color/Teal_700</item>
- <item>@color/Blue_700</item>
- <item>@color/Indigo_700</item>
- <item>@color/Purple_700</item>
- <item>@color/Pink_700</item>
- <item>@color/Red_700</item>
+ <item>@color/SIM_color_cyan</item>
+ <item>@color/SIM_color_blue</item>
+ <item>@color/SIM_color_green</item>
+ <item>@color/SIM_color_purple</item>
+ <item>@color/SIM_color_pink</item>
+ <item>@color/SIM_color_orange</item>
</array>
<!-- Used in ResolverTargetActionsDialogFragment -->
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 77d7c43..8c356b4 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -198,18 +198,18 @@
<color name="instant_app_badge">#ff757575</color><!-- Grey -->
<!-- Multi-sim sim colors -->
- <color name="Teal_700">#ff00796b</color>
- <color name="Teal_800">#ff00695c</color>
- <color name="Blue_700">#ff3367d6</color>
- <color name="Blue_800">#ff2a56c6</color>
- <color name="Indigo_700">#ff303f9f</color>
- <color name="Indigo_800">#ff283593</color>
- <color name="Purple_700">#ff7b1fa2</color>
- <color name="Purple_800">#ff6a1b9a</color>
- <color name="Pink_700">#ffc2185b</color>
- <color name="Pink_800">#ffad1457</color>
- <color name="Red_700">#ffc53929</color>
- <color name="Red_800">#ffb93221</color>
+ <color name="SIM_color_cyan">#ff006D74</color><!-- Material Custom Cyan -->
+ <color name="SIM_dark_mode_color_cyan">#ff4DD0E1</color><!-- Material Cyan 300 -->
+ <color name="SIM_color_blue">#ff185ABC</color><!-- Material Blue 800 -->
+ <color name="SIM_dark_mode_color_blue">#ff8AB4F8</color><!-- Material Blue 300 -->
+ <color name="SIM_color_green">#ff137333</color><!-- Material Green 800 -->
+ <color name="SIM_dark_mode_color_green">#ff81C995</color><!-- Material Green 300 -->
+ <color name="SIM_color_purple">#ff7627bb</color><!-- Material Purple 800 -->
+ <color name="SIM_dark_mode_color_purple">#ffC58AF9</color><!-- Material Purple 300 -->
+ <color name="SIM_color_pink">#ffb80672</color><!-- Material Pink 800 -->
+ <color name="SIM_dark_mode_color_pink">#ffff8bcb</color><!-- Material Pink 300 -->
+ <color name="SIM_color_orange">#ff995400</color><!-- Material Custom Orange -->
+ <color name="SIM_dark_mode_color_orange">#fffcad70</color><!-- Material Orange 300 -->
<color name="resize_shadow_start_color">#2a000000</color>
<color name="resize_shadow_end_color">#00000000</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3d245e9..0218215 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -342,22 +342,6 @@
<!-- Mask to use when checking skb mark defined in config_networkWakeupPacketMark above. -->
<integer name="config_networkWakeupPacketMask">0</integer>
- <!-- Whether the APF Filter in the device should filter out IEEE 802.3 Frames
- Those frames are identified by the field Eth-type having values
- less than 0x600 -->
- <bool translatable="false" name="config_apfDrop802_3Frames">true</bool>
-
- <!-- An array of Denylisted EtherType, packets with EtherTypes within this array
- will be dropped
- TODO: need to put proper values, these are for testing purposes only -->
- <integer-array translatable="false" name="config_apfEthTypeBlackList">
- <item>0x88A2</item>
- <item>0x88A4</item>
- <item>0x88B8</item>
- <item>0x88CD</item>
- <item>0x88E3</item>
- </integer-array>
-
<!-- Default value for ConnectivityManager.getMultipathPreference() on metered networks. Actual
device behaviour is controlled by Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE.
This is the default value of that setting. -->
@@ -2901,7 +2885,7 @@
>com.android.systemui/com.android.systemui.usb.UsbDebuggingActivity</string>
<!-- Name of the activity that prompts the secondary user to acknowledge they need to
- switch to the primary user to enable USB debugging.
+ switch to an admin user to enable USB debugging.
Can be customized for other product types -->
<string name="config_customAdbPublicKeyConfirmationSecondaryUserComponent"
>com.android.systemui/com.android.systemui.usb.UsbDebuggingSecondaryUserActivity</string>
@@ -2913,7 +2897,7 @@
>com.android.systemui/com.android.systemui.wifi.WifiDebuggingActivity</string>
<!-- Name of the activity that prompts the secondary user to acknowledge they need to
- switch to the primary user to enable wireless debugging.
+ switch to an admin user to enable wireless debugging.
Can be customized for other product types -->
<string name="config_customAdbWifiNetworkConfirmationSecondaryUserComponent"
>com.android.systemui/com.android.systemui.wifi.WifiDebuggingSecondaryUserActivity</string>
@@ -2974,6 +2958,10 @@
<string name="config_carrierAppInstallDialogComponent" translatable="false"
>com.android.simappdialog/com.android.simappdialog.InstallCarrierAppActivity</string>
+ <!-- Name of the dialog that is used to get or save an app credential -->
+ <string name="config_credentialManagerDialogComponent" translatable="false"
+ >com.android.credentialmanager/com.android.credentialmanager.CredentialSelectorActivity</string>
+
<!-- Apps that are authorized to access shared accounts, overridden by product overlays -->
<string name="config_appsAuthorizedForSharedAccounts" translatable="false">;com.android.settings;</string>
@@ -3980,7 +3968,7 @@
<!-- Colon separated list of package names that should be granted DND access -->
<string name="config_defaultDndAccessPackages" translatable="false">com.android.camera2</string>
- <!-- User restrictions set when the first user is created.
+ <!-- User restrictions set on the SYSTEM user when it is first created.
Note: Also update appropriate overlay files. -->
<string-array translatable="false" name="config_defaultFirstUserRestrictions">
</string-array>
@@ -4422,6 +4410,10 @@
or empty if the default should be used. -->
<string translatable="false" name="config_deviceSpecificDeviceStatePolicyProvider"></string>
+ <!-- Class name of the device specific implementation of InputMethodManagerService
+ or empty if the default should be used. -->
+ <string translatable="false" name="config_deviceSpecificInputMethodManagerService"></string>
+
<!-- Component name of media projection permission dialog -->
<string name="config_mediaProjectionPermissionDialogComponent" translatable="false">com.android.systemui/com.android.systemui.media.MediaProjectionPermissionActivity</string>
@@ -5951,4 +5943,9 @@
<!-- Whether the lock screen is allowed to run its own live wallpaper,
different from the home screen wallpaper. -->
<bool name="config_independentLockscreenLiveWallpaper">false</bool>
+
+ <!-- List of certificate to be used for font fs-verity integrity verification -->
+ <string-array translatable="false" name="config_fontManagerServiceCerts">
+ </string-array>
+
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 1f459c6..48484c7 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1302,6 +1302,11 @@
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] -->
<string name="permdesc_recordBackgroundAudio">This app can record audio using the microphone at any time.</string>
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
+ <string name="permlab_detectScreenCapture">detect screen captures of app windows</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this.[CHAR_LIMIT=NONE] -->
+ <string name="permdesc_detectScreenCapture">This app will get notified when a screenshot is taken while the app is in use.</string>
+
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_sim_communication">send commands to the SIM</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -5759,32 +5764,6 @@
<!-- Title for the harmful app warning dialog. [CHAR LIMIT=40] -->
<string name="harmful_app_warning_title">Harmful app detected</string>
- <!-- Title for the log access confirmation dialog. [CHAR LIMIT=NONE] -->
- <string name="log_access_confirmation_title">Allow <xliff:g id="log_access_app_name" example="Example App">%s</xliff:g> to access all device logs?</string>
- <!-- Label for the allow button on the log access confirmation dialog. [CHAR LIMIT=40] -->
- <string name="log_access_confirmation_allow">Allow one-time access</string>
- <!-- Label for the deny button on the log access confirmation dialog. [CHAR LIMIT=20] -->
- <string name="log_access_confirmation_deny">Don\u2019t allow</string>
-
- <!-- Content for the log access confirmation dialog. [CHAR LIMIT=NONE]-->
- <string name="log_access_confirmation_body" product="default">Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps you trust to access all device logs.
- \n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device.
- </string>
-
- <!-- Content for the log access confirmation dialog. [CHAR LIMIT=NONE]-->
- <string name="log_access_confirmation_body" product="tv">Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps you trust to access all device logs.
- \n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device.\n\nLearn more at g.co/android/devicelogs.
- </string>
-
- <!-- Learn more URL for the log access confirmation dialog. [DO NOT TRANSLATE]-->
- <string name="log_access_confirmation_learn_more" product="default" translatable="false"><a href="https://support.google.com/android?p=system_logs#topic=7313011">Learn more</a></string>
-
- <!-- Learn more URL for the log access confirmation dialog. [DO NOT TRANSLATE]-->
- <string name="log_access_confirmation_learn_more" product="tv" translatable="false"></string>
-
- <!-- Privacy notice do not show [CHAR LIMIT=20] -->
- <string name="log_access_do_not_show_again">Don\u2019t show again</string>
-
<!-- Text describing a permission request for one app to show another app's
slices [CHAR LIMIT=NONE] -->
<string name="slices_permission_request"><xliff:g id="app" example="Example App">%1$s</xliff:g> wants to show <xliff:g id="app_2" example="Other Example App">%2$s</xliff:g> slices</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 2dd563d..476c18e 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1541,40 +1541,4 @@
<style name="NotificationTombstoneAction" parent="NotificationAction">
<item name="textColor">#555555</item>
</style>
-
- <!-- The style for log access consent text -->
- <style name="AllowLogAccess">
- <item name="android:textSize">24sp</item>
- <item name="android:fontFamily">google-sans</item>
- </style>
-
- <style name="PrimaryAllowLogAccess">
- <item name="android:textSize">14sp</item>
- <item name="android:fontFamily">google-sans-text</item>
- </style>
-
- <style name="PermissionGrantButtonTextAppearance">
- <item name="android:fontFamily">google-sans-medium</item>
- <item name="android:textSize">14sp</item>
- <item name="android:textColor">@android:color/system_neutral1_900</item>
- </style>
-
- <style name="PermissionGrantButtonTop"
- parent="@android:style/Widget.DeviceDefault.Button.Borderless.Colored">
- <item name="android:layout_width">332dp</item>
- <item name="android:layout_height">56dp</item>
- <item name="android:layout_marginTop">2dp</item>
- <item name="android:layout_marginBottom">2dp</item>
- <item name="android:background">@drawable/grant_permissions_buttons_top</item>
- </style>
-
- <style name="PermissionGrantButtonBottom"
- parent="@android:style/Widget.DeviceDefault.Button.Borderless.Colored">
- <item name="android:layout_width">332dp</item>
- <item name="android:layout_height">56dp</item>
- <item name="android:layout_marginTop">2dp</item>
- <item name="android:layout_marginBottom">2dp</item>
- <item name="android:background">@drawable/grant_permissions_buttons_bottom</item>
- </style>
-
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f2bbbc4..b5d534f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -483,6 +483,7 @@
<java-symbol type="array" name="config_deviceSpecificSystemServices" />
<java-symbol type="string" name="config_deviceSpecificDevicePolicyManagerService" />
<java-symbol type="string" name="config_deviceSpecificAudioService" />
+ <java-symbol type="string" name="config_deviceSpecificInputMethodManagerService" />
<java-symbol type="integer" name="config_num_physical_slots" />
<java-symbol type="integer" name="config_default_cellular_usage_setting" />
<java-symbol type="array" name="config_supported_cellular_usage_settings" />
@@ -2045,8 +2046,6 @@
<java-symbol type="integer" name="config_networkAvoidBadWifi" />
<java-symbol type="integer" name="config_networkWakeupPacketMark" />
<java-symbol type="integer" name="config_networkWakeupPacketMask" />
- <java-symbol type="bool" name="config_apfDrop802_3Frames" />
- <java-symbol type="array" name="config_apfEthTypeBlackList" />
<java-symbol type="integer" name="config_networkDefaultDailyMultipathQuotaBytes" />
<java-symbol type="integer" name="config_networkMeteredMultipathPreference" />
<java-symbol type="array" name="config_networkSupportedKeepaliveCount" />
@@ -2265,6 +2264,7 @@
<java-symbol type="string" name="config_customVpnAlwaysOnDisconnectedDialogComponent" />
<java-symbol type="string" name="config_platformVpnConfirmDialogComponent" />
<java-symbol type="string" name="config_carrierAppInstallDialogComponent" />
+ <java-symbol type="string" name="config_credentialManagerDialogComponent" />
<java-symbol type="string" name="config_defaultNetworkScorerPackageName" />
<java-symbol type="string" name="config_persistentDataPackageName" />
<java-symbol type="string" name="config_deviceConfiguratorPackageName" />
@@ -2309,6 +2309,7 @@
<java-symbol type="id" name="media_actions" />
<java-symbol type="dimen" name="config_mediaMetadataBitmapMaxSize" />
+ <java-symbol type="array" name="config_fontManagerServiceCerts" />
<!-- From SystemUI -->
<java-symbol type="anim" name="push_down_in" />
@@ -3446,7 +3447,7 @@
<java-symbol type="array" name="config_displayWhiteBalanceDisplayNominalWhite" />
<java-symbol type="bool" name="config_displayWhiteBalanceLightModeAllowed" />
- <!-- Default first user restrictions -->
+ <!-- Default user restrictions for the SYSTEM user -->
<java-symbol type="array" name="config_defaultFirstUserRestrictions" />
<java-symbol type="bool" name="config_permissionsIndividuallyControlled" />
@@ -3939,17 +3940,6 @@
<java-symbol type="string" name="harmful_app_warning_title" />
<java-symbol type="layout" name="harmful_app_warning_dialog" />
- <java-symbol type="string" name="log_access_confirmation_allow" />
- <java-symbol type="string" name="log_access_confirmation_deny" />
- <java-symbol type="string" name="log_access_confirmation_title" />
- <java-symbol type="string" name="log_access_confirmation_body" />
- <java-symbol type="string" name="log_access_confirmation_learn_more" />
- <java-symbol type="layout" name="log_access_user_consent_dialog_permission" />
- <java-symbol type="id" name="log_access_dialog_title" />
- <java-symbol type="id" name="log_access_dialog_body" />
- <java-symbol type="id" name="log_access_dialog_allow_button" />
- <java-symbol type="id" name="log_access_dialog_deny_button" />
-
<java-symbol type="string" name="config_defaultAssistantAccessComponent" />
<java-symbol type="string" name="slices_permission_request" />
diff --git a/core/tests/BroadcastRadioTests/Android.bp b/core/tests/BroadcastRadioTests/Android.bp
index 7cb64c8..436f058 100644
--- a/core/tests/BroadcastRadioTests/Android.bp
+++ b/core/tests/BroadcastRadioTests/Android.bp
@@ -48,7 +48,7 @@
],
// mockito-target-inline dependency
jni_libs: [
- "libcarservicejni",
"libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
],
}
diff --git a/core/tests/BroadcastRadioTests/AndroidTest.xml b/core/tests/BroadcastRadioTests/AndroidTest.xml
new file mode 100644
index 0000000..ed88537
--- /dev/null
+++ b/core/tests/BroadcastRadioTests/AndroidTest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs Broadcast Radio Tests.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="BroadcastRadioTests.apk" />
+ </target_preparer>
+
+ <option name="test-tag" value="BroadcastRadioTests" />
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.hardware.radio.tests" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/DefaultRadioTunerTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/DefaultRadioTunerTest.java
new file mode 100644
index 0000000..2fa3f876
--- /dev/null
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/DefaultRadioTunerTest.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.radio.tests.unittests;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertThrows;
+
+import android.graphics.Bitmap;
+import android.hardware.radio.ProgramList;
+import android.hardware.radio.ProgramSelector;
+import android.hardware.radio.RadioManager;
+import android.hardware.radio.RadioTuner;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public final class DefaultRadioTunerTest {
+
+ private static final RadioTuner DEFAULT_RADIO_TUNER = new RadioTuner() {
+ @Override
+ public void close() {}
+
+ @Override
+ public int setConfiguration(RadioManager.BandConfig config) {
+ return 0;
+ }
+
+ @Override
+ public int getConfiguration(RadioManager.BandConfig[] config) {
+ return 0;
+ }
+
+ @Override
+ public int setMute(boolean mute) {
+ return 0;
+ }
+
+ @Override
+ public boolean getMute() {
+ return false;
+ }
+
+ @Override
+ public int step(int direction, boolean skipSubChannel) {
+ return 0;
+ }
+
+ @Override
+ public int scan(int direction, boolean skipSubChannel) {
+ return 0;
+ }
+
+ @Override
+ public int tune(int channel, int subChannel) {
+ return 0;
+ }
+
+ @Override
+ public void tune(@NonNull ProgramSelector selector) {}
+
+ @Override
+ public int cancel() {
+ return 0;
+ }
+
+ @Override
+ public void cancelAnnouncement() {}
+
+ @Override
+ public int getProgramInformation(RadioManager.ProgramInfo[] info) {
+ return 0;
+ }
+
+ @Nullable
+ @Override
+ public Bitmap getMetadataImage(int id) {
+ return null;
+ }
+
+ @Override
+ public boolean startBackgroundScan() {
+ return false;
+ }
+
+ @NonNull
+ @Override
+ public List<RadioManager.ProgramInfo> getProgramList(
+ @Nullable Map<String, String> vendorFilter) {
+ return new ArrayList<>();
+ }
+
+ @Override
+ public boolean isAnalogForced() {
+ return false;
+ }
+
+ @Override
+ public void setAnalogForced(boolean isForced) {}
+
+ @Override
+ public boolean isAntennaConnected() {
+ return false;
+ }
+
+ @Override
+ public boolean hasControl() {
+ return false;
+ }
+ };
+
+ @Test
+ public void getDynamicProgramList_forRadioTuner_returnsNull() {
+ assertWithMessage("Dynamic program list obtained from default radio tuner")
+ .that(DEFAULT_RADIO_TUNER.getDynamicProgramList(new ProgramList.Filter())).isNull();
+ }
+
+ @Test
+ public void isConfigFlagSupported_forRadioTuner_throwsException() {
+ assertWithMessage("Dynamic program list obtained from default radio tuner")
+ .that(DEFAULT_RADIO_TUNER.isConfigFlagSupported(/* flag= */ 1)).isFalse();
+ }
+
+ @Test
+ public void isConfigFlagSet_forRadioTuner_throwsException() {
+ assertThrows(UnsupportedOperationException.class, () -> {
+ DEFAULT_RADIO_TUNER.isConfigFlagSet(/* flag= */ 1);
+ });
+ }
+
+ @Test
+ public void setConfigFlag_forRadioTuner_throwsException() {
+ assertThrows(UnsupportedOperationException.class, () -> {
+ DEFAULT_RADIO_TUNER.setConfigFlag(/* flag= */ 1, /* value= */ false);
+ });
+ }
+
+ @Test
+ public void setParameters_forRadioTuner_throwsException() {
+ assertThrows(UnsupportedOperationException.class, () -> {
+ DEFAULT_RADIO_TUNER.setParameters(Map.of("testKey", "testValue"));
+ });
+ }
+
+ @Test
+ public void getParameters_forRadioTuner_throwsException() {
+ assertThrows(UnsupportedOperationException.class, () -> {
+ DEFAULT_RADIO_TUNER.getParameters(List.of("testKey"));
+ });
+ }
+}
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/ProgramListTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/ProgramListTest.java
new file mode 100644
index 0000000..2b9de18
--- /dev/null
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/ProgramListTest.java
@@ -0,0 +1,511 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.radio.tests.unittests;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.hardware.radio.IRadioService;
+import android.hardware.radio.ITuner;
+import android.hardware.radio.ITunerCallback;
+import android.hardware.radio.ProgramList;
+import android.hardware.radio.ProgramSelector;
+import android.hardware.radio.RadioManager;
+import android.hardware.radio.RadioMetadata;
+import android.hardware.radio.RadioTuner;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.util.ArraySet;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.mockito.verification.VerificationWithTimeout;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Executor;
+
+@RunWith(MockitoJUnitRunner.class)
+public final class ProgramListTest {
+
+ private static final int CREATOR_ARRAY_SIZE = 3;
+ private static final VerificationWithTimeout CALLBACK_TIMEOUT = timeout(/* millis= */ 500);
+
+ private static final boolean IS_PURGE = false;
+ private static final boolean IS_COMPLETE = true;
+
+ private static final boolean INCLUDE_CATEGORIES = true;
+ private static final boolean EXCLUDE_MODIFICATIONS = false;
+
+ private static final ProgramSelector.Identifier FM_IDENTIFIER =
+ new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY,
+ /* value= */ 94300);
+ private static final ProgramSelector.Identifier RDS_IDENTIFIER =
+ new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_RDS_PI, 15019);
+ private static final ProgramSelector.Identifier DAB_SID_EXT_IDENTIFIER =
+ new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_SID_EXT,
+ /* value= */ 0x10000111);
+ private static final ProgramSelector.Identifier DAB_ENSEMBLE_IDENTIFIER =
+ new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE,
+ /* value= */ 0x1013);
+ private static final RadioManager.ProgramInfo FM_PROGRAM_INFO = createFmProgramInfo(
+ createProgramSelector(ProgramSelector.PROGRAM_TYPE_FM, FM_IDENTIFIER));
+ private static final RadioManager.ProgramInfo RDS_PROGRAM_INFO = createFmProgramInfo(
+ createProgramSelector(ProgramSelector.PROGRAM_TYPE_FM, RDS_IDENTIFIER));
+
+ private static final Set<Integer> FILTER_IDENTIFIER_TYPES = Set.of(
+ ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, ProgramSelector.IDENTIFIER_TYPE_RDS_PI);
+ private static final Set<ProgramSelector.Identifier> FILTER_IDENTIFIERS = Set.of(FM_IDENTIFIER);
+
+ private static final ProgramList.Chunk FM_RDS_ADD_CHUNK = new ProgramList.Chunk(IS_PURGE,
+ IS_COMPLETE, Set.of(FM_PROGRAM_INFO, RDS_PROGRAM_INFO),
+ Set.of(DAB_SID_EXT_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER));
+ private static final ProgramList.Chunk FM_ADD_INCOMPLETE_CHUNK = new ProgramList.Chunk(IS_PURGE,
+ /* complete= */ false, Set.of(FM_PROGRAM_INFO), new ArraySet<>());
+ private static final ProgramList.Filter TEST_FILTER = new ProgramList.Filter(
+ FILTER_IDENTIFIER_TYPES, FILTER_IDENTIFIERS, INCLUDE_CATEGORIES, EXCLUDE_MODIFICATIONS);
+ private static final Map<String, String> VENDOR_FILTER = Map.of("testVendorKey1",
+ "testVendorValue1", "testVendorKey2", "testVendorValue2");
+
+ private final Executor mExecutor = new Executor() {
+ @Override
+ public void execute(Runnable command) {
+ command.run();
+ }
+ };
+
+ private RadioTuner mRadioTuner;
+ private ITunerCallback mTunerCallback;
+ private ProgramList mProgramList;
+
+ private ProgramList.ListCallback[] mListCallbackMocks;
+ private ProgramList.OnCompleteListener[] mOnCompleteListenerMocks;
+ @Mock
+ private IRadioService mRadioServiceMock;
+ @Mock
+ private Context mContextMock;
+ @Mock
+ private ITuner mTunerMock;
+ @Mock
+ private RadioTuner.Callback mTunerCallbackMock;
+
+ @Test
+ public void getIdentifierTypes_forFilter() {
+ ProgramList.Filter filter = new ProgramList.Filter(FILTER_IDENTIFIER_TYPES,
+ FILTER_IDENTIFIERS, INCLUDE_CATEGORIES, EXCLUDE_MODIFICATIONS);
+
+ assertWithMessage("Filtered identifier types").that(filter.getIdentifierTypes())
+ .containsExactlyElementsIn(FILTER_IDENTIFIER_TYPES);
+ }
+
+ @Test
+ public void getIdentifiers_forFilter() {
+ ProgramList.Filter filter = new ProgramList.Filter(FILTER_IDENTIFIER_TYPES,
+ FILTER_IDENTIFIERS, INCLUDE_CATEGORIES, EXCLUDE_MODIFICATIONS);
+
+ assertWithMessage("Filtered identifiers").that(filter.getIdentifiers())
+ .containsExactlyElementsIn(FILTER_IDENTIFIERS);
+ }
+
+ @Test
+ public void areCategoriesIncluded_forFilter() {
+ ProgramList.Filter filter = new ProgramList.Filter(FILTER_IDENTIFIER_TYPES,
+ FILTER_IDENTIFIERS, INCLUDE_CATEGORIES, EXCLUDE_MODIFICATIONS);
+
+ assertWithMessage("Filter including categories")
+ .that(filter.areCategoriesIncluded()).isEqualTo(INCLUDE_CATEGORIES);
+ }
+
+ @Test
+ public void areModificationsExcluded_forFilter() {
+ ProgramList.Filter filter = new ProgramList.Filter(FILTER_IDENTIFIER_TYPES,
+ FILTER_IDENTIFIERS, INCLUDE_CATEGORIES, EXCLUDE_MODIFICATIONS);
+
+ assertWithMessage("Filter excluding modifications")
+ .that(filter.areModificationsExcluded()).isEqualTo(EXCLUDE_MODIFICATIONS);
+ }
+
+ @Test
+ public void getVendorFilter_forFilterWithoutVendorFilter_returnsNull() {
+ ProgramList.Filter filter = new ProgramList.Filter(FILTER_IDENTIFIER_TYPES,
+ FILTER_IDENTIFIERS, INCLUDE_CATEGORIES, EXCLUDE_MODIFICATIONS);
+
+ assertWithMessage("Filter vendor obtained from filter without vendor filter")
+ .that(filter.getVendorFilter()).isNull();
+ }
+
+ @Test
+ public void getVendorFilter_forFilterWithVendorFilter() {
+ ProgramList.Filter vendorFilter = new ProgramList.Filter(VENDOR_FILTER);
+
+ assertWithMessage("Filter vendor obtained from filter with vendor filter")
+ .that(vendorFilter.getVendorFilter()).isEqualTo(VENDOR_FILTER);
+ }
+
+ @Test
+ public void describeContents_forFilter() {
+ assertWithMessage("Filter contents").that(TEST_FILTER.describeContents()).isEqualTo(0);
+ }
+
+ @Test
+ public void hashCode_withTheSameFilters_equals() {
+ ProgramList.Filter filterCompared = new ProgramList.Filter(FILTER_IDENTIFIER_TYPES,
+ FILTER_IDENTIFIERS, INCLUDE_CATEGORIES, EXCLUDE_MODIFICATIONS);
+
+ assertWithMessage("Hash code of the same filter")
+ .that(filterCompared.hashCode()).isEqualTo(TEST_FILTER.hashCode());
+ }
+
+ @Test
+ public void hashCode_withDifferentFilters_notEquals() {
+ ProgramList.Filter filterCompared = new ProgramList.Filter();
+
+ assertWithMessage("Hash code of the different filter")
+ .that(filterCompared.hashCode()).isNotEqualTo(TEST_FILTER.hashCode());
+ }
+
+ @Test
+ public void writeToParcel_forFilter() {
+ Parcel parcel = Parcel.obtain();
+
+ TEST_FILTER.writeToParcel(parcel, /* flags= */ 0);
+ parcel.setDataPosition(0);
+
+ ProgramList.Filter filterFromParcel =
+ ProgramList.Filter.CREATOR.createFromParcel(parcel);
+ assertWithMessage("Filter created from parcel")
+ .that(filterFromParcel).isEqualTo(TEST_FILTER);
+ }
+
+ @Test
+ public void newArray_forFilterCreator() {
+ ProgramList.Filter[] filters = ProgramList.Filter.CREATOR.newArray(CREATOR_ARRAY_SIZE);
+
+ assertWithMessage("Program filters").that(filters).hasLength(CREATOR_ARRAY_SIZE);
+ }
+
+ @Test
+ public void isPurge_forChunk() {
+ ProgramList.Chunk chunk = new ProgramList.Chunk(IS_PURGE, IS_COMPLETE,
+ Set.of(FM_PROGRAM_INFO, RDS_PROGRAM_INFO),
+ Set.of(DAB_SID_EXT_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER));
+
+ assertWithMessage("Puring chunk").that(chunk.isPurge()).isEqualTo(IS_PURGE);
+ }
+
+ @Test
+ public void isComplete_forChunk() {
+ ProgramList.Chunk chunk = new ProgramList.Chunk(IS_PURGE, IS_COMPLETE,
+ Set.of(FM_PROGRAM_INFO, RDS_PROGRAM_INFO),
+ Set.of(DAB_SID_EXT_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER));
+
+ assertWithMessage("Complete chunk").that(chunk.isComplete()).isEqualTo(IS_COMPLETE);
+ }
+
+ @Test
+ public void getModified_forChunk() {
+ ProgramList.Chunk chunk = new ProgramList.Chunk(IS_PURGE, IS_COMPLETE,
+ Set.of(FM_PROGRAM_INFO, RDS_PROGRAM_INFO),
+ Set.of(DAB_SID_EXT_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER));
+
+ assertWithMessage("Modified program info in chunk")
+ .that(chunk.getModified()).containsExactly(FM_PROGRAM_INFO, RDS_PROGRAM_INFO);
+ }
+
+ @Test
+ public void getRemoved_forChunk() {
+ ProgramList.Chunk chunk = new ProgramList.Chunk(IS_PURGE, IS_COMPLETE,
+ Set.of(FM_PROGRAM_INFO, RDS_PROGRAM_INFO),
+ Set.of(DAB_SID_EXT_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER));
+
+ assertWithMessage("Removed program identifiers in chunk").that(chunk.getRemoved())
+ .containsExactly(DAB_SID_EXT_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER);
+ }
+
+ @Test
+ public void describeContents_forChunk() {
+ assertWithMessage("Chunk contents").that(FM_RDS_ADD_CHUNK.describeContents()).isEqualTo(0);
+ }
+
+ @Test
+ public void writeToParcel_forChunk() {
+ Parcel parcel = Parcel.obtain();
+
+ FM_RDS_ADD_CHUNK.writeToParcel(parcel, /* flags= */ 0);
+ parcel.setDataPosition(0);
+
+ ProgramList.Chunk chunkFromParcel =
+ ProgramList.Chunk.CREATOR.createFromParcel(parcel);
+ assertWithMessage("Chunk created from parcel")
+ .that(chunkFromParcel).isEqualTo(FM_RDS_ADD_CHUNK);
+ }
+
+ @Test
+ public void newArray_forChunkCreator() {
+ ProgramList.Chunk[] chunks = ProgramList.Chunk.CREATOR.newArray(CREATOR_ARRAY_SIZE);
+
+ assertWithMessage("Chunks").that(chunks).hasLength(CREATOR_ARRAY_SIZE);
+ }
+
+ @Test
+ public void getDynamicProgramList_forTunerAdapter() throws Exception {
+ createRadioTuner();
+
+ mRadioTuner.getDynamicProgramList(TEST_FILTER);
+
+ verify(mTunerMock).startProgramListUpdates(TEST_FILTER);
+ }
+
+ @Test
+ public void getDynamicProgramList_forTunerAdapterWithServiceDied_throwsException()
+ throws Exception {
+ createRadioTuner();
+ doThrow(new RemoteException()).when(mTunerMock).startProgramListUpdates(any());
+
+ RuntimeException thrown = assertThrows(RuntimeException.class, () -> {
+ mRadioTuner.getDynamicProgramList(TEST_FILTER);
+ });
+
+ assertWithMessage("Exception for radio HAL client service died")
+ .that(thrown).hasMessageThat().contains("Service died");
+ }
+
+ @Test
+ public void onProgramListUpdated_withNewIdsAdded_invokesMockedCallbacks() throws Exception {
+ createRadioTuner();
+ mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
+ registerListCallbacks(/* numCallbacks= */ 1);
+ addOnCompleteListeners(/* numListeners= */ 1);
+
+ mTunerCallback.onProgramListUpdated(FM_RDS_ADD_CHUNK);
+
+ verify(mListCallbackMocks[0], CALLBACK_TIMEOUT).onItemChanged(FM_IDENTIFIER);
+ verify(mListCallbackMocks[0], CALLBACK_TIMEOUT).onItemChanged(RDS_IDENTIFIER);
+ verify(mOnCompleteListenerMocks[0], CALLBACK_TIMEOUT).onComplete();
+ assertWithMessage("Program info in program list after adding FM and RDS info")
+ .that(mProgramList.toList()).containsExactly(FM_PROGRAM_INFO, RDS_PROGRAM_INFO);
+ }
+
+ @Test
+ public void onProgramListUpdated_withIdsRemoved_invokesMockedCallbacks() throws Exception {
+ ProgramList.Chunk fmRemovedChunk = new ProgramList.Chunk(/* purge= */ false,
+ /* complete= */ false, new ArraySet<>(), Set.of(FM_IDENTIFIER));
+ createRadioTuner();
+ mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
+ registerListCallbacks(/* numCallbacks= */ 1);
+ mTunerCallback.onProgramListUpdated(FM_RDS_ADD_CHUNK);
+
+ mTunerCallback.onProgramListUpdated(fmRemovedChunk);
+
+ verify(mListCallbackMocks[0], CALLBACK_TIMEOUT).onItemRemoved(FM_IDENTIFIER);
+ assertWithMessage("Program info in program list after removing FM id")
+ .that(mProgramList.toList()).containsExactly(RDS_PROGRAM_INFO);
+ assertWithMessage("Program info FM identifier")
+ .that(mProgramList.get(RDS_IDENTIFIER)).isEqualTo(RDS_PROGRAM_INFO);
+ }
+
+ @Test
+ public void onProgramListUpdated_withIncompleteChunk_notInvokesOnCompleteListener()
+ throws Exception {
+ createRadioTuner();
+ mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
+ addOnCompleteListeners(/* numListeners= */ 1);
+
+ mTunerCallback.onProgramListUpdated(FM_ADD_INCOMPLETE_CHUNK);
+
+ verify(mOnCompleteListenerMocks[0], CALLBACK_TIMEOUT.times(0)).onComplete();
+ }
+
+ @Test
+ public void onProgramListUpdated_withPurgeChunk() throws Exception {
+ ProgramList.Chunk purgeChunk = new ProgramList.Chunk(/* purge= */ true,
+ /* complete= */ true, new ArraySet<>(), new ArraySet<>());
+ createRadioTuner();
+ mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
+ registerListCallbacks(/* numCallbacks= */ 1);
+ mTunerCallback.onProgramListUpdated(FM_RDS_ADD_CHUNK);
+
+ mTunerCallback.onProgramListUpdated(purgeChunk);
+
+ verify(mListCallbackMocks[0], CALLBACK_TIMEOUT).onItemRemoved(FM_IDENTIFIER);
+ verify(mListCallbackMocks[0], CALLBACK_TIMEOUT).onItemRemoved(RDS_IDENTIFIER);
+ assertWithMessage("Program list after purge chunk applied")
+ .that(mProgramList.toList()).isEmpty();
+ }
+
+ @Test
+ public void onItemChanged_forListCallbackRegisteredWithExecutor_invokesWhenIdAdded()
+ throws Exception {
+ createRadioTuner();
+ mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
+ ProgramList.ListCallback listCallbackMock = mock(ProgramList.ListCallback.class);
+ mProgramList.registerListCallback(mExecutor, listCallbackMock);
+
+ mTunerCallback.onProgramListUpdated(FM_ADD_INCOMPLETE_CHUNK);
+
+ verify(listCallbackMock, CALLBACK_TIMEOUT).onItemChanged(FM_IDENTIFIER);
+ }
+
+ @Test
+ public void onItemRemoved_forListCallbackRegisteredWithExecutor_invokesWhenIdRemoved()
+ throws Exception {
+ ProgramList.Chunk purgeChunk = new ProgramList.Chunk(/* purge= */ true,
+ /* complete= */ true, new ArraySet<>(), new ArraySet<>());
+ createRadioTuner();
+ mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
+ ProgramList.ListCallback listCallbackMock = mock(ProgramList.ListCallback.class);
+ mProgramList.registerListCallback(mExecutor, listCallbackMock);
+ mTunerCallback.onProgramListUpdated(FM_ADD_INCOMPLETE_CHUNK);
+
+ mTunerCallback.onProgramListUpdated(purgeChunk);
+
+ verify(listCallbackMock, CALLBACK_TIMEOUT).onItemRemoved(FM_IDENTIFIER);
+ }
+
+ @Test
+ public void onProgramListUpdated_withMultipleListCallBacks() throws Exception {
+ int numCallbacks = 3;
+ createRadioTuner();
+ mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
+ registerListCallbacks(numCallbacks);
+
+ mTunerCallback.onProgramListUpdated(FM_ADD_INCOMPLETE_CHUNK);
+
+ for (int index = 0; index < numCallbacks; index++) {
+ verify(mListCallbackMocks[index], CALLBACK_TIMEOUT).onItemChanged(FM_IDENTIFIER);
+ }
+ }
+
+ @Test
+ public void unregisterListCallback_withProgramUpdated_notInvokesCallback() throws Exception {
+ createRadioTuner();
+ mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
+ registerListCallbacks(/* numCallbacks= */ 1);
+
+ mProgramList.unregisterListCallback(mListCallbackMocks[0]);
+ mTunerCallback.onProgramListUpdated(FM_ADD_INCOMPLETE_CHUNK);
+
+ verify(mListCallbackMocks[0], CALLBACK_TIMEOUT.times(0)).onItemChanged(any());
+ }
+
+ @Test
+ public void addOnCompleteListener_withExecutor() throws Exception {
+ createRadioTuner();
+ mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
+ ProgramList.OnCompleteListener onCompleteListenerMock =
+ mock(ProgramList.OnCompleteListener.class);
+
+ mProgramList.addOnCompleteListener(mExecutor, onCompleteListenerMock);
+ mTunerCallback.onProgramListUpdated(FM_RDS_ADD_CHUNK);
+
+ verify(onCompleteListenerMock, CALLBACK_TIMEOUT).onComplete();
+ }
+
+ @Test
+ public void onProgramListUpdated_withMultipleOnCompleteListeners() throws Exception {
+ int numListeners = 3;
+ createRadioTuner();
+ mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
+ addOnCompleteListeners(numListeners);
+
+ mTunerCallback.onProgramListUpdated(FM_RDS_ADD_CHUNK);
+
+ for (int index = 0; index < numListeners; index++) {
+ verify(mOnCompleteListenerMocks[index], CALLBACK_TIMEOUT).onComplete();
+ }
+ }
+
+ @Test
+ public void removeOnCompleteListener_withProgramUpdated_notInvokesListener() throws Exception {
+ createRadioTuner();
+ mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
+ addOnCompleteListeners(/* numListeners= */ 1);
+
+ mProgramList.removeOnCompleteListener(mOnCompleteListenerMocks[0]);
+ mTunerCallback.onProgramListUpdated(FM_RDS_ADD_CHUNK);
+
+ verify(mOnCompleteListenerMocks[0], CALLBACK_TIMEOUT.times(0)).onComplete();
+ }
+
+ @Test
+ public void close_forProgramList_invokesStopProgramListUpdates() throws Exception {
+ createRadioTuner();
+ ProgramList programList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
+
+ programList.close();
+
+ verify(mTunerMock, CALLBACK_TIMEOUT).stopProgramListUpdates();
+ }
+
+ private static ProgramSelector createProgramSelector(int programType,
+ ProgramSelector.Identifier identifier) {
+ return new ProgramSelector(programType, identifier, /* secondaryIds= */ null,
+ /* vendorIds= */ null);
+ }
+
+ private static RadioManager.ProgramInfo createFmProgramInfo(ProgramSelector selector) {
+ return new RadioManager.ProgramInfo(selector, selector.getPrimaryId(),
+ selector.getPrimaryId(), /* relatedContents= */ null, /* infoFlags= */ 0,
+ /* signalQuality= */ 1, new RadioMetadata.Builder().build(),
+ /* vendorInfo= */ null);
+ }
+
+ private void createRadioTuner() throws Exception {
+ RadioManager radioManager = new RadioManager(mContextMock, mRadioServiceMock);
+ RadioManager.BandConfig band = new RadioManager.FmBandConfig(
+ new RadioManager.FmBandDescriptor(RadioManager.REGION_ITU_1, RadioManager.BAND_FM,
+ /* lowerLimit= */ 87500, /* upperLimit= */ 108000, /* spacing= */ 200,
+ /* stereo= */ true, /* rds= */ false, /* ta= */ false, /* af= */ false,
+ /* es= */ false));
+
+ doAnswer(invocation -> {
+ mTunerCallback = (ITunerCallback) invocation.getArguments()[3];
+ return mTunerMock;
+ }).when(mRadioServiceMock).openTuner(anyInt(), any(), anyBoolean(), any());
+
+ mRadioTuner = radioManager.openTuner(/* moduleId= */ 0, band,
+ /* withAudio= */ true, mTunerCallbackMock, /* handler= */ null);
+ }
+
+ private void registerListCallbacks(int numCallbacks) {
+ mListCallbackMocks = new ProgramList.ListCallback[numCallbacks];
+ for (int index = 0; index < numCallbacks; index++) {
+ mListCallbackMocks[index] = mock(ProgramList.ListCallback.class);
+ mProgramList.registerListCallback(mListCallbackMocks[index]);
+ }
+ }
+
+ private void addOnCompleteListeners(int numListeners) {
+ mOnCompleteListenerMocks = new ProgramList.OnCompleteListener[numListeners];
+ for (int index = 0; index < numListeners; index++) {
+ mOnCompleteListenerMocks[index] = mock(ProgramList.OnCompleteListener.class);
+ mProgramList.addOnCompleteListener(mOnCompleteListenerMocks[index]);
+ }
+ }
+}
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/RadioManagerTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/RadioManagerTest.java
index f838a5d..365b901 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/RadioManagerTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/RadioManagerTest.java
@@ -24,6 +24,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.annotation.Nullable;
import android.content.Context;
import android.hardware.radio.Announcement;
import android.hardware.radio.IAnnouncementListener;
@@ -46,6 +47,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
import java.util.Set;
@RunWith(MockitoJUnitRunner.class)
@@ -89,7 +91,8 @@
createAmBandDescriptor();
private static final RadioManager.FmBandConfig FM_BAND_CONFIG = createFmBandConfig();
private static final RadioManager.AmBandConfig AM_BAND_CONFIG = createAmBandConfig();
- private static final RadioManager.ModuleProperties AMFM_PROPERTIES = createAmFmProperties();
+ private static final RadioManager.ModuleProperties AMFM_PROPERTIES =
+ createAmFmProperties(/* dabFrequencyTable= */ null);
/**
* Info flags with live, tuned and stereo enabled
@@ -709,12 +712,20 @@
}
@Test
- public void getDabFrequencyTable_forModuleProperties() {
+ public void getDabFrequencyTable_forModulePropertiesInitializedWithNullTable() {
assertWithMessage("Properties DAB frequency table")
.that(AMFM_PROPERTIES.getDabFrequencyTable()).isNull();
}
@Test
+ public void getDabFrequencyTable_forModulePropertiesInitializedWithEmptyTable() {
+ RadioManager.ModuleProperties properties = createAmFmProperties(new ArrayMap<>());
+
+ assertWithMessage("Properties DAB frequency table")
+ .that(properties.getDabFrequencyTable()).isNull();
+ }
+
+ @Test
public void getVendorInfo_forModuleProperties() {
assertWithMessage("Properties vendor info")
.that(AMFM_PROPERTIES.getVendorInfo()).isEmpty();
@@ -734,8 +745,37 @@
}
@Test
+ public void writeToParcel_forModulePropertiesWithNullDabFrequencyTable() {
+ Parcel parcel = Parcel.obtain();
+
+ AMFM_PROPERTIES.writeToParcel(parcel, /* flags= */ 0);
+ parcel.setDataPosition(0);
+
+ RadioManager.ModuleProperties modulePropertiesFromParcel =
+ RadioManager.ModuleProperties.CREATOR.createFromParcel(parcel);
+ assertWithMessage("Module properties created from parcel")
+ .that(modulePropertiesFromParcel).isEqualTo(AMFM_PROPERTIES);
+ }
+
+ @Test
+ public void writeToParcel_forModulePropertiesWithNonnullDabFrequencyTable() {
+ Parcel parcel = Parcel.obtain();
+ RadioManager.ModuleProperties propertiesToParcel = createAmFmProperties(
+ Map.of("5A", 174928, "12D", 229072));
+
+ propertiesToParcel.writeToParcel(parcel, /* flags= */ 0);
+ parcel.setDataPosition(0);
+
+ RadioManager.ModuleProperties modulePropertiesFromParcel =
+ RadioManager.ModuleProperties.CREATOR.createFromParcel(parcel);
+ assertWithMessage("Module properties created from parcel")
+ .that(modulePropertiesFromParcel).isEqualTo(propertiesToParcel);
+ }
+
+ @Test
public void equals_withSameProperties_returnsTrue() {
- RadioManager.ModuleProperties propertiesCompared = createAmFmProperties();
+ RadioManager.ModuleProperties propertiesCompared =
+ createAmFmProperties(/* dabFrequencyTable= */ null);
assertWithMessage("The same module properties")
.that(AMFM_PROPERTIES).isEqualTo(propertiesCompared);
@@ -747,7 +787,7 @@
PROPERTIES_ID + 1, SERVICE_NAME, CLASS_ID, IMPLEMENTOR, PRODUCT, VERSION,
SERIAL, NUM_TUNERS, NUM_AUDIO_SOURCES, IS_INITIALIZATION_REQUIRED,
IS_CAPTURE_SUPPORTED, /* bands= */ null, IS_BG_SCAN_SUPPORTED,
- SUPPORTED_PROGRAM_TYPES, SUPPORTED_IDENTIFIERS_TYPES, /* dabFrequencyTable= */ null,
+ SUPPORTED_PROGRAM_TYPES, SUPPORTED_IDENTIFIERS_TYPES, Map.of("5A", 174928),
/* vendorInfo= */ null);
assertWithMessage("Module properties of different id")
@@ -756,7 +796,8 @@
@Test
public void hashCode_withSameModuleProperties_equals() {
- RadioManager.ModuleProperties propertiesCompared = createAmFmProperties();
+ RadioManager.ModuleProperties propertiesCompared =
+ createAmFmProperties(/* dabFrequencyTable= */ null);
assertWithMessage("Hash code of the same module properties")
.that(propertiesCompared.hashCode()).isEqualTo(AMFM_PROPERTIES.hashCode());
@@ -989,13 +1030,14 @@
verify(mCloseHandleMock).close();
}
- private static RadioManager.ModuleProperties createAmFmProperties() {
+ private static RadioManager.ModuleProperties createAmFmProperties(
+ @Nullable Map<String, Integer> dabFrequencyTable) {
return new RadioManager.ModuleProperties(PROPERTIES_ID, SERVICE_NAME, CLASS_ID,
IMPLEMENTOR, PRODUCT, VERSION, SERIAL, NUM_TUNERS, NUM_AUDIO_SOURCES,
IS_INITIALIZATION_REQUIRED, IS_CAPTURE_SUPPORTED,
new RadioManager.BandDescriptor[]{AM_BAND_DESCRIPTOR, FM_BAND_DESCRIPTOR},
IS_BG_SCAN_SUPPORTED, SUPPORTED_PROGRAM_TYPES, SUPPORTED_IDENTIFIERS_TYPES,
- /* dabFrequencyTable= */ null, /* vendorInfo= */ null);
+ dabFrequencyTable, /* vendorInfo= */ null);
}
private static RadioManager.FmBandDescriptor createFmBandDescriptor() {
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/TunerAdapterTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/TunerAdapterTest.java
index fe3ab62..bdba6a1 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/TunerAdapterTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/TunerAdapterTest.java
@@ -280,6 +280,16 @@
}
@Test
+ public void startBackgroundScan_forTunerAdapter() throws Exception {
+ when(mTunerMock.startBackgroundScan()).thenReturn(false);
+
+ boolean scanStatus = mRadioTuner.startBackgroundScan();
+
+ verify(mTunerMock).startBackgroundScan();
+ assertWithMessage("Status for starting background scan").that(scanStatus).isFalse();
+ }
+
+ @Test
public void isAnalogForced_forTunerAdapter() throws Exception {
when(mTunerMock.isConfigFlagSet(RadioManager.CONFIG_FORCE_ANALOG)).thenReturn(true);
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/ExtendedRadioMockitoTestCase.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/ExtendedRadioMockitoTestCase.java
new file mode 100644
index 0000000..c6021ec
--- /dev/null
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/ExtendedRadioMockitoTestCase.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.broadcastradio;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+import android.util.Log;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
+
+import org.junit.After;
+import org.junit.Before;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+/**
+ * Base class to make it easier to write tests that uses {@code ExtendedMockito} for radio.
+ *
+ */
+public abstract class ExtendedRadioMockitoTestCase {
+
+ private static final String TAG = "RadioMockitoTestCase";
+
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private MockitoSession mSession;
+
+ @Before
+ public void startSession() {
+ StaticMockitoSessionBuilder builder = mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.LENIENT);
+ initializeSession(builder);
+ mSession = builder.startMocking();
+ }
+
+ /**
+ * Initializes the mockito session for radio test.
+ *
+ * <p>Typically used to define which classes should have static methods mocked or spied.
+ */
+ protected void initializeSession(StaticMockitoSessionBuilder builder) {
+ if (DEBUG) {
+ Log.d(TAG, "initializeSession()");
+ }
+ }
+
+ @After
+ public final void finishSession() {
+ if (mSession == null) {
+ Log.w(TAG, "finishSession(): no session");
+ return;
+ }
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "finishSession()");
+ }
+ } finally {
+ // mSession.finishMocking() must ALWAYS be called (hence the over-protective try/finally
+ // statements), otherwise it would cause failures on future tests as mockito
+ // cannot start a session when a previous one is not finished
+ mSession.finishMocking();
+ }
+ }
+}
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/IRadioServiceAidlImplTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/IRadioServiceAidlImplTest.java
index 7f4ea11..a2df426 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/IRadioServiceAidlImplTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/IRadioServiceAidlImplTest.java
@@ -16,11 +16,13 @@
package com.android.server.broadcastradio;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -31,30 +33,36 @@
import android.hardware.radio.ITuner;
import android.hardware.radio.ITunerCallback;
import android.hardware.radio.RadioManager;
+import android.os.IBinder;
+import android.os.ServiceManager;
+import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
import com.android.server.broadcastradio.aidl.BroadcastRadioServiceImpl;
import org.junit.Before;
import org.junit.Test;
-import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
-import java.util.Arrays;
+import java.util.List;
/**
* Tests for {@link android.hardware.radio.IRadioService} with AIDL HAL implementation
*/
-@RunWith(MockitoJUnitRunner.class)
-public final class IRadioServiceAidlImplTest {
+public final class IRadioServiceAidlImplTest extends ExtendedRadioMockitoTestCase {
private static final int[] ENABLE_TYPES = new int[]{Announcement.TYPE_TRAFFIC};
+ private static final String AM_FM_SERVICE_NAME =
+ "android.hardware.broadcastradio.IBroadcastRadio/amfm";
+ private static final String DAB_SERVICE_NAME =
+ "android.hardware.broadcastradio.IBroadcastRadio/dab";
private IRadioServiceAidlImpl mAidlImpl;
@Mock
private BroadcastRadioService mServiceMock;
@Mock
+ private IBinder mServiceBinderMock;
+ @Mock
private BroadcastRadioServiceImpl mHalMock;
@Mock
private RadioManager.ModuleProperties mModuleMock;
@@ -73,7 +81,7 @@
public void setUp() throws Exception {
doNothing().when(mServiceMock).enforcePolicyAccess();
- when(mHalMock.listModules()).thenReturn(Arrays.asList(mModuleMock));
+ when(mHalMock.listModules()).thenReturn(List.of(mModuleMock));
when(mHalMock.openSession(anyInt(), any(), anyBoolean(), any()))
.thenReturn(mTunerMock);
when(mHalMock.addAnnouncementListener(any(), any())).thenReturn(mICloseHandle);
@@ -81,11 +89,26 @@
mAidlImpl = new IRadioServiceAidlImpl(mServiceMock, mHalMock);
}
+ @Override
+ protected void initializeSession(StaticMockitoSessionBuilder builder) {
+ builder.spyStatic(ServiceManager.class);
+ }
+
+ @Test
+ public void getServicesNames_forAidlImpl() {
+ doReturn(null).when(() -> ServiceManager.waitForDeclaredService(
+ AM_FM_SERVICE_NAME));
+ doReturn(mServiceBinderMock).when(() -> ServiceManager.waitForDeclaredService(
+ DAB_SERVICE_NAME));
+
+ assertWithMessage("Names of services available")
+ .that(IRadioServiceAidlImpl.getServicesNames()).containsExactly(DAB_SERVICE_NAME);
+ }
+
@Test
public void loadModules_forAidlImpl() {
assertWithMessage("Modules loaded in AIDL HAL")
- .that(mAidlImpl.listModules())
- .containsExactly(mModuleMock);
+ .that(mAidlImpl.listModules()).containsExactly(mModuleMock);
}
@Test
@@ -105,5 +128,4 @@
assertWithMessage("Close handle of announcement listener for HAL 2")
.that(closeHandle).isEqualTo(mICloseHandle);
}
-
}
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/IRadioServiceHidlImplTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/IRadioServiceHidlImplTest.java
index f28e27d..5ab9435 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/IRadioServiceHidlImplTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/IRadioServiceHidlImplTest.java
@@ -18,9 +18,9 @@
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.verify;
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java
index 2cb058b..a421218 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java
@@ -31,6 +31,16 @@
throw new UnsupportedOperationException("AidlTestUtils class is noninstantiable");
}
+ static RadioManager.ModuleProperties makeDefaultModuleProperties() {
+ return new RadioManager.ModuleProperties(
+ /* id= */ 0, /* serviceName= */ "", /* classId= */ 0, /* implementor= */ "",
+ /* product= */ "", /* version= */ "", /* serial= */ "", /* numTuners= */ 0,
+ /* numAudioSources= */ 0, /* isInitializationRequired= */ false,
+ /* isCaptureSupported= */ false, /* bands= */ null,
+ /* isBgScanSupported= */ false, new int[] {}, new int[] {},
+ /* dabFrequencyTable= */ null, /* vendorInfo= */ null);
+ }
+
static RadioManager.ProgramInfo makeProgramInfo(ProgramSelector selector, int signalQuality) {
return new RadioManager.ProgramInfo(selector,
selector.getPrimaryId(), selector.getPrimaryId(), /* relatedContents= */ null,
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AnnouncementAggregatorTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AnnouncementAggregatorTest.java
index 699212a..9a1af19 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AnnouncementAggregatorTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AnnouncementAggregatorTest.java
@@ -19,7 +19,6 @@
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@@ -57,11 +56,8 @@
private IAnnouncementListener mListenerMock;
@Mock
private IBinder mBinderMock;
- // Array of mocked radio modules
private RadioModule[] mRadioModuleMocks;
- // Array of mocked close handles
private ICloseHandle[] mCloseHandleMocks;
- // Array of mocked announcements
private Announcement[] mAnnouncementMocks;
@Before
@@ -72,7 +68,7 @@
mAnnouncementAggregator = new AnnouncementAggregator(mListenerMock, mLock);
- verify(mBinderMock).linkToDeath(deathRecipientCaptor.capture(), anyInt());
+ verify(mBinderMock).linkToDeath(deathRecipientCaptor.capture(), eq(0));
mDeathRecipient = deathRecipientCaptor.getValue();
}
@@ -105,7 +101,8 @@
moduleWatcherCaptor.getValue().onListUpdated(Arrays.asList(mAnnouncementMocks[index]));
verify(mListenerMock, times(index + 1)).onListUpdated(announcementsCaptor.capture());
- assertWithMessage("Number of announcements %s", announcementsCaptor.getValue())
+ assertWithMessage("Number of announcements %s after %s announcements were updated",
+ announcementsCaptor.getValue(), index + 1)
.that(announcementsCaptor.getValue().size()).isEqualTo(index + 1);
}
}
@@ -117,7 +114,7 @@
mAnnouncementAggregator.close();
verify(mCloseHandleMocks[0]).close();
- verify(mBinderMock).unlinkToDeath(eq(mDeathRecipient), anyInt());
+ verify(mBinderMock).unlinkToDeath(mDeathRecipient, 0);
}
@Test
@@ -140,7 +137,7 @@
mAnnouncementAggregator.close();
verify(mCloseHandleMocks[0]).close();
- verify(mBinderMock).unlinkToDeath(eq(mDeathRecipient), anyInt());
+ verify(mBinderMock).unlinkToDeath(mDeathRecipient, 0);
}
@Test
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java
new file mode 100644
index 0000000..635d1e7
--- /dev/null
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.broadcastradio.aidl;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.broadcastradio.IBroadcastRadio;
+import android.hardware.radio.ITuner;
+import android.hardware.radio.ITunerCallback;
+import android.hardware.radio.RadioManager;
+import android.hardware.radio.RadioTuner;
+import android.os.IBinder;
+import android.os.IServiceCallback;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
+import com.android.server.broadcastradio.ExtendedRadioMockitoTestCase;
+
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.stubbing.Answer;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+public final class BroadcastRadioServiceImplTest extends ExtendedRadioMockitoTestCase {
+
+ private static final int FM_RADIO_MODULE_ID = 0;
+ private static final int DAB_RADIO_MODULE_ID = 1;
+ private static final ArrayList<String> SERVICE_LIST =
+ new ArrayList<>(Arrays.asList("FmService", "DabService"));
+
+ private BroadcastRadioServiceImpl mBroadcastRadioService;
+ private IBinder.DeathRecipient mFmDeathRecipient;
+
+ @Mock
+ private RadioManager.ModuleProperties mFmModuleMock;
+ @Mock
+ private RadioManager.ModuleProperties mDabModuleMock;
+ @Mock
+ private RadioModule mFmRadioModuleMock;
+ @Mock
+ private RadioModule mDabRadioModuleMock;
+ @Mock
+ private IBroadcastRadio mFmHalServiceMock;
+ @Mock
+ private IBroadcastRadio mDabHalServiceMock;
+ @Mock
+ private IBinder mFmBinderMock;
+ @Mock
+ private IBinder mDabBinderMock;
+ @Mock
+ private TunerSession mFmTunerSessionMock;
+ @Mock
+ private ITunerCallback mTunerCallbackMock;
+
+ @Override
+ protected void initializeSession(StaticMockitoSessionBuilder builder) {
+ builder.spyStatic(ServiceManager.class)
+ .spyStatic(RadioModule.class);
+ }
+
+ @Test
+ public void listModules_withMultipleServiceNames() throws Exception {
+ createBroadcastRadioService();
+
+ assertWithMessage("Radio modules in AIDL broadcast radio HAL client")
+ .that(mBroadcastRadioService.listModules())
+ .containsExactly(mFmModuleMock, mDabModuleMock);
+ }
+
+ @Test
+ public void hasModules_withIdFoundInModules() throws Exception {
+ createBroadcastRadioService();
+
+ assertWithMessage("DAB radio module in AIDL broadcast radio HAL client")
+ .that(mBroadcastRadioService.hasModule(DAB_RADIO_MODULE_ID)).isTrue();
+ }
+
+ @Test
+ public void hasModules_withIdNotFoundInModules() throws Exception {
+ createBroadcastRadioService();
+
+ assertWithMessage("Radio module of id not found in AIDL broadcast radio HAL client")
+ .that(mBroadcastRadioService.hasModule(DAB_RADIO_MODULE_ID + 1)).isFalse();
+ }
+
+ @Test
+ public void hasAnyModules_withModulesExist() throws Exception {
+ createBroadcastRadioService();
+
+ assertWithMessage("Any radio module in AIDL broadcast radio HAL client")
+ .that(mBroadcastRadioService.hasAnyModules()).isTrue();
+ }
+
+ @Test
+ public void openSession_withIdFound() throws Exception {
+ createBroadcastRadioService();
+
+ ITuner session = mBroadcastRadioService.openSession(FM_RADIO_MODULE_ID,
+ /* legacyConfig= */ null, /* withAudio= */ true, mTunerCallbackMock);
+
+ assertWithMessage("Session opened in FM radio module")
+ .that(session).isEqualTo(mFmTunerSessionMock);
+ }
+
+ @Test
+ public void openSession_withIdNotFound() throws Exception {
+ createBroadcastRadioService();
+
+ ITuner session = mBroadcastRadioService.openSession(DAB_RADIO_MODULE_ID + 1,
+ /* legacyConfig= */ null, /* withAudio= */ true, mTunerCallbackMock);
+
+ assertWithMessage("Session opened with id not found").that(session).isNull();
+ }
+
+ @Test
+ public void binderDied_forDeathRecipient() throws Exception {
+ createBroadcastRadioService();
+
+ mFmDeathRecipient.binderDied();
+
+ verify(mFmRadioModuleMock).closeSessions(eq(RadioTuner.ERROR_HARDWARE_FAILURE));
+ assertWithMessage("FM radio module after FM broadcast radio HAL service died")
+ .that(mBroadcastRadioService.hasModule(FM_RADIO_MODULE_ID)).isFalse();
+ }
+
+ private void createBroadcastRadioService() throws RemoteException {
+ mockServiceManager();
+ mBroadcastRadioService = new BroadcastRadioServiceImpl(SERVICE_LIST);
+ }
+
+ private void mockServiceManager() throws RemoteException {
+ doAnswer((Answer<Void>) invocation -> {
+ String serviceName = (String) invocation.getArguments()[0];
+ IServiceCallback serviceCallback = (IServiceCallback) invocation.getArguments()[1];
+ IBinder mockBinder = serviceName.equals("FmService") ? mFmBinderMock : mDabBinderMock;
+ serviceCallback.onRegistration(serviceName, mockBinder);
+ return null;
+ }).when(() -> ServiceManager.registerForNotifications(anyString(),
+ any(IServiceCallback.class)));
+
+ doReturn(mFmRadioModuleMock).when(() -> RadioModule.tryLoadingModule(
+ eq(FM_RADIO_MODULE_ID), anyString(), any(IBinder.class), any(Object.class)));
+ doReturn(mDabRadioModuleMock).when(() -> RadioModule.tryLoadingModule(
+ eq(DAB_RADIO_MODULE_ID), anyString(), any(IBinder.class), any(Object.class)));
+
+ when(mFmRadioModuleMock.getProperties()).thenReturn(mFmModuleMock);
+ when(mDabRadioModuleMock.getProperties()).thenReturn(mDabModuleMock);
+
+ when(mFmRadioModuleMock.getService()).thenReturn(mFmHalServiceMock);
+ when(mDabRadioModuleMock.getService()).thenReturn(mDabHalServiceMock);
+
+ when(mFmHalServiceMock.asBinder()).thenReturn(mFmBinderMock);
+ when(mDabHalServiceMock.asBinder()).thenReturn(mDabBinderMock);
+
+ doAnswer(invocation -> {
+ mFmDeathRecipient = (IBinder.DeathRecipient) invocation.getArguments()[0];
+ return null;
+ }).when(mFmBinderMock).linkToDeath(any(), anyInt());
+
+ when(mFmRadioModuleMock.openSession(eq(mTunerCallbackMock)))
+ .thenReturn(mFmTunerSessionMock);
+ }
+}
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java
index cd1cd7e..7a8475f 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java
@@ -19,9 +19,9 @@
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -47,6 +47,8 @@
public final class RadioModuleTest {
private static final int TEST_ENABLED_TYPE = Announcement.TYPE_EVENT;
+ private static final RadioManager.ModuleProperties TEST_MODULE_PROPERTIES =
+ AidlTestUtils.makeDefaultModuleProperties();
// Mocks
@Mock
@@ -63,14 +65,7 @@
@Before
public void setup() throws RemoteException {
- mRadioModule = new RadioModule(mBroadcastRadioMock, new RadioManager.ModuleProperties(
- /* id= */ 0, /* serviceName= */ "", /* classId= */ 0, /* implementor= */ "",
- /* product= */ "", /* version= */ "", /* serial= */ "", /* numTuners= */ 0,
- /* numAudioSources= */ 0, /* isInitializationRequired= */ false,
- /* isCaptureSupported= */ false, /* bands= */ null, /* isBgScanSupported= */ false,
- /* supportedProgramTypes= */ new int[]{},
- /* supportedIdentifierTypes */ new int[]{},
- /* dabFrequencyTable= */ null, /* vendorInfo= */ null), mLock);
+ mRadioModule = new RadioModule(mBroadcastRadioMock, TEST_MODULE_PROPERTIES, mLock);
// TODO(b/241118988): test non-null image for getImage method
when(mBroadcastRadioMock.getImage(anyInt())).thenReturn(null);
@@ -88,6 +83,12 @@
}
@Test
+ public void getProperties() {
+ assertWithMessage("Module properties of radio module")
+ .that(mRadioModule.getProperties()).isEqualTo(TEST_MODULE_PROPERTIES);
+ }
+
+ @Test
public void setInternalHalCallback_callbackSetInHal() throws Exception {
mRadioModule.setInternalHalCallback();
@@ -100,7 +101,7 @@
Bitmap imageTest = mRadioModule.getImage(imageId);
- assertWithMessage("Image got from radio module").that(imageTest).isNull();
+ assertWithMessage("Image from radio module").that(imageTest).isNull();
}
@Test
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
index 06d7cdd..3bf993c 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
@@ -19,10 +19,10 @@
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.timeout;
@@ -93,13 +93,8 @@
@Before
public void setup() throws Exception {
- mRadioModule = new RadioModule(mBroadcastRadioMock, new RadioManager.ModuleProperties(
- /* id= */ 0, /* serviceName= */ "", /* classId= */ 0, /* implementor= */ "",
- /* product= */ "", /* version= */ "", /* serial= */ "", /* numTuners= */ 0,
- /* numAudioSources= */ 0, /* isInitializationRequired= */ false,
- /* isCaptureSupported= */ false, /* bands= */ null, /* isBgScanSupported= */ false,
- new int[] {}, new int[] {},
- /* dabFrequencyTable= */ null, /* vendorInfo= */ null), mLock);
+ mRadioModule = new RadioModule(mBroadcastRadioMock,
+ AidlTestUtils.makeDefaultModuleProperties(), mLock);
doAnswer(invocation -> {
mHalTunerCallback = (ITunerCallback) invocation.getArguments()[0];
@@ -210,7 +205,7 @@
mTunerSessions[0].setMuted(/* mute= */ false);
- assertWithMessage("Session mute state after setting muted %s", false)
+ assertWithMessage("Session mute state after setting unmuted")
.that(mTunerSessions[0].isMuted()).isFalse();
}
@@ -220,7 +215,7 @@
mTunerSessions[0].setMuted(/* mute= */ true);
- assertWithMessage("Session mute state after setting muted %s", true)
+ assertWithMessage("Session mute state after setting muted")
.that(mTunerSessions[0].isMuted()).isTrue();
}
@@ -424,7 +419,7 @@
mTunerSessions[0].getImage(imageId);
});
- assertWithMessage("Exception for getting image with invalid ID")
+ assertWithMessage("Get image exception")
.that(thrown).hasMessageThat().contains("Image ID is missing");
}
@@ -467,7 +462,7 @@
boolean isSupported = mTunerSessions[0].isConfigFlagSupported(flag);
verify(mBroadcastRadioMock).isConfigFlagSet(flag);
- assertWithMessage("Config flag %s is supported", flag).that(isSupported).isFalse();
+ assertWithMessage("Config flag %s is supported", flag).that(isSupported).isFalse();
}
@Test
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/AnnouncementAggregatorHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/AnnouncementAggregatorHidlTest.java
new file mode 100644
index 0000000..b68e65f
--- /dev/null
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/AnnouncementAggregatorHidlTest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.broadcastradio.hal2;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.radio.Announcement;
+import android.hardware.radio.IAnnouncementListener;
+import android.hardware.radio.ICloseHandle;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tests for HIDL 2.0 HAL AnnouncementAggregator.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public final class AnnouncementAggregatorHidlTest {
+
+ private static final int[] TEST_ENABLED_TYPES = new int[]{Announcement.TYPE_TRAFFIC};
+
+ private final Object mLock = new Object();
+ private AnnouncementAggregator mAnnouncementAggregator;
+ private IBinder.DeathRecipient mDeathRecipient;
+
+ @Mock
+ private IAnnouncementListener mListenerMock;
+ @Mock
+ private IBinder mBinderMock;
+ private RadioModule[] mRadioModuleMocks;
+ private ICloseHandle[] mCloseHandleMocks;
+ private Announcement[] mAnnouncementMocks;
+
+ @Before
+ public void setUp() throws Exception {
+ ArgumentCaptor<IBinder.DeathRecipient> deathRecipientCaptor =
+ ArgumentCaptor.forClass(IBinder.DeathRecipient.class);
+ when(mListenerMock.asBinder()).thenReturn(mBinderMock);
+
+ mAnnouncementAggregator = new AnnouncementAggregator(mListenerMock, mLock);
+
+ verify(mBinderMock).linkToDeath(deathRecipientCaptor.capture(), eq(0));
+ mDeathRecipient = deathRecipientCaptor.getValue();
+ }
+
+ @Test
+ public void onListUpdated_withOneModuleWatcher() throws Exception {
+ ArgumentCaptor<IAnnouncementListener> moduleWatcherCaptor =
+ ArgumentCaptor.forClass(IAnnouncementListener.class);
+ watchModules(/* moduleNumber= */ 1);
+
+ verify(mRadioModuleMocks[0]).addAnnouncementListener(any(), moduleWatcherCaptor.capture());
+
+ moduleWatcherCaptor.getValue().onListUpdated(Arrays.asList(mAnnouncementMocks[0]));
+
+ verify(mListenerMock).onListUpdated(any());
+ }
+
+ @Test
+ public void onListUpdated_withMultipleModuleWatchers() throws Exception {
+ int moduleNumber = 3;
+ watchModules(moduleNumber);
+
+ for (int index = 0; index < moduleNumber; index++) {
+ ArgumentCaptor<IAnnouncementListener> moduleWatcherCaptor =
+ ArgumentCaptor.forClass(IAnnouncementListener.class);
+ ArgumentCaptor<List<Announcement>> announcementsCaptor =
+ ArgumentCaptor.forClass(List.class);
+ verify(mRadioModuleMocks[index])
+ .addAnnouncementListener(any(), moduleWatcherCaptor.capture());
+
+ moduleWatcherCaptor.getValue().onListUpdated(Arrays.asList(mAnnouncementMocks[index]));
+
+ verify(mListenerMock, times(index + 1)).onListUpdated(announcementsCaptor.capture());
+ assertWithMessage("Number of announcements %s after %s announcements were updated",
+ announcementsCaptor.getValue(), index + 1)
+ .that(announcementsCaptor.getValue().size()).isEqualTo(index + 1);
+ }
+ }
+
+ @Test
+ public void close_withOneModuleWatcher_invokesCloseHandle() throws Exception {
+ watchModules(/* moduleNumber= */ 1);
+
+ mAnnouncementAggregator.close();
+
+ verify(mCloseHandleMocks[0]).close();
+ verify(mBinderMock).unlinkToDeath(mDeathRecipient, 0);
+ }
+
+ @Test
+ public void close_withMultipleModuleWatcher_invokesCloseHandles() throws Exception {
+ int moduleNumber = 3;
+ watchModules(moduleNumber);
+
+ mAnnouncementAggregator.close();
+
+ for (int index = 0; index < moduleNumber; index++) {
+ verify(mCloseHandleMocks[index]).close();
+ }
+ }
+
+ @Test
+ public void close_twice_invokesCloseHandleOnce() throws Exception {
+ watchModules(/* moduleNumber= */ 1);
+
+ mAnnouncementAggregator.close();
+ mAnnouncementAggregator.close();
+
+ verify(mCloseHandleMocks[0]).close();
+ verify(mBinderMock).unlinkToDeath(mDeathRecipient, 0);
+ }
+
+ @Test
+ public void binderDied_forDeathRecipient_invokesCloseHandle() throws Exception {
+ watchModules(/* moduleNumber= */ 1);
+
+ mDeathRecipient.binderDied();
+
+ verify(mCloseHandleMocks[0]).close();
+
+ }
+
+ private void watchModules(int moduleNumber) throws RemoteException {
+ mRadioModuleMocks = new RadioModule[moduleNumber];
+ mCloseHandleMocks = new ICloseHandle[moduleNumber];
+ mAnnouncementMocks = new Announcement[moduleNumber];
+
+ for (int index = 0; index < moduleNumber; index++) {
+ mRadioModuleMocks[index] = mock(RadioModule.class);
+ mCloseHandleMocks[index] = mock(ICloseHandle.class);
+ mAnnouncementMocks[index] = mock(Announcement.class);
+
+ when(mRadioModuleMocks[index].addAnnouncementListener(any(), any()))
+ .thenReturn(mCloseHandleMocks[index]);
+ mAnnouncementAggregator.watchModule(mRadioModuleMocks[index], TEST_ENABLED_TYPES);
+ }
+ }
+}
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/BroadcastRadioServiceHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/BroadcastRadioServiceHidlTest.java
new file mode 100644
index 0000000..4d0b753
--- /dev/null
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/BroadcastRadioServiceHidlTest.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.broadcastradio.hal2;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.broadcastradio.V2_0.IBroadcastRadio;
+import android.hardware.radio.Announcement;
+import android.hardware.radio.IAnnouncementListener;
+import android.hardware.radio.ICloseHandle;
+import android.hardware.radio.ITuner;
+import android.hardware.radio.ITunerCallback;
+import android.hardware.radio.RadioManager;
+import android.hardware.radio.RadioTuner;
+import android.hidl.manager.V1_0.IServiceManager;
+import android.hidl.manager.V1_0.IServiceNotification;
+import android.os.IBinder;
+import android.os.IHwBinder.DeathRecipient;
+import android.os.RemoteException;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
+import com.android.server.broadcastradio.ExtendedRadioMockitoTestCase;
+
+import org.junit.Test;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+public final class BroadcastRadioServiceHidlTest extends ExtendedRadioMockitoTestCase {
+
+ private static final int FM_RADIO_MODULE_ID = 0;
+ private static final int DAB_RADIO_MODULE_ID = 1;
+ private static final ArrayList<String> SERVICE_LIST =
+ new ArrayList<>(Arrays.asList("FmService", "DabService"));
+ private static final int[] TEST_ENABLED_TYPES = new int[]{Announcement.TYPE_TRAFFIC};
+
+ private final Object mLock = new Object();
+
+ private BroadcastRadioService mBroadcastRadioService;
+ private DeathRecipient mFmDeathRecipient;
+
+ @Mock
+ private IServiceManager mServiceManagerMock;
+ @Mock
+ private RadioManager.ModuleProperties mFmModuleMock;
+ @Mock
+ private RadioManager.ModuleProperties mDabModuleMock;
+ @Mock
+ private RadioModule mFmRadioModuleMock;
+ @Mock
+ private RadioModule mDabRadioModuleMock;
+ @Mock
+ private IBroadcastRadio mFmHalServiceMock;
+ @Mock
+ private IBroadcastRadio mDabHalServiceMock;
+ @Mock
+ private TunerSession mFmTunerSessionMock;
+ @Mock
+ private ITunerCallback mTunerCallbackMock;
+ @Mock
+ private ICloseHandle mFmCloseHandleMock;
+ @Mock
+ private ICloseHandle mDabCloseHandleMock;
+ @Mock
+ private IAnnouncementListener mAnnouncementListenerMock;
+ @Mock
+ private IBinder mBinderMock;
+
+ @Override
+ protected void initializeSession(StaticMockitoSessionBuilder builder) {
+ builder.spyStatic(RadioModule.class);
+ }
+
+ @Test
+ public void listModules_withMultipleServiceNames() throws Exception {
+ createBroadcastRadioService();
+
+ assertWithMessage("Radio modules in HIDL broadcast radio HAL client")
+ .that(mBroadcastRadioService.listModules())
+ .containsExactly(mFmModuleMock, mDabModuleMock);
+ }
+
+ @Test
+ public void hasModules_withIdFoundInModules() throws Exception {
+ createBroadcastRadioService();
+
+ assertWithMessage("DAB radio module in HIDL broadcast radio HAL client")
+ .that(mBroadcastRadioService.hasModule(FM_RADIO_MODULE_ID)).isTrue();
+ }
+
+ @Test
+ public void hasModules_withIdNotFoundInModules() throws Exception {
+ createBroadcastRadioService();
+
+ assertWithMessage("Radio module of id not found in HIDL broadcast radio HAL client")
+ .that(mBroadcastRadioService.hasModule(DAB_RADIO_MODULE_ID + 1)).isFalse();
+ }
+
+ @Test
+ public void hasAnyModules_withModulesExist() throws Exception {
+ createBroadcastRadioService();
+
+ assertWithMessage("Any radio module in HIDL broadcast radio HAL client")
+ .that(mBroadcastRadioService.hasAnyModules()).isTrue();
+ }
+
+ @Test
+ public void openSession_withIdFound() throws Exception {
+ createBroadcastRadioService();
+
+ ITuner session = mBroadcastRadioService.openSession(FM_RADIO_MODULE_ID,
+ /* legacyConfig= */ null, /* withAudio= */ true, mTunerCallbackMock);
+
+ assertWithMessage("Session opened in FM radio module")
+ .that(session).isEqualTo(mFmTunerSessionMock);
+ }
+
+ @Test
+ public void openSession_withIdNotFound() throws Exception {
+ createBroadcastRadioService();
+ int moduleIdInvalid = DAB_RADIO_MODULE_ID + 1;
+
+ IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> {
+ mBroadcastRadioService.openSession(moduleIdInvalid, /* legacyConfig= */ null,
+ /* withAudio= */ true, mTunerCallbackMock);
+ });
+
+ assertWithMessage("Exception for opening session with module id %s", moduleIdInvalid)
+ .that(thrown).hasMessageThat().contains("Invalid module ID");
+ }
+
+ @Test
+ public void addAnnouncementListener_addsOnAllRadioModules() throws Exception {
+ createBroadcastRadioService();
+ when(mAnnouncementListenerMock.asBinder()).thenReturn(mBinderMock);
+ when(mFmRadioModuleMock.addAnnouncementListener(any(), any()))
+ .thenReturn(mFmCloseHandleMock);
+ when(mDabRadioModuleMock.addAnnouncementListener(any(), any()))
+ .thenReturn(mDabCloseHandleMock);
+
+ mBroadcastRadioService.addAnnouncementListener(TEST_ENABLED_TYPES,
+ mAnnouncementListenerMock);
+
+ verify(mFmRadioModuleMock).addAnnouncementListener(any(), any());
+ verify(mDabRadioModuleMock).addAnnouncementListener(any(), any());
+ }
+
+ @Test
+ public void binderDied_forDeathRecipient() throws Exception {
+ createBroadcastRadioService();
+
+ mFmDeathRecipient.serviceDied(FM_RADIO_MODULE_ID);
+
+ verify(mFmRadioModuleMock).closeSessions(eq(RadioTuner.ERROR_HARDWARE_FAILURE));
+ assertWithMessage("FM radio module after FM broadcast radio HAL service died")
+ .that(mBroadcastRadioService.hasModule(FM_RADIO_MODULE_ID)).isFalse();
+ }
+
+ private void createBroadcastRadioService() throws RemoteException {
+ mockServiceManager();
+ mBroadcastRadioService = new BroadcastRadioService(/* nextModuleId= */ FM_RADIO_MODULE_ID,
+ mLock, mServiceManagerMock);
+ }
+
+ private void mockServiceManager() throws RemoteException {
+ doAnswer(invocation -> {
+ mFmDeathRecipient = (DeathRecipient) invocation.getArguments()[0];
+ return null;
+ }).when(mFmHalServiceMock).linkToDeath(any(), eq((long) FM_RADIO_MODULE_ID));
+
+ when(mServiceManagerMock.registerForNotifications(anyString(), anyString(),
+ any(IServiceNotification.class))).thenAnswer(invocation -> {
+ IServiceNotification serviceCallback =
+ (IServiceNotification) invocation.getArguments()[2];
+ for (int index = 0; index < SERVICE_LIST.size(); index++) {
+ serviceCallback.onRegistration(IBroadcastRadio.kInterfaceName,
+ SERVICE_LIST.get(index), /* b= */ false);
+ }
+ return true;
+ }).thenReturn(true);
+
+ doReturn(mFmRadioModuleMock).when(() -> RadioModule.tryLoadingModule(
+ eq(FM_RADIO_MODULE_ID), anyString(), any(Object.class)));
+ doReturn(mDabRadioModuleMock).when(() -> RadioModule.tryLoadingModule(
+ eq(DAB_RADIO_MODULE_ID), anyString(), any(Object.class)));
+
+ when(mFmRadioModuleMock.getProperties()).thenReturn(mFmModuleMock);
+ when(mDabRadioModuleMock.getProperties()).thenReturn(mDabModuleMock);
+
+ when(mFmRadioModuleMock.getService()).thenReturn(mFmHalServiceMock);
+ when(mDabRadioModuleMock.getService()).thenReturn(mDabHalServiceMock);
+
+ when(mFmRadioModuleMock.openSession(mTunerCallbackMock))
+ .thenReturn(mFmTunerSessionMock);
+ }
+}
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java
new file mode 100644
index 0000000..3de4f5d
--- /dev/null
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.broadcastradio.hal2;
+
+import android.hardware.broadcastradio.V2_0.AmFmBandRange;
+import android.hardware.broadcastradio.V2_0.AmFmRegionConfig;
+import android.hardware.broadcastradio.V2_0.DabTableEntry;
+import android.hardware.broadcastradio.V2_0.IdentifierType;
+import android.hardware.broadcastradio.V2_0.Properties;
+import android.hardware.broadcastradio.V2_0.VendorKeyValue;
+import android.hardware.radio.Announcement;
+import android.hardware.radio.ProgramSelector;
+import android.hardware.radio.RadioManager;
+
+import com.google.common.truth.Expect;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+public final class ConvertTest {
+
+ private static final int FM_LOWER_LIMIT = 87500;
+ private static final int FM_UPPER_LIMIT = 108000;
+ private static final int FM_SPACING = 200;
+ private static final int AM_LOWER_LIMIT = 540;
+ private static final int AM_UPPER_LIMIT = 1700;
+ private static final int AM_SPACING = 10;
+ private static final String DAB_ENTRY_LABEL_1 = "5A";
+ private static final int DAB_ENTRY_FREQUENCY_1 = 174928;
+ private static final String DAB_ENTRY_LABEL_2 = "12D";
+ private static final int DAB_ENTRY_FREQUENCY_2 = 229072;
+ private static final String VENDOR_INFO_KEY_1 = "vendorKey1";
+ private static final String VENDOR_INFO_VALUE_1 = "vendorValue1";
+ private static final String VENDOR_INFO_KEY_2 = "vendorKey2";
+ private static final String VENDOR_INFO_VALUE_2 = "vendorValue2";
+ private static final String TEST_SERVICE_NAME = "serviceMock";
+ private static final int TEST_ID = 1;
+ private static final String TEST_MAKER = "makerMock";
+ private static final String TEST_PRODUCT = "productMock";
+ private static final String TEST_VERSION = "versionMock";
+ private static final String TEST_SERIAL = "serialMock";
+
+ private static final int TEST_ENABLED_TYPE = Announcement.TYPE_EMERGENCY;
+ private static final int TEST_ANNOUNCEMENT_FREQUENCY = FM_LOWER_LIMIT + FM_SPACING;
+
+ private static final RadioManager.ModuleProperties MODULE_PROPERTIES =
+ convertToModuleProperties();
+ private static final Announcement ANNOUNCEMENT =
+ Convert.announcementFromHal(
+ TestUtils.makeAnnouncement(TEST_ENABLED_TYPE, TEST_ANNOUNCEMENT_FREQUENCY));
+
+ @Rule
+ public final Expect expect = Expect.create();
+
+ @Test
+ public void propertiesFromHalProperties_idsMatch() {
+ expect.withMessage("Properties id")
+ .that(MODULE_PROPERTIES.getId()).isEqualTo(TEST_ID);
+ }
+
+ @Test
+ public void propertiesFromHalProperties_serviceNamesMatch() {
+ expect.withMessage("Service name")
+ .that(MODULE_PROPERTIES.getServiceName()).isEqualTo(TEST_SERVICE_NAME);
+ }
+
+ @Test
+ public void propertiesFromHalProperties_implementorsMatch() {
+ expect.withMessage("Implementor")
+ .that(MODULE_PROPERTIES.getImplementor()).isEqualTo(TEST_MAKER);
+ }
+
+
+ @Test
+ public void propertiesFromHalProperties_productsMatch() {
+ expect.withMessage("Product")
+ .that(MODULE_PROPERTIES.getProduct()).isEqualTo(TEST_PRODUCT);
+ }
+
+ @Test
+ public void propertiesFromHalProperties_versionsMatch() {
+ expect.withMessage("Version")
+ .that(MODULE_PROPERTIES.getVersion()).isEqualTo(TEST_VERSION);
+ }
+
+ @Test
+ public void propertiesFromHalProperties_serialsMatch() {
+ expect.withMessage("Serial")
+ .that(MODULE_PROPERTIES.getSerial()).isEqualTo(TEST_SERIAL);
+ }
+
+ @Test
+ public void propertiesFromHalProperties_dabTableInfoMatch() {
+ Map<String, Integer> dabTableExpected = Map.of(DAB_ENTRY_LABEL_1, DAB_ENTRY_FREQUENCY_1,
+ DAB_ENTRY_LABEL_2, DAB_ENTRY_FREQUENCY_2);
+
+ expect.withMessage("Supported program types")
+ .that(MODULE_PROPERTIES.getDabFrequencyTable())
+ .containsExactlyEntriesIn(dabTableExpected);
+ }
+
+ @Test
+ public void propertiesFromHalProperties_vendorInfoMatch() {
+ Map<String, String> vendorInfoExpected = Map.of(VENDOR_INFO_KEY_1, VENDOR_INFO_VALUE_1,
+ VENDOR_INFO_KEY_2, VENDOR_INFO_VALUE_2);
+
+ expect.withMessage("Vendor info").that(MODULE_PROPERTIES.getVendorInfo())
+ .containsExactlyEntriesIn(vendorInfoExpected);
+ }
+
+ @Test
+ public void propertiesFromHalProperties_bandsMatch() {
+ RadioManager.BandDescriptor[] bands = MODULE_PROPERTIES.getBands();
+
+ expect.withMessage("Band descriptors").that(bands).hasLength(2);
+
+ expect.withMessage("FM band frequency lower limit")
+ .that(bands[0].getLowerLimit()).isEqualTo(FM_LOWER_LIMIT);
+ expect.withMessage("FM band frequency upper limit")
+ .that(bands[0].getUpperLimit()).isEqualTo(FM_UPPER_LIMIT);
+ expect.withMessage("FM band frequency spacing")
+ .that(bands[0].getSpacing()).isEqualTo(FM_SPACING);
+
+ expect.withMessage("AM band frequency lower limit")
+ .that(bands[1].getLowerLimit()).isEqualTo(AM_LOWER_LIMIT);
+ expect.withMessage("AM band frequency upper limit")
+ .that(bands[1].getUpperLimit()).isEqualTo(AM_UPPER_LIMIT);
+ expect.withMessage("AM band frequency spacing")
+ .that(bands[1].getSpacing()).isEqualTo(AM_SPACING);
+ }
+
+ @Test
+ public void announcementFromHalAnnouncement_typesMatch() {
+ expect.withMessage("Announcement type")
+ .that(ANNOUNCEMENT.getType()).isEqualTo(TEST_ENABLED_TYPE);
+ }
+
+ @Test
+ public void announcementFromHalAnnouncement_selectorsMatch() {
+ ProgramSelector.Identifier primaryIdExpected = new ProgramSelector.Identifier(
+ ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, TEST_ANNOUNCEMENT_FREQUENCY);
+
+ ProgramSelector selector = ANNOUNCEMENT.getSelector();
+
+ expect.withMessage("Primary id of announcement selector")
+ .that(selector.getPrimaryId()).isEqualTo(primaryIdExpected);
+ expect.withMessage("Secondary ids of announcement selector")
+ .that(selector.getSecondaryIds()).isEmpty();
+ }
+
+ @Test
+ public void announcementFromHalAnnouncement_VendorInfoMatch() {
+ expect.withMessage("Announcement vendor info")
+ .that(ANNOUNCEMENT.getVendorInfo()).isEmpty();
+ }
+
+ private static RadioManager.ModuleProperties convertToModuleProperties() {
+ AmFmRegionConfig amFmConfig = createAmFmRegionConfig();
+ List<DabTableEntry> dabTableEntries = Arrays.asList(
+ createDabTableEntry(DAB_ENTRY_LABEL_1, DAB_ENTRY_FREQUENCY_1),
+ createDabTableEntry(DAB_ENTRY_LABEL_2, DAB_ENTRY_FREQUENCY_2));
+ Properties properties = createHalProperties();
+
+ return Convert.propertiesFromHal(TEST_ID, TEST_SERVICE_NAME, properties,
+ amFmConfig, dabTableEntries);
+ }
+
+ private static AmFmRegionConfig createAmFmRegionConfig() {
+ AmFmRegionConfig amFmRegionConfig = new AmFmRegionConfig();
+ amFmRegionConfig.ranges = new ArrayList<AmFmBandRange>(Arrays.asList(
+ createAmFmBandRange(FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING),
+ createAmFmBandRange(AM_LOWER_LIMIT, AM_UPPER_LIMIT, AM_SPACING)));
+ return amFmRegionConfig;
+ }
+
+ private static AmFmBandRange createAmFmBandRange(int lowerBound, int upperBound, int spacing) {
+ AmFmBandRange bandRange = new AmFmBandRange();
+ bandRange.lowerBound = lowerBound;
+ bandRange.upperBound = upperBound;
+ bandRange.spacing = spacing;
+ bandRange.scanSpacing = bandRange.spacing;
+ return bandRange;
+ }
+
+ private static DabTableEntry createDabTableEntry(String label, int value) {
+ DabTableEntry dabTableEntry = new DabTableEntry();
+ dabTableEntry.label = label;
+ dabTableEntry.frequency = value;
+ return dabTableEntry;
+ }
+
+ private static Properties createHalProperties() {
+ Properties halProperties = new Properties();
+ halProperties.supportedIdentifierTypes = new ArrayList<Integer>(Arrays.asList(
+ IdentifierType.AMFM_FREQUENCY, IdentifierType.RDS_PI, IdentifierType.DAB_SID_EXT));
+ halProperties.maker = TEST_MAKER;
+ halProperties.product = TEST_PRODUCT;
+ halProperties.version = TEST_VERSION;
+ halProperties.serial = TEST_SERIAL;
+ halProperties.vendorInfo = new ArrayList<VendorKeyValue>(Arrays.asList(
+ TestUtils.makeVendorKeyValue(VENDOR_INFO_KEY_1, VENDOR_INFO_VALUE_1),
+ TestUtils.makeVendorKeyValue(VENDOR_INFO_KEY_2, VENDOR_INFO_VALUE_2)));
+ return halProperties;
+ }
+}
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/RadioModuleHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/RadioModuleHidlTest.java
new file mode 100644
index 0000000..48f5a46
--- /dev/null
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/RadioModuleHidlTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.broadcastradio.hal2;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.graphics.Bitmap;
+import android.hardware.broadcastradio.V2_0.Constants;
+import android.hardware.broadcastradio.V2_0.IBroadcastRadio;
+import android.hardware.broadcastradio.V2_0.Result;
+import android.hardware.radio.Announcement;
+import android.hardware.radio.IAnnouncementListener;
+import android.hardware.radio.ICloseHandle;
+import android.hardware.radio.RadioManager;
+import android.os.RemoteException;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Tests for HIDL HAL RadioModule.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public final class RadioModuleHidlTest {
+
+ private static final int TEST_ENABLED_TYPE = Announcement.TYPE_EVENT;
+ private static final RadioManager.ModuleProperties TEST_MODULE_PROPERTIES =
+ TestUtils.makeDefaultModuleProperties();
+
+ @Mock
+ private IBroadcastRadio mBroadcastRadioMock;
+ @Mock
+ private IAnnouncementListener mListenerMock;
+ @Mock
+ private android.hardware.broadcastradio.V2_0.ICloseHandle mHalCloseHandleMock;
+
+ private final Object mLock = new Object();
+ private RadioModule mRadioModule;
+ private android.hardware.broadcastradio.V2_0.IAnnouncementListener mHalListener;
+
+ @Before
+ public void setup() throws RemoteException {
+ mRadioModule = new RadioModule(mBroadcastRadioMock, TEST_MODULE_PROPERTIES, mLock);
+
+ when(mBroadcastRadioMock.getImage(anyInt())).thenReturn(new ArrayList<Byte>(0));
+
+ doAnswer(invocation -> {
+ mHalListener = (android.hardware.broadcastradio.V2_0.IAnnouncementListener) invocation
+ .getArguments()[1];
+ IBroadcastRadio.registerAnnouncementListenerCallback cb =
+ (IBroadcastRadio.registerAnnouncementListenerCallback)
+ invocation.getArguments()[2];
+ cb.onValues(Result.OK, mHalCloseHandleMock);
+ return null;
+ }).when(mBroadcastRadioMock).registerAnnouncementListener(any(), any(), any());
+ }
+
+ @Test
+ public void getService() {
+ assertWithMessage("Service of radio module")
+ .that(mRadioModule.getService()).isEqualTo(mBroadcastRadioMock);
+ }
+
+ @Test
+ public void getProperties() {
+ assertWithMessage("Module properties of radio module")
+ .that(mRadioModule.getProperties()).isEqualTo(TEST_MODULE_PROPERTIES);
+ }
+
+ @Test
+ public void getImage_withValidIdFromRadioModule() {
+ int imageId = 1;
+
+ Bitmap imageTest = mRadioModule.getImage(imageId);
+
+ assertWithMessage("Image from radio module").that(imageTest).isNull();
+ }
+
+ @Test
+ public void getImage_withInvalidIdFromRadioModule_throwsIllegalArgumentException() {
+ int invalidImageId = Constants.INVALID_IMAGE;
+
+ IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> {
+ mRadioModule.getImage(invalidImageId);
+ });
+
+ assertWithMessage("Exception for getting image with invalid ID")
+ .that(thrown).hasMessageThat().contains("Image ID is missing");
+ }
+
+ @Test
+ public void addAnnouncementListener_listenerRegistered() throws Exception {
+ ArrayList<Byte> enabledListExpected = new ArrayList<Byte>(Arrays.asList(
+ (byte) TEST_ENABLED_TYPE));
+ mRadioModule.addAnnouncementListener(new int[]{TEST_ENABLED_TYPE}, mListenerMock);
+
+ verify(mBroadcastRadioMock)
+ .registerAnnouncementListener(eq(enabledListExpected), any(), any());
+ }
+
+ @Test
+ public void onListUpdate_forAnnouncementListener() throws Exception {
+ android.hardware.broadcastradio.V2_0.Announcement halAnnouncement =
+ TestUtils.makeAnnouncement(TEST_ENABLED_TYPE, /* selectorFreq= */ 96300);
+ mRadioModule.addAnnouncementListener(new int[]{TEST_ENABLED_TYPE}, mListenerMock);
+
+ mHalListener.onListUpdated(
+ new ArrayList<android.hardware.broadcastradio.V2_0.Announcement>(
+ Arrays.asList(halAnnouncement)));
+
+ verify(mListenerMock).onListUpdated(any());
+ }
+
+ @Test
+ public void close_forCloseHandle() throws Exception {
+ ICloseHandle closeHandle =
+ mRadioModule.addAnnouncementListener(new int[]{TEST_ENABLED_TYPE}, mListenerMock);
+
+ closeHandle.close();
+
+ verify(mHalCloseHandleMock).close();
+ }
+}
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
index 25bf93f..d104359 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
@@ -95,9 +95,8 @@
public void setup() throws RemoteException {
MockitoAnnotations.initMocks(this);
- mRadioModule = new RadioModule(mBroadcastRadioMock, new RadioManager.ModuleProperties(0, "",
- 0, "", "", "", "", 0, 0, false, false, null, false, new int[] {}, new int[] {},
- null, null), mLock);
+ mRadioModule = new RadioModule(mBroadcastRadioMock,
+ TestUtils.makeDefaultModuleProperties(), mLock);
doAnswer((Answer) invocation -> {
mHalTunerCallback = (ITunerCallback) invocation.getArguments()[0];
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TestUtils.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TestUtils.java
index 4944803..4eedd2f 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TestUtils.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TestUtils.java
@@ -15,22 +15,61 @@
*/
package com.android.server.broadcastradio.hal2;
+import android.hardware.broadcastradio.V2_0.IdentifierType;
+import android.hardware.broadcastradio.V2_0.ProgramIdentifier;
import android.hardware.broadcastradio.V2_0.ProgramInfo;
+import android.hardware.broadcastradio.V2_0.VendorKeyValue;
import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
import android.hardware.radio.RadioMetadata;
+import android.util.ArrayMap;
+import java.util.ArrayList;
import java.util.HashMap;
final class TestUtils {
+
+ private TestUtils() {
+ throw new UnsupportedOperationException("TestUtils class is noninstantiable");
+ }
+
+ static RadioManager.ModuleProperties makeDefaultModuleProperties() {
+ return new RadioManager.ModuleProperties(
+ /* id= */ 0, /* serviceName= */ "", /* classId= */ 0, /* implementor= */ "",
+ /* product= */ "", /* version= */ "", /* serial= */ "", /* numTuners= */ 0,
+ /* numAudioSources= */ 0, /* isInitializationRequired= */ false,
+ /* isCaptureSupported= */ false, /* bands= */ null,
+ /* isBgScanSupported= */ false, new int[] {}, new int[] {},
+ /* dabFrequencyTable= */ null, /* vendorInfo= */ null);
+ }
+
+ static RadioManager.ProgramInfo makeProgramInfo(ProgramSelector selector, int signalQuality) {
+ return new RadioManager.ProgramInfo(selector,
+ selector.getPrimaryId(), selector.getPrimaryId(), /* relatedContents= */ null,
+ /* infoFlags= */ 0, signalQuality,
+ new RadioMetadata.Builder().build(), new ArrayMap<>());
+ }
+
static RadioManager.ProgramInfo makeProgramInfo(int programType,
ProgramSelector.Identifier identifier, int signalQuality) {
// Note: If you set new fields, check if programInfoToHal() needs to be updated as well.
- return new RadioManager.ProgramInfo(new ProgramSelector(programType, identifier, null,
- null), null, null, null, 0, signalQuality, new RadioMetadata.Builder().build(),
+ return new RadioManager.ProgramInfo(makeProgramSelector(programType, identifier), null,
+ null, null, 0, signalQuality, new RadioMetadata.Builder().build(),
new HashMap<String, String>());
}
+ static ProgramSelector makeFmSelector(long freq) {
+ return makeProgramSelector(ProgramSelector.PROGRAM_TYPE_FM,
+ new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY,
+ freq));
+ }
+
+ static ProgramSelector makeProgramSelector(int programType,
+ ProgramSelector.Identifier identifier) {
+ return new ProgramSelector(programType, identifier, /* secondaryIds= */ null,
+ /* vendorIds= */ null);
+ }
+
static ProgramInfo programInfoToHal(RadioManager.ProgramInfo info) {
// Note that because Convert does not by design provide functions for all conversions, this
// function only copies fields that are set by makeProgramInfo().
@@ -39,4 +78,45 @@
hwInfo.signalQuality = info.getSignalStrength();
return hwInfo;
}
+
+ static android.hardware.broadcastradio.V2_0.ProgramSelector makeHalFmSelector(int freq) {
+ ProgramIdentifier halId = new ProgramIdentifier();
+ halId.type = IdentifierType.AMFM_FREQUENCY;
+ halId.value = freq;
+
+ android.hardware.broadcastradio.V2_0.ProgramSelector halSelector =
+ new android.hardware.broadcastradio.V2_0.ProgramSelector();
+ halSelector.primaryId = halId;
+ halSelector.secondaryIds = new ArrayList<ProgramIdentifier>();
+ return halSelector;
+ }
+
+ static ProgramInfo makeHalProgramInfo(
+ android.hardware.broadcastradio.V2_0.ProgramSelector hwSel, int hwSignalQuality) {
+ ProgramInfo hwInfo = new ProgramInfo();
+ hwInfo.selector = hwSel;
+ hwInfo.logicallyTunedTo = hwSel.primaryId;
+ hwInfo.physicallyTunedTo = hwSel.primaryId;
+ hwInfo.signalQuality = hwSignalQuality;
+ hwInfo.relatedContent = new ArrayList<>();
+ hwInfo.metadata = new ArrayList<>();
+ return hwInfo;
+ }
+
+ static VendorKeyValue makeVendorKeyValue(String vendorKey, String vendorValue) {
+ VendorKeyValue vendorKeyValue = new VendorKeyValue();
+ vendorKeyValue.key = vendorKey;
+ vendorKeyValue.value = vendorValue;
+ return vendorKeyValue;
+ }
+
+ static android.hardware.broadcastradio.V2_0.Announcement makeAnnouncement(int type,
+ int selectorFreq) {
+ android.hardware.broadcastradio.V2_0.Announcement halAnnouncement =
+ new android.hardware.broadcastradio.V2_0.Announcement();
+ halAnnouncement.type = (byte) type;
+ halAnnouncement.selector = makeHalFmSelector(selectorFreq);
+ halAnnouncement.vendorInfo = new ArrayList<>();
+ return halAnnouncement;
+ }
}
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
new file mode 100644
index 0000000..936e606
--- /dev/null
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
@@ -0,0 +1,622 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.broadcastradio.hal2;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertThrows;
+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.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.graphics.Bitmap;
+import android.hardware.broadcastradio.V2_0.Constants;
+import android.hardware.broadcastradio.V2_0.IBroadcastRadio;
+import android.hardware.broadcastradio.V2_0.ITunerCallback;
+import android.hardware.broadcastradio.V2_0.ITunerSession;
+import android.hardware.broadcastradio.V2_0.IdentifierType;
+import android.hardware.broadcastradio.V2_0.ProgramInfo;
+import android.hardware.broadcastradio.V2_0.Result;
+import android.hardware.broadcastradio.V2_0.VendorKeyValue;
+import android.hardware.radio.ProgramList;
+import android.hardware.radio.ProgramSelector;
+import android.hardware.radio.RadioManager;
+import android.hardware.radio.RadioTuner;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.mockito.verification.VerificationWithTimeout;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Map;
+
+/**
+ * Tests for HIDL HAL TunerSession.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public final class TunerSessionHidlTest {
+
+ private static final VerificationWithTimeout CALLBACK_TIMEOUT =
+ timeout(/* millis= */ 200);
+ private static final int SIGNAL_QUALITY = 1;
+ private static final long AM_FM_FREQUENCY_SPACING = 500;
+ private static final long[] AM_FM_FREQUENCY_LIST = {97500, 98100, 99100};
+ private static final RadioManager.FmBandDescriptor FM_BAND_DESCRIPTOR =
+ new RadioManager.FmBandDescriptor(RadioManager.REGION_ITU_1, RadioManager.BAND_FM,
+ /* lowerLimit= */ 87500, /* upperLimit= */ 108000, /* spacing= */ 100,
+ /* stereo= */ false, /* rds= */ false, /* ta= */ false, /* af= */ false,
+ /* ea= */ false);
+ private static final RadioManager.BandConfig FM_BAND_CONFIG =
+ new RadioManager.FmBandConfig(FM_BAND_DESCRIPTOR);
+ private static final int UNSUPPORTED_CONFIG_FLAG = 0;
+
+ private final Object mLock = new Object();
+ private final ArrayMap<Integer, Boolean> mHalConfigMap = new ArrayMap<>();
+ private RadioModule mRadioModule;
+ private ITunerCallback mHalTunerCallback;
+ private ProgramInfo mHalCurrentInfo;
+ private TunerSession[] mTunerSessions;
+
+ @Mock private IBroadcastRadio mBroadcastRadioMock;
+ @Mock ITunerSession mHalTunerSessionMock;
+ private android.hardware.radio.ITunerCallback[] mAidlTunerCallbackMocks;
+
+ @Before
+ public void setup() throws Exception {
+ mRadioModule = new RadioModule(mBroadcastRadioMock,
+ TestUtils.makeDefaultModuleProperties(), mLock);
+
+ doAnswer(invocation -> {
+ mHalTunerCallback = (ITunerCallback) invocation.getArguments()[0];
+ IBroadcastRadio.openSessionCallback cb = (IBroadcastRadio.openSessionCallback)
+ invocation.getArguments()[1];
+ cb.onValues(Result.OK, mHalTunerSessionMock);
+ return null;
+ }).when(mBroadcastRadioMock).openSession(any(), any());
+
+ doAnswer(invocation -> {
+ android.hardware.broadcastradio.V2_0.ProgramSelector halSel =
+ (android.hardware.broadcastradio.V2_0.ProgramSelector)
+ invocation.getArguments()[0];
+ mHalCurrentInfo = TestUtils.makeHalProgramInfo(halSel, SIGNAL_QUALITY);
+ if (halSel.primaryId.type != IdentifierType.AMFM_FREQUENCY) {
+ return Result.NOT_SUPPORTED;
+ }
+ mHalTunerCallback.onCurrentProgramInfoChanged(mHalCurrentInfo);
+ return Result.OK;
+ }).when(mHalTunerSessionMock).tune(any());
+
+ doAnswer(invocation -> {
+ if ((boolean) invocation.getArguments()[0]) {
+ mHalCurrentInfo.selector.primaryId.value += AM_FM_FREQUENCY_SPACING;
+ } else {
+ mHalCurrentInfo.selector.primaryId.value -= AM_FM_FREQUENCY_SPACING;
+ }
+ mHalCurrentInfo.logicallyTunedTo = mHalCurrentInfo.selector.primaryId;
+ mHalCurrentInfo.physicallyTunedTo = mHalCurrentInfo.selector.primaryId;
+ mHalTunerCallback.onCurrentProgramInfoChanged(mHalCurrentInfo);
+ return Result.OK;
+ }).when(mHalTunerSessionMock).step(anyBoolean());
+
+ doAnswer(invocation -> {
+ if (mHalCurrentInfo == null) {
+ android.hardware.broadcastradio.V2_0.ProgramSelector placeHolderSelector =
+ TestUtils.makeHalFmSelector(/* freq= */ 97300);
+
+ mHalTunerCallback.onTuneFailed(Result.TIMEOUT, placeHolderSelector);
+ return Result.OK;
+ }
+ mHalCurrentInfo.selector.primaryId.value = getSeekFrequency(
+ mHalCurrentInfo.selector.primaryId.value,
+ !(boolean) invocation.getArguments()[0]);
+ mHalCurrentInfo.logicallyTunedTo = mHalCurrentInfo.selector.primaryId;
+ mHalCurrentInfo.physicallyTunedTo = mHalCurrentInfo.selector.primaryId;
+ mHalTunerCallback.onCurrentProgramInfoChanged(mHalCurrentInfo);
+ return Result.OK;
+ }).when(mHalTunerSessionMock).scan(anyBoolean(), anyBoolean());
+
+ when(mBroadcastRadioMock.getImage(anyInt())).thenReturn(new ArrayList<Byte>(0));
+
+ doAnswer(invocation -> {
+ int configFlag = (int) invocation.getArguments()[0];
+ ITunerSession.isConfigFlagSetCallback cb = (ITunerSession.isConfigFlagSetCallback)
+ invocation.getArguments()[1];
+ if (configFlag == UNSUPPORTED_CONFIG_FLAG) {
+ cb.onValues(Result.NOT_SUPPORTED, false);
+ return null;
+ }
+ cb.onValues(Result.OK, mHalConfigMap.getOrDefault(configFlag, false));
+ return null;
+ }).when(mHalTunerSessionMock).isConfigFlagSet(anyInt(), any());
+
+ doAnswer(invocation -> {
+ int configFlag = (int) invocation.getArguments()[0];
+ if (configFlag == UNSUPPORTED_CONFIG_FLAG) {
+ return Result.NOT_SUPPORTED;
+ }
+ mHalConfigMap.put(configFlag, (boolean) invocation.getArguments()[1]);
+ return Result.OK;
+ }).when(mHalTunerSessionMock).setConfigFlag(anyInt(), anyBoolean());
+ }
+
+ @After
+ public void cleanUp() {
+ mHalConfigMap.clear();
+ }
+
+ @Test
+ public void openSession_withMultipleSessions() throws Exception {
+ int numSessions = 3;
+
+ openAidlClients(numSessions);
+
+ for (int index = 0; index < numSessions; index++) {
+ assertWithMessage("Session of index %s close state", index)
+ .that(mTunerSessions[index].isClosed()).isFalse();
+ }
+ }
+
+ @Test
+ public void setConfiguration() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+
+ mTunerSessions[0].setConfiguration(FM_BAND_CONFIG);
+
+ verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onConfigurationChanged(FM_BAND_CONFIG);
+ }
+
+ @Test
+ public void getConfiguration() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+ mTunerSessions[0].setConfiguration(FM_BAND_CONFIG);
+
+ RadioManager.BandConfig config = mTunerSessions[0].getConfiguration();
+
+ assertWithMessage("Session configuration").that(config)
+ .isEqualTo(FM_BAND_CONFIG);
+ }
+
+ @Test
+ public void setMuted_withUnmuted() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+
+ mTunerSessions[0].setMuted(/* mute= */ false);
+
+ assertWithMessage("Session mute state after setting unmuted")
+ .that(mTunerSessions[0].isMuted()).isFalse();
+ }
+
+ @Test
+ public void setMuted_withMuted() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+
+ mTunerSessions[0].setMuted(/* mute= */ true);
+
+ assertWithMessage("Session mute state after setting muted")
+ .that(mTunerSessions[0].isMuted()).isTrue();
+ }
+
+ @Test
+ public void close_withOneSession() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+
+ mTunerSessions[0].close();
+
+ assertWithMessage("Close state of broadcast radio service session")
+ .that(mTunerSessions[0].isClosed()).isTrue();
+ }
+
+ @Test
+ public void close_withOnlyOneSession_withMultipleSessions() throws Exception {
+ int numSessions = 3;
+ openAidlClients(numSessions);
+ int closeIdx = 0;
+
+ mTunerSessions[closeIdx].close();
+
+ for (int index = 0; index < numSessions; index++) {
+ if (index == closeIdx) {
+ assertWithMessage(
+ "Close state of broadcast radio service session of index %s", index)
+ .that(mTunerSessions[index].isClosed()).isTrue();
+ } else {
+ assertWithMessage(
+ "Close state of broadcast radio service session of index %s", index)
+ .that(mTunerSessions[index].isClosed()).isFalse();
+ }
+ }
+ }
+
+ @Test
+ public void close_withOneSession_withError() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+ int errorCode = RadioTuner.ERROR_SERVER_DIED;
+
+ mTunerSessions[0].close(errorCode);
+
+ verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onError(errorCode);
+ assertWithMessage("Close state of broadcast radio service session")
+ .that(mTunerSessions[0].isClosed()).isTrue();
+ }
+
+ @Test
+ public void closeSessions_withMultipleSessions_withError() throws Exception {
+ int numSessions = 3;
+ openAidlClients(numSessions);
+
+ int errorCode = RadioTuner.ERROR_SERVER_DIED;
+ mRadioModule.closeSessions(errorCode);
+
+ for (int index = 0; index < numSessions; index++) {
+ verify(mAidlTunerCallbackMocks[index], CALLBACK_TIMEOUT).onError(errorCode);
+ assertWithMessage("Close state of broadcast radio service session of index %s", index)
+ .that(mTunerSessions[index].isClosed()).isTrue();
+ }
+ }
+
+ @Test
+ public void tune_withOneSession() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+ ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
+ RadioManager.ProgramInfo tuneInfo =
+ TestUtils.makeProgramInfo(initialSel, SIGNAL_QUALITY);
+
+ mTunerSessions[0].tune(initialSel);
+
+ verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo);
+ }
+
+ @Test
+ public void tune_withMultipleSessions() throws Exception {
+ int numSessions = 3;
+ openAidlClients(numSessions);
+ ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
+ RadioManager.ProgramInfo tuneInfo =
+ TestUtils.makeProgramInfo(initialSel, SIGNAL_QUALITY);
+
+ mTunerSessions[0].tune(initialSel);
+
+ for (int index = 0; index < numSessions; index++) {
+ verify(mAidlTunerCallbackMocks[index], CALLBACK_TIMEOUT)
+ .onCurrentProgramInfoChanged(tuneInfo);
+ }
+ }
+
+ @Test
+ public void tune_withUnsupportedSelector_throwsException() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+ ProgramSelector unsupportedSelector = TestUtils.makeProgramSelector(
+ ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY, new ProgramSelector.Identifier(
+ ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY, /* value= */ 300));
+
+ UnsupportedOperationException thrown = assertThrows(UnsupportedOperationException.class,
+ () -> mTunerSessions[0].tune(unsupportedSelector));
+
+ assertWithMessage("Exception for tuning on unsupported program selector")
+ .that(thrown).hasMessageThat().contains("tune: NOT_SUPPORTED");
+ }
+
+ @Test
+ public void step_withDirectionUp() throws Exception {
+ long initFreq = AM_FM_FREQUENCY_LIST[1];
+ ProgramSelector initialSel = TestUtils.makeFmSelector(initFreq);
+ RadioManager.ProgramInfo stepUpInfo = TestUtils.makeProgramInfo(
+ TestUtils.makeFmSelector(initFreq + AM_FM_FREQUENCY_SPACING), SIGNAL_QUALITY);
+ openAidlClients(/* numClients= */ 1);
+ mHalCurrentInfo = TestUtils.makeHalProgramInfo(
+ Convert.programSelectorToHal(initialSel), SIGNAL_QUALITY);
+
+ mTunerSessions[0].step(/* directionDown= */ false, /* skipSubChannel= */ false);
+
+ verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT)
+ .onCurrentProgramInfoChanged(stepUpInfo);
+ }
+
+ @Test
+ public void step_withDirectionDown() throws Exception {
+ long initFreq = AM_FM_FREQUENCY_LIST[1];
+ ProgramSelector initialSel = TestUtils.makeFmSelector(initFreq);
+ RadioManager.ProgramInfo stepDownInfo = TestUtils.makeProgramInfo(
+ TestUtils.makeFmSelector(initFreq - AM_FM_FREQUENCY_SPACING),
+ SIGNAL_QUALITY);
+ openAidlClients(/* numClients= */ 1);
+ mHalCurrentInfo = TestUtils.makeHalProgramInfo(
+ Convert.programSelectorToHal(initialSel), SIGNAL_QUALITY);
+
+ mTunerSessions[0].step(/* directionDown= */ true, /* skipSubChannel= */ false);
+
+ verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT)
+ .onCurrentProgramInfoChanged(stepDownInfo);
+ }
+
+ @Test
+ public void scan_withDirectionUp() throws Exception {
+ long initFreq = AM_FM_FREQUENCY_LIST[2];
+ ProgramSelector initialSel = TestUtils.makeFmSelector(initFreq);
+ RadioManager.ProgramInfo scanUpInfo = TestUtils.makeProgramInfo(
+ TestUtils.makeFmSelector(getSeekFrequency(initFreq, /* seekDown= */ false)),
+ SIGNAL_QUALITY);
+ openAidlClients(/* numClients= */ 1);
+ mHalCurrentInfo = TestUtils.makeHalProgramInfo(
+ Convert.programSelectorToHal(initialSel), SIGNAL_QUALITY);
+
+ mTunerSessions[0].scan(/* directionDown= */ false, /* skipSubChannel= */ false);
+
+ verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT)
+ .onCurrentProgramInfoChanged(scanUpInfo);
+ }
+
+ @Test
+ public void scan_callsOnTuneFailedWhenTimeout() throws Exception {
+ int numSessions = 2;
+ openAidlClients(numSessions);
+
+ mTunerSessions[0].scan(/* directionDown= */ false, /* skipSubChannel= */ false);
+
+ for (int index = 0; index < numSessions; index++) {
+ verify(mAidlTunerCallbackMocks[index], CALLBACK_TIMEOUT)
+ .onTuneFailed(eq(Result.TIMEOUT), any());
+ }
+ }
+
+ @Test
+ public void scan_withDirectionDown() throws Exception {
+ long initFreq = AM_FM_FREQUENCY_LIST[2];
+ ProgramSelector initialSel = TestUtils.makeFmSelector(initFreq);
+ RadioManager.ProgramInfo scanUpInfo = TestUtils.makeProgramInfo(
+ TestUtils.makeFmSelector(getSeekFrequency(initFreq, /* seekDown= */ true)),
+ SIGNAL_QUALITY);
+ openAidlClients(/* numClients= */ 1);
+ mHalCurrentInfo = TestUtils.makeHalProgramInfo(
+ Convert.programSelectorToHal(initialSel), SIGNAL_QUALITY);
+
+ mTunerSessions[0].scan(/* directionDown= */ true, /* skipSubChannel= */ false);
+ verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT)
+ .onCurrentProgramInfoChanged(scanUpInfo);
+ }
+
+ @Test
+ public void cancel() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+ ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
+ mTunerSessions[0].tune(initialSel);
+
+ mTunerSessions[0].cancel();
+
+ verify(mHalTunerSessionMock).cancel();
+ }
+
+ @Test
+ public void getImage_withInvalidId_throwsIllegalArgumentException() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+ int imageId = Constants.INVALID_IMAGE;
+
+ IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> {
+ mTunerSessions[0].getImage(imageId);
+ });
+
+ assertWithMessage("Get image exception")
+ .that(thrown).hasMessageThat().contains("Image ID is missing");
+ }
+
+ @Test
+ public void getImage_withValidId() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+ int imageId = 1;
+
+ Bitmap imageTest = mTunerSessions[0].getImage(imageId);
+
+ assertWithMessage("Null image").that(imageTest).isEqualTo(null);
+ }
+
+ @Test
+ public void startBackgroundScan() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+
+ mTunerSessions[0].startBackgroundScan();
+
+ verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onBackgroundScanComplete();
+ }
+
+ @Test
+ public void stopProgramListUpdates() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+ ProgramList.Filter aidlFilter = new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(),
+ /* includeCategories= */ true, /* excludeModifications= */ false);
+ mTunerSessions[0].startProgramListUpdates(aidlFilter);
+
+ mTunerSessions[0].stopProgramListUpdates();
+
+ verify(mHalTunerSessionMock).stopProgramListUpdates();
+ }
+
+ @Test
+ public void isConfigFlagSupported_withUnsupportedFlag_returnsFalse() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+ int flag = UNSUPPORTED_CONFIG_FLAG;
+
+ boolean isSupported = mTunerSessions[0].isConfigFlagSupported(flag);
+
+ verify(mHalTunerSessionMock).isConfigFlagSet(eq(flag), any());
+ assertWithMessage("Config flag %s is supported", flag).that(isSupported).isFalse();
+ }
+
+ @Test
+ public void isConfigFlagSupported_withSupportedFlag_returnsTrue() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+ int flag = UNSUPPORTED_CONFIG_FLAG + 1;
+
+ boolean isSupported = mTunerSessions[0].isConfigFlagSupported(flag);
+
+ verify(mHalTunerSessionMock).isConfigFlagSet(eq(flag), any());
+ assertWithMessage("Config flag %s is supported", flag).that(isSupported).isTrue();
+ }
+
+ @Test
+ public void setConfigFlag_withUnsupportedFlag_throwsRuntimeException() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+ int flag = UNSUPPORTED_CONFIG_FLAG;
+
+ RuntimeException thrown = assertThrows(RuntimeException.class, () -> {
+ mTunerSessions[0].setConfigFlag(flag, /* value= */ true);
+ });
+
+ assertWithMessage("Exception for setting unsupported flag %s", flag)
+ .that(thrown).hasMessageThat().contains("setConfigFlag: NOT_SUPPORTED");
+ }
+
+ @Test
+ public void setConfigFlag_withFlagSetToTrue() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+ int flag = UNSUPPORTED_CONFIG_FLAG + 1;
+
+ mTunerSessions[0].setConfigFlag(flag, /* value= */ true);
+
+ verify(mHalTunerSessionMock).setConfigFlag(flag, /* value= */ true);
+ }
+
+ @Test
+ public void setConfigFlag_withFlagSetToFalse() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+ int flag = UNSUPPORTED_CONFIG_FLAG + 1;
+
+ mTunerSessions[0].setConfigFlag(flag, /* value= */ false);
+
+ verify(mHalTunerSessionMock).setConfigFlag(flag, /* value= */ false);
+ }
+
+ @Test
+ public void isConfigFlagSet_withUnsupportedFlag_throwsRuntimeException()
+ throws Exception {
+ openAidlClients(/* numClients= */ 1);
+ int flag = UNSUPPORTED_CONFIG_FLAG;
+
+ RuntimeException thrown = assertThrows(RuntimeException.class, () -> {
+ mTunerSessions[0].isConfigFlagSet(flag);
+ });
+
+ assertWithMessage("Exception for check if unsupported flag %s is set", flag)
+ .that(thrown).hasMessageThat().contains("isConfigFlagSet: NOT_SUPPORTED");
+ }
+
+ @Test
+ public void isConfigFlagSet_withSupportedFlag() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+ int flag = UNSUPPORTED_CONFIG_FLAG + 1;
+ boolean expectedConfigFlagValue = true;
+ mTunerSessions[0].setConfigFlag(flag, /* value= */ expectedConfigFlagValue);
+
+ boolean isSet = mTunerSessions[0].isConfigFlagSet(flag);
+
+ assertWithMessage("Config flag %s is set", flag)
+ .that(isSet).isEqualTo(expectedConfigFlagValue);
+ }
+
+ @Test
+ public void setParameters_withMockParameters() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+ Map<String, String> parametersSet = Map.of("mockParam1", "mockValue1",
+ "mockParam2", "mockValue2");
+
+ mTunerSessions[0].setParameters(parametersSet);
+
+ verify(mHalTunerSessionMock).setParameters(Convert.vendorInfoToHal(parametersSet));
+ }
+
+ @Test
+ public void getParameters_withMockKeys() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+ ArrayList<String> parameterKeys = new ArrayList<>(Arrays.asList("mockKey1", "mockKey2"));
+
+ mTunerSessions[0].getParameters(parameterKeys);
+
+ verify(mHalTunerSessionMock).getParameters(parameterKeys);
+ }
+
+ @Test
+ public void onConfigFlagUpdated_forTunerCallback() throws Exception {
+ int numSessions = 3;
+ openAidlClients(numSessions);
+
+ mHalTunerCallback.onAntennaStateChange(/* connected= */ false);
+
+ for (int index = 0; index < numSessions; index++) {
+ verify(mAidlTunerCallbackMocks[index], CALLBACK_TIMEOUT)
+ .onAntennaState(/* connected= */ false);
+ }
+ }
+
+ @Test
+ public void onParametersUpdated_forTunerCallback() throws Exception {
+ int numSessions = 3;
+ openAidlClients(numSessions);
+ ArrayList<VendorKeyValue> parametersUpdates = new ArrayList<VendorKeyValue>(Arrays.asList(
+ TestUtils.makeVendorKeyValue("com.vendor.parameter1", "value1")));
+ Map<String, String> parametersExpected = Map.of("com.vendor.parameter1", "value1");
+
+ mHalTunerCallback.onParametersUpdated(parametersUpdates);
+
+ for (int index = 0; index < numSessions; index++) {
+ verify(mAidlTunerCallbackMocks[index], CALLBACK_TIMEOUT)
+ .onParametersUpdated(parametersExpected);
+ }
+ }
+
+ private void openAidlClients(int numClients) throws Exception {
+ mAidlTunerCallbackMocks = new android.hardware.radio.ITunerCallback[numClients];
+ mTunerSessions = new TunerSession[numClients];
+ for (int index = 0; index < numClients; index++) {
+ mAidlTunerCallbackMocks[index] = mock(android.hardware.radio.ITunerCallback.class);
+ mTunerSessions[index] = mRadioModule.openSession(mAidlTunerCallbackMocks[index]);
+ }
+ }
+
+ private long getSeekFrequency(long currentFrequency, boolean seekDown) {
+ long seekFrequency;
+ if (seekDown) {
+ seekFrequency = AM_FM_FREQUENCY_LIST[AM_FM_FREQUENCY_LIST.length - 1];
+ for (int i = AM_FM_FREQUENCY_LIST.length - 1; i >= 0; i--) {
+ if (AM_FM_FREQUENCY_LIST[i] < currentFrequency) {
+ seekFrequency = AM_FM_FREQUENCY_LIST[i];
+ break;
+ }
+ }
+ } else {
+ seekFrequency = AM_FM_FREQUENCY_LIST[0];
+ for (int index = 0; index < AM_FM_FREQUENCY_LIST.length; index++) {
+ if (AM_FM_FREQUENCY_LIST[index] > currentFrequency) {
+ seekFrequency = AM_FM_FREQUENCY_LIST[index];
+ break;
+ }
+ }
+ }
+ return seekFrequency;
+ }
+}
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java
index 100eb99..b3af895 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java
@@ -145,7 +145,8 @@
mIntentFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
mIntentFilter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
mIntentFilter.addAction(TetheringManager.ACTION_TETHER_STATE_CHANGED);
- mContext.registerReceiver(mWifiReceiver, mIntentFilter);
+ mContext.registerReceiver(mWifiReceiver, mIntentFilter,
+ Context.RECEIVER_EXPORTED_UNAUDITED);
logv("Clear Wifi before we start the test.");
removeConfiguredNetworksAndDisableWifi();
diff --git a/core/tests/GameManagerTests/OWNERS b/core/tests/GameManagerTests/OWNERS
new file mode 100644
index 0000000..0992440
--- /dev/null
+++ b/core/tests/GameManagerTests/OWNERS
@@ -0,0 +1 @@
+include /GAME_MANAGER_OWNERS
\ No newline at end of file
diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
index 1a63660..191756a 100644
--- a/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
+++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
@@ -99,7 +99,8 @@
// Register a download receiver for ACTION_DOWNLOAD_COMPLETE
mDownloadReceiver = new DownloadReceiver();
mContext.registerReceiver(mDownloadReceiver,
- new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
+ new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE),
+ Context.RECEIVER_EXPORTED_UNAUDITED);
// Register a wifi receiver
mWifiReceiver = new WifiReceiver();
diff --git a/core/tests/coretests/AndroidTest.xml b/core/tests/coretests/AndroidTest.xml
index 04952bd..e2cdbf3 100644
--- a/core/tests/coretests/AndroidTest.xml
+++ b/core/tests/coretests/AndroidTest.xml
@@ -25,6 +25,11 @@
<option name="test-file-name" value="BinderDeathRecipientHelperApp2.apk" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <!-- TODO(b/254155965): Design a mechanism to finally remove this command. -->
+ <option name="run-command" value="settings put global device_config_sync_disabled 0" />
+ </target_preparer>
+
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.DeviceInteractionHelperInstaller" />
<option name="test-tag" value="FrameworksCoreTests" />
diff --git a/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
index f4709ff..cb66fc8 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
@@ -471,7 +471,7 @@
protected MultipleDownloadsCompletedReceiver registerNewMultipleDownloadsReceiver() {
MultipleDownloadsCompletedReceiver receiver = new MultipleDownloadsCompletedReceiver();
mContext.registerReceiver(receiver, new IntentFilter(
- DownloadManager.ACTION_DOWNLOAD_COMPLETE));
+ DownloadManager.ACTION_DOWNLOAD_COMPLETE), Context.RECEIVER_EXPORTED_UNAUDITED);
return receiver;
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index b292d7d..a0ed026 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -294,10 +294,9 @@
StopActivityItem lifecycleRequest = StopActivityItem.obtain(78 /* configChanges */);
- IApplicationThread appThread = new StubAppThread();
Binder activityToken = new Binder();
- ClientTransaction transaction = ClientTransaction.obtain(appThread, activityToken);
+ ClientTransaction transaction = ClientTransaction.obtain(null, activityToken);
transaction.addCallback(callback1);
transaction.addCallback(callback2);
transaction.setLifecycleStateRequest(lifecycleRequest);
@@ -318,10 +317,9 @@
ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain(
config());
- IApplicationThread appThread = new StubAppThread();
Binder activityToken = new Binder();
- ClientTransaction transaction = ClientTransaction.obtain(appThread, activityToken);
+ ClientTransaction transaction = ClientTransaction.obtain(null, activityToken);
transaction.addCallback(callback1);
transaction.addCallback(callback2);
@@ -339,10 +337,9 @@
// Write to parcel
StopActivityItem lifecycleRequest = StopActivityItem.obtain(78 /* configChanges */);
- IApplicationThread appThread = new StubAppThread();
Binder activityToken = new Binder();
- ClientTransaction transaction = ClientTransaction.obtain(appThread, activityToken);
+ ClientTransaction transaction = ClientTransaction.obtain(null, activityToken);
transaction.setLifecycleStateRequest(lifecycleRequest);
writeAndPrepareForReading(transaction);
@@ -400,286 +397,4 @@
}
};
}
-
- /** Stub implementation of IApplicationThread that can be presented as {@link Binder}. */
- class StubAppThread extends android.app.IApplicationThread.Stub {
-
- @Override
- public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
- }
-
- @Override
- public void scheduleReceiver(Intent intent, ActivityInfo activityInfo,
- CompatibilityInfo compatibilityInfo, int i, String s, Bundle bundle, boolean b,
- int i1, int i2) throws RemoteException {
- }
-
- @Override
- public void scheduleCreateService(IBinder iBinder, ServiceInfo serviceInfo,
- CompatibilityInfo compatibilityInfo, int i) throws RemoteException {
- }
-
- @Override
- public void scheduleStopService(IBinder iBinder) throws RemoteException {
- }
-
- @Override
- public void bindApplication(String s, ApplicationInfo applicationInfo,
- String sdkSandboxClientAppVolumeUuid, String sdkSandboxClientAppPackage,
- ProviderInfoList list, ComponentName componentName, ProfilerInfo profilerInfo,
- Bundle bundle, IInstrumentationWatcher iInstrumentationWatcher,
- IUiAutomationConnection iUiAutomationConnection, int i, boolean b, boolean b1,
- boolean b2, boolean b3, Configuration configuration,
- CompatibilityInfo compatibilityInfo, Map map, Bundle bundle1, String s1,
- AutofillOptions ao, ContentCaptureOptions co, long[] disableCompatChanges,
- SharedMemory serializedSystemFontMap,
- long startRequestedElapsedTime, long startRequestedUptime)
- throws RemoteException {
- }
-
- @Override
- public void scheduleExit() throws RemoteException {
- }
-
- @Override
- public void scheduleServiceArgs(IBinder iBinder, ParceledListSlice parceledListSlice)
- throws RemoteException {
- }
-
- @Override
- public void updateTimeZone() throws RemoteException {
- }
-
- @Override
- public void processInBackground() throws RemoteException {
- }
-
- @Override
- public void scheduleBindService(IBinder iBinder, Intent intent, boolean b, int i)
- throws RemoteException {
- }
-
- @Override
- public void scheduleUnbindService(IBinder iBinder, Intent intent) throws RemoteException {
- }
-
- @Override
- public void dumpService(ParcelFileDescriptor parcelFileDescriptor, IBinder iBinder,
- String[] strings) throws RemoteException {
- }
-
- @Override
- public void scheduleRegisteredReceiver(IIntentReceiver iIntentReceiver, Intent intent,
- int i, String s, Bundle bundle, boolean b, boolean b1, int i1, int i2)
- throws RemoteException {
- }
-
- @Override
- public void scheduleLowMemory() throws RemoteException {
- }
-
- @Override
- public void profilerControl(boolean b, ProfilerInfo profilerInfo, int i)
- throws RemoteException {
- }
-
- @Override
- public void setSchedulingGroup(int i) throws RemoteException {
- }
-
- @Override
- public void scheduleCreateBackupAgent(ApplicationInfo applicationInfo,
- int i, int userId, int operatioType)
- throws RemoteException {
- }
-
- @Override
- public void scheduleDestroyBackupAgent(ApplicationInfo applicationInfo,
- int userId) throws RemoteException {
- }
-
- @Override
- public void scheduleOnNewActivityOptions(IBinder iBinder, Bundle bundle)
- throws RemoteException {
- }
-
- @Override
- public void scheduleSuicide() throws RemoteException {
- }
-
- @Override
- public void dispatchPackageBroadcast(int i, String[] strings) throws RemoteException {
- }
-
- @Override
- public void scheduleCrash(String s, int i, Bundle extras) throws RemoteException {
- }
-
- @Override
- public void dumpActivity(ParcelFileDescriptor parcelFileDescriptor, IBinder iBinder,
- String s, String[] strings) throws RemoteException {
- }
-
- @Override
- public void clearDnsCache() throws RemoteException {
- }
-
- @Override
- public void updateHttpProxy() throws RemoteException {
- }
-
- @Override
- public void setCoreSettings(Bundle bundle) throws RemoteException {
- }
-
- @Override
- public void updatePackageCompatibilityInfo(String s, CompatibilityInfo compatibilityInfo)
- throws RemoteException {
- }
-
- @Override
- public void scheduleTrimMemory(int i) throws RemoteException {
- }
-
- @Override
- public void dumpMemInfo(ParcelFileDescriptor parcelFileDescriptor,
- Debug.MemoryInfo memoryInfo, boolean b, boolean b1, boolean b2, boolean b3,
- boolean b4, String[] strings) throws RemoteException {
- }
-
- @Override
- public void dumpMemInfoProto(ParcelFileDescriptor parcelFileDescriptor,
- Debug.MemoryInfo memoryInfo, boolean b, boolean b1, boolean b2,
- boolean b3, String[] strings) throws RemoteException {
- }
-
- @Override
- public void dumpGfxInfo(ParcelFileDescriptor parcelFileDescriptor, String[] strings)
- throws RemoteException {
- }
-
- @Override
- public void dumpCacheInfo(ParcelFileDescriptor parcelFileDescriptor, String[] strings)
- throws RemoteException {
- }
-
- @Override
- public void dumpProvider(ParcelFileDescriptor parcelFileDescriptor, IBinder iBinder,
- String[] strings) throws RemoteException {
- }
-
- @Override
- public void dumpDbInfo(ParcelFileDescriptor parcelFileDescriptor, String[] strings)
- throws RemoteException {
- }
-
- @Override
- public void unstableProviderDied(IBinder iBinder) throws RemoteException {
- }
-
- @Override
- public void requestAssistContextExtras(IBinder iBinder, IBinder iBinder1, int i, int i1,
- int i2) throws RemoteException {
- }
-
- @Override
- public void scheduleTranslucentConversionComplete(IBinder iBinder, boolean b)
- throws RemoteException {
- }
-
- @Override
- public void setProcessState(int i) throws RemoteException {
- }
-
- @Override
- public void scheduleInstallProvider(ProviderInfo providerInfo) throws RemoteException {
- }
-
- @Override
- public void updateTimePrefs(int i) throws RemoteException {
- }
-
- @Override
- public void scheduleEnterAnimationComplete(IBinder iBinder) throws RemoteException {
- }
-
- @Override
- public void notifyCleartextNetwork(byte[] bytes) throws RemoteException {
- }
-
- @Override
- public void startBinderTracking() throws RemoteException {
- }
-
- @Override
- public void stopBinderTrackingAndDump(ParcelFileDescriptor parcelFileDescriptor)
- throws RemoteException {
- }
-
- @Override
- public void scheduleLocalVoiceInteractionStarted(IBinder iBinder,
- IVoiceInteractor iVoiceInteractor) throws RemoteException {
- }
-
- @Override
- public void handleTrustStorageUpdate() throws RemoteException {
- }
-
- @Override
- public void attachAgent(String s) throws RemoteException {
- }
-
- @Override
- public void attachStartupAgents(String s) throws RemoteException {
- }
-
- @Override
- public void scheduleApplicationInfoChanged(ApplicationInfo applicationInfo)
- throws RemoteException {
- }
-
- @Override
- public void setNetworkBlockSeq(long l) throws RemoteException {
- }
-
- @Override
- public void dumpHeap(boolean managed, boolean mallocInfo, boolean runGc, String path,
- ParcelFileDescriptor fd, RemoteCallback finishCallback) {
- }
-
- @Override
- public void dumpResources(ParcelFileDescriptor fd, RemoteCallback finishCallback) {
- }
-
- @Override
- public final void runIsolatedEntryPoint(String entryPoint, String[] entryPointArgs) {
- }
-
- @Override
- public void requestDirectActions(IBinder activityToken, IVoiceInteractor interactor,
- RemoteCallback cancellationCallback, RemoteCallback resultCallback) {
- }
-
- @Override
- public void performDirectAction(IBinder activityToken, String actionId, Bundle arguments,
- RemoteCallback cancellationCallback, RemoteCallback resultCallback) {
- }
-
- @Override
- public void notifyContentProviderPublishStatus(ContentProviderHolder holder, String auth,
- int userId, boolean published) {
- }
-
- @Override
- public void instrumentWithoutRestart(ComponentName instrumentationName,
- Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher,
- IUiAutomationConnection instrumentationUiConnection, ApplicationInfo targetInfo) {
- }
-
- @Override
- public void updateUiTranslationState(IBinder activityToken, int state,
- TranslationSpec sourceSpec, TranslationSpec targetSpec, List<AutofillId> viewIds,
- UiTranslationSpec uiTranslationSpec) {
- }
- }
}
diff --git a/core/tests/coretests/src/android/content/BroadcastReceiverTests.java b/core/tests/coretests/src/android/content/BroadcastReceiverTests.java
index 1509ff9..5dbeac2 100644
--- a/core/tests/coretests/src/android/content/BroadcastReceiverTests.java
+++ b/core/tests/coretests/src/android/content/BroadcastReceiverTests.java
@@ -49,7 +49,8 @@
final IntentFilter mockFilter = new IntentFilter("android.content.tests.TestAction");
try {
for (int i = 0; i < RECEIVER_LIMIT_PER_APP + 1; i++) {
- mContext.registerReceiver(new EmptyReceiver(), mockFilter);
+ mContext.registerReceiver(new EmptyReceiver(), mockFilter,
+ Context.RECEIVER_EXPORTED_UNAUDITED);
}
fail("No exception thrown when registering "
+ (RECEIVER_LIMIT_PER_APP + 1) + " receivers");
diff --git a/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java b/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java
index 98485c0..ee73f00 100644
--- a/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java
+++ b/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java
@@ -29,10 +29,11 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
/**
- * Test class for {@link ConstrainDisplayApisConfig}.
+ * Test for {@link ConstrainDisplayApisConfig}.
*
* Build/Install/Run:
* atest FrameworksCoreTests:ConstrainDisplayApisConfigTest
@@ -72,6 +73,7 @@
testNeverConstrainDisplayApis("com.android.test", /* version= */ 1, /* expected= */ false);
}
+ @Ignore("b/257375674")
@Test
public void neverConstrainDisplayApis_flagsHasSingleEntry_returnsTrueForPackageWithinRange() {
setNeverConstrainDisplayApisFlag("com.android.test:1:1");
@@ -107,6 +109,7 @@
testNeverConstrainDisplayApis("com.android.test4", /* version= */ 9, /* expected= */ false);
}
+ @Ignore("b/257375674")
@Test
public void neverConstrainDisplayApis_flagHasInvalidEntries_ignoresInvalidEntries() {
// We add a valid entry before and after the invalid ones to make sure they are applied.
diff --git a/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java b/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java
index 69eb13f..d1d14f6 100644
--- a/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java
+++ b/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java
@@ -114,6 +114,23 @@
}
@Test
+ public void testSendHint() {
+ Session s = createSession();
+ assumeNotNull(s);
+ s.sendHint(Session.CPU_LOAD_UP);
+ s.sendHint(Session.CPU_LOAD_RESET);
+ }
+
+ @Test
+ public void testSendHintWithNegativeHint() {
+ Session s = createSession();
+ assumeNotNull(s);
+ assertThrows(IllegalArgumentException.class, () -> {
+ s.sendHint(-1);
+ });
+ }
+
+ @Test
public void testCloseHintSession() {
Session s = createSession();
assumeNotNull(s);
diff --git a/core/tests/coretests/src/android/service/timezone/TimeZoneProviderEventTest.java b/core/tests/coretests/src/android/service/timezone/TimeZoneProviderEventTest.java
index ab63f14..86ebdf3 100644
--- a/core/tests/coretests/src/android/service/timezone/TimeZoneProviderEventTest.java
+++ b/core/tests/coretests/src/android/service/timezone/TimeZoneProviderEventTest.java
@@ -17,13 +17,18 @@
package android.service.timezone;
import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
-import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_WORKING;
+import static android.service.timezone.TimeZoneProviderEvent.EVENT_TYPE_PERMANENT_FAILURE;
+import static android.service.timezone.TimeZoneProviderEvent.EVENT_TYPE_SUGGESTION;
+import static android.service.timezone.TimeZoneProviderEvent.EVENT_TYPE_UNCERTAIN;
+import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_OK;
import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_FAILED;
-import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_WORKING;
+import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_OK;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -34,12 +39,92 @@
public class TimeZoneProviderEventTest {
+ public static final TimeZoneProviderStatus ARBITRARY_TIME_ZONE_PROVIDER_STATUS =
+ new TimeZoneProviderStatus.Builder()
+ .setConnectivityDependencyStatus(DEPENDENCY_STATUS_OK)
+ .setLocationDetectionDependencyStatus(DEPENDENCY_STATUS_OK)
+ .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_OK)
+ .build();
+
+ @Test
+ public void createPermanentFailure() {
+ long creationElapsedMillis = 1111L;
+ String cause = "Cause";
+ TimeZoneProviderEvent event = TimeZoneProviderEvent.createPermanentFailureEvent(
+ creationElapsedMillis, cause);
+
+ assertEquals(EVENT_TYPE_PERMANENT_FAILURE, event.getType());
+ assertEquals(cause, event.getFailureCause());
+ assertEquals(creationElapsedMillis, event.getCreationElapsedMillis());
+ assertNull(event.getSuggestion());
+ assertNull(event.getTimeZoneProviderStatus());
+ }
+
+ @Test
+ public void createSuggestion() {
+ long creationElapsedMillis = 1111L;
+ TimeZoneProviderSuggestion suggestion = new TimeZoneProviderSuggestion.Builder()
+ .setElapsedRealtimeMillis(2222L)
+ .setTimeZoneIds(Collections.singletonList("Europe/London"))
+ .build();
+
+ TimeZoneProviderStatus reportedStatus = new TimeZoneProviderStatus.Builder()
+ .setConnectivityDependencyStatus(DEPENDENCY_STATUS_OK)
+ .setLocationDetectionDependencyStatus(DEPENDENCY_STATUS_OK)
+ .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_OK)
+ .build();
+
+ assertThrows(NullPointerException.class, () -> TimeZoneProviderEvent.createSuggestionEvent(
+ creationElapsedMillis, /*suggestion=*/null, reportedStatus));
+
+ // Only TimeZoneProvider can report itself certain.
+ {
+ TimeZoneProviderEvent event = TimeZoneProviderEvent.createSuggestionEvent(
+ creationElapsedMillis, suggestion, reportedStatus);
+ assertEquals(EVENT_TYPE_SUGGESTION, event.getType());
+ assertEquals(creationElapsedMillis, event.getCreationElapsedMillis());
+ assertNull(event.getFailureCause());
+ assertEquals(suggestion, event.getSuggestion());
+ }
+
+ // Legacy API events can be created where the TimeZoneProviderStatus is omitted.
+ {
+ TimeZoneProviderStatus legacyStatus = null;
+ TimeZoneProviderEvent legacyEvent = TimeZoneProviderEvent.createSuggestionEvent(
+ creationElapsedMillis, suggestion, legacyStatus);
+ assertEquals(legacyStatus, legacyEvent.getTimeZoneProviderStatus());
+ }
+ }
+
+ @Test
+ public void createUncertain() {
+ long creationElapsedMillis = 1111L;
+
+ // The TimeZoneProvider can report itself uncertain.
+ {
+ TimeZoneProviderEvent event = TimeZoneProviderEvent.createUncertainEvent(
+ creationElapsedMillis, ARBITRARY_TIME_ZONE_PROVIDER_STATUS);
+ assertEquals(EVENT_TYPE_UNCERTAIN, event.getType());
+ assertEquals(creationElapsedMillis, event.getCreationElapsedMillis());
+ assertNull(event.getFailureCause());
+ assertNull(event.getSuggestion());
+ }
+
+ // Legacy API events can be created where the TimeZoneProviderStatus is omitted.
+ {
+ TimeZoneProviderStatus legacyStatus = null;
+ TimeZoneProviderEvent legacyEvent = TimeZoneProviderEvent.createUncertainEvent(
+ creationElapsedMillis, legacyStatus);
+ assertEquals(legacyStatus, legacyEvent.getTimeZoneProviderStatus());
+ }
+ }
+
@Test
public void isEquivalentToAndEquals() {
long creationElapsedMillis = 1111L;
TimeZoneProviderEvent failEvent =
TimeZoneProviderEvent.createPermanentFailureEvent(creationElapsedMillis, "one");
- TimeZoneProviderStatus providerStatus = TimeZoneProviderStatus.UNKNOWN;
+ TimeZoneProviderStatus providerStatus = ARBITRARY_TIME_ZONE_PROVIDER_STATUS;
TimeZoneProviderEvent uncertainEvent =
TimeZoneProviderEvent.createUncertainEvent(creationElapsedMillis, providerStatus);
@@ -85,14 +170,14 @@
@Test
public void isEquivalentToAndEquals_uncertain() {
TimeZoneProviderStatus status1 = new TimeZoneProviderStatus.Builder()
- .setLocationDetectionStatus(DEPENDENCY_STATUS_WORKING)
- .setConnectivityStatus(DEPENDENCY_STATUS_WORKING)
- .setTimeZoneResolutionStatus(OPERATION_STATUS_WORKING)
+ .setLocationDetectionDependencyStatus(DEPENDENCY_STATUS_OK)
+ .setConnectivityDependencyStatus(DEPENDENCY_STATUS_OK)
+ .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_OK)
.build();
TimeZoneProviderStatus status2 = new TimeZoneProviderStatus.Builder()
- .setLocationDetectionStatus(DEPENDENCY_STATUS_WORKING)
- .setConnectivityStatus(DEPENDENCY_STATUS_WORKING)
- .setTimeZoneResolutionStatus(OPERATION_STATUS_FAILED)
+ .setLocationDetectionDependencyStatus(DEPENDENCY_STATUS_OK)
+ .setConnectivityDependencyStatus(DEPENDENCY_STATUS_OK)
+ .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_FAILED)
.build();
TimeZoneProviderEvent uncertain1v1 =
@@ -123,14 +208,14 @@
@Test
public void isEquivalentToAndEquals_suggestion() {
TimeZoneProviderStatus status1 = new TimeZoneProviderStatus.Builder()
- .setLocationDetectionStatus(DEPENDENCY_STATUS_WORKING)
- .setConnectivityStatus(DEPENDENCY_STATUS_WORKING)
- .setTimeZoneResolutionStatus(OPERATION_STATUS_WORKING)
+ .setLocationDetectionDependencyStatus(DEPENDENCY_STATUS_OK)
+ .setConnectivityDependencyStatus(DEPENDENCY_STATUS_OK)
+ .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_OK)
.build();
TimeZoneProviderStatus status2 = new TimeZoneProviderStatus.Builder()
- .setLocationDetectionStatus(DEPENDENCY_STATUS_WORKING)
- .setConnectivityStatus(DEPENDENCY_STATUS_WORKING)
- .setTimeZoneResolutionStatus(OPERATION_STATUS_FAILED)
+ .setLocationDetectionDependencyStatus(DEPENDENCY_STATUS_OK)
+ .setConnectivityDependencyStatus(DEPENDENCY_STATUS_OK)
+ .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_FAILED)
.build();
TimeZoneProviderSuggestion suggestion1 = new TimeZoneProviderSuggestion.Builder()
.setElapsedRealtimeMillis(1111L)
@@ -194,7 +279,13 @@
@Test
public void testParcelable_uncertain() {
TimeZoneProviderEvent event = TimeZoneProviderEvent.createUncertainEvent(
- 1111L, TimeZoneProviderStatus.UNKNOWN);
+ 1111L, ARBITRARY_TIME_ZONE_PROVIDER_STATUS);
+ assertRoundTripParcelable(event);
+ }
+
+ @Test
+ public void testParcelable_uncertain_legacy() {
+ TimeZoneProviderEvent event = TimeZoneProviderEvent.createUncertainEvent(1111L, null);
assertRoundTripParcelable(event);
}
@@ -204,7 +295,17 @@
.setTimeZoneIds(Arrays.asList("Europe/London", "Europe/Paris"))
.build();
TimeZoneProviderEvent event = TimeZoneProviderEvent.createSuggestionEvent(
- 1111L, suggestion, TimeZoneProviderStatus.UNKNOWN);
+ 1111L, suggestion, ARBITRARY_TIME_ZONE_PROVIDER_STATUS);
+ assertRoundTripParcelable(event);
+ }
+
+ @Test
+ public void testParcelable_suggestion_legacy() {
+ TimeZoneProviderSuggestion suggestion = new TimeZoneProviderSuggestion.Builder()
+ .setTimeZoneIds(Arrays.asList("Europe/London", "Europe/Paris"))
+ .build();
+ TimeZoneProviderEvent event = TimeZoneProviderEvent.createSuggestionEvent(
+ 1111L, suggestion, null);
assertRoundTripParcelable(event);
}
diff --git a/core/tests/coretests/src/android/service/timezone/TimeZoneProviderStatusTest.java b/core/tests/coretests/src/android/service/timezone/TimeZoneProviderStatusTest.java
index d61c33c..b7a595c 100644
--- a/core/tests/coretests/src/android/service/timezone/TimeZoneProviderStatusTest.java
+++ b/core/tests/coretests/src/android/service/timezone/TimeZoneProviderStatusTest.java
@@ -18,9 +18,10 @@
import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT;
-import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_WORKING;
+import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS;
+import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_OK;
import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_FAILED;
-import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_WORKING;
+import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_OK;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
@@ -32,33 +33,44 @@
public class TimeZoneProviderStatusTest {
@Test
+ public void parseProviderStatus() {
+ TimeZoneProviderStatus status = new TimeZoneProviderStatus.Builder()
+ .setConnectivityDependencyStatus(DEPENDENCY_STATUS_OK)
+ .setLocationDetectionDependencyStatus(DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS)
+ .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_OK)
+ .build();
+
+ assertEquals(status, TimeZoneProviderStatus.parseProviderStatus(status.toString()));
+ }
+
+ @Test
public void testStatusValidation() {
TimeZoneProviderStatus status = new TimeZoneProviderStatus.Builder()
- .setLocationDetectionStatus(DEPENDENCY_STATUS_WORKING)
- .setConnectivityStatus(DEPENDENCY_STATUS_WORKING)
- .setTimeZoneResolutionStatus(DEPENDENCY_STATUS_WORKING)
+ .setLocationDetectionDependencyStatus(DEPENDENCY_STATUS_OK)
+ .setConnectivityDependencyStatus(DEPENDENCY_STATUS_OK)
+ .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_OK)
.build();
assertThrows(IllegalArgumentException.class,
() -> new TimeZoneProviderStatus.Builder(status)
- .setLocationDetectionStatus(-1)
+ .setLocationDetectionDependencyStatus(-1)
.build());
assertThrows(IllegalArgumentException.class,
() -> new TimeZoneProviderStatus.Builder(status)
- .setConnectivityStatus(-1)
+ .setConnectivityDependencyStatus(-1)
.build());
assertThrows(IllegalArgumentException.class,
() -> new TimeZoneProviderStatus.Builder(status)
- .setTimeZoneResolutionStatus(-1)
+ .setTimeZoneResolutionOperationStatus(-1)
.build());
}
@Test
public void testEqualsAndHashcode() {
TimeZoneProviderStatus status1_1 = new TimeZoneProviderStatus.Builder()
- .setLocationDetectionStatus(DEPENDENCY_STATUS_WORKING)
- .setConnectivityStatus(DEPENDENCY_STATUS_WORKING)
- .setTimeZoneResolutionStatus(OPERATION_STATUS_WORKING)
+ .setLocationDetectionDependencyStatus(DEPENDENCY_STATUS_OK)
+ .setConnectivityDependencyStatus(DEPENDENCY_STATUS_OK)
+ .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_OK)
.build();
assertEqualsAndHashcode(status1_1, status1_1);
assertNotEquals(status1_1, null);
@@ -72,21 +84,21 @@
{
TimeZoneProviderStatus status2 = new TimeZoneProviderStatus.Builder(status1_1)
- .setLocationDetectionStatus(DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT)
+ .setLocationDetectionDependencyStatus(DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT)
.build();
assertNotEquals(status1_1, status2);
}
{
TimeZoneProviderStatus status2 = new TimeZoneProviderStatus.Builder(status1_1)
- .setConnectivityStatus(DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT)
+ .setConnectivityDependencyStatus(DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT)
.build();
assertNotEquals(status1_1, status2);
}
{
TimeZoneProviderStatus status2 = new TimeZoneProviderStatus.Builder(status1_1)
- .setTimeZoneResolutionStatus(OPERATION_STATUS_FAILED)
+ .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_FAILED)
.build();
assertNotEquals(status1_1, status2);
}
@@ -101,9 +113,9 @@
@Test
public void testParcelable() {
TimeZoneProviderStatus status = new TimeZoneProviderStatus.Builder()
- .setLocationDetectionStatus(DEPENDENCY_STATUS_WORKING)
- .setConnectivityStatus(DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT)
- .setTimeZoneResolutionStatus(OPERATION_STATUS_FAILED)
+ .setLocationDetectionDependencyStatus(DEPENDENCY_STATUS_OK)
+ .setConnectivityDependencyStatus(DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT)
+ .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_FAILED)
.build();
assertRoundTripParcelable(status);
}
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index 44bb062..ddcb175 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -100,12 +100,12 @@
mImeConsumer.onWindowFocusGained(true);
mController.show(WindowInsets.Type.ime(), true /* fromIme */);
mController.cancelExistingAnimations();
- assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
+ assertTrue((mController.getRequestedVisibleTypes() & WindowInsets.Type.ime()) != 0);
// test if setVisibility can hide IME
mController.hide(WindowInsets.Type.ime(), true /* fromIme */);
mController.cancelExistingAnimations();
- assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
+ assertFalse((mController.getRequestedVisibleTypes() & WindowInsets.Type.ime()) != 0);
});
}
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index d0f7fe04..e9cd8ad 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -27,7 +27,6 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -90,7 +89,6 @@
mInsetsState = new InsetsState();
mInsetsState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 500, 100));
mInsetsState.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(400, 0, 500, 500));
- doNothing().when(mMockController).onRequestedVisibilityChanged(any());
InsetsSourceConsumer topConsumer = new InsetsSourceConsumer(ITYPE_STATUS_BAR, mInsetsState,
() -> mMockTransaction, mMockController);
topConsumer.setControl(
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 5e12313..409bae8 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -255,10 +255,7 @@
@Test
public void testAnimationEndState() {
- InsetsSourceControl[] controls = prepareControls();
- InsetsSourceControl navBar = controls[0];
- InsetsSourceControl statusBar = controls[1];
- InsetsSourceControl ime = controls[2];
+ prepareControls();
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained(true);
@@ -267,16 +264,13 @@
mController.show(all());
// quickly jump to final state by cancelling it.
mController.cancelExistingAnimations();
- assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
- assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
- assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
+ final @InsetsType int types = navigationBars() | statusBars() | ime();
+ assertEquals(types, mController.getRequestedVisibleTypes() & types);
mController.hide(ime(), true /* fromIme */);
mController.hide(all());
mController.cancelExistingAnimations();
- assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
- assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
+ assertEquals(0, mController.getRequestedVisibleTypes() & types);
mController.getSourceConsumer(ITYPE_IME).onWindowFocusLost();
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -290,10 +284,10 @@
mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained(true);
mController.show(ime(), true /* fromIme */);
mController.cancelExistingAnimations();
- assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
+ assertTrue(isRequestedVisible(mController, ime()));
mController.hide(ime(), true /* fromIme */);
mController.cancelExistingAnimations();
- assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
+ assertFalse(isRequestedVisible(mController, ime()));
mController.getSourceConsumer(ITYPE_IME).onWindowFocusLost();
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -307,26 +301,22 @@
InsetsSourceControl ime = controls[2];
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- int types = navigationBars() | systemBars();
+ int types = navigationBars() | statusBars();
// test hide select types.
mController.hide(types);
- assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
- assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_STATUS_BAR));
+ assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(navigationBars()));
+ assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(statusBars()));
mController.cancelExistingAnimations();
- assertEquals(ANIMATION_TYPE_NONE, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
- assertEquals(ANIMATION_TYPE_NONE, mController.getAnimationType(ITYPE_STATUS_BAR));
- assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
- assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
+ assertEquals(ANIMATION_TYPE_NONE, mController.getAnimationType(navigationBars()));
+ assertEquals(ANIMATION_TYPE_NONE, mController.getAnimationType(statusBars()));
+ assertEquals(0, mController.getRequestedVisibleTypes() & (types | ime()));
- // test hide all
+ // test show all
mController.show(types);
- assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
- assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_STATUS_BAR));
+ assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(navigationBars()));
+ assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(statusBars()));
mController.cancelExistingAnimations();
- assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
- assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
+ assertEquals(types, mController.getRequestedVisibleTypes() & (types | ime()));
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
@@ -339,33 +329,27 @@
InsetsSourceControl ime = controls[2];
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- int types = navigationBars() | systemBars();
+ int types = navigationBars() | statusBars();
// test show select types.
mController.show(types);
mController.cancelExistingAnimations();
- assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
- assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
+ assertEquals(types, mController.getRequestedVisibleTypes() & types);
+ assertEquals(0, mController.getRequestedVisibleTypes() & ime());
// test hide all
mController.hide(all());
mController.cancelExistingAnimations();
- assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
- assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
+ assertEquals(0, mController.getRequestedVisibleTypes() & (types | ime()));
// test single show
mController.show(navigationBars());
mController.cancelExistingAnimations();
- assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
- assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
+ assertEquals(navigationBars(),
+ mController.getRequestedVisibleTypes() & (types | ime()));
// test single hide
mController.hide(navigationBars());
- assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
- assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
+ assertEquals(0, mController.getRequestedVisibleTypes() & (types | ime()));
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -373,49 +357,38 @@
@Test
public void testShowHideMultiple() {
- InsetsSourceControl[] controls = prepareControls();
- InsetsSourceControl navBar = controls[0];
- InsetsSourceControl statusBar = controls[1];
- InsetsSourceControl ime = controls[2];
+ prepareControls();
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
// start two animations and see if previous is cancelled and final state is reached.
mController.hide(navigationBars());
mController.hide(systemBars());
- assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
- assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_STATUS_BAR));
+ int types = navigationBars() | statusBars();
+ assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(navigationBars()));
+ assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(statusBars()));
mController.cancelExistingAnimations();
- assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
- assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
+ assertEquals(0, mController.getRequestedVisibleTypes() & (types | ime()));
mController.show(navigationBars());
mController.show(systemBars());
- assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
- assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_STATUS_BAR));
+ assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(navigationBars()));
+ assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(statusBars()));
mController.cancelExistingAnimations();
- assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
- assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
+ assertEquals(types, mController.getRequestedVisibleTypes() & (types | ime()));
- int types = navigationBars() | systemBars();
// show two at a time and hide one by one.
mController.show(types);
mController.hide(navigationBars());
- assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
- assertEquals(ANIMATION_TYPE_NONE, mController.getAnimationType(ITYPE_STATUS_BAR));
+ assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(navigationBars()));
+ assertEquals(ANIMATION_TYPE_NONE, mController.getAnimationType(statusBars()));
mController.cancelExistingAnimations();
- assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
- assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
+ assertEquals(statusBars(), mController.getRequestedVisibleTypes() & (types | ime()));
mController.hide(systemBars());
- assertEquals(ANIMATION_TYPE_NONE, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
- assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_STATUS_BAR));
+ assertEquals(ANIMATION_TYPE_NONE, mController.getAnimationType(navigationBars()));
+ assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(statusBars()));
mController.cancelExistingAnimations();
- assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
- assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
+ assertEquals(0, mController.getRequestedVisibleTypes() & (types | ime()));
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
@@ -428,20 +401,16 @@
InsetsSourceControl ime = controls[2];
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- int types = navigationBars() | systemBars();
+ int types = navigationBars() | statusBars();
// show two at a time and hide one by one.
mController.show(types);
mController.hide(navigationBars());
mController.cancelExistingAnimations();
- assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
- assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
+ assertEquals(statusBars(), mController.getRequestedVisibleTypes() & (types | ime()));
mController.hide(systemBars());
mController.cancelExistingAnimations();
- assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
- assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
+ assertEquals(0, mController.getRequestedVisibleTypes() & (types | ime()));
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
@@ -453,7 +422,7 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mController.hide(statusBars());
mController.cancelExistingAnimations();
- assertFalse(mController.getSourceConsumer(ITYPE_STATUS_BAR).isRequestedVisible());
+ assertFalse(isRequestedVisible(mController, statusBars()));
assertFalse(mController.getState().getSource(ITYPE_STATUS_BAR).isVisible());
// Loosing control
@@ -461,14 +430,14 @@
state.setSourceVisible(ITYPE_STATUS_BAR, true);
mController.onStateChanged(state);
mController.onControlsChanged(new InsetsSourceControl[0]);
- assertFalse(mController.getSourceConsumer(ITYPE_STATUS_BAR).isRequestedVisible());
+ assertFalse(isRequestedVisible(mController, statusBars()));
assertTrue(mController.getState().getSource(ITYPE_STATUS_BAR).isVisible());
// Gaining control
mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
- assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_STATUS_BAR));
+ assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(statusBars()));
mController.cancelExistingAnimations();
- assertFalse(mController.getSourceConsumer(ITYPE_STATUS_BAR).isRequestedVisible());
+ assertFalse(isRequestedVisible(mController, statusBars()));
assertFalse(mController.getState().getSource(ITYPE_STATUS_BAR).isVisible());
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -488,9 +457,9 @@
// Gaining control shortly after
mController.onControlsChanged(createSingletonControl(ITYPE_IME));
- assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_IME));
+ assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ime()));
mController.cancelExistingAnimations();
- assertTrue(mController.getSourceConsumer(ITYPE_IME).isRequestedVisible());
+ assertTrue(isRequestedVisible(mController, ime()));
assertTrue(mController.getState().getSource(ITYPE_IME).isVisible());
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -509,9 +478,9 @@
// Pretend IME is calling
mController.show(ime(), true /* fromIme */);
- assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_IME));
+ assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ime()));
mController.cancelExistingAnimations();
- assertTrue(mController.getSourceConsumer(ITYPE_IME).isRequestedVisible());
+ assertTrue(isRequestedVisible(mController, ime()));
assertTrue(mController.getState().getSource(ITYPE_IME).isVisible());
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -538,7 +507,7 @@
});
waitUntilNextFrame();
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- assertFalse(mController.getSourceConsumer(ITYPE_STATUS_BAR).isRequestedVisible());
+ assertFalse(isRequestedVisible(mController, statusBars()));
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
@@ -565,7 +534,7 @@
});
waitUntilNextFrame();
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- assertFalse(mController.getSourceConsumer(ITYPE_STATUS_BAR).isRequestedVisible());
+ assertFalse(isRequestedVisible(mController, statusBars()));
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
@@ -701,6 +670,7 @@
private void doTestResizeAnimation_insetsTypes(@InternalInsetsType int type,
@AnimationType int expectedAnimationType) {
+ final @InsetsType int publicType = InsetsState.toPublicType(type);
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
final InsetsState state1 = new InsetsState();
state1.getSource(type).setVisible(true);
@@ -711,15 +681,15 @@
// New insets source won't cause the resize animation.
mController.onStateChanged(state1);
- assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type));
+ assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(publicType));
// Changing frame might cause the resize animation. This depends on the insets type.
mController.onStateChanged(state2);
- assertEquals(message, expectedAnimationType, mController.getAnimationType(type));
+ assertEquals(message, expectedAnimationType, mController.getAnimationType(publicType));
// Cancel the existing animations for the next iteration.
mController.cancelExistingAnimations();
- assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type));
+ assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(publicType));
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
@@ -728,6 +698,7 @@
public void testResizeAnimation_displayFrame() {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
final @InternalInsetsType int type = ITYPE_STATUS_BAR;
+ final @InsetsType int publicType = statusBars();
final InsetsState state1 = new InsetsState();
state1.setDisplayFrame(new Rect(0, 0, 500, 1000));
state1.getSource(type).setFrame(0, 0, 500, 50);
@@ -738,11 +709,11 @@
// New insets source won't cause the resize animation.
mController.onStateChanged(state1);
- assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type));
+ assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(publicType));
// Changing frame won't cause the resize animation if the display frame is also changed.
mController.onStateChanged(state2);
- assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type));
+ assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(publicType));
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
@@ -751,6 +722,7 @@
public void testResizeAnimation_visibility() {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
final @InternalInsetsType int type = ITYPE_STATUS_BAR;
+ final @InsetsType int publicType = statusBars();
final InsetsState state1 = new InsetsState();
state1.getSource(type).setVisible(true);
state1.getSource(type).setFrame(0, 0, 500, 50);
@@ -764,17 +736,17 @@
// New insets source won't cause the resize animation.
mController.onStateChanged(state1);
- assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type));
+ assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(publicType));
// Changing source visibility (visible --> invisible) won't cause the resize animation.
// The previous source and the current one must be both visible.
mController.onStateChanged(state2);
- assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type));
+ assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(publicType));
// Changing source visibility (invisible --> visible) won't cause the resize animation.
// The previous source and the current one must be both visible.
mController.onStateChanged(state3);
- assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type));
+ assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(publicType));
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
@@ -940,10 +912,10 @@
// Verify IME requested visibility should be updated to IME consumer from controller.
mController.show(ime());
- assertTrue(imeInsetsConsumer.isRequestedVisible());
+ assertTrue(isRequestedVisible(mController, ime()));
mController.hide(ime());
- assertFalse(imeInsetsConsumer.isRequestedVisible());
+ assertFalse(isRequestedVisible(mController, ime()));
});
}
@@ -980,6 +952,10 @@
return controls;
}
+ private static boolean isRequestedVisible(InsetsController controller, @InsetsType int type) {
+ return (controller.getRequestedVisibleTypes() & type) != 0;
+ }
+
public static class TestHost extends ViewRootInsetsControllerHost {
private @InsetsType int mRequestedVisibleTypes = defaultVisible();
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
index 8cf118c..1253278 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -122,7 +122,6 @@
public void testHide() {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mConsumer.hide();
- assertFalse("Consumer should not be visible", mConsumer.isRequestedVisible());
verify(mSpyInsetsSource).setVisible(eq(false));
});
@@ -134,7 +133,6 @@
// Insets source starts out visible
mConsumer.hide();
mConsumer.show(false /* fromIme */);
- assertTrue("Consumer should be visible", mConsumer.isRequestedVisible());
verify(mSpyInsetsSource).setVisible(eq(false));
verify(mSpyInsetsSource).setVisible(eq(true));
});
@@ -240,7 +238,7 @@
// visibility won't be updated when the consumer received the same leash in setControl.
insetsController.controlWindowInsetsAnimation(ime(), 0L,
null /* interpolator */, null /* cancellationSignal */, null /* listener */);
- assertTrue(insetsController.getAnimationType(ITYPE_IME) == ANIMATION_TYPE_USER);
+ assertEquals(ANIMATION_TYPE_USER, insetsController.getAnimationType(ime()));
imeConsumer.setControl(new InsetsSourceControl(ITYPE_IME, mLeash,
true /* initialVisible */, new Point(), Insets.NONE), new int[1], new int[1]);
verify(mMockTransaction, never()).show(mLeash);
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index 99670d9..cc02bbb 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -46,7 +46,7 @@
// The number of fields tested in the corresponding CTS AccessibilityNodeInfoTest:
// See fullyPopulateAccessibilityNodeInfo, assertEqualsAccessibilityNodeInfo,
// and assertAccessibilityNodeInfoCleared in that class.
- private static final int NUM_MARSHALLED_PROPERTIES = 40;
+ private static final int NUM_MARSHALLED_PROPERTIES = 42;
/**
* The number of properties that are purposely not marshalled
diff --git a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
index 599cf97..6e73b9f 100644
--- a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
@@ -52,6 +52,8 @@
import org.junit.runner.RunWith;
import java.util.Arrays;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* Supplemental tests that cannot be covered by CTS (e.g. due to hidden API dependencies).
@@ -500,6 +502,7 @@
+ "prefix: extras=null\n"
+ "prefix: hintLocales=null\n"
+ "prefix: supportedHandwritingGestureTypes=(none)\n"
+ + "prefix: supportedHandwritingGesturePreviewTypes=(none)\n"
+ "prefix: contentMimeTypes=null\n");
}
@@ -516,6 +519,8 @@
info.label = "testLabel";
info.setInitialToolType(MotionEvent.TOOL_TYPE_STYLUS);
info.setSupportedHandwritingGestures(Arrays.asList(SelectGesture.class));
+ info.setSupportedHandwritingGesturePreviews(
+ Stream.of(SelectGesture.class).collect(Collectors.toSet()));
info.packageName = "android.view.inputmethod";
info.autofillId = new AutofillId(123);
info.fieldId = 456;
@@ -538,6 +543,7 @@
+ "prefix2: extras=Bundle[{testKey=testValue}]\n"
+ "prefix2: hintLocales=[en,es,zh]\n"
+ "prefix2: supportedHandwritingGestureTypes=SELECT\n"
+ + "prefix2: supportedHandwritingGesturePreviewTypes=SELECT\n"
+ "prefix2: contentMimeTypes=[image/png]\n"
+ "prefix2: targetInputMethodUserId=10\n");
}
@@ -558,6 +564,7 @@
+ "prefix: packageName=null autofillId=null fieldId=0 fieldName=null\n"
+ "prefix: hintLocales=null\n"
+ "prefix: supportedHandwritingGestureTypes=(none)\n"
+ + "prefix: supportedHandwritingGesturePreviewTypes=(none)\n"
+ "prefix: contentMimeTypes=null\n");
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityOverrideData.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityOverrideData.java
index 499f7a5..875cd0b 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityOverrideData.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityOverrideData.java
@@ -24,11 +24,13 @@
import android.database.Cursor;
import android.graphics.Bitmap;
import android.os.UserHandle;
+import android.util.Pair;
import com.android.internal.app.chooser.TargetInfo;
import com.android.internal.logging.MetricsLogger;
import java.util.List;
+import java.util.function.BiFunction;
import java.util.function.Function;
/**
@@ -50,6 +52,9 @@
public Function<PackageManager, PackageManager> createPackageManager;
public Function<TargetInfo, Boolean> onSafelyStartCallback;
public Function<ChooserListAdapter, Void> onQueryDirectShareTargets;
+ public BiFunction<
+ IChooserWrapper, ChooserListAdapter, Pair<Integer, ChooserActivity.ServiceResultInfo[]>>
+ directShareTargets;
public ResolverListController resolverListController;
public ResolverListController workResolverListController;
public Boolean isVoiceInteraction;
@@ -72,6 +77,7 @@
public void reset() {
onSafelyStartCallback = null;
onQueryDirectShareTargets = null;
+ directShareTargets = null;
isVoiceInteraction = null;
createPackageManager = null;
previewThumbnail = null;
@@ -112,4 +118,3 @@
private ChooserActivityOverrideData() {}
}
-
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index e8c7ce0..d656678 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -81,6 +81,7 @@
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.service.chooser.ChooserTarget;
+import android.util.Pair;
import android.view.View;
import androidx.annotation.CallSuper;
@@ -89,6 +90,7 @@
import androidx.test.rule.ActivityTestRule;
import com.android.internal.R;
+import com.android.internal.app.ChooserActivity.ServiceResultInfo;
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
import com.android.internal.app.chooser.DisplayResolveInfo;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
@@ -2166,8 +2168,8 @@
assertThat(logger.numCalls(), is(6));
}
- @Test @Ignore
- public void testDirectTargetLogging() throws InterruptedException {
+ @Test
+ public void testDirectTargetLogging() {
Intent sendIntent = createSendTextIntent();
// We need app targets for direct targets to get displayed
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
@@ -2187,30 +2189,35 @@
resolvedComponentInfos.get(0).getResolveInfoAt(0).activityInfo.packageName);
ResolveInfo ri = ResolverDataProvider.createResolveInfo(3, 0);
+ ChooserActivityOverrideData
+ .getInstance()
+ .directShareTargets = (activity, adapter) -> {
+ DisplayResolveInfo displayInfo = activity.createTestDisplayResolveInfo(
+ sendIntent,
+ ri,
+ "testLabel",
+ "testInfo",
+ sendIntent,
+ /* resolveInfoPresentationGetter */ null);
+ ServiceResultInfo[] results = {
+ new ServiceResultInfo(
+ displayInfo,
+ serviceTargets,
+ adapter.getUserHandle())};
+ // TODO: consider covering the other type.
+ // Only 2 types are expected out of the shortcut loading logic:
+ // - TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER, if shortcuts were loaded from
+ // the ShortcutManager, and;
+ // - TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE, if shortcuts were loaded
+ // from AppPredictor.
+ // Ideally, our tests should cover all of them.
+ return new Pair<>(TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER, results);
+ };
+
// Start activity
final IChooserWrapper activity = (IChooserWrapper)
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- // Insert the direct share target
- Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos = new HashMap<>();
- directShareToShortcutInfos.put(serviceTargets.get(0), null);
- InstrumentationRegistry.getInstrumentation().runOnMainSync(
- () -> activity.getAdapter().addServiceResults(
- activity.createTestDisplayResolveInfo(sendIntent,
- ri,
- "testLabel",
- "testInfo",
- sendIntent,
- /* resolveInfoPresentationGetter */ null),
- serviceTargets,
- TARGET_TYPE_CHOOSER_TARGET,
- directShareToShortcutInfos)
- );
- // Thread.sleep shouldn't be a thing in an integration test but it's
- // necessary here because of the way the code is structured
- // TODO: restructure the tests b/129870719
- Thread.sleep(((ChooserActivity) activity).mListViewUpdateDelayMs);
-
assertThat("Chooser should have 3 targets (2 apps, 1 direct)",
activity.getAdapter().getCount(), is(3));
assertThat("Chooser should have exactly one selectable direct target",
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index 7f85982..4c3235c 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -31,6 +31,7 @@
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.UserHandle;
+import android.util.Pair;
import android.util.Size;
import com.android.internal.app.ResolverListAdapter.ResolveInfoPresentationGetter;
@@ -239,6 +240,12 @@
@Override
protected void queryDirectShareTargets(ChooserListAdapter adapter,
boolean skipAppPredictionService) {
+ if (sOverrides.directShareTargets != null) {
+ Pair<Integer, ServiceResultInfo[]> result =
+ sOverrides.directShareTargets.apply(this, adapter);
+ sendShortcutManagerShareTargetResults(result.first, result.second);
+ return;
+ }
if (sOverrides.onQueryDirectShareTargets != null) {
sOverrides.onQueryDirectShareTargets.apply(adapter);
}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index ca543f4..4cc06e3 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1357,6 +1357,12 @@
"group": "WM_DEBUG_ANIM",
"at": "com\/android\/server\/wm\/WindowState.java"
},
+ "-787664727": {
+ "message": "Cannot launch dream activity due to invalid state. dream component: %s packageName: %s",
+ "level": "ERROR",
+ "group": "WM_DEBUG_DREAM",
+ "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+ },
"-784959154": {
"message": "Attempted to add private presentation window to a non-private display. Aborting.",
"level": "WARN",
@@ -1951,6 +1957,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/TaskFragment.java"
},
+ "-240296576": {
+ "message": "handleAppTransitionReady: displayId=%d appTransition={%s} openingApps=[%s] closingApps=[%s] transit=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/AppTransitionController.java"
+ },
"-237664290": {
"message": "Pause the recording session on display %s",
"level": "VERBOSE",
@@ -2053,12 +2065,6 @@
"group": "WM_DEBUG_CONTENT_RECORDING",
"at": "com\/android\/server\/wm\/ContentRecorder.java"
},
- "-134793542": {
- "message": "handleAppTransitionReady: displayId=%d appTransition={%s} excludeLauncherFromAnimation=%b openingApps=[%s] closingApps=[%s] transit=%s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_APP_TRANSITIONS",
- "at": "com\/android\/server\/wm\/AppTransitionController.java"
- },
"-134091882": {
"message": "Screenshotting Activity %s",
"level": "VERBOSE",
@@ -2173,12 +2179,6 @@
"group": "WM_DEBUG_ANIM",
"at": "com\/android\/server\/wm\/WindowContainer.java"
},
- "-23020844": {
- "message": "Back: Reset surfaces",
- "level": "DEBUG",
- "group": "WM_DEBUG_BACK_PREVIEW",
- "at": "com\/android\/server\/wm\/BackNavigationController.java"
- },
"-21399771": {
"message": "activity %s already destroying, skipping request with reason:%s",
"level": "VERBOSE",
@@ -2599,6 +2599,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "323235828": {
+ "message": "Delaying app transition for recents animation to finish",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/AppTransitionController.java"
+ },
"327461496": {
"message": "Complete pause: %s",
"level": "VERBOSE",
@@ -2887,6 +2893,12 @@
"group": "WM_DEBUG_BOOT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "601283564": {
+ "message": "Dream packageName does not match active dream. Package %s does not match %s",
+ "level": "ERROR",
+ "group": "WM_DEBUG_DREAM",
+ "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+ },
"608694300": {
"message": " NEW SURFACE SESSION %s",
"level": "INFO",
@@ -3127,12 +3139,6 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/WindowStateAnimator.java"
},
- "829869827": {
- "message": "Cannot launch dream activity due to invalid state. dreaming: %b packageName: %s",
- "level": "ERROR",
- "group": "WM_DEBUG_DREAM",
- "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
- },
"835814848": {
"message": "%s",
"level": "INFO",
@@ -3811,12 +3817,6 @@
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "1544805551": {
- "message": "Skipping app transition animation. task=%s",
- "level": "DEBUG",
- "group": "WM_DEBUG_BACK_PREVIEW",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"1557732761": {
"message": "For Intent %s bringing to top: %s",
"level": "DEBUG",
@@ -4153,12 +4153,6 @@
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/TaskOrganizerController.java"
},
- "1918771553": {
- "message": "Dream packageName does not match active dream. Package %s does not match %s or %s",
- "level": "ERROR",
- "group": "WM_DEBUG_DREAM",
- "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
- },
"1921821199": {
"message": "Preserving %s until the new one is added",
"level": "VERBOSE",
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index dbd918e..6245598 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -30,6 +30,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECParameterSpec;
import java.security.spec.MGF1ParameterSpec;
import java.util.Collection;
import java.util.Locale;
@@ -914,6 +915,51 @@
}
/**
+ * @hide
+ */
+ public abstract static class EcCurve {
+ private EcCurve() {}
+
+ /**
+ * @hide
+ */
+ public static int toKeymasterCurve(ECParameterSpec spec) {
+ int keySize = spec.getCurve().getField().getFieldSize();
+ switch (keySize) {
+ case 224:
+ return android.hardware.security.keymint.EcCurve.P_224;
+ case 256:
+ return android.hardware.security.keymint.EcCurve.P_256;
+ case 384:
+ return android.hardware.security.keymint.EcCurve.P_384;
+ case 521:
+ return android.hardware.security.keymint.EcCurve.P_521;
+ default:
+ return -1;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static int fromKeymasterCurve(int ecCurve) {
+ switch (ecCurve) {
+ case android.hardware.security.keymint.EcCurve.P_224:
+ return 224;
+ case android.hardware.security.keymint.EcCurve.P_256:
+ case android.hardware.security.keymint.EcCurve.CURVE_25519:
+ return 256;
+ case android.hardware.security.keymint.EcCurve.P_384:
+ return 384;
+ case android.hardware.security.keymint.EcCurve.P_521:
+ return 521;
+ default:
+ return -1;
+ }
+ }
+ }
+
+ /**
* Namespaces provide system developers and vendors with a way to use keystore without
* requiring an applications uid. Namespaces can be configured using SEPolicy.
* See <a href="https://source.android.com/security/keystore#access-control">
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
index 5216a90..ace2053 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
@@ -203,6 +203,11 @@
for (Authorization a : key.getAuthorizations()) {
if (a.keyParameter.tag == KeymasterDefs.KM_TAG_KEY_SIZE) {
keySizeBits = KeyStore2ParameterUtils.getUnsignedInt(a);
+ break;
+ } else if (a.keyParameter.tag == KeymasterDefs.KM_TAG_EC_CURVE) {
+ keySizeBits = KeyProperties.EcCurve.fromKeymasterCurve(
+ a.keyParameter.value.getEcCurve());
+ break;
}
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 94bf122..7a320ba 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -66,6 +66,7 @@
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
+import java.security.interfaces.ECKey;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -566,6 +567,22 @@
spec.getMaxUsageCount()
));
}
+ if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(key.getAlgorithm())) {
+ if (key instanceof ECKey) {
+ ECKey ecKey = (ECKey) key;
+ importArgs.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_EC_CURVE,
+ KeyProperties.EcCurve.toKeymasterCurve(ecKey.getParams())
+ ));
+ }
+ }
+ /* TODO: check for Ed25519(EdDSA) or X25519(XDH) key algorithm and
+ * add import args for KM_TAG_EC_CURVE as EcCurve.CURVE_25519.
+ * Currently conscrypt does not support EdDSA key import and XDH keys are not an
+ * instance of XECKey, hence these conditions are not added, once it is fully
+ * implemented by conscrypt, we can add CURVE_25519 argument for EdDSA and XDH
+ * algorithms.
+ */
} catch (IllegalArgumentException | IllegalStateException e) {
throw new KeyStoreException(e);
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 16760e26..a7fa2d9 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -96,7 +96,7 @@
ActivityEmbeddingComponent {
static final String TAG = "SplitController";
static final boolean ENABLE_SHELL_TRANSITIONS =
- SystemProperties.getBoolean("persist.wm.debug.shell_transit", false);
+ SystemProperties.getBoolean("persist.wm.debug.shell_transit", true);
@VisibleForTesting
@GuardedBy("mLock")
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index fc0c20e..42da075 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -88,4 +88,9 @@
<string name="close_button_text" msgid="2913281996024033299">"Maak toe"</string>
<string name="back_button_text" msgid="1469718707134137085">"Terug"</string>
<string name="handle_text" msgid="1766582106752184456">"Handvatsel"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Volskerm"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Rekenaarmodus"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Verdeelde skerm"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Meer"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Sweef"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index 405cb06..97ae8b3 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"ዝጋ"</string>
<string name="back_button_text" msgid="1469718707134137085">"ተመለስ"</string>
<string name="handle_text" msgid="1766582106752184456">"መያዣ"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index 9321d8d..83710f5 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"إغلاق"</string>
<string name="back_button_text" msgid="1469718707134137085">"رجوع"</string>
<string name="handle_text" msgid="1766582106752184456">"مقبض"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index 268827c..349f3502 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"বন্ধ কৰক"</string>
<string name="back_button_text" msgid="1469718707134137085">"উভতি যাওক"</string>
<string name="handle_text" msgid="1766582106752184456">"হেণ্ডেল"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index 779bfb6..ad7ad9a 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Bağlayın"</string>
<string name="back_button_text" msgid="1469718707134137085">"Geriyə"</string>
<string name="handle_text" msgid="1766582106752184456">"Hər kəsə açıq istifadəçi adı"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</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 1e78b3c..d3e010b 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Zatvorite"</string>
<string name="back_button_text" msgid="1469718707134137085">"Nazad"</string>
<string name="handle_text" msgid="1766582106752184456">"Identifikator"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index 5524c19..e57d2c6 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Закрыць"</string>
<string name="back_button_text" msgid="1469718707134137085">"Назад"</string>
<string name="handle_text" msgid="1766582106752184456">"Маркер"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index 42dbe3e..14195ef 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Затваряне"</string>
<string name="back_button_text" msgid="1469718707134137085">"Назад"</string>
<string name="handle_text" msgid="1766582106752184456">"Манипулатор"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index 31a11cd..52cde90 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"বন্ধ করুন"</string>
<string name="back_button_text" msgid="1469718707134137085">"ফিরে যান"</string>
<string name="handle_text" msgid="1766582106752184456">"হাতল"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index 1d674a5..4342700 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -88,4 +88,9 @@
<string name="close_button_text" msgid="2913281996024033299">"Zatvaranje"</string>
<string name="back_button_text" msgid="1469718707134137085">"Nazad"</string>
<string name="handle_text" msgid="1766582106752184456">"Identifikator"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Puni zaslon"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Stolni način rada"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Razdvojeni zaslon"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Više"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Plutajući"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index 3346938..cd6b00d 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Tanca"</string>
<string name="back_button_text" msgid="1469718707134137085">"Enrere"</string>
<string name="handle_text" msgid="1766582106752184456">"Ansa"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index 555c252..99ccf07f 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Zavřít"</string>
<string name="back_button_text" msgid="1469718707134137085">"Zpět"</string>
<string name="handle_text" msgid="1766582106752184456">"Úchyt"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index 829bb91..ccd32e2 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Luk"</string>
<string name="back_button_text" msgid="1469718707134137085">"Tilbage"</string>
<string name="handle_text" msgid="1766582106752184456">"Håndtag"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index b3bd9ea..f261b02 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Schließen"</string>
<string name="back_button_text" msgid="1469718707134137085">"Zurück"</string>
<string name="handle_text" msgid="1766582106752184456">"Ziehpunkt"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index 79e2dab..f35a2a0 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Κλείσιμο"</string>
<string name="back_button_text" msgid="1469718707134137085">"Πίσω"</string>
<string name="handle_text" msgid="1766582106752184456">"Λαβή"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index 6db010a..231c2649 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -88,4 +88,9 @@
<string name="close_button_text" msgid="2913281996024033299">"Close"</string>
<string name="back_button_text" msgid="1469718707134137085">"Back"</string>
<string name="handle_text" msgid="1766582106752184456">"Handle"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Full screen"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Desktop mode"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Split screen"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"More"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Float"</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 6db010a..231c2649 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -88,4 +88,9 @@
<string name="close_button_text" msgid="2913281996024033299">"Close"</string>
<string name="back_button_text" msgid="1469718707134137085">"Back"</string>
<string name="handle_text" msgid="1766582106752184456">"Handle"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Full screen"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Desktop mode"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Split screen"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"More"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Float"</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 6db010a..231c2649 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -88,4 +88,9 @@
<string name="close_button_text" msgid="2913281996024033299">"Close"</string>
<string name="back_button_text" msgid="1469718707134137085">"Back"</string>
<string name="handle_text" msgid="1766582106752184456">"Handle"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Full screen"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Desktop mode"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Split screen"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"More"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Float"</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 6db010a..231c2649 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -88,4 +88,9 @@
<string name="close_button_text" msgid="2913281996024033299">"Close"</string>
<string name="back_button_text" msgid="1469718707134137085">"Back"</string>
<string name="handle_text" msgid="1766582106752184456">"Handle"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Full screen"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Desktop mode"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Split screen"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"More"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Float"</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 37b4fc7..f3e60d2 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
@@ -88,4 +88,9 @@
<string name="close_button_text" msgid="2913281996024033299">"Close"</string>
<string name="back_button_text" msgid="1469718707134137085">"Back"</string>
<string name="handle_text" msgid="1766582106752184456">"Handle"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Fullscreen"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Desktop Mode"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Split Screen"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"More"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Float"</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 1e7174d..fe29baa 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -88,4 +88,9 @@
<string name="close_button_text" msgid="2913281996024033299">"Cerrar"</string>
<string name="back_button_text" msgid="1469718707134137085">"Atrás"</string>
<string name="handle_text" msgid="1766582106752184456">"Controlador"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Pantalla completa"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Modo de escritorio"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Pantalla dividida"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Más"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Flotante"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index 87cdca4..7788f47 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Cerrar"</string>
<string name="back_button_text" msgid="1469718707134137085">"Atrás"</string>
<string name="handle_text" msgid="1766582106752184456">"Controlador"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index f392560..d10fd23 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Sule"</string>
<string name="back_button_text" msgid="1469718707134137085">"Tagasi"</string>
<string name="handle_text" msgid="1766582106752184456">"Käepide"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index 6bc1d91..fbd0fc0 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Itxi"</string>
<string name="back_button_text" msgid="1469718707134137085">"Atzera"</string>
<string name="handle_text" msgid="1766582106752184456">"Kontu-izena"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index 9949dd2..45a56f6 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"بستن"</string>
<string name="back_button_text" msgid="1469718707134137085">"برگشتن"</string>
<string name="handle_text" msgid="1766582106752184456">"دستگیره"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index e701452..15b8bf0 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Sulje"</string>
<string name="back_button_text" msgid="1469718707134137085">"Takaisin"</string>
<string name="handle_text" msgid="1766582106752184456">"Kahva"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index ff8417b..a56232e 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Fermer"</string>
<string name="back_button_text" msgid="1469718707134137085">"Retour"</string>
<string name="handle_text" msgid="1766582106752184456">"Identifiant"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index 8ceeec0..4256d27 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Fermer"</string>
<string name="back_button_text" msgid="1469718707134137085">"Retour"</string>
<string name="handle_text" msgid="1766582106752184456">"Poignée"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index 999921a..5276979 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Pechar"</string>
<string name="back_button_text" msgid="1469718707134137085">"Atrás"</string>
<string name="handle_text" msgid="1766582106752184456">"Controlador"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index 5207e19..c663489 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"બંધ કરો"</string>
<string name="back_button_text" msgid="1469718707134137085">"પાછળ"</string>
<string name="handle_text" msgid="1766582106752184456">"હૅન્ડલ"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index 1b9b90b..0896907 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"बंद करें"</string>
<string name="back_button_text" msgid="1469718707134137085">"वापस जाएं"</string>
<string name="handle_text" msgid="1766582106752184456">"हैंडल"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index 08aa262..8d20c9d1 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -88,4 +88,9 @@
<string name="close_button_text" msgid="2913281996024033299">"Zatvori"</string>
<string name="back_button_text" msgid="1469718707134137085">"Natrag"</string>
<string name="handle_text" msgid="1766582106752184456">"Pokazivač"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Puni zaslon"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Stolni način rada"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Razdvojeni zaslon"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Više"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Plutajući"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index 8ad0a01..27483e5 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Bezárás"</string>
<string name="back_button_text" msgid="1469718707134137085">"Vissza"</string>
<string name="handle_text" msgid="1766582106752184456">"Fogópont"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index b61ea1d..fd799da 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Փակել"</string>
<string name="back_button_text" msgid="1469718707134137085">"Հետ"</string>
<string name="handle_text" msgid="1766582106752184456">"Նշիչ"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index 79e926d..fdc30ae 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Tutup"</string>
<string name="back_button_text" msgid="1469718707134137085">"Kembali"</string>
<string name="handle_text" msgid="1766582106752184456">"Tuas"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index 0645c41..d38c90b 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Loka"</string>
<string name="back_button_text" msgid="1469718707134137085">"Til baka"</string>
<string name="handle_text" msgid="1766582106752184456">"Handfang"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index 9a023f5..455050c 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Chiudi"</string>
<string name="back_button_text" msgid="1469718707134137085">"Indietro"</string>
<string name="handle_text" msgid="1766582106752184456">"Handle"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index 57238ea..396474c 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"סגירה"</string>
<string name="back_button_text" msgid="1469718707134137085">"חזרה"</string>
<string name="handle_text" msgid="1766582106752184456">"נקודת אחיזה"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index d0b5462..8bc3cd2 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"閉じる"</string>
<string name="back_button_text" msgid="1469718707134137085">"戻る"</string>
<string name="handle_text" msgid="1766582106752184456">"ハンドル"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index b0cf539..05161b1 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"დახურვა"</string>
<string name="back_button_text" msgid="1469718707134137085">"უკან"</string>
<string name="handle_text" msgid="1766582106752184456">"იდენტიფიკატორი"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index a9f350e..7ad3aa8 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Жабу"</string>
<string name="back_button_text" msgid="1469718707134137085">"Артқа"</string>
<string name="handle_text" msgid="1766582106752184456">"Идентификатор"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index a1d2691..f85fd83 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"បិទ"</string>
<string name="back_button_text" msgid="1469718707134137085">"ថយក្រោយ"</string>
<string name="handle_text" msgid="1766582106752184456">"ឈ្មោះអ្នកប្រើប្រាស់"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index acad7c1..6475cac 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"ಮುಚ್ಚಿರಿ"</string>
<string name="back_button_text" msgid="1469718707134137085">"ಹಿಂದಕ್ಕೆ"</string>
<string name="handle_text" msgid="1766582106752184456">"ಹ್ಯಾಂಡಲ್"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index f6ea6cc..6837ed3 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"닫기"</string>
<string name="back_button_text" msgid="1469718707134137085">"뒤로"</string>
<string name="handle_text" msgid="1766582106752184456">"핸들"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 9ad82de..2ac4a67 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Жабуу"</string>
<string name="back_button_text" msgid="1469718707134137085">"Артка"</string>
<string name="handle_text" msgid="1766582106752184456">"Маркер"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index 53d4f34..e0a92b8 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -88,4 +88,9 @@
<string name="close_button_text" msgid="2913281996024033299">"ປິດ"</string>
<string name="back_button_text" msgid="1469718707134137085">"ກັບຄືນ"</string>
<string name="handle_text" msgid="1766582106752184456">"ມືບັງຄັບ"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"ເຕັມຈໍ"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"ໂໝດເດັສທັອບ"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"ແບ່ງໜ້າຈໍ"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"ເພີ່ມເຕີມ"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"ລອຍ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index 331281a..3d9f626 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Uždaryti"</string>
<string name="back_button_text" msgid="1469718707134137085">"Atgal"</string>
<string name="handle_text" msgid="1766582106752184456">"Rankenėlė"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index d301721..3661562 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Aizvērt"</string>
<string name="back_button_text" msgid="1469718707134137085">"Atpakaļ"</string>
<string name="handle_text" msgid="1766582106752184456">"Turis"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index 00f2900..41f549e 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -88,4 +88,9 @@
<string name="close_button_text" msgid="2913281996024033299">"Затвори"</string>
<string name="back_button_text" msgid="1469718707134137085">"Назад"</string>
<string name="handle_text" msgid="1766582106752184456">"Прекар"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Цел екран"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Режим за компјутер"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Поделен екран"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Повеќе"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Лебдечко"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index 9722868..9ff9d78 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"അടയ്ക്കുക"</string>
<string name="back_button_text" msgid="1469718707134137085">"മടങ്ങുക"</string>
<string name="handle_text" msgid="1766582106752184456">"ഹാൻഡിൽ"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index 3d598e4..32149e7 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Хаах"</string>
<string name="back_button_text" msgid="1469718707134137085">"Буцах"</string>
<string name="handle_text" msgid="1766582106752184456">"Бариул"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index 29f57fb..8c0f69d 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"बंद करा"</string>
<string name="back_button_text" msgid="1469718707134137085">"मागे जा"</string>
<string name="handle_text" msgid="1766582106752184456">"हँडल"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index 4dc8dca..e648a7a 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -88,4 +88,9 @@
<string name="close_button_text" msgid="2913281996024033299">"Tutup"</string>
<string name="back_button_text" msgid="1469718707134137085">"Kembali"</string>
<string name="handle_text" msgid="1766582106752184456">"Pemegang"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Skrin penuh"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Mod Desktop"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Skrin Pisah"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Lagi"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Terapung"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index 0bb6acf..9ee25ab 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"ပိတ်ရန်"</string>
<string name="back_button_text" msgid="1469718707134137085">"နောက်သို့"</string>
<string name="handle_text" msgid="1766582106752184456">"သုံးသူအမည်"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index f4f3af8..d8b1f28 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Lukk"</string>
<string name="back_button_text" msgid="1469718707134137085">"Tilbake"</string>
<string name="handle_text" msgid="1766582106752184456">"Håndtak"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index 4b90e92..6a946db 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"बन्द गर्नुहोस्"</string>
<string name="back_button_text" msgid="1469718707134137085">"पछाडि"</string>
<string name="handle_text" msgid="1766582106752184456">"ह्यान्डल"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index 18a2021..98e6d62 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Sluiten"</string>
<string name="back_button_text" msgid="1469718707134137085">"Terug"</string>
<string name="handle_text" msgid="1766582106752184456">"Gebruikersnaam"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index 9e5a96d..a7b5c30 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="back_button_text" msgid="1469718707134137085">"ପଛକୁ ଫେରନ୍ତୁ"</string>
<string name="handle_text" msgid="1766582106752184456">"ହେଣ୍ଡେଲ"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index 5c255d8..7f84480 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"ਬੰਦ ਕਰੋ"</string>
<string name="back_button_text" msgid="1469718707134137085">"ਪਿੱਛੇ"</string>
<string name="handle_text" msgid="1766582106752184456">"ਹੈਂਡਲ"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index 086726c..fb807e5 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Zamknij"</string>
<string name="back_button_text" msgid="1469718707134137085">"Wstecz"</string>
<string name="handle_text" msgid="1766582106752184456">"Uchwyt"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index 353c02d..cad59e0 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -88,4 +88,9 @@
<string name="close_button_text" msgid="2913281996024033299">"Fechar"</string>
<string name="back_button_text" msgid="1469718707134137085">"Voltar"</string>
<string name="handle_text" msgid="1766582106752184456">"Alça"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Tela cheia"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Modo área de trabalho"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Tela dividida"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Mais"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Ponto flutuante"</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 97d40b5..26772dc 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -88,4 +88,9 @@
<string name="close_button_text" msgid="2913281996024033299">"Fechar"</string>
<string name="back_button_text" msgid="1469718707134137085">"Anterior"</string>
<string name="handle_text" msgid="1766582106752184456">"Indicador"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Ecrã inteiro"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Modo de ambiente de trabalho"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Ecrã dividido"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Mais"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Flutuar"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index 353c02d..cad59e0 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -88,4 +88,9 @@
<string name="close_button_text" msgid="2913281996024033299">"Fechar"</string>
<string name="back_button_text" msgid="1469718707134137085">"Voltar"</string>
<string name="handle_text" msgid="1766582106752184456">"Alça"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Tela cheia"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Modo área de trabalho"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Tela dividida"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Mais"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Ponto flutuante"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index a085f02..c187fe3 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Închide"</string>
<string name="back_button_text" msgid="1469718707134137085">"Înapoi"</string>
<string name="handle_text" msgid="1766582106752184456">"Ghidaj"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index 97c5a06..dabeacb 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Закрыть"</string>
<string name="back_button_text" msgid="1469718707134137085">"Назад"</string>
<string name="handle_text" msgid="1766582106752184456">"Маркер"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index 60e1222..b0e2c80 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"වසන්න"</string>
<string name="back_button_text" msgid="1469718707134137085">"ආපසු"</string>
<string name="handle_text" msgid="1766582106752184456">"හැඬලය"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index 4007498..f20e940 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -88,4 +88,9 @@
<string name="close_button_text" msgid="2913281996024033299">"Zavrieť"</string>
<string name="back_button_text" msgid="1469718707134137085">"Späť"</string>
<string name="handle_text" msgid="1766582106752184456">"Rukoväť"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Celá obrazovka"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Režim počítača"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Rozdelená obrazovka"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Viac"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Plávajúce"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index 6dbd883..83156e7 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Zapri"</string>
<string name="back_button_text" msgid="1469718707134137085">"Nazaj"</string>
<string name="handle_text" msgid="1766582106752184456">"Ročica"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index 11e5713..6f7704c 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Mbyll"</string>
<string name="back_button_text" msgid="1469718707134137085">"Pas"</string>
<string name="handle_text" msgid="1766582106752184456">"Emërtimi"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index 5beb31c..8b6794f 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Затворите"</string>
<string name="back_button_text" msgid="1469718707134137085">"Назад"</string>
<string name="handle_text" msgid="1766582106752184456">"Идентификатор"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index c175583..3163fa1 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Stäng"</string>
<string name="back_button_text" msgid="1469718707134137085">"Tillbaka"</string>
<string name="handle_text" msgid="1766582106752184456">"Handtag"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index a049364..a7ad6f8 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Funga"</string>
<string name="back_button_text" msgid="1469718707134137085">"Rudi nyuma"</string>
<string name="handle_text" msgid="1766582106752184456">"Ncha"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index 21b7412..e55e231 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"மூடும்"</string>
<string name="back_button_text" msgid="1469718707134137085">"பின்செல்லும்"</string>
<string name="handle_text" msgid="1766582106752184456">"ஹேண்டில்"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 18c3719..bec1a07 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"మూసివేయండి"</string>
<string name="back_button_text" msgid="1469718707134137085">"వెనుకకు"</string>
<string name="handle_text" msgid="1766582106752184456">"హ్యాండిల్"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index 9e11d66..76cbb1a 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -88,4 +88,9 @@
<string name="close_button_text" msgid="2913281996024033299">"ปิด"</string>
<string name="back_button_text" msgid="1469718707134137085">"กลับ"</string>
<string name="handle_text" msgid="1766582106752184456">"แฮนเดิล"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"เต็มหน้าจอ"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"โหมดเดสก์ท็อป"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"แยกหน้าจอ"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"เพิ่มเติม"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"ล่องลอย"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index fbe0347..a990b52 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Isara"</string>
<string name="back_button_text" msgid="1469718707134137085">"Bumalik"</string>
<string name="handle_text" msgid="1766582106752184456">"Handle"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index dca58b8..44791e0 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Kapat"</string>
<string name="back_button_text" msgid="1469718707134137085">"Geri"</string>
<string name="handle_text" msgid="1766582106752184456">"Herkese açık kullanıcı adı"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index f7a59d3..dbfb389 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Закрити"</string>
<string name="back_button_text" msgid="1469718707134137085">"Назад"</string>
<string name="handle_text" msgid="1766582106752184456">"Маркер"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index 0ff1b6c..e9ac973 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"بند کریں"</string>
<string name="back_button_text" msgid="1469718707134137085">"پیچھے"</string>
<string name="handle_text" msgid="1766582106752184456">"ہینڈل"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index ee9b4dc..3b9324f 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -88,4 +88,9 @@
<string name="close_button_text" msgid="2913281996024033299">"Yopish"</string>
<string name="back_button_text" msgid="1469718707134137085">"Orqaga"</string>
<string name="handle_text" msgid="1766582106752184456">"Identifikator"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Butun ekran"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Desktop rejimi"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Ekranni ikkiga ajratish"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Yana"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Pufakli"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index 67ab2a0..ccea4e0 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Đóng"</string>
<string name="back_button_text" msgid="1469718707134137085">"Quay lại"</string>
<string name="handle_text" msgid="1766582106752184456">"Xử lý"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index 8fdf1d4..9497ae8 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"关闭"</string>
<string name="back_button_text" msgid="1469718707134137085">"返回"</string>
<string name="handle_text" msgid="1766582106752184456">"处理"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index 5dce250..c6fdd14 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"關閉"</string>
<string name="back_button_text" msgid="1469718707134137085">"返去"</string>
<string name="handle_text" msgid="1766582106752184456">"控點"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index c449c2e..84afaa8 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"關閉"</string>
<string name="back_button_text" msgid="1469718707134137085">"返回"</string>
<string name="handle_text" msgid="1766582106752184456">"控點"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index b470e7f..c5e8e38 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -88,4 +88,14 @@
<string name="close_button_text" msgid="2913281996024033299">"Vala"</string>
<string name="back_button_text" msgid="1469718707134137085">"Emuva"</string>
<string name="handle_text" msgid="1766582106752184456">"Isibambo"</string>
+ <!-- no translation found for fullscreen_text (1162316685217676079) -->
+ <skip />
+ <!-- no translation found for desktop_text (1077633567027630454) -->
+ <skip />
+ <!-- no translation found for split_screen_text (1396336058129570886) -->
+ <skip />
+ <!-- no translation found for more_button_text (3655388105592893530) -->
+ <skip />
+ <!-- no translation found for float_button_text (9221657008391364581) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 64220c8..b6327e5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -62,7 +62,6 @@
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
-import com.android.wm.shell.transition.Transitions;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -82,8 +81,6 @@
SETTING_VALUE_OFF) == SETTING_VALUE_ON;
/** Predictive back animation developer option */
private final AtomicBoolean mEnableAnimations = new AtomicBoolean(false);
- // TODO (b/241808055) Find a appropriate time to remove during refactor
- private static final boolean ENABLE_SHELL_TRANSITIONS = Transitions.ENABLE_SHELL_TRANSITIONS;
/**
* Max duration to wait for a transition to finish before accepting another gesture start
* request.
@@ -121,8 +118,6 @@
private final TouchTracker mTouchTracker = new TouchTracker();
private final SparseArray<BackAnimationRunner> mAnimationDefinition = new SparseArray<>();
- private final Transitions mTransitions;
- private BackTransitionHandler mBackTransitionHandler;
@VisibleForTesting
final IWindowFocusObserver mFocusObserver = new IWindowFocusObserver.Stub() {
@@ -148,11 +143,9 @@
@NonNull ShellController shellController,
@NonNull @ShellMainThread ShellExecutor shellExecutor,
@NonNull @ShellBackgroundThread Handler backgroundHandler,
- Context context,
- Transitions transitions) {
+ Context context) {
this(shellInit, shellController, shellExecutor, backgroundHandler,
- ActivityTaskManager.getService(), context, context.getContentResolver(),
- transitions);
+ ActivityTaskManager.getService(), context, context.getContentResolver());
}
@VisibleForTesting
@@ -162,8 +155,7 @@
@NonNull @ShellMainThread ShellExecutor shellExecutor,
@NonNull @ShellBackgroundThread Handler bgHandler,
@NonNull IActivityTaskManager activityTaskManager,
- Context context, ContentResolver contentResolver,
- Transitions transitions) {
+ Context context, ContentResolver contentResolver) {
mShellController = shellController;
mShellExecutor = shellExecutor;
mActivityTaskManager = activityTaskManager;
@@ -171,7 +163,6 @@
mContentResolver = contentResolver;
mBgHandler = bgHandler;
shellInit.addInitCallback(this::onInit, this);
- mTransitions = transitions;
}
@VisibleForTesting
@@ -182,10 +173,6 @@
private void onInit() {
setupAnimationDeveloperSettingsObserver(mContentResolver, mBgHandler);
createAdapter();
- if (ENABLE_SHELL_TRANSITIONS) {
- mBackTransitionHandler = new BackTransitionHandler(this);
- mTransitions.addHandler(mBackTransitionHandler);
- }
mShellController.addExternalInterface(KEY_EXTRA_SHELL_BACK_ANIMATION,
this::createExternalInterface, this);
@@ -335,17 +322,7 @@
}
}
- // In legacy transition, it would use `Task.mBackGestureStarted` in core to handle the
- // following transition when back callback is invoked.
- // If the back callback is not invoked, we should reset the token and finish the whole back
- // navigation without waiting the transition.
- if (!ENABLE_SHELL_TRANSITIONS) {
- finishBackNavigation();
- } else if (!mTriggerBack) {
- // reset the token to prevent it consume next transition.
- mBackTransitionHandler.setDepartingWindowContainerToken(null);
- finishBackNavigation();
- }
+ finishBackNavigation();
}
/**
@@ -614,10 +591,6 @@
return;
}
mTransitionInProgress = true;
- if (ENABLE_SHELL_TRANSITIONS) {
- mBackTransitionHandler.setDepartingWindowContainerToken(
- mBackNavigationInfo.getDepartingWindowContainerToken());
- }
mShellExecutor.executeDelayed(mResetTransitionRunnable, MAX_TRANSITION_DURATION);
}
@@ -626,19 +599,6 @@
mTransitionInProgress = false;
}
- /**
- * This should be called from {@link BackTransitionHandler#startAnimation} when the following
- * transition is triggered by the real back callback in {@link #onBackAnimationFinished}.
- * Will consume the default transition and finish current back navigation.
- */
- void finishTransition(Transitions.TransitionFinishCallback finishCallback) {
- ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: finishTransition()");
- mShellExecutor.execute(() -> {
- finishBackNavigation();
- finishCallback.onTransitionFinished(null, null);
- });
- }
-
private void createAdapter() {
IBackAnimationRunner runner = new IBackAnimationRunner.Stub() {
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackTransitionHandler.java
deleted file mode 100644
index 6d72d9c..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackTransitionHandler.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.back;
-
-import android.os.IBinder;
-import android.view.SurfaceControl;
-import android.window.TransitionInfo;
-import android.window.TransitionRequestInfo;
-import android.window.WindowContainerToken;
-import android.window.WindowContainerTransaction;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.wm.shell.transition.Transitions;
-
-class BackTransitionHandler implements Transitions.TransitionHandler {
- private BackAnimationController mBackAnimationController;
- private WindowContainerToken mDepartingWindowContainerToken;
-
- BackTransitionHandler(@NonNull BackAnimationController backAnimationController) {
- mBackAnimationController = backAnimationController;
- }
-
- @Override
- public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction startTransaction,
- @NonNull SurfaceControl.Transaction finishTransaction,
- @NonNull Transitions.TransitionFinishCallback finishCallback) {
- if (mDepartingWindowContainerToken != null) {
- final TransitionInfo.Change change = info.getChange(mDepartingWindowContainerToken);
- if (change == null) {
- return false;
- }
-
- startTransaction.hide(change.getLeash());
- startTransaction.apply();
- mDepartingWindowContainerToken = null;
- mBackAnimationController.finishTransition(finishCallback);
- return true;
- }
-
- return false;
- }
-
- @Nullable
- @Override
- public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
- @NonNull TransitionRequestInfo request) {
- return null;
- }
-
- @Override
- public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
- @NonNull Transitions.TransitionFinishCallback finishCallback) {
- }
-
- void setDepartingWindowContainerToken(
- @Nullable WindowContainerToken departingWindowContainerToken) {
- mDepartingWindowContainerToken = departingWindowContainerToken;
- }
-}
-
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/back/OWNERS
new file mode 100644
index 0000000..1e0f9bc
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/OWNERS
@@ -0,0 +1,5 @@
+# WM shell sub-module back navigation owners
+# Bug component: 1152663
+shanh@google.com
+arthurhung@google.com
+wilsonshih@google.com
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 3972b59..a400555 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
@@ -24,6 +24,7 @@
import static android.view.View.VISIBLE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static com.android.wm.shell.bubbles.Bubble.KEY_APP_BUBBLE;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_CONTROLLER;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_GESTURE;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
@@ -37,7 +38,6 @@
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_PACKAGE_REMOVED;
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_SHORTCUT_REMOVED;
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_USER_CHANGED;
-import static com.android.wm.shell.floating.FloatingTasksController.SHOW_FLOATING_TASKS_AS_BUBBLES;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
@@ -150,6 +150,9 @@
private final ShellExecutor mBackgroundExecutor;
+ // Whether or not we should show bubbles pinned at the bottom of the screen.
+ private boolean mIsBubbleBarEnabled;
+
private BubbleLogger mLogger;
private BubbleData mBubbleData;
@Nullable private BubbleStackView mStackView;
@@ -210,7 +213,6 @@
/** Drag and drop controller to register listener for onDragStarted. */
private DragAndDropController mDragAndDropController;
-
public BubbleController(Context context,
ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
@@ -526,6 +528,12 @@
mDataRepository.removeBubblesForUser(removedUserId, parentUserId);
}
+ // TODO(b/256873975): Should pass this into the constructor once flags are available to shell.
+ /** Sets whether the bubble bar is enabled (i.e. bubbles pinned to bottom on large screens). */
+ public void setBubbleBarEnabled(boolean enabled) {
+ mIsBubbleBarEnabled = enabled;
+ }
+
/** Whether this userId belongs to the current user. */
private boolean isCurrentProfile(int userId) {
return userId == UserHandle.USER_ALL
@@ -591,7 +599,8 @@
}
mStackView.setUnbubbleConversationCallback(mSysuiProxy::onUnbubbleConversation);
}
- if (SHOW_FLOATING_TASKS_AS_BUBBLES && mBubblePositioner.isLargeScreen()) {
+
+ if (mIsBubbleBarEnabled && mBubblePositioner.isLargeScreen()) {
mBubblePositioner.setUsePinnedLocation(true);
} else {
mBubblePositioner.setUsePinnedLocation(false);
@@ -967,14 +976,18 @@
}
/**
- * Adds a bubble for a specific intent. These bubbles are <b>not</b> backed by a notification
- * and remain until the user dismisses the bubble or bubble stack. Only one intent bubble
- * is supported at a time.
+ * Adds and expands bubble for a specific intent. These bubbles are <b>not</b> backed by a n
+ * otification and remain until the user dismisses the bubble or bubble stack. Only one intent
+ * bubble is supported at a time.
*
* @param intent the intent to display in the bubble expanded view.
*/
- public void addAppBubble(Intent intent) {
+ public void showAppBubble(Intent intent) {
if (intent == null || intent.getPackage() == null) return;
+
+ PackageManager packageManager = getPackageManagerForUser(mContext, mCurrentUserId);
+ if (!isResizableActivity(intent, packageManager, KEY_APP_BUBBLE)) return;
+
Bubble b = new Bubble(intent, UserHandle.of(mCurrentUserId), mMainExecutor);
b.setShouldAutoExpand(true);
inflateAndAdd(b, /* suppressFlyout= */ true, /* showInShade= */ false);
@@ -1497,18 +1510,23 @@
}
PackageManager packageManager = getPackageManagerForUser(
context, entry.getStatusBarNotification().getUser().getIdentifier());
- ActivityInfo info =
- intent.getIntent().resolveActivityInfo(packageManager, 0);
+ return isResizableActivity(intent.getIntent(), packageManager, entry.getKey());
+ }
+
+ static boolean isResizableActivity(Intent intent, PackageManager packageManager, String key) {
+ if (intent == null) {
+ Log.w(TAG, "Unable to send as bubble: " + key + " null intent");
+ return false;
+ }
+ ActivityInfo info = intent.resolveActivityInfo(packageManager, 0);
if (info == null) {
- Log.w(TAG, "Unable to send as bubble, "
- + entry.getKey() + " couldn't find activity info for intent: "
- + intent);
+ Log.w(TAG, "Unable to send as bubble: " + key
+ + " couldn't find activity info for intent: " + intent);
return false;
}
if (!ActivityInfo.isResizeableMode(info.resizeMode)) {
- Log.w(TAG, "Unable to send as bubble, "
- + entry.getKey() + " activity is not resizable for intent: "
- + intent);
+ Log.w(TAG, "Unable to send as bubble: " + key
+ + " activity is not resizable for intent: " + intent);
return false;
}
return true;
@@ -1682,6 +1700,13 @@
}
@Override
+ public void showAppBubble(Intent intent) {
+ mMainExecutor.execute(() -> {
+ BubbleController.this.showAppBubble(intent);
+ });
+ }
+
+ @Override
public boolean handleDismissalInterception(BubbleEntry entry,
@Nullable List<BubbleEntry> children, IntConsumer removeCallback,
Executor callbackExecutor) {
@@ -1792,6 +1817,13 @@
}
@Override
+ public void setBubbleBarEnabled(boolean enabled) {
+ mMainExecutor.execute(() -> {
+ BubbleController.this.setBubbleBarEnabled(enabled);
+ });
+ }
+
+ @Override
public void onNotificationPanelExpandedChanged(boolean expanded) {
mMainExecutor.execute(
() -> BubbleController.this.onNotificationPanelExpandedChanged(expanded));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index f31a27d..f621351 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -83,7 +83,6 @@
import com.android.wm.shell.bubbles.animation.ExpandedAnimationController;
import com.android.wm.shell.bubbles.animation.ExpandedViewAnimationController;
import com.android.wm.shell.bubbles.animation.ExpandedViewAnimationControllerImpl;
-import com.android.wm.shell.bubbles.animation.ExpandedViewAnimationControllerStub;
import com.android.wm.shell.bubbles.animation.PhysicsAnimationLayout;
import com.android.wm.shell.bubbles.animation.StackAnimationController;
import com.android.wm.shell.common.FloatingContentCoordinator;
@@ -105,11 +104,6 @@
*/
public class BubbleStackView extends FrameLayout
implements ViewTreeObserver.OnComputeInternalInsetsListener {
- /**
- * Set to {@code true} to enable home gesture handling in bubbles
- */
- public static final boolean HOME_GESTURE_ENABLED =
- SystemProperties.getBoolean("persist.wm.debug.bubbles_home_gesture", true);
public static final boolean ENABLE_FLING_TO_DISMISS_BUBBLE =
SystemProperties.getBoolean("persist.wm.debug.fling_to_dismiss_bubble", true);
@@ -898,12 +892,8 @@
mExpandedAnimationController = new ExpandedAnimationController(mPositioner,
onBubbleAnimatedOut, this);
- if (HOME_GESTURE_ENABLED) {
- mExpandedViewAnimationController =
- new ExpandedViewAnimationControllerImpl(context, mPositioner);
- } else {
- mExpandedViewAnimationController = new ExpandedViewAnimationControllerStub();
- }
+ mExpandedViewAnimationController =
+ new ExpandedViewAnimationControllerImpl(context, mPositioner);
mSurfaceSynchronizer = synchronizer != null ? synchronizer : DEFAULT_SURFACE_SYNCHRONIZER;
@@ -1964,11 +1954,7 @@
if (wasExpanded) {
stopMonitoringSwipeUpGesture();
- if (HOME_GESTURE_ENABLED) {
- animateCollapse();
- } else {
- animateCollapseWithScale();
- }
+ animateCollapse();
logBubbleEvent(mExpandedBubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
} else {
animateExpansion();
@@ -1976,13 +1962,11 @@
logBubbleEvent(mExpandedBubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
logBubbleEvent(mExpandedBubble,
FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__STACK_EXPANDED);
- if (HOME_GESTURE_ENABLED) {
- mBubbleController.isNotificationPanelExpanded(notifPanelExpanded -> {
- if (!notifPanelExpanded && mIsExpanded) {
- startMonitoringSwipeUpGesture();
- }
- });
- }
+ mBubbleController.isNotificationPanelExpanded(notifPanelExpanded -> {
+ if (!notifPanelExpanded && mIsExpanded) {
+ startMonitoringSwipeUpGesture();
+ }
+ });
}
notifyExpansionChanged(mExpandedBubble, mIsExpanded);
}
@@ -2298,106 +2282,6 @@
mMainExecutor.executeDelayed(mDelayedAnimation, startDelay);
}
- private void animateCollapseWithScale() {
- cancelDelayedExpandCollapseSwitchAnimations();
-
- if (mManageEduView != null && mManageEduView.getVisibility() == VISIBLE) {
- mManageEduView.hide();
- }
- // Hide the menu if it's visible.
- showManageMenu(false);
-
- mIsExpanded = false;
- mIsExpansionAnimating = true;
-
- showScrim(false);
-
- mBubbleContainer.cancelAllAnimations();
-
- // If we were in the middle of swapping, the animating-out surface would have been scaling
- // to zero - finish it off.
- PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer).cancel();
- mAnimatingOutSurfaceContainer.setScaleX(0f);
- mAnimatingOutSurfaceContainer.setScaleY(0f);
-
- // Let the expanded animation controller know that it shouldn't animate child adds/reorders
- // since we're about to animate collapsed.
- mExpandedAnimationController.notifyPreparingToCollapse();
-
- mExpandedAnimationController.collapseBackToStack(
- mStackAnimationController.getStackPositionAlongNearestHorizontalEdge()
- /* collapseTo */,
- () -> mBubbleContainer.setActiveController(mStackAnimationController));
-
- int index;
- if (mExpandedBubble != null && BubbleOverflow.KEY.equals(mExpandedBubble.getKey())) {
- index = mBubbleData.getBubbles().size();
- } else {
- index = mBubbleData.getBubbles().indexOf(mExpandedBubble);
- }
- // Value the bubble is animating from (back into the stack).
- final PointF p = mPositioner.getExpandedBubbleXY(index, getState());
- if (mPositioner.showBubblesVertically()) {
- float pivotX;
- float pivotY = p.y + mBubbleSize / 2f;
- if (mStackOnLeftOrWillBe) {
- pivotX = mPositioner.getAvailableRect().left + mBubbleSize + mExpandedViewPadding;
- } else {
- pivotX = mPositioner.getAvailableRect().right - mBubbleSize - mExpandedViewPadding;
- }
- mExpandedViewContainerMatrix.setScale(
- 1f, 1f,
- pivotX, pivotY);
- } else {
- mExpandedViewContainerMatrix.setScale(
- 1f, 1f,
- p.x + mBubbleSize / 2f,
- p.y + mBubbleSize + mExpandedViewPadding);
- }
-
- mExpandedViewAlphaAnimator.reverse();
-
- // When the animation completes, we should no longer be showing the content.
- if (mExpandedBubble.getExpandedView() != null) {
- mExpandedBubble.getExpandedView().setContentVisibility(false);
- }
-
- PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
- PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
- .spring(AnimatableScaleMatrix.SCALE_X,
- AnimatableScaleMatrix.getAnimatableValueForScaleFactor(
- 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT),
- mScaleOutSpringConfig)
- .spring(AnimatableScaleMatrix.SCALE_Y,
- AnimatableScaleMatrix.getAnimatableValueForScaleFactor(
- 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT),
- mScaleOutSpringConfig)
- .addUpdateListener((target, values) -> {
- mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
- })
- .withEndActions(() -> {
- final BubbleViewProvider previouslySelected = mExpandedBubble;
- beforeExpandedViewAnimation();
- if (mManageEduView != null) {
- mManageEduView.hide();
- }
-
- if (DEBUG_BUBBLE_STACK_VIEW) {
- Log.d(TAG, "animateCollapse");
- Log.d(TAG, BubbleDebugConfig.formatBubblesString(getBubblesOnScreen(),
- mExpandedBubble));
- }
- updateOverflowVisibility();
- updateZOrder();
- updateBadges(true /* setBadgeForCollapsedStack */);
- afterExpandedViewAnimation();
- if (previouslySelected != null) {
- previouslySelected.setTaskViewVisibility(false);
- }
- })
- .start();
- }
-
private void animateCollapse() {
cancelDelayedExpandCollapseSwitchAnimations();
@@ -2579,65 +2463,6 @@
* and clip the expanded view.
*/
public void setImeVisible(boolean visible) {
- if (HOME_GESTURE_ENABLED) {
- setImeVisibleInternal(visible);
- } else {
- setImeVisibleWithoutClipping(visible);
- }
- }
-
- private void setImeVisibleWithoutClipping(boolean visible) {
- if ((mIsExpansionAnimating || mIsBubbleSwitchAnimating) && mIsExpanded) {
- // This will update the animation so the bubbles move to position for the IME
- mExpandedAnimationController.expandFromStack(() -> {
- updatePointerPosition(false /* forIme */);
- afterExpandedViewAnimation();
- } /* after */);
- return;
- }
-
- if (!mIsExpanded && getBubbleCount() > 0) {
- final float stackDestinationY =
- mStackAnimationController.animateForImeVisibility(visible);
-
- // How far the stack is animating due to IME, we'll just animate the flyout by that
- // much too.
- final float stackDy =
- stackDestinationY - mStackAnimationController.getStackPosition().y;
-
- // If the flyout is visible, translate it along with the bubble stack.
- if (mFlyout.getVisibility() == VISIBLE) {
- PhysicsAnimator.getInstance(mFlyout)
- .spring(DynamicAnimation.TRANSLATION_Y,
- mFlyout.getTranslationY() + stackDy,
- FLYOUT_IME_ANIMATION_SPRING_CONFIG)
- .start();
- }
- } else if (mPositioner.showBubblesVertically() && mIsExpanded
- && mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
- float selectedY = mPositioner.getExpandedBubbleXY(getState().selectedIndex,
- getState()).y;
- float newExpandedViewTop = mPositioner.getExpandedViewY(mExpandedBubble, selectedY);
- mExpandedBubble.getExpandedView().setImeVisible(visible);
- if (!mExpandedBubble.getExpandedView().isUsingMaxHeight()) {
- mExpandedViewContainer.animate().translationY(newExpandedViewTop);
- }
-
- List<Animator> animList = new ArrayList();
- for (int i = 0; i < mBubbleContainer.getChildCount(); i++) {
- View child = mBubbleContainer.getChildAt(i);
- float transY = mPositioner.getExpandedBubbleXY(i, getState()).y;
- ObjectAnimator anim = ObjectAnimator.ofFloat(child, TRANSLATION_Y, transY);
- animList.add(anim);
- }
- updatePointerPosition(true /* forIme */);
- AnimatorSet set = new AnimatorSet();
- set.playTogether(animList);
- set.start();
- }
- }
-
- private void setImeVisibleInternal(boolean visible) {
if ((mIsExpansionAnimating || mIsBubbleSwitchAnimating) && mIsExpanded) {
// This will update the animation so the bubbles move to position for the IME
mExpandedAnimationController.expandFromStack(() -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 7f891ec..465d1ab 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -22,6 +22,7 @@
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.app.NotificationChannel;
+import android.content.Intent;
import android.content.pm.UserInfo;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
@@ -108,6 +109,15 @@
void expandStackAndSelectBubble(Bubble bubble);
/**
+ * Adds and expands bubble that is not notification based, but instead based on an intent from
+ * the app. The intent must be explicit (i.e. include a package name or fully qualified
+ * component class name) and the activity for it should be resizable.
+ *
+ * @param intent the intent to populate the bubble.
+ */
+ void showAppBubble(Intent intent);
+
+ /**
* @return a bubble that matches the provided shortcutId, if one exists.
*/
@Nullable
@@ -232,6 +242,11 @@
*/
void onUserRemoved(int removedUserId);
+ /**
+ * Sets whether bubble bar should be enabled or not.
+ */
+ void setBubbleBarEnabled(boolean enabled);
+
/** Listener to find out about stack expansion / collapse events. */
interface BubbleExpandListener {
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
index b91062f..33629f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
@@ -20,7 +20,6 @@
import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RESTING;
import static com.android.wm.shell.bubbles.BubbleStackView.ENABLE_FLING_TO_DISMISS_BUBBLE;
-import static com.android.wm.shell.bubbles.BubbleStackView.HOME_GESTURE_ENABLED;
import android.content.res.Resources;
import android.graphics.Path;
@@ -81,11 +80,6 @@
new PhysicsAnimator.SpringConfig(
EXPAND_COLLAPSE_ANIM_STIFFNESS, SpringForce.DAMPING_RATIO_NO_BOUNCY);
- private final PhysicsAnimator.SpringConfig mAnimateOutSpringConfigWithoutHomeGesture =
- new PhysicsAnimator.SpringConfig(
- EXPAND_COLLAPSE_ANIM_STIFFNESS_WITHOUT_HOME_GESTURE,
- SpringForce.DAMPING_RATIO_NO_BOUNCY);
-
/** Horizontal offset between bubbles, which we need to know to re-stack them. */
private float mStackOffsetPx;
/** Size of each bubble. */
@@ -307,14 +301,8 @@
(firstBubbleLeads && index == 0)
|| (!firstBubbleLeads && index == mLayout.getChildCount() - 1);
- Interpolator interpolator;
- if (HOME_GESTURE_ENABLED) {
- // When home gesture is enabled, we use a different animation timing for collapse
- interpolator = expanding
- ? Interpolators.EMPHASIZED_ACCELERATE : Interpolators.EMPHASIZED_DECELERATE;
- } else {
- interpolator = Interpolators.LINEAR;
- }
+ Interpolator interpolator = expanding
+ ? Interpolators.EMPHASIZED_ACCELERATE : Interpolators.EMPHASIZED_DECELERATE;
animation
.followAnimatedTargetAlongPath(
@@ -564,16 +552,10 @@
finishRemoval.run();
mOnBubbleAnimatedOutAction.run();
} else {
- PhysicsAnimator.SpringConfig springConfig;
- if (HOME_GESTURE_ENABLED) {
- springConfig = mAnimateOutSpringConfig;
- } else {
- springConfig = mAnimateOutSpringConfigWithoutHomeGesture;
- }
PhysicsAnimator.getInstance(child)
.spring(DynamicAnimation.ALPHA, 0f)
- .spring(DynamicAnimation.SCALE_X, 0f, springConfig)
- .spring(DynamicAnimation.SCALE_Y, 0f, springConfig)
+ .spring(DynamicAnimation.SCALE_X, 0f, mAnimateOutSpringConfig)
+ .spring(DynamicAnimation.SCALE_Y, 0f, mAnimateOutSpringConfig)
.withEndActions(finishRemoval, mOnBubbleAnimatedOutAction)
.start();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerStub.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerStub.java
deleted file mode 100644
index bb8a3aa..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerStub.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.wm.shell.bubbles.animation;
-
-import com.android.wm.shell.bubbles.BubbleExpandedView;
-
-/**
- * Stub implementation {@link ExpandedViewAnimationController} that does not animate the
- * {@link BubbleExpandedView}
- */
-public class ExpandedViewAnimationControllerStub implements ExpandedViewAnimationController {
- @Override
- public void setExpandedView(BubbleExpandedView expandedView) {
- }
-
- @Override
- public void updateDrag(float distance) {
- }
-
- @Override
- public void setSwipeVelocity(float velocity) {
- }
-
- @Override
- public boolean shouldCollapse() {
- return false;
- }
-
- @Override
- public void animateCollapse(Runnable startStackCollapse, Runnable after) {
- }
-
- @Override
- public void animateBackToExpanded() {
- }
-
- @Override
- public void animateForImeVisibilityChange(boolean visible) {
- }
-
- @Override
- public void reset() {
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index 8bc16bc..1474754 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -46,8 +46,10 @@
import androidx.annotation.Nullable;
import com.android.internal.policy.DividerSnapAlgorithm;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.animation.Interpolators;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
/**
* Divider for multi window splits.
@@ -364,8 +366,11 @@
mViewHost.relayout(lp);
}
- void setInteractive(boolean interactive) {
+ void setInteractive(boolean interactive, String from) {
if (interactive == mInteractive) return;
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ "Set divider bar %s from %s", interactive ? "interactive" : "non-interactive",
+ from);
mInteractive = interactive;
releaseTouching();
mHandle.setVisibility(mInteractive ? View.VISIBLE : View.INVISIBLE);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
index 74f8bf9..5b7ed27 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
@@ -48,6 +48,7 @@
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.R;
+import com.android.wm.shell.common.ScreenshotUtils;
import com.android.wm.shell.common.SurfaceUtils;
import java.util.function.Consumer;
@@ -74,10 +75,14 @@
private boolean mShown;
private boolean mIsResizing;
- private Rect mBounds = new Rect();
+ private final Rect mBounds = new Rect();
+ private final Rect mResizingBounds = new Rect();
+ private final Rect mTempRect = new Rect();
private ValueAnimator mFadeAnimator;
private int mIconSize;
+ private int mOffsetX;
+ private int mOffsetY;
public SplitDecorManager(Configuration configuration, IconProvider iconProvider,
SurfaceSession surfaceSession) {
@@ -158,7 +163,7 @@
/** Showing resizing hint. */
public void onResizing(ActivityManager.RunningTaskInfo resizingTask, Rect newBounds,
- Rect sideBounds, SurfaceControl.Transaction t) {
+ Rect sideBounds, SurfaceControl.Transaction t, int offsetX, int offsetY) {
if (mResizingIconView == null) {
return;
}
@@ -167,6 +172,9 @@
mIsResizing = true;
mBounds.set(newBounds);
}
+ mResizingBounds.set(newBounds);
+ mOffsetX = offsetX;
+ mOffsetY = offsetY;
final boolean show =
newBounds.width() > mBounds.width() || newBounds.height() > mBounds.height();
@@ -221,11 +229,37 @@
/** Stops showing resizing hint. */
public void onResized(SurfaceControl.Transaction t) {
+ if (!mShown && mIsResizing) {
+ mTempRect.set(mResizingBounds);
+ mTempRect.offsetTo(-mOffsetX, -mOffsetY);
+ final SurfaceControl screenshot = ScreenshotUtils.takeScreenshot(t,
+ mHostLeash, mTempRect, Integer.MAX_VALUE - 1);
+
+ final SurfaceControl.Transaction animT = new SurfaceControl.Transaction();
+ final ValueAnimator va = ValueAnimator.ofFloat(1, 0);
+ va.addUpdateListener(valueAnimator -> {
+ final float progress = (float) valueAnimator.getAnimatedValue();
+ animT.setAlpha(screenshot, progress);
+ animT.apply();
+ });
+ va.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(@androidx.annotation.NonNull Animator animation) {
+ animT.remove(screenshot);
+ animT.apply();
+ animT.close();
+ }
+ });
+ va.start();
+ }
+
if (mResizingIconView == null) {
return;
}
mIsResizing = false;
+ mOffsetX = 0;
+ mOffsetY = 0;
if (mFadeAnimator != null && mFadeAnimator.isRunning()) {
if (!mShown) {
// If fade-out animation is running, just add release callback to it.
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 295a2e3..839edc8 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
@@ -448,7 +448,8 @@
*/
void updateDivideBounds(int position) {
updateBounds(position);
- mSplitLayoutHandler.onLayoutSizeChanging(this);
+ mSplitLayoutHandler.onLayoutSizeChanging(this, mSurfaceEffectPolicy.mParallaxOffset.x,
+ mSurfaceEffectPolicy.mParallaxOffset.y);
}
void setDividePosition(int position, boolean applyLayoutChange) {
@@ -811,7 +812,7 @@
* @see #applySurfaceChanges(SurfaceControl.Transaction, SurfaceControl, SurfaceControl,
* SurfaceControl, SurfaceControl, boolean)
*/
- void onLayoutSizeChanging(SplitLayout layout);
+ void onLayoutSizeChanging(SplitLayout layout, int offsetX, int offsetY);
/**
* Calls when finish resizing the split bounds.
@@ -1092,7 +1093,8 @@
// ImePositionProcessor#onImeVisibilityChanged directly in DividerView is not enough
// because DividerView won't receive onImeVisibilityChanged callback after it being
// re-inflated.
- mSplitWindowManager.setInteractive(!mImeShown || !mHasImeFocus);
+ mSplitWindowManager.setInteractive(!mImeShown || !mHasImeFocus,
+ "onImeStartPositioning");
return needOffset ? IME_ANIMATION_NO_ALPHA : 0;
}
@@ -1118,7 +1120,7 @@
// Restore the split layout when wm-shell is not controlling IME insets anymore.
if (!controlling && mImeShown) {
reset();
- mSplitWindowManager.setInteractive(true);
+ mSplitWindowManager.setInteractive(true, "onImeControlTargetChanged");
mSplitLayoutHandler.setLayoutOffsetTarget(0, 0, SplitLayout.this);
mSplitLayoutHandler.onLayoutPositionChanging(SplitLayout.this);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
index 7fea237..5397552 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
@@ -167,9 +167,9 @@
}
}
- void setInteractive(boolean interactive) {
+ void setInteractive(boolean interactive, String from) {
if (mDividerView == null) return;
- mDividerView.setInteractive(interactive);
+ mDividerView.setInteractive(interactive, from);
}
View getDividerView() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 8b8e192..962be9d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -24,7 +24,6 @@
import android.os.Handler;
import android.os.SystemProperties;
import android.view.IWindowManager;
-import android.view.WindowManager;
import com.android.internal.logging.UiEventLogger;
import com.android.launcher3.icons.IconProvider;
@@ -65,8 +64,6 @@
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
import com.android.wm.shell.displayareahelper.DisplayAreaHelperController;
import com.android.wm.shell.draganddrop.DragAndDropController;
-import com.android.wm.shell.floating.FloatingTasks;
-import com.android.wm.shell.floating.FloatingTasksController;
import com.android.wm.shell.freeform.FreeformComponents;
import com.android.wm.shell.fullscreen.FullscreenTaskListener;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController;
@@ -263,13 +260,12 @@
ShellInit shellInit,
ShellController shellController,
@ShellMainThread ShellExecutor shellExecutor,
- @ShellBackgroundThread Handler backgroundHandler,
- Transitions transitions
+ @ShellBackgroundThread Handler backgroundHandler
) {
if (BackAnimationController.IS_ENABLED) {
return Optional.of(
new BackAnimationController(shellInit, shellController, shellExecutor,
- backgroundHandler, context, transitions));
+ backgroundHandler, context));
}
return Optional.empty();
}
@@ -576,47 +572,6 @@
}
//
- // Floating tasks
- //
-
- @WMSingleton
- @Provides
- static Optional<FloatingTasks> provideFloatingTasks(
- Optional<FloatingTasksController> floatingTaskController) {
- return floatingTaskController.map((controller) -> controller.asFloatingTasks());
- }
-
- @WMSingleton
- @Provides
- static Optional<FloatingTasksController> provideFloatingTasksController(Context context,
- ShellInit shellInit,
- ShellController shellController,
- ShellCommandHandler shellCommandHandler,
- Optional<BubbleController> bubbleController,
- WindowManager windowManager,
- ShellTaskOrganizer organizer,
- TaskViewTransitions taskViewTransitions,
- @ShellMainThread ShellExecutor mainExecutor,
- @ShellBackgroundThread ShellExecutor bgExecutor,
- SyncTransactionQueue syncQueue) {
- if (FloatingTasksController.FLOATING_TASKS_ENABLED) {
- return Optional.of(new FloatingTasksController(context,
- shellInit,
- shellController,
- shellCommandHandler,
- bubbleController,
- windowManager,
- organizer,
- taskViewTransitions,
- mainExecutor,
- bgExecutor,
- syncQueue));
- } else {
- return Optional.empty();
- }
- }
-
- //
// Starting window
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
index 497a6f6..55378a8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
@@ -311,7 +311,7 @@
animateSplitContainers(true, null /* animCompleteCallback */);
animateHighlight(target);
}
- } else {
+ } else if (mCurrentTarget.type != target.type) {
// Switching between targets
mDropZoneView1.animateSwitch();
mDropZoneView2.animateSwitch();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingDismissController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingDismissController.java
deleted file mode 100644
index 83a1734..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingDismissController.java
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.floating;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.view.MotionEvent;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.dynamicanimation.animation.DynamicAnimation;
-
-import com.android.wm.shell.R;
-import com.android.wm.shell.bubbles.DismissView;
-import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
-import com.android.wm.shell.floating.views.FloatingTaskLayer;
-import com.android.wm.shell.floating.views.FloatingTaskView;
-
-import java.util.Objects;
-
-/**
- * Controls a floating dismiss circle that has a 'magnetic' field around it, causing views moved
- * close to the target to be stuck to it unless moved out again.
- */
-public class FloatingDismissController {
-
- /** Velocity required to dismiss the view without dragging it into the dismiss target. */
- private static final float FLING_TO_DISMISS_MIN_VELOCITY = 4000f;
- /**
- * Max velocity that the view can be moving through the target with to stick (i.e. if it's
- * more than this velocity, it will pass through the target.
- */
- private static final float STICK_TO_TARGET_MAX_X_VELOCITY = 2000f;
- /**
- * Percentage of the target width to use to determine if an object flung towards the target
- * should dismiss (e.g. if target is 100px and this is set ot 2f, anything flung within a
- * 200px-wide area around the target will be considered 'near' enough get dismissed).
- */
- private static final float FLING_TO_TARGET_WIDTH_PERCENT = 2f;
- /** Minimum alpha to apply to the view being dismissed when it is in the target. */
- private static final float DISMISS_VIEW_MIN_ALPHA = 0.6f;
- /** Amount to scale down the view being dismissed when it is in the target. */
- private static final float DISMISS_VIEW_SCALE_DOWN_PERCENT = 0.15f;
-
- private Context mContext;
- private FloatingTasksController mController;
- private FloatingTaskLayer mParent;
-
- private DismissView mDismissView;
- private ValueAnimator mDismissAnimator;
- private View mViewBeingDismissed;
- private float mDismissSizePercent;
- private float mDismissSize;
-
- /**
- * The currently magnetized object, which is being dragged and will be attracted to the magnetic
- * dismiss target.
- */
- private MagnetizedObject<View> mMagnetizedObject;
- /**
- * The MagneticTarget instance for our circular dismiss view. This is added to the
- * MagnetizedObject instances for the view being dragged.
- */
- private MagnetizedObject.MagneticTarget mMagneticTarget;
- /** Magnet listener that handles animating and dismissing the view. */
- private MagnetizedObject.MagnetListener mFloatingViewMagnetListener;
-
- public FloatingDismissController(Context context, FloatingTasksController controller,
- FloatingTaskLayer parent) {
- mContext = context;
- mController = controller;
- mParent = parent;
- updateSizes();
- createAndAddDismissView();
-
- mDismissAnimator = ValueAnimator.ofFloat(1f, 0f);
- mDismissAnimator.addUpdateListener(animation -> {
- final float value = (float) animation.getAnimatedValue();
- if (mDismissView != null) {
- mDismissView.setPivotX((mDismissView.getRight() - mDismissView.getLeft()) / 2f);
- mDismissView.setPivotY((mDismissView.getBottom() - mDismissView.getTop()) / 2f);
- final float scaleValue = Math.max(value, mDismissSizePercent);
- mDismissView.getCircle().setScaleX(scaleValue);
- mDismissView.getCircle().setScaleY(scaleValue);
- }
- if (mViewBeingDismissed != null) {
- // TODO: alpha doesn't actually apply to taskView currently.
- mViewBeingDismissed.setAlpha(Math.max(value, DISMISS_VIEW_MIN_ALPHA));
- mViewBeingDismissed.setScaleX(Math.max(value, DISMISS_VIEW_SCALE_DOWN_PERCENT));
- mViewBeingDismissed.setScaleY(Math.max(value, DISMISS_VIEW_SCALE_DOWN_PERCENT));
- }
- });
-
- mFloatingViewMagnetListener = new MagnetizedObject.MagnetListener() {
- @Override
- public void onStuckToTarget(
- @NonNull MagnetizedObject.MagneticTarget target) {
- animateDismissing(/* dismissing= */ true);
- }
-
- @Override
- public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target,
- float velX, float velY, boolean wasFlungOut) {
- animateDismissing(/* dismissing= */ false);
- mParent.onUnstuckFromTarget((FloatingTaskView) mViewBeingDismissed, velX, velY,
- wasFlungOut);
- }
-
- @Override
- public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
- doDismiss();
- }
- };
- }
-
- /** Updates all the sizes used and applies them to the {@link DismissView}. */
- public void updateSizes() {
- Resources res = mContext.getResources();
- mDismissSize = res.getDimensionPixelSize(
- R.dimen.floating_task_dismiss_circle_size);
- final float minDismissSize = res.getDimensionPixelSize(
- R.dimen.floating_dismiss_circle_small);
- mDismissSizePercent = minDismissSize / mDismissSize;
-
- if (mDismissView != null) {
- mDismissView.updateResources();
- }
- }
-
- /** Prepares the view being dragged to be magnetic. */
- public void setUpMagneticObject(View viewBeingDragged) {
- mViewBeingDismissed = viewBeingDragged;
- mMagnetizedObject = getMagnetizedView(viewBeingDragged);
- mMagnetizedObject.clearAllTargets();
- mMagnetizedObject.addTarget(mMagneticTarget);
- mMagnetizedObject.setMagnetListener(mFloatingViewMagnetListener);
- }
-
- /** Shows or hides the dismiss target. */
- public void showDismiss(boolean show) {
- if (show) {
- mDismissView.show();
- } else {
- mDismissView.hide();
- }
- }
-
- /** Passes the MotionEvent to the magnetized object and returns true if it was consumed. */
- public boolean passEventToMagnetizedObject(MotionEvent event) {
- return mMagnetizedObject != null && mMagnetizedObject.maybeConsumeMotionEvent(event);
- }
-
- private void createAndAddDismissView() {
- if (mDismissView != null) {
- mParent.removeView(mDismissView);
- }
- mDismissView = new DismissView(mContext);
- mDismissView.setTargetSizeResId(R.dimen.floating_task_dismiss_circle_size);
- mDismissView.updateResources();
- mParent.addView(mDismissView);
-
- final float dismissRadius = mDismissSize;
- // Save the MagneticTarget instance for the newly set up view - we'll add this to the
- // MagnetizedObjects when the dismiss view gets shown.
- mMagneticTarget = new MagnetizedObject.MagneticTarget(
- mDismissView.getCircle(), (int) dismissRadius);
- }
-
- private MagnetizedObject<View> getMagnetizedView(View v) {
- if (mMagnetizedObject != null
- && Objects.equals(mMagnetizedObject.getUnderlyingObject(), v)) {
- // Same view being dragged, we can reuse the magnetic object.
- return mMagnetizedObject;
- }
- MagnetizedObject<View> magnetizedView = new MagnetizedObject<View>(
- mContext,
- v,
- DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y
- ) {
- @Override
- public float getWidth(@NonNull View underlyingObject) {
- return underlyingObject.getWidth();
- }
-
- @Override
- public float getHeight(@NonNull View underlyingObject) {
- return underlyingObject.getHeight();
- }
-
- @Override
- public void getLocationOnScreen(@NonNull View underlyingObject,
- @NonNull int[] loc) {
- loc[0] = (int) underlyingObject.getTranslationX();
- loc[1] = (int) underlyingObject.getTranslationY();
- }
- };
- magnetizedView.setHapticsEnabled(true);
- magnetizedView.setFlingToTargetMinVelocity(FLING_TO_DISMISS_MIN_VELOCITY);
- magnetizedView.setStickToTargetMaxXVelocity(STICK_TO_TARGET_MAX_X_VELOCITY);
- magnetizedView.setFlingToTargetWidthPercent(FLING_TO_TARGET_WIDTH_PERCENT);
- return magnetizedView;
- }
-
- /** Animates the dismiss treatment on the view being dismissed. */
- private void animateDismissing(boolean shouldDismiss) {
- if (mViewBeingDismissed == null) {
- return;
- }
- if (shouldDismiss) {
- mDismissAnimator.removeAllListeners();
- mDismissAnimator.start();
- } else {
- mDismissAnimator.removeAllListeners();
- mDismissAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- resetDismissAnimator();
- }
- });
- mDismissAnimator.reverse();
- }
- }
-
- /** Actually dismisses the view. */
- private void doDismiss() {
- mDismissView.hide();
- mController.removeTask();
- resetDismissAnimator();
- mViewBeingDismissed = null;
- }
-
- private void resetDismissAnimator() {
- mDismissAnimator.removeAllListeners();
- mDismissAnimator.cancel();
- if (mDismissView != null) {
- mDismissView.cancelAnimators();
- mDismissView.getCircle().setScaleX(1f);
- mDismissView.getCircle().setScaleY(1f);
- }
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasks.java b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasks.java
deleted file mode 100644
index f86d467..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasks.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.floating;
-
-import android.content.Intent;
-
-import com.android.wm.shell.common.annotations.ExternalThread;
-
-/**
- * Interface to interact with floating tasks.
- */
-@ExternalThread
-public interface FloatingTasks {
-
- /**
- * Shows, stashes, or un-stashes the floating task depending on state:
- * - If there is no floating task for this intent, it shows the task for the provided intent.
- * - If there is a floating task for this intent, but it's stashed, this un-stashes it.
- * - If there is a floating task for this intent, and it's not stashed, this stashes it.
- */
- void showOrSetStashed(Intent intent);
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasksController.java
deleted file mode 100644
index b3c09d3..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasksController.java
+++ /dev/null
@@ -1,454 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.floating;
-
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-
-import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_FLOATING_APPS;
-import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_FLOATING_TASKS;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ShortcutInfo;
-import android.content.res.Configuration;
-import android.graphics.PixelFormat;
-import android.graphics.Point;
-import android.os.SystemProperties;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-
-import androidx.annotation.BinderThread;
-import androidx.annotation.VisibleForTesting;
-
-import com.android.internal.protolog.common.ProtoLog;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.TaskViewTransitions;
-import com.android.wm.shell.bubbles.BubbleController;
-import com.android.wm.shell.common.ExternalInterfaceBinder;
-import com.android.wm.shell.common.RemoteCallable;
-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.ShellBackgroundThread;
-import com.android.wm.shell.common.annotations.ShellMainThread;
-import com.android.wm.shell.floating.views.FloatingTaskLayer;
-import com.android.wm.shell.floating.views.FloatingTaskView;
-import com.android.wm.shell.sysui.ConfigurationChangeListener;
-import com.android.wm.shell.sysui.ShellCommandHandler;
-import com.android.wm.shell.sysui.ShellController;
-import com.android.wm.shell.sysui.ShellInit;
-
-import java.io.PrintWriter;
-import java.util.Objects;
-import java.util.Optional;
-
-/**
- * Entry point for creating and managing floating tasks.
- *
- * A single window layer is added and the task(s) are displayed using a {@link FloatingTaskView}
- * within that window.
- *
- * Currently optimized for a single task. Multiple tasks are not supported.
- */
-public class FloatingTasksController implements RemoteCallable<FloatingTasksController>,
- ConfigurationChangeListener {
-
- private static final String TAG = FloatingTasksController.class.getSimpleName();
-
- public static final boolean FLOATING_TASKS_ENABLED =
- SystemProperties.getBoolean("persist.wm.debug.floating_tasks", false);
- public static final boolean SHOW_FLOATING_TASKS_AS_BUBBLES =
- SystemProperties.getBoolean("persist.wm.debug.floating_tasks_as_bubbles", false);
-
- @VisibleForTesting
- static final int SMALLEST_SCREEN_WIDTH_DP_TO_BE_TABLET = 600;
-
- // Only used for testing
- private Configuration mConfig;
- private boolean mFloatingTasksEnabledForTests;
-
- private FloatingTaskImpl mImpl = new FloatingTaskImpl();
- private Context mContext;
- private ShellController mShellController;
- private ShellCommandHandler mShellCommandHandler;
- private @Nullable BubbleController mBubbleController;
- private WindowManager mWindowManager;
- private ShellTaskOrganizer mTaskOrganizer;
- private TaskViewTransitions mTaskViewTransitions;
- private @ShellMainThread ShellExecutor mMainExecutor;
- // TODO: mBackgroundThread is not used but we'll probs need it eventually?
- private @ShellBackgroundThread ShellExecutor mBackgroundThread;
- private SyncTransactionQueue mSyncQueue;
-
- private boolean mIsFloatingLayerAdded;
- private FloatingTaskLayer mFloatingTaskLayer;
- private final Point mLastPosition = new Point(-1, -1);
-
- private Task mTask;
-
- // Simple class to hold onto info for intent or shortcut based tasks.
- public static class Task {
- public int taskId = INVALID_TASK_ID;
- @Nullable
- public Intent intent;
- @Nullable
- public ShortcutInfo info;
- @Nullable
- public FloatingTaskView floatingView;
- }
-
- public FloatingTasksController(Context context,
- ShellInit shellInit,
- ShellController shellController,
- ShellCommandHandler shellCommandHandler,
- Optional<BubbleController> bubbleController,
- WindowManager windowManager,
- ShellTaskOrganizer organizer,
- TaskViewTransitions transitions,
- @ShellMainThread ShellExecutor mainExecutor,
- @ShellBackgroundThread ShellExecutor bgExceutor,
- SyncTransactionQueue syncTransactionQueue) {
- mContext = context;
- mShellController = shellController;
- mShellCommandHandler = shellCommandHandler;
- mBubbleController = bubbleController.get();
- mWindowManager = windowManager;
- mTaskOrganizer = organizer;
- mTaskViewTransitions = transitions;
- mMainExecutor = mainExecutor;
- mBackgroundThread = bgExceutor;
- mSyncQueue = syncTransactionQueue;
- if (isFloatingTasksEnabled()) {
- shellInit.addInitCallback(this::onInit, this);
- }
- }
-
- protected void onInit() {
- mShellController.addConfigurationChangeListener(this);
- mShellController.addExternalInterface(KEY_EXTRA_SHELL_FLOATING_TASKS,
- this::createExternalInterface, this);
- mShellCommandHandler.addDumpCallback(this::dump, this);
- }
-
- /** Only used for testing. */
- @VisibleForTesting
- void setConfig(Configuration config) {
- mConfig = config;
- }
-
- /** Only used for testing. */
- @VisibleForTesting
- void setFloatingTasksEnabled(boolean enabled) {
- mFloatingTasksEnabledForTests = enabled;
- }
-
- /** Whether the floating layer is available. */
- boolean isFloatingLayerAvailable() {
- Configuration config = mConfig == null
- ? mContext.getResources().getConfiguration()
- : mConfig;
- return config.smallestScreenWidthDp >= SMALLEST_SCREEN_WIDTH_DP_TO_BE_TABLET;
- }
-
- /** Whether floating tasks are enabled. */
- boolean isFloatingTasksEnabled() {
- return FLOATING_TASKS_ENABLED || mFloatingTasksEnabledForTests;
- }
-
- private ExternalInterfaceBinder createExternalInterface() {
- return new IFloatingTasksImpl(this);
- }
-
- @Override
- public void onThemeChanged() {
- if (mIsFloatingLayerAdded) {
- mFloatingTaskLayer.updateSizes();
- }
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- // TODO: probably other stuff here to do (e.g. handle rotation)
- if (mIsFloatingLayerAdded) {
- mFloatingTaskLayer.updateSizes();
- }
- }
-
- /** Returns false if the task shouldn't be shown. */
- private boolean canShowTask(Intent intent) {
- ProtoLog.d(WM_SHELL_FLOATING_APPS, "canShowTask -- %s", intent);
- if (!isFloatingTasksEnabled() || !isFloatingLayerAvailable()) return false;
- if (intent == null) {
- ProtoLog.e(WM_SHELL_FLOATING_APPS, "canShowTask given null intent, doing nothing");
- return false;
- }
- return true;
- }
-
- /** Returns true if the task was or should be shown as a bubble. */
- private boolean maybeShowTaskAsBubble(Intent intent) {
- if (SHOW_FLOATING_TASKS_AS_BUBBLES && mBubbleController != null) {
- removeFloatingLayer();
- if (intent.getPackage() != null) {
- mBubbleController.addAppBubble(intent);
- ProtoLog.d(WM_SHELL_FLOATING_APPS, "showing floating task as bubble: %s", intent);
- } else {
- ProtoLog.d(WM_SHELL_FLOATING_APPS,
- "failed to show floating task as bubble: %s; unknown package", intent);
- }
- return true;
- }
- return false;
- }
-
- /**
- * Shows, stashes, or un-stashes the floating task depending on state:
- * - If there is no floating task for this intent, it shows this the provided task.
- * - If there is a floating task for this intent, but it's stashed, this un-stashes it.
- * - If there is a floating task for this intent, and it's not stashed, this stashes it.
- */
- public void showOrSetStashed(Intent intent) {
- if (!canShowTask(intent)) return;
- if (maybeShowTaskAsBubble(intent)) return;
-
- addFloatingLayer();
-
- if (isTaskAttached(mTask) && intent.filterEquals(mTask.intent)) {
- // The task is already added, toggle the stash state.
- mFloatingTaskLayer.setStashed(mTask, !mTask.floatingView.isStashed());
- return;
- }
-
- // If we're here it's either a new or different task
- showNewTask(intent);
- }
-
- /**
- * Shows a floating task with the provided intent.
- * If the same task is present it will un-stash it or do nothing if it is already un-stashed.
- * Removes any other floating tasks that might exist.
- */
- public void showTask(Intent intent) {
- if (!canShowTask(intent)) return;
- if (maybeShowTaskAsBubble(intent)) return;
-
- addFloatingLayer();
-
- if (isTaskAttached(mTask) && intent.filterEquals(mTask.intent)) {
- // The task is already added, show it if it's stashed.
- if (mTask.floatingView.isStashed()) {
- mFloatingTaskLayer.setStashed(mTask, false);
- }
- return;
- }
- showNewTask(intent);
- }
-
- private void showNewTask(Intent intent) {
- if (mTask != null && !intent.filterEquals(mTask.intent)) {
- mFloatingTaskLayer.removeAllTaskViews();
- mTask.floatingView.cleanUpTaskView();
- mTask = null;
- }
-
- FloatingTaskView ftv = new FloatingTaskView(mContext, this);
- ftv.createTaskView(mContext, mTaskOrganizer, mTaskViewTransitions, mSyncQueue);
-
- mTask = new Task();
- mTask.floatingView = ftv;
- mTask.intent = intent;
-
- // Add & start the task.
- mFloatingTaskLayer.addTask(mTask);
- ProtoLog.d(WM_SHELL_FLOATING_APPS, "showNewTask, startingIntent: %s", intent);
- mTask.floatingView.startTask(mMainExecutor, mTask);
- }
-
- /**
- * Removes the task and cleans up the view.
- */
- public void removeTask() {
- if (mTask != null) {
- ProtoLog.d(WM_SHELL_FLOATING_APPS, "Removing task with id=%d", mTask.taskId);
-
- if (mTask.floatingView != null) {
- // TODO: animate it
- mFloatingTaskLayer.removeView(mTask.floatingView);
- mTask.floatingView.cleanUpTaskView();
- }
- removeFloatingLayer();
- }
- }
-
- /**
- * Whether there is a floating task and if it is stashed.
- */
- public boolean isStashed() {
- return isTaskAttached(mTask) && mTask.floatingView.isStashed();
- }
-
- /**
- * If a floating task exists, this sets whether it is stashed and animates if needed.
- */
- public void setStashed(boolean shouldStash) {
- if (mTask != null && mTask.floatingView != null && mIsFloatingLayerAdded) {
- mFloatingTaskLayer.setStashed(mTask, shouldStash);
- }
- }
-
- /**
- * Saves the last position the floating task was in so that it can be put there again.
- */
- public void setLastPosition(int x, int y) {
- mLastPosition.set(x, y);
- }
-
- /**
- * Returns the last position the floating task was in.
- */
- public Point getLastPosition() {
- return mLastPosition;
- }
-
- /**
- * Whether the provided task has a view that's attached to the floating layer.
- */
- private boolean isTaskAttached(Task t) {
- return t != null && t.floatingView != null
- && mIsFloatingLayerAdded
- && mFloatingTaskLayer.getTaskViewCount() > 0
- && Objects.equals(mFloatingTaskLayer.getFirstTaskView(), t.floatingView);
- }
-
- // TODO: when this is added, if there are bubbles, they get hidden? Is only one layer of this
- // type allowed? Bubbles & floating tasks should probably be in the same layer to reduce
- // # of windows.
- private void addFloatingLayer() {
- if (mIsFloatingLayerAdded) {
- return;
- }
-
- mFloatingTaskLayer = new FloatingTaskLayer(mContext, this, mWindowManager);
-
- WindowManager.LayoutParams params = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
- PixelFormat.TRANSLUCENT
- );
- params.setTrustedOverlay();
- params.setFitInsetsTypes(0);
- params.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
- params.setTitle("FloatingTaskLayer");
- params.packageName = mContext.getPackageName();
- params.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
-
- try {
- mIsFloatingLayerAdded = true;
- mWindowManager.addView(mFloatingTaskLayer, params);
- } catch (IllegalStateException e) {
- // This means the floating layer has already been added which shouldn't happen.
- e.printStackTrace();
- }
- }
-
- private void removeFloatingLayer() {
- if (!mIsFloatingLayerAdded) {
- return;
- }
- try {
- mIsFloatingLayerAdded = false;
- if (mFloatingTaskLayer != null) {
- mWindowManager.removeView(mFloatingTaskLayer);
- }
- } catch (IllegalArgumentException e) {
- // This means the floating layer has already been removed which shouldn't happen.
- e.printStackTrace();
- }
- }
-
- /**
- * Description of current floating task state.
- */
- private void dump(PrintWriter pw, String prefix) {
- pw.println("FloatingTaskController state:");
- pw.print(" isFloatingLayerAvailable= "); pw.println(isFloatingLayerAvailable());
- pw.print(" isFloatingTasksEnabled= "); pw.println(isFloatingTasksEnabled());
- pw.print(" mIsFloatingLayerAdded= "); pw.println(mIsFloatingLayerAdded);
- pw.print(" mLastPosition= "); pw.println(mLastPosition);
- pw.println();
- }
-
- /** Returns the {@link FloatingTasks} implementation. */
- public FloatingTasks asFloatingTasks() {
- return mImpl;
- }
-
- @Override
- public Context getContext() {
- return mContext;
- }
-
- @Override
- public ShellExecutor getRemoteCallExecutor() {
- return mMainExecutor;
- }
-
- /**
- * The interface for calls from outside the shell, within the host process.
- */
- @ExternalThread
- private class FloatingTaskImpl implements FloatingTasks {
- @Override
- public void showOrSetStashed(Intent intent) {
- mMainExecutor.execute(() -> FloatingTasksController.this.showOrSetStashed(intent));
- }
- }
-
- /**
- * The interface for calls from outside the host process.
- */
- @BinderThread
- private static class IFloatingTasksImpl extends IFloatingTasks.Stub
- implements ExternalInterfaceBinder {
- private FloatingTasksController mController;
-
- IFloatingTasksImpl(FloatingTasksController controller) {
- mController = controller;
- }
-
- /**
- * Invalidates this instance, preventing future calls from updating the controller.
- */
- @Override
- public void invalidate() {
- mController = null;
- }
-
- public void showTask(Intent intent) {
- executeRemoteCallWithTaskPermission(mController, "showTask",
- (controller) -> controller.showTask(intent));
- }
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingMenuView.java
deleted file mode 100644
index c922109..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingMenuView.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.floating.views;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-import com.android.wm.shell.R;
-
-/**
- * Displays the menu items for a floating task view (e.g. close).
- */
-public class FloatingMenuView extends LinearLayout {
-
- private int mItemSize;
- private int mItemMargin;
-
- public FloatingMenuView(Context context) {
- super(context);
- setOrientation(LinearLayout.HORIZONTAL);
- setGravity(Gravity.CENTER);
-
- mItemSize = context.getResources().getDimensionPixelSize(
- R.dimen.floating_task_menu_item_size);
- mItemMargin = context.getResources().getDimensionPixelSize(
- R.dimen.floating_task_menu_item_padding);
- }
-
- /** Adds a clickable item to the menu bar. Items are ordered as added. */
- public void addMenuItem(@Nullable Drawable drawable, View.OnClickListener listener) {
- ImageView itemView = new ImageView(getContext());
- itemView.setScaleType(ImageView.ScaleType.CENTER);
- if (drawable != null) {
- itemView.setImageDrawable(drawable);
- }
- LinearLayout.LayoutParams lp = new LayoutParams(mItemSize,
- ViewGroup.LayoutParams.MATCH_PARENT);
- lp.setMarginStart(mItemMargin);
- lp.setMarginEnd(mItemMargin);
- addView(itemView, lp);
-
- itemView.setOnClickListener(listener);
- }
-
- /**
- * The menu extends past the top of the TaskView because of the rounded corners. This means
- * to center content in the menu we must subtract the radius (i.e. the amount of space covered
- * by TaskView).
- */
- public void setCornerRadius(float radius) {
- setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), (int) radius);
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingTaskLayer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingTaskLayer.java
deleted file mode 100644
index 16dab24..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingTaskLayer.java
+++ /dev/null
@@ -1,687 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.floating.views;
-
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_FLOATING_APPS;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.Insets;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewPropertyAnimator;
-import android.view.ViewTreeObserver;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-import android.view.WindowMetrics;
-import android.widget.FrameLayout;
-
-import androidx.annotation.NonNull;
-import androidx.dynamicanimation.animation.DynamicAnimation;
-import androidx.dynamicanimation.animation.FlingAnimation;
-
-import com.android.internal.protolog.common.ProtoLog;
-import com.android.wm.shell.R;
-import com.android.wm.shell.floating.FloatingDismissController;
-import com.android.wm.shell.floating.FloatingTasksController;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * This is the layout that {@link FloatingTaskView}s are contained in. It handles input and
- * movement of the task views.
- */
-public class FloatingTaskLayer extends FrameLayout
- implements ViewTreeObserver.OnComputeInternalInsetsListener {
-
- private static final String TAG = FloatingTaskLayer.class.getSimpleName();
-
- /** How big to make the task view based on screen width of the largest size. */
- private static final float START_SIZE_WIDTH_PERCENT = 0.33f;
- /** Min fling velocity required to move the view from one side of the screen to the other. */
- private static final float ESCAPE_VELOCITY = 750f;
- /** Amount of friction to apply to fling animations. */
- private static final float FLING_FRICTION = 1.9f;
-
- private final FloatingTasksController mController;
- private final FloatingDismissController mDismissController;
- private final WindowManager mWindowManager;
- private final TouchHandlerImpl mTouchHandler;
-
- private final Region mTouchableRegion = new Region();
- private final Rect mPositionRect = new Rect();
- private final Point mDefaultStartPosition = new Point();
- private final Point mTaskViewSize = new Point();
- private WindowInsets mWindowInsets;
- private int mVerticalPadding;
- private int mOverhangWhenStashed;
-
- private final List<Rect> mSystemGestureExclusionRects = Collections.singletonList(new Rect());
- private ViewTreeObserver.OnDrawListener mSystemGestureExclusionListener =
- this::updateSystemGestureExclusion;
-
- /** Interface allowing something to handle the touch events going to a task. */
- interface FloatingTaskTouchHandler {
- void onDown(@NonNull FloatingTaskView v, @NonNull MotionEvent ev,
- float viewInitialX, float viewInitialY);
-
- void onMove(@NonNull FloatingTaskView v, @NonNull MotionEvent ev,
- float dx, float dy);
-
- void onUp(@NonNull FloatingTaskView v, @NonNull MotionEvent ev,
- float dx, float dy, float velX, float velY);
-
- void onClick(@NonNull FloatingTaskView v);
- }
-
- public FloatingTaskLayer(Context context,
- FloatingTasksController controller,
- WindowManager windowManager) {
- super(context);
- // TODO: Why is this necessary? Without it FloatingTaskView does not render correctly.
- setBackgroundColor(Color.argb(0, 0, 0, 0));
-
- mController = controller;
- mWindowManager = windowManager;
- updateSizes();
-
- // TODO: Might make sense to put dismiss controller in the touch handler since that's the
- // main user of dismiss controller.
- mDismissController = new FloatingDismissController(context, mController, this);
- mTouchHandler = new TouchHandlerImpl();
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- getViewTreeObserver().addOnComputeInternalInsetsListener(this);
- getViewTreeObserver().addOnDrawListener(mSystemGestureExclusionListener);
- setOnApplyWindowInsetsListener((view, windowInsets) -> {
- if (!windowInsets.equals(mWindowInsets)) {
- mWindowInsets = windowInsets;
- updateSizes();
- }
- return windowInsets;
- });
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
- getViewTreeObserver().removeOnDrawListener(mSystemGestureExclusionListener);
- }
-
- @Override
- public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
- inoutInfo.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
- mTouchableRegion.setEmpty();
- getTouchableRegion(mTouchableRegion);
- inoutInfo.touchableRegion.set(mTouchableRegion);
- }
-
- /** Adds a floating task to the layout. */
- public void addTask(FloatingTasksController.Task task) {
- if (task.floatingView == null) return;
-
- task.floatingView.setTouchHandler(mTouchHandler);
- addView(task.floatingView, new LayoutParams(mTaskViewSize.x, mTaskViewSize.y));
- updateTaskViewPosition(task.floatingView);
- }
-
- /** Animates the stashed state of the provided task, if it's part of the floating layer. */
- public void setStashed(FloatingTasksController.Task task, boolean shouldStash) {
- if (task.floatingView != null && task.floatingView.getParent() == this) {
- mTouchHandler.stashTaskView(task.floatingView, shouldStash);
- }
- }
-
- /** Removes all {@link FloatingTaskView} from the layout. */
- public void removeAllTaskViews() {
- int childCount = getChildCount();
- ArrayList<View> viewsToRemove = new ArrayList<>();
- for (int i = 0; i < childCount; i++) {
- if (getChildAt(i) instanceof FloatingTaskView) {
- viewsToRemove.add(getChildAt(i));
- }
- }
- for (View v : viewsToRemove) {
- removeView(v);
- }
- }
-
- /** Returns the number of task views in the layout. */
- public int getTaskViewCount() {
- int taskViewCount = 0;
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- if (getChildAt(i) instanceof FloatingTaskView) {
- taskViewCount++;
- }
- }
- return taskViewCount;
- }
-
- /**
- * Called when the task view is un-stuck from the dismiss target.
- * @param v the task view being moved.
- * @param velX the x velocity of the motion event.
- * @param velY the y velocity of the motion event.
- * @param wasFlungOut true if the user flung the task view out of the dismiss target (i.e. there
- * was an 'up' event), otherwise the user is still dragging.
- */
- public void onUnstuckFromTarget(FloatingTaskView v, float velX, float velY,
- boolean wasFlungOut) {
- mTouchHandler.onUnstuckFromTarget(v, velX, velY, wasFlungOut);
- }
-
- /**
- * Updates dimensions and applies them to any task views.
- */
- public void updateSizes() {
- if (mDismissController != null) {
- mDismissController.updateSizes();
- }
-
- mOverhangWhenStashed = getResources().getDimensionPixelSize(
- R.dimen.floating_task_stash_offset);
- mVerticalPadding = getResources().getDimensionPixelSize(
- R.dimen.floating_task_vertical_padding);
-
- WindowMetrics windowMetrics = mWindowManager.getCurrentWindowMetrics();
- WindowInsets windowInsets = windowMetrics.getWindowInsets();
- Insets insets = windowInsets.getInsetsIgnoringVisibility(WindowInsets.Type.navigationBars()
- | WindowInsets.Type.statusBars()
- | WindowInsets.Type.displayCutout());
- Rect bounds = windowMetrics.getBounds();
- mPositionRect.set(bounds.left + insets.left,
- bounds.top + insets.top + mVerticalPadding,
- bounds.right - insets.right,
- bounds.bottom - insets.bottom - mVerticalPadding);
-
- int taskViewWidth = Math.max(bounds.height(), bounds.width());
- int taskViewHeight = Math.min(bounds.height(), bounds.width());
- taskViewHeight = taskViewHeight - (insets.top + insets.bottom + (mVerticalPadding * 2));
- mTaskViewSize.set((int) (taskViewWidth * START_SIZE_WIDTH_PERCENT), taskViewHeight);
- mDefaultStartPosition.set(mPositionRect.left, mPositionRect.top);
-
- // Update existing views
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- if (getChildAt(i) instanceof FloatingTaskView) {
- FloatingTaskView child = (FloatingTaskView) getChildAt(i);
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
- lp.width = mTaskViewSize.x;
- lp.height = mTaskViewSize.y;
- child.setLayoutParams(lp);
- updateTaskViewPosition(child);
- }
- }
- }
-
- /** Returns the first floating task view in the layout. (Currently only ever 1 view). */
- @Nullable
- public FloatingTaskView getFirstTaskView() {
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child instanceof FloatingTaskView) {
- return (FloatingTaskView) child;
- }
- }
- return null;
- }
-
- private void updateTaskViewPosition(FloatingTaskView floatingView) {
- Point lastPosition = mController.getLastPosition();
- if (lastPosition.x == -1 && lastPosition.y == -1) {
- floatingView.setX(mDefaultStartPosition.x);
- floatingView.setY(mDefaultStartPosition.y);
- } else {
- floatingView.setX(lastPosition.x);
- floatingView.setY(lastPosition.y);
- }
- if (mTouchHandler.isStashedPosition(floatingView)) {
- floatingView.setStashed(true);
- }
- floatingView.updateLocation();
- }
-
- /**
- * Updates the area of the screen that shouldn't allow the back gesture due to the placement
- * of task view (i.e. when task view is stashed on an edge, tapping or swiping that edge would
- * un-stash the task view instead of performing the back gesture).
- */
- private void updateSystemGestureExclusion() {
- Rect excludeZone = mSystemGestureExclusionRects.get(0);
- FloatingTaskView floatingTaskView = getFirstTaskView();
- if (floatingTaskView != null && floatingTaskView.isStashed()) {
- excludeZone.set(floatingTaskView.getLeft(),
- floatingTaskView.getTop(),
- floatingTaskView.getRight(),
- floatingTaskView.getBottom());
- excludeZone.offset((int) (floatingTaskView.getTranslationX()),
- (int) (floatingTaskView.getTranslationY()));
- setSystemGestureExclusionRects(mSystemGestureExclusionRects);
- } else {
- excludeZone.setEmpty();
- setSystemGestureExclusionRects(Collections.emptyList());
- }
- }
-
- /**
- * Fills in the touchable region for floating windows. This is used by WindowManager to
- * decide which touch events go to the floating windows.
- */
- private void getTouchableRegion(Region outRegion) {
- int childCount = getChildCount();
- Rect temp = new Rect();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child instanceof FloatingTaskView) {
- child.getBoundsOnScreen(temp);
- outRegion.op(temp, Region.Op.UNION);
- }
- }
- }
-
- /**
- * Implementation of the touch handler. Animates the task view based on touch events.
- */
- private class TouchHandlerImpl implements FloatingTaskTouchHandler {
- /**
- * The view can be stashed by swiping it towards the current edge or moving it there. If
- * the view gets moved in a way that is not one of these gestures, this is flipped to false.
- */
- private boolean mCanStash = true;
- /**
- * This is used to indicate that the view has been un-stuck from the dismiss target and
- * needs to spring to the current touch location.
- */
- // TODO: implement this behavior
- private boolean mSpringToTouchOnNextMotionEvent = false;
-
- private ArrayList<FlingAnimation> mFlingAnimations;
- private ViewPropertyAnimator mViewPropertyAnimation;
-
- private float mViewInitialX;
- private float mViewInitialY;
-
- private float[] mMinMax = new float[2];
-
- @Override
- public void onDown(@NonNull FloatingTaskView v, @NonNull MotionEvent ev, float viewInitialX,
- float viewInitialY) {
- mCanStash = true;
- mViewInitialX = viewInitialX;
- mViewInitialY = viewInitialY;
- mDismissController.setUpMagneticObject(v);
- mDismissController.passEventToMagnetizedObject(ev);
- }
-
- @Override
- public void onMove(@NonNull FloatingTaskView v, @NonNull MotionEvent ev,
- float dx, float dy) {
- // Shows the magnetic dismiss target if needed.
- mDismissController.showDismiss(/* show= */ true);
-
- // Send it to magnetic target first.
- if (mDismissController.passEventToMagnetizedObject(ev)) {
- v.setStashed(false);
- mCanStash = true;
-
- return;
- }
-
- // If we're here magnetic target didn't want it so move as per normal.
-
- v.setTranslationX(capX(v, mViewInitialX + dx, /* isMoving= */ true));
- v.setTranslationY(capY(v, mViewInitialY + dy));
- if (v.isStashed()) {
- // Check if we've moved far enough to be not stashed.
- final float centerX = mPositionRect.centerX() - (v.getWidth() / 2f);
- final boolean viewInitiallyOnLeftSide = mViewInitialX < centerX;
- if (viewInitiallyOnLeftSide) {
- if (v.getTranslationX() > mPositionRect.left) {
- v.setStashed(false);
- mCanStash = true;
- }
- } else if (v.getTranslationX() + v.getWidth() < mPositionRect.right) {
- v.setStashed(false);
- mCanStash = true;
- }
- }
- }
-
- // Reference for math / values: StackAnimationController#flingStackThenSpringToEdge.
- // TODO clean up the code here, pretty hard to comprehend
- // TODO code here doesn't work the best when in portrait (e.g. can't fling up/down on edges)
- @Override
- public void onUp(@NonNull FloatingTaskView v, @NonNull MotionEvent ev,
- float dx, float dy, float velX, float velY) {
-
- // Send it to magnetic target first.
- if (mDismissController.passEventToMagnetizedObject(ev)) {
- v.setStashed(false);
- return;
- }
- mDismissController.showDismiss(/* show= */ false);
-
- // If we're here magnetic target didn't want it so handle up as per normal.
-
- final float x = capX(v, mViewInitialX + dx, /* isMoving= */ false);
- final float centerX = mPositionRect.centerX();
- final boolean viewInitiallyOnLeftSide = mViewInitialX + v.getWidth() < centerX;
- final boolean viewOnLeftSide = x + v.getWidth() < centerX;
- final boolean isFling = Math.abs(velX) > ESCAPE_VELOCITY;
- final boolean isFlingLeft = isFling && velX < ESCAPE_VELOCITY;
- // TODO: check velX here sometimes it doesn't stash on move when I think it should
- final boolean shouldStashFromMove =
- (velX < 0 && v.getTranslationX() < mPositionRect.left)
- || (velX > 0
- && v.getTranslationX() + v.getWidth() > mPositionRect.right);
- final boolean shouldStashFromFling = viewInitiallyOnLeftSide == viewOnLeftSide
- && isFling
- && ((viewOnLeftSide && velX < ESCAPE_VELOCITY)
- || (!viewOnLeftSide && velX > ESCAPE_VELOCITY));
- final boolean shouldStash = mCanStash && (shouldStashFromFling || shouldStashFromMove);
-
- ProtoLog.d(WM_SHELL_FLOATING_APPS,
- "shouldStash=%s shouldStashFromFling=%s shouldStashFromMove=%s"
- + " viewInitiallyOnLeftSide=%s viewOnLeftSide=%s isFling=%s velX=%f"
- + " isStashed=%s", shouldStash, shouldStashFromFling, shouldStashFromMove,
- viewInitiallyOnLeftSide, viewOnLeftSide, isFling, velX, v.isStashed());
-
- if (v.isStashed()) {
- mMinMax[0] = viewOnLeftSide
- ? mPositionRect.left - v.getWidth() + mOverhangWhenStashed
- : mPositionRect.right - v.getWidth();
- mMinMax[1] = viewOnLeftSide
- ? mPositionRect.left
- : mPositionRect.right - mOverhangWhenStashed;
- } else {
- populateMinMax(v, viewOnLeftSide, shouldStash, mMinMax);
- }
-
- boolean movingLeft = isFling ? isFlingLeft : viewOnLeftSide;
- float destinationRelativeX = movingLeft
- ? mMinMax[0]
- : mMinMax[1];
-
- // TODO: why is this necessary / when does this happen?
- if (mMinMax[1] < v.getTranslationX()) {
- mMinMax[1] = v.getTranslationX();
- }
- if (v.getTranslationX() < mMinMax[0]) {
- mMinMax[0] = v.getTranslationX();
- }
-
- // Use the touch event's velocity if it's sufficient, otherwise use the minimum velocity
- // so that it'll make it all the way to the side of the screen.
- final float minimumVelocityToReachEdge =
- getMinimumVelocityToReachEdge(v, destinationRelativeX);
- final float startXVelocity = movingLeft
- ? Math.min(minimumVelocityToReachEdge, velX)
- : Math.max(minimumVelocityToReachEdge, velX);
-
- cancelAnyAnimations(v);
-
- mFlingAnimations = getAnimationForUpEvent(v, shouldStash,
- startXVelocity, mMinMax[0], mMinMax[1], destinationRelativeX);
- for (int i = 0; i < mFlingAnimations.size(); i++) {
- mFlingAnimations.get(i).start();
- }
- }
-
- @Override
- public void onClick(@NonNull FloatingTaskView v) {
- if (v.isStashed()) {
- final float centerX = mPositionRect.centerX() - (v.getWidth() / 2f);
- final boolean viewOnLeftSide = v.getTranslationX() < centerX;
- final float destinationRelativeX = viewOnLeftSide
- ? mPositionRect.left
- : mPositionRect.right - v.getWidth();
- final float minimumVelocityToReachEdge =
- getMinimumVelocityToReachEdge(v, destinationRelativeX);
- populateMinMax(v, viewOnLeftSide, /* stashed= */ true, mMinMax);
-
- cancelAnyAnimations(v);
-
- FlingAnimation flingAnimation = new FlingAnimation(v,
- DynamicAnimation.TRANSLATION_X);
- flingAnimation.setFriction(FLING_FRICTION)
- .setStartVelocity(minimumVelocityToReachEdge)
- .setMinValue(mMinMax[0])
- .setMaxValue(mMinMax[1])
- .addEndListener((animation, canceled, value, velocity) -> {
- if (canceled) return;
- mController.setLastPosition((int) v.getTranslationX(),
- (int) v.getTranslationY());
- v.setStashed(false);
- v.updateLocation();
- });
- mFlingAnimations = new ArrayList<>();
- mFlingAnimations.add(flingAnimation);
- flingAnimation.start();
- }
- }
-
- public void onUnstuckFromTarget(FloatingTaskView v, float velX, float velY,
- boolean wasFlungOut) {
- if (wasFlungOut) {
- snapTaskViewToEdge(v, velX, /* shouldStash= */ false);
- } else {
- // TODO: use this for something / to spring the view to the touch location
- mSpringToTouchOnNextMotionEvent = true;
- }
- }
-
- public void stashTaskView(FloatingTaskView v, boolean shouldStash) {
- if (v.isStashed() == shouldStash) {
- return;
- }
- final float centerX = mPositionRect.centerX() - (v.getWidth() / 2f);
- final boolean viewOnLeftSide = v.getTranslationX() < centerX;
- snapTaskViewToEdge(v, viewOnLeftSide ? -ESCAPE_VELOCITY : ESCAPE_VELOCITY, shouldStash);
- }
-
- public boolean isStashedPosition(View v) {
- return v.getTranslationX() < mPositionRect.left
- || v.getTranslationX() + v.getWidth() > mPositionRect.right;
- }
-
- // TODO: a lot of this is duplicated in onUp -- can it be unified?
- private void snapTaskViewToEdge(FloatingTaskView v, float velX, boolean shouldStash) {
- final boolean movingLeft = velX < ESCAPE_VELOCITY;
- populateMinMax(v, movingLeft, shouldStash, mMinMax);
- float destinationRelativeX = movingLeft
- ? mMinMax[0]
- : mMinMax[1];
-
- // TODO: why is this necessary / when does this happen?
- if (mMinMax[1] < v.getTranslationX()) {
- mMinMax[1] = v.getTranslationX();
- }
- if (v.getTranslationX() < mMinMax[0]) {
- mMinMax[0] = v.getTranslationX();
- }
-
- // Use the touch event's velocity if it's sufficient, otherwise use the minimum velocity
- // so that it'll make it all the way to the side of the screen.
- final float minimumVelocityToReachEdge =
- getMinimumVelocityToReachEdge(v, destinationRelativeX);
- final float startXVelocity = movingLeft
- ? Math.min(minimumVelocityToReachEdge, velX)
- : Math.max(minimumVelocityToReachEdge, velX);
-
- cancelAnyAnimations(v);
-
- mFlingAnimations = getAnimationForUpEvent(v,
- shouldStash, startXVelocity, mMinMax[0], mMinMax[1],
- destinationRelativeX);
- for (int i = 0; i < mFlingAnimations.size(); i++) {
- mFlingAnimations.get(i).start();
- }
- }
-
- private void cancelAnyAnimations(FloatingTaskView v) {
- if (mFlingAnimations != null) {
- for (int i = 0; i < mFlingAnimations.size(); i++) {
- if (mFlingAnimations.get(i).isRunning()) {
- mFlingAnimations.get(i).cancel();
- }
- }
- }
- if (mViewPropertyAnimation != null) {
- mViewPropertyAnimation.cancel();
- mViewPropertyAnimation = null;
- }
- }
-
- private ArrayList<FlingAnimation> getAnimationForUpEvent(FloatingTaskView v,
- boolean shouldStash, float startVelX, float minValue, float maxValue,
- float destinationRelativeX) {
- final float ty = v.getTranslationY();
- final ArrayList<FlingAnimation> animations = new ArrayList<>();
- if (ty != capY(v, ty)) {
- // The view was being dismissed so the Y is out of bounds, need to animate that.
- FlingAnimation yFlingAnimation = new FlingAnimation(v,
- DynamicAnimation.TRANSLATION_Y);
- yFlingAnimation.setFriction(FLING_FRICTION)
- .setStartVelocity(startVelX)
- .setMinValue(mPositionRect.top)
- .setMaxValue(mPositionRect.bottom - mTaskViewSize.y);
- animations.add(yFlingAnimation);
- }
- FlingAnimation flingAnimation = new FlingAnimation(v, DynamicAnimation.TRANSLATION_X);
- flingAnimation.setFriction(FLING_FRICTION)
- .setStartVelocity(startVelX)
- .setMinValue(minValue)
- .setMaxValue(maxValue)
- .addEndListener((animation, canceled, value, velocity) -> {
- if (canceled) return;
- Runnable endAction = () -> {
- v.setStashed(shouldStash);
- v.updateLocation();
- if (!v.isStashed()) {
- mController.setLastPosition((int) v.getTranslationX(),
- (int) v.getTranslationY());
- }
- };
- if (!shouldStash) {
- final int xTranslation = (int) v.getTranslationX();
- if (xTranslation != destinationRelativeX) {
- // TODO: this animation doesn't feel great, should figure out
- // a better way to do this or remove the need for it all together.
- mViewPropertyAnimation = v.animate()
- .translationX(destinationRelativeX)
- .setListener(getAnimationListener(endAction));
- mViewPropertyAnimation.start();
- } else {
- endAction.run();
- }
- } else {
- endAction.run();
- }
- });
- animations.add(flingAnimation);
- return animations;
- }
-
- private AnimatorListenerAdapter getAnimationListener(Runnable endAction) {
- return new AnimatorListenerAdapter() {
- boolean translationCanceled = false;
- @Override
- public void onAnimationCancel(Animator animation) {
- super.onAnimationCancel(animation);
- translationCanceled = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- if (!translationCanceled) {
- endAction.run();
- }
- }
- };
- }
-
- private void populateMinMax(FloatingTaskView v, boolean onLeft, boolean shouldStash,
- float[] out) {
- if (shouldStash) {
- out[0] = onLeft
- ? mPositionRect.left - v.getWidth() + mOverhangWhenStashed
- : mPositionRect.right - v.getWidth();
- out[1] = onLeft
- ? mPositionRect.left
- : mPositionRect.right - mOverhangWhenStashed;
- } else {
- out[0] = mPositionRect.left;
- out[1] = mPositionRect.right - mTaskViewSize.x;
- }
- }
-
- private float getMinimumVelocityToReachEdge(FloatingTaskView v,
- float destinationRelativeX) {
- // Minimum velocity required for the view to make it to the targeted side of the screen,
- // taking friction into account (4.2f is the number that friction scalars are multiplied
- // by in DynamicAnimation.DragForce). This is an estimate and could be slightly off, the
- // animation at the end will ensure that it reaches the destination X regardless.
- return (destinationRelativeX - v.getTranslationX()) * (FLING_FRICTION * 4.2f);
- }
-
- private float capX(FloatingTaskView v, float x, boolean isMoving) {
- final int width = v.getWidth();
- if (v.isStashed() || isMoving) {
- if (x < mPositionRect.left - v.getWidth() + mOverhangWhenStashed) {
- return mPositionRect.left - v.getWidth() + mOverhangWhenStashed;
- }
- if (x > mPositionRect.right - mOverhangWhenStashed) {
- return mPositionRect.right - mOverhangWhenStashed;
- }
- } else {
- if (x < mPositionRect.left) {
- return mPositionRect.left;
- }
- if (x > mPositionRect.right - width) {
- return mPositionRect.right - width;
- }
- }
- return x;
- }
-
- private float capY(FloatingTaskView v, float y) {
- final int height = v.getHeight();
- if (y < mPositionRect.top) {
- return mPositionRect.top;
- }
- if (y > mPositionRect.bottom - height) {
- return mPositionRect.bottom - height;
- }
- return y;
- }
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingTaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingTaskView.java
deleted file mode 100644
index 581204a..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingTaskView.java
+++ /dev/null
@@ -1,385 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.floating.views;
-
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
-
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_FLOATING_APPS;
-
-import android.app.ActivityManager;
-import android.app.ActivityOptions;
-import android.app.ActivityTaskManager;
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.graphics.Outline;
-import android.graphics.Rect;
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewOutlineProvider;
-import android.widget.FrameLayout;
-
-import androidx.annotation.NonNull;
-
-import com.android.internal.policy.ScreenDecorationsUtils;
-import com.android.internal.protolog.common.ProtoLog;
-import com.android.wm.shell.R;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.TaskView;
-import com.android.wm.shell.TaskViewTransitions;
-import com.android.wm.shell.bubbles.RelativeTouchListener;
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.annotations.ShellMainThread;
-import com.android.wm.shell.floating.FloatingTasksController;
-
-/**
- * A view that holds a floating task using {@link TaskView} along with additional UI to manage
- * the task.
- */
-public class FloatingTaskView extends FrameLayout {
-
- private static final String TAG = FloatingTaskView.class.getSimpleName();
-
- private FloatingTasksController mController;
-
- private FloatingMenuView mMenuView;
- private int mMenuHeight;
- private TaskView mTaskView;
-
- private float mCornerRadius = 0f;
- private int mBackgroundColor;
-
- private FloatingTasksController.Task mTask;
-
- private boolean mIsStashed;
-
- /**
- * Creates a floating task view.
- *
- * @param context the context to use.
- * @param controller the controller to notify about changes in the floating task (e.g. removal).
- */
- public FloatingTaskView(Context context, FloatingTasksController controller) {
- super(context);
- mController = controller;
- setElevation(getResources().getDimensionPixelSize(R.dimen.floating_task_elevation));
- mMenuHeight = context.getResources().getDimensionPixelSize(R.dimen.floating_task_menu_size);
- mMenuView = new FloatingMenuView(context);
- addView(mMenuView);
-
- applyThemeAttrs();
-
- setClipToOutline(true);
- setOutlineProvider(new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mCornerRadius);
- }
- });
- }
-
- // TODO: call this when theme/config changes
- void applyThemeAttrs() {
- boolean supportsRoundedCorners = ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
- mContext.getResources());
- final TypedArray ta = mContext.obtainStyledAttributes(new int[] {
- android.R.attr.dialogCornerRadius,
- android.R.attr.colorBackgroundFloating});
- mCornerRadius = supportsRoundedCorners ? ta.getDimensionPixelSize(0, 0) : 0;
- mCornerRadius = mCornerRadius / 2f;
- mBackgroundColor = ta.getColor(1, Color.WHITE);
-
- ta.recycle();
-
- mMenuView.setCornerRadius(mCornerRadius);
- mMenuHeight = getResources().getDimensionPixelSize(
- R.dimen.floating_task_menu_size);
-
- if (mTaskView != null) {
- mTaskView.setCornerRadius(mCornerRadius);
- }
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- int height = MeasureSpec.getSize(heightMeasureSpec);
-
- // Add corner radius here so that the menu extends behind the rounded corners of TaskView.
- int menuViewHeight = Math.min((int) (mMenuHeight + mCornerRadius), height);
- measureChild(mMenuView, widthMeasureSpec, MeasureSpec.makeMeasureSpec(menuViewHeight,
- MeasureSpec.getMode(heightMeasureSpec)));
-
- if (mTaskView != null) {
- int taskViewHeight = height - menuViewHeight;
- measureChild(mTaskView, widthMeasureSpec, MeasureSpec.makeMeasureSpec(taskViewHeight,
- MeasureSpec.getMode(heightMeasureSpec)));
- }
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- // Drag handle above
- final int dragHandleBottom = t + mMenuView.getMeasuredHeight();
- mMenuView.layout(l, t, r, dragHandleBottom);
- if (mTaskView != null) {
- // Subtract radius so that the menu extends behind the rounded corners of TaskView.
- mTaskView.layout(l, (int) (dragHandleBottom - mCornerRadius), r,
- dragHandleBottom + mTaskView.getMeasuredHeight());
- }
- }
-
- /**
- * Constructs the TaskView to display the task. Must be called for {@link #startTask} to work.
- */
- public void createTaskView(Context context, ShellTaskOrganizer organizer,
- TaskViewTransitions transitions, SyncTransactionQueue syncQueue) {
- mTaskView = new TaskView(context, organizer, transitions, syncQueue);
- addView(mTaskView);
- mTaskView.setEnableSurfaceClipping(true);
- mTaskView.setCornerRadius(mCornerRadius);
- }
-
- /**
- * Starts the provided task in the TaskView, if the TaskView exists. This should be called after
- * {@link #createTaskView}.
- */
- public void startTask(@ShellMainThread ShellExecutor executor,
- FloatingTasksController.Task task) {
- if (mTaskView == null) {
- Log.e(TAG, "starting task before creating the view!");
- return;
- }
- mTask = task;
- mTaskView.setListener(executor, mTaskViewListener);
- }
-
- /**
- * Sets the touch handler for the view.
- *
- * @param handler the touch handler for the view.
- */
- public void setTouchHandler(FloatingTaskLayer.FloatingTaskTouchHandler handler) {
- setOnTouchListener(new RelativeTouchListener() {
- @Override
- public boolean onDown(@NonNull View v, @NonNull MotionEvent ev) {
- handler.onDown(FloatingTaskView.this, ev, v.getTranslationX(), v.getTranslationY());
- return true;
- }
-
- @Override
- public void onMove(@NonNull View v, @NonNull MotionEvent ev, float viewInitialX,
- float viewInitialY, float dx, float dy) {
- handler.onMove(FloatingTaskView.this, ev, dx, dy);
- }
-
- @Override
- public void onUp(@NonNull View v, @NonNull MotionEvent ev, float viewInitialX,
- float viewInitialY, float dx, float dy, float velX, float velY) {
- handler.onUp(FloatingTaskView.this, ev, dx, dy, velX, velY);
- }
- });
- setOnClickListener(view -> {
- handler.onClick(FloatingTaskView.this);
- });
-
- mMenuView.addMenuItem(null, view -> {
- if (mIsStashed) {
- // If we're stashed all clicks un-stash.
- handler.onClick(FloatingTaskView.this);
- }
- });
- }
-
- private void setContentVisibility(boolean visible) {
- if (mTaskView == null) return;
- mTaskView.setAlpha(visible ? 1f : 0f);
- }
-
- /**
- * Sets the alpha of both this view and the TaskView.
- */
- public void setTaskViewAlpha(float alpha) {
- if (mTaskView != null) {
- mTaskView.setAlpha(alpha);
- }
- setAlpha(alpha);
- }
-
- /**
- * Call when the location or size of the view has changed to update TaskView.
- */
- public void updateLocation() {
- if (mTaskView == null) return;
- mTaskView.onLocationChanged();
- }
-
- private void updateMenuColor() {
- ActivityManager.RunningTaskInfo info = mTaskView.getTaskInfo();
- int color = info != null ? info.taskDescription.getBackgroundColor() : -1;
- if (color != -1) {
- mMenuView.setBackgroundColor(color);
- } else {
- mMenuView.setBackgroundColor(mBackgroundColor);
- }
- }
-
- /**
- * Sets whether the view is stashed or not.
- *
- * Also updates the touchable area based on this. If the view is stashed we don't direct taps
- * on the activity to the activity, instead a tap will un-stash the view.
- */
- public void setStashed(boolean isStashed) {
- if (mIsStashed != isStashed) {
- mIsStashed = isStashed;
- if (mTaskView == null) {
- return;
- }
- updateObscuredTouchRect();
- }
- }
-
- /** Whether the view is stashed at the edge of the screen or not. **/
- public boolean isStashed() {
- return mIsStashed;
- }
-
- private void updateObscuredTouchRect() {
- if (mIsStashed) {
- Rect tmpRect = new Rect();
- getBoundsOnScreen(tmpRect);
- mTaskView.setObscuredTouchRect(tmpRect);
- } else {
- mTaskView.setObscuredTouchRect(null);
- }
- }
-
- /**
- * Whether the task needs to be restarted, this can happen when {@link #cleanUpTaskView()} has
- * been called on this view or if
- * {@link #startTask(ShellExecutor, FloatingTasksController.Task)} was never called.
- */
- public boolean needsTaskStarted() {
- // If the task needs to be restarted then TaskView would have been cleaned up.
- return mTaskView == null;
- }
-
- /** Call this when the floating task activity is no longer in use. */
- public void cleanUpTaskView() {
- if (mTask != null && mTask.taskId != INVALID_TASK_ID) {
- try {
- ActivityTaskManager.getService().removeTask(mTask.taskId);
- } catch (RemoteException e) {
- Log.e(TAG, e.getMessage());
- }
- }
- if (mTaskView != null) {
- mTaskView.release();
- removeView(mTaskView);
- mTaskView = null;
- }
- }
-
- // TODO: use task background colour / how to get the taskInfo ?
- private static int getDragBarColor(ActivityManager.RunningTaskInfo taskInfo) {
- final int taskBgColor = taskInfo.taskDescription.getStatusBarColor();
- return Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor).toArgb();
- }
-
- private final TaskView.Listener mTaskViewListener = new TaskView.Listener() {
- private boolean mInitialized = false;
- private boolean mDestroyed = false;
-
- @Override
- public void onInitialized() {
- if (mDestroyed || mInitialized) {
- return;
- }
- // Custom options so there is no activity transition animation
- ActivityOptions options = ActivityOptions.makeCustomAnimation(getContext(),
- /* enterResId= */ 0, /* exitResId= */ 0);
-
- Rect launchBounds = new Rect();
- mTaskView.getBoundsOnScreen(launchBounds);
-
- try {
- options.setTaskAlwaysOnTop(true);
- if (mTask.intent != null) {
- Intent fillInIntent = new Intent();
- // Apply flags to make behaviour match documentLaunchMode=always.
- fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
- fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
-
- PendingIntent pi = PendingIntent.getActivity(mContext, 0, mTask.intent,
- PendingIntent.FLAG_MUTABLE,
- null);
- mTaskView.startActivity(pi, fillInIntent, options, launchBounds);
- } else {
- ProtoLog.e(WM_SHELL_FLOATING_APPS, "Tried to start a task with null intent");
- }
- } catch (RuntimeException e) {
- ProtoLog.e(WM_SHELL_FLOATING_APPS, "Exception while starting task: %s",
- e.getMessage());
- mController.removeTask();
- }
- mInitialized = true;
- }
-
- @Override
- public void onReleased() {
- mDestroyed = true;
- }
-
- @Override
- public void onTaskCreated(int taskId, ComponentName name) {
- mTask.taskId = taskId;
- updateMenuColor();
- setContentVisibility(true);
- }
-
- @Override
- public void onTaskVisibilityChanged(int taskId, boolean visible) {
- setContentVisibility(visible);
- }
-
- @Override
- public void onTaskRemovalStarted(int taskId) {
- // Must post because this is called from a binder thread.
- post(() -> {
- mController.removeTask();
- cleanUpTaskView();
- });
- }
-
- @Override
- public void onBackPressedOnTaskRoot(int taskId) {
- if (mTask.taskId == taskId && !mIsStashed) {
- // TODO: is removing the window the desired behavior?
- post(() -> mController.removeTask());
- }
- }
- };
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
index f4888fb..168f6d7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
@@ -98,9 +98,11 @@
switch (change.getMode()) {
case WindowManager.TRANSIT_OPEN:
- case WindowManager.TRANSIT_TO_FRONT:
onOpenTransitionReady(change, startT, finishT);
break;
+ case WindowManager.TRANSIT_TO_FRONT:
+ onToFrontTransitionReady(change, startT, finishT);
+ break;
case WindowManager.TRANSIT_CLOSE: {
taskInfoList.add(change.getTaskInfo());
onCloseTransitionReady(change, startT, finishT);
@@ -138,6 +140,21 @@
change.getTaskInfo(), startT, finishT);
}
+ private void onToFrontTransitionReady(
+ TransitionInfo.Change change,
+ SurfaceControl.Transaction startT,
+ SurfaceControl.Transaction finishT) {
+ boolean exists = mWindowDecorViewModel.setupWindowDecorationForTransition(
+ change.getTaskInfo(),
+ startT,
+ finishT);
+ if (!exists) {
+ // Window caption does not exist, create it
+ mWindowDecorViewModel.createWindowDecoration(
+ change.getTaskInfo(), change.getLeash(), startT, finishT);
+ }
+ }
+
@Override
public void onTransitionStarting(@NonNull IBinder transition) {}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
index f707363..0006244 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
@@ -23,7 +23,6 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.RemoteAction;
@@ -74,11 +73,6 @@
void setAppActions(List<RemoteAction> appActions, RemoteAction closeAction);
/**
- * Wait until the next frame to run the given Runnable.
- */
- void runWithNextFrame(@NonNull Runnable runnable);
-
- /**
* Resize the PiP menu with the given bounds. The PiP SurfaceControl is given if there is a
* need to synchronize the movements on the same frame as PiP.
*/
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 2d7c5ce..f170e77 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
@@ -179,10 +179,8 @@
// This is necessary in case there was a resize animation ongoing when exit PIP
// started, in which case the first resize will be skipped to let the exit
// operation handle the final resize out of PIP mode. See b/185306679.
- finishResizeDelayedIfNeeded(() -> {
- finishResize(tx, destinationBounds, direction, animationType);
- sendOnPipTransitionFinished(direction);
- });
+ finishResize(tx, destinationBounds, direction, animationType);
+ sendOnPipTransitionFinished(direction);
}
}
@@ -198,34 +196,6 @@
}
};
- /**
- * Finishes resizing the PiP, delaying the operation if it has to be synced with the PiP menu.
- *
- * This is done to avoid a race condition between the last transaction applied in
- * onAnimationUpdate and the finishResize in onAnimationEnd. finishResize creates a
- * WindowContainerTransaction, which is to be applied by WmCore later. It may happen that it
- * gets applied before the transaction created by the last onAnimationUpdate. As a result of
- * this, the PiP surface may get scaled after the new bounds are applied by WmCore, which
- * makes the PiP surface have unexpected bounds. To avoid this, we delay the finishResize
- * operation until the next frame. This aligns the last onAnimationUpdate transaction with the
- * WCT application.
- *
- * The race only happens when the PiP surface transaction has to be synced with the PiP menu
- * due to the necessity for a delay when syncing the PiP surface, the PiP menu surface and
- * the PiP menu contents.
- */
- private void finishResizeDelayedIfNeeded(Runnable finishResizeRunnable) {
- if (!shouldSyncPipTransactionWithMenu()) {
- finishResizeRunnable.run();
- return;
- }
- mPipMenuController.runWithNextFrame(finishResizeRunnable);
- }
-
- private boolean shouldSyncPipTransactionWithMenu() {
- return mPipMenuController.isMenuVisible();
- }
-
@VisibleForTesting
final PipTransitionController.PipTransitionCallback mPipTransitionCallback =
new PipTransitionController.PipTransitionCallback() {
@@ -251,7 +221,7 @@
@Override
public boolean handlePipTransaction(SurfaceControl leash,
SurfaceControl.Transaction tx, Rect destinationBounds) {
- if (shouldSyncPipTransactionWithMenu()) {
+ if (mPipMenuController.isMenuVisible()) {
mPipMenuController.movePipMenu(leash, tx, destinationBounds);
return true;
}
@@ -1253,7 +1223,7 @@
mSurfaceTransactionHelper
.crop(tx, mLeash, toBounds)
.round(tx, mLeash, mPipTransitionState.isInPip());
- if (shouldSyncPipTransactionWithMenu()) {
+ if (mPipMenuController.isMenuVisible()) {
mPipMenuController.resizePipMenu(mLeash, tx, toBounds);
} else {
tx.apply();
@@ -1295,7 +1265,7 @@
mSurfaceTransactionHelper
.scale(tx, mLeash, startBounds, toBounds, degrees)
.round(tx, mLeash, startBounds, toBounds);
- if (shouldSyncPipTransactionWithMenu()) {
+ if (mPipMenuController.isMenuVisible()) {
mPipMenuController.movePipMenu(mLeash, tx, toBounds);
} else {
tx.apply();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index 531b9de..a0a8f9f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -305,18 +305,6 @@
showResizeHandle);
}
- @Override
- public void runWithNextFrame(Runnable runnable) {
- if (mPipMenuView == null || mPipMenuView.getViewRootImpl() == null) {
- runnable.run();
- }
-
- mPipMenuView.getViewRootImpl().registerRtFrameCallback(frame -> {
- mMainHandler.post(runnable);
- });
- mPipMenuView.invalidate();
- }
-
/**
* Move the PiP menu, which does a translation and possibly a scale transformation.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
index c39400a..ab7edbf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
@@ -421,18 +421,6 @@
}
@Override
- public void runWithNextFrame(Runnable runnable) {
- if (mPipMenuView == null || mPipMenuView.getViewRootImpl() == null) {
- runnable.run();
- }
-
- mPipMenuView.getViewRootImpl().registerRtFrameCallback(frame -> {
- mMainHandler.post(runnable);
- });
- mPipMenuView.invalidate();
- }
-
- @Override
public void movePipMenu(SurfaceControl pipLeash, SurfaceControl.Transaction transaction,
Rect pipDestBounds) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
index c52ed24..75f9a4c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -42,8 +42,8 @@
Consts.TAG_WM_SHELL),
WM_SHELL_PICTURE_IN_PICTURE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM_SHELL),
- WM_SHELL_SPLIT_SCREEN(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
- Consts.TAG_WM_SHELL),
+ WM_SHELL_SPLIT_SCREEN(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
+ Consts.TAG_WM_SPLIT_SCREEN),
WM_SHELL_SYSUI_EVENTS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM_SHELL),
WM_SHELL_DESKTOP_MODE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
@@ -110,6 +110,7 @@
private static class Consts {
private static final String TAG_WM_SHELL = "WindowManagerShell";
private static final String TAG_WM_STARTING_WINDOW = "ShellStartingWindow";
+ private static final String TAG_WM_SPLIT_SCREEN = "ShellSplitScreen";
private static final boolean ENABLE_DEBUG = true;
private static final boolean ENABLE_LOG_TO_PROTO_DEBUG = true;
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 15a1133..e888c6f 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
@@ -1097,7 +1097,7 @@
activityTaskManagerService.setFocusedTask(getTaskId(stageToFocus));
} catch (RemoteException | NullPointerException e) {
ProtoLog.e(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
- "%s: Unable to update focus on the chosen stage, %s", TAG, e);
+ "Unable to update focus on the chosen stage: %s", e.getMessage());
}
}
@@ -1434,14 +1434,14 @@
}
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
- "%s: Request to %s divider bar from %s.", TAG,
+ "Request to %s divider bar from %s.",
(visible ? "show" : "hide"), Debug.getCaller());
// Defer showing divider bar after keyguard dismissed, so it won't interfere with keyguard
// dismissing animation.
if (visible && mKeyguardShowing) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
- "%s: Defer showing divider bar due to keyguard showing.", TAG);
+ " Defer showing divider bar due to keyguard showing.");
return;
}
@@ -1450,7 +1450,7 @@
if (mIsDividerRemoteAnimating) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
- "%s: Skip animating divider bar due to it's remote animating.", TAG);
+ " Skip animating divider bar due to it's remote animating.");
return;
}
@@ -1465,12 +1465,12 @@
final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
if (dividerLeash == null) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
- "%s: Skip animating divider bar due to divider leash not ready.", TAG);
+ " Skip animating divider bar due to divider leash not ready.");
return;
}
if (mIsDividerRemoteAnimating) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
- "%s: Skip animating divider bar due to it's remote animating.", TAG);
+ " Skip animating divider bar due to it's remote animating.");
return;
}
@@ -1633,14 +1633,14 @@
}
@Override
- public void onLayoutSizeChanging(SplitLayout layout) {
+ public void onLayoutSizeChanging(SplitLayout layout, int offsetX, int offsetY) {
final SurfaceControl.Transaction t = mTransactionPool.acquire();
t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
updateSurfaceBounds(layout, t, true /* applyResizingOffset */);
getMainStageBounds(mTempRect1);
getSideStageBounds(mTempRect2);
- mMainStage.onResizing(mTempRect1, mTempRect2, t);
- mSideStage.onResizing(mTempRect2, mTempRect1, t);
+ mMainStage.onResizing(mTempRect1, mTempRect2, t, offsetX, offsetY);
+ mSideStage.onResizing(mTempRect2, mTempRect1, t, offsetX, offsetY);
t.apply();
mTransactionPool.release(t);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 6b90eab..acad5d9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -288,9 +288,11 @@
}
}
- void onResizing(Rect newBounds, Rect sideBounds, SurfaceControl.Transaction t) {
+ void onResizing(Rect newBounds, Rect sideBounds, SurfaceControl.Transaction t, int offsetX,
+ int offsetY) {
if (mSplitDecorManager != null && mRootTaskInfo != null) {
- mSplitDecorManager.onResizing(mRootTaskInfo, newBounds, sideBounds, t);
+ mSplitDecorManager.onResizing(mRootTaskInfo, newBounds, sideBounds, t, offsetX,
+ offsetY);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index a0e176c..ff6f2b0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -430,7 +430,8 @@
}
@Override
- public @Nullable SplashScreenView get() {
+ @Nullable
+ public SplashScreenView get() {
synchronized (this) {
while (!mIsViewSet) {
try {
@@ -691,7 +692,7 @@
private final TaskSnapshotWindow mTaskSnapshotWindow;
private SplashScreenView mContentView;
private boolean mSetSplashScreen;
- private @StartingWindowType int mSuggestType;
+ @StartingWindowType private int mSuggestType;
private int mBGColor;
private final long mCreateTime;
private int mSystemBarAppearance;
@@ -732,7 +733,7 @@
// Reset the system bar color which set by splash screen, make it align to the app.
private void clearSystemBarColor() {
- if (mDecorView == null) {
+ if (mDecorView == null || !mDecorView.isAttachedToWindow()) {
return;
}
if (mDecorView.getLayoutParams() instanceof WindowManager.LayoutParams) {
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 928e71f..63d4a6f 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
@@ -41,6 +41,7 @@
import static android.view.WindowManager.TRANSIT_RELAUNCH;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
import static android.window.TransitionInfo.FLAG_CROSS_PROFILE_OWNER_THUMBNAIL;
import static android.window.TransitionInfo.FLAG_CROSS_PROFILE_WORK_THUMBNAIL;
import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS;
@@ -395,6 +396,11 @@
}
}
+ // The back gesture has animated this change before transition happen, so here we don't
+ // play the animation again.
+ if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) {
+ continue;
+ }
// Don't animate anything that isn't independent.
if (!TransitionInfo.isIndependent(change, info)) continue;
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 519ec14..c6f31c2 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
@@ -24,7 +24,6 @@
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.fixScale;
-import static android.window.TransitionInfo.FLAG_IS_INPUT_METHOD;
import static android.window.TransitionInfo.FLAG_IS_OCCLUDED;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
@@ -81,7 +80,7 @@
/** Set to {@code true} to enable shell transitions. */
public static final boolean ENABLE_SHELL_TRANSITIONS =
- SystemProperties.getBoolean("persist.wm.debug.shell_transit", false);
+ SystemProperties.getBoolean("persist.wm.debug.shell_transit", true);
public static final boolean SHELL_TRANSITIONS_ROTATION = ENABLE_SHELL_TRANSITIONS
&& SystemProperties.getBoolean("persist.wm.debug.shell_transit_rotate", false);
@@ -336,9 +335,12 @@
boolean isOpening = isOpeningType(info.getType());
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
- if ((change.getFlags() & TransitionInfo.FLAG_IS_SYSTEM_WINDOW) != 0) {
+ if (change.hasFlags(TransitionInfo.FLAGS_IS_NON_APP_WINDOW)) {
// Currently system windows are controlled by WindowState, so don't change their
- // surfaces. Otherwise their window tokens could be hidden unexpectedly.
+ // surfaces. Otherwise their surfaces could be hidden or cropped unexpectedly.
+ // This includes Wallpaper (always z-ordered at bottom) and IME (associated with
+ // app), because there may not be a transition associated with their visibility
+ // changes, and currently they don't need transition animation.
continue;
}
final SurfaceControl leash = change.getLeash();
@@ -375,16 +377,7 @@
finishT.setAlpha(leash, 1.f);
}
} else if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) {
- // Wallpaper/IME are anomalies: their visibility is tied to other WindowStates.
- // As a result, we actually can't hide their WindowTokens because there may not be a
- // transition associated with them becoming visible again. Fortunately, since
- // wallpapers are always z-ordered to the back, we don't have to worry about it
- // flickering to the front during reparenting. Similarly, the IME is reparented to
- // the associated app, so its visibility is coupled. So, an explicit hide is not
- // needed visually anyways.
- if ((change.getFlags() & (FLAG_IS_WALLPAPER | FLAG_IS_INPUT_METHOD)) == 0) {
- finishT.hide(leash);
- }
+ finishT.hide(leash);
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 36dd8ed..ca15f00 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -105,6 +105,11 @@
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
if (!shouldShowWindowDecor(taskInfo)) return false;
+ CaptionWindowDecoration oldDecoration = mWindowDecorByTaskId.get(taskInfo.taskId);
+ if (oldDecoration != null) {
+ // close the old decoration if it exists to avoid two window decorations being added
+ oldDecoration.close();
+ }
final CaptionWindowDecoration windowDecoration = new CaptionWindowDecoration(
mContext,
mDisplayController,
@@ -141,23 +146,25 @@
}
@Override
- public void setupWindowDecorationForTransition(
+ public boolean setupWindowDecorationForTransition(
RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
- if (decoration == null) return;
+ if (decoration == null) return false;
decoration.relayout(taskInfo, startT, finishT);
+ return true;
}
@Override
- public void destroyWindowDecoration(RunningTaskInfo taskInfo) {
+ public boolean destroyWindowDecoration(RunningTaskInfo taskInfo) {
final CaptionWindowDecoration decoration =
mWindowDecorByTaskId.removeReturnOld(taskInfo.taskId);
- if (decoration == null) return;
+ if (decoration == null) return false;
decoration.close();
+ return true;
}
private class CaptionTouchEventListener implements
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
index d7f71c8..2ce4d04 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
@@ -44,7 +44,7 @@
* @param taskSurface the surface of the task
* @param startT the start transaction to be applied before the transition
* @param finishT the finish transaction to restore states after the transition
- * @return the window decoration object
+ * @return {@code true} if window decoration was created, {@code false} otherwise
*/
boolean createWindowDecoration(
ActivityManager.RunningTaskInfo taskInfo,
@@ -66,8 +66,9 @@
*
* @param startT the start transaction to be applied before the transition
* @param finishT the finish transaction to restore states after the transition
+ * @return {@code true} if window decoration exists, {@code false} otherwise
*/
- void setupWindowDecorationForTransition(
+ boolean setupWindowDecorationForTransition(
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT);
@@ -76,6 +77,7 @@
* Destroys the window decoration of the give task.
*
* @param taskInfo the info of the task
+ * @return {@code true} if window decoration was destroyed, {@code false} otherwise
*/
- void destroyWindowDecoration(ActivityManager.RunningTaskInfo taskInfo);
+ boolean destroyWindowDecoration(ActivityManager.RunningTaskInfo taskInfo);
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
index ead451f..4a3284e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
@@ -41,6 +41,7 @@
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.LAUNCHER_UI_PACKAGE_NAME
import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME
+import org.junit.Assert.assertNotNull
import java.util.Collections
internal object SplitScreenUtils {
@@ -50,7 +51,7 @@
private const val DIVIDER_BAR = "docked_divider_handle"
private const val OVERVIEW_SNAPSHOT = "snapshot"
private const val GESTURE_STEP_MS = 16L
- private const val LONG_PRESS_TIME_MS = 100L
+ private val LONG_PRESS_TIME_MS = ViewConfiguration.getLongPressTimeout() * 2L
private val SPLIT_DECOR_MANAGER = ComponentNameMatcher("", "SplitDecorManager#")
private val notificationScrollerSelector: BySelector
@@ -275,13 +276,6 @@
}
}
- fun longPress(instrumentation: Instrumentation, point: Point) {
- val downTime = SystemClock.uptimeMillis()
- touch(instrumentation, MotionEvent.ACTION_DOWN, downTime, downTime, TIMEOUT_MS, point)
- SystemClock.sleep(LONG_PRESS_TIME_MS)
- touch(instrumentation, MotionEvent.ACTION_UP, downTime, downTime, TIMEOUT_MS, point)
- }
-
fun createShortcutOnHotseatIfNotExist(tapl: LauncherInstrumentation, appName: String) {
tapl.workspace.deleteAppIcon(tapl.workspace.getHotseatAppIcon(0))
val allApps = tapl.workspace.switchToAllApps()
@@ -353,9 +347,11 @@
Until.findObject(By.res(sourceApp.packageName, "SplitScreenTest")),
TIMEOUT_MS
)
- longPress(instrumentation, textView.visibleCenter)
+ assertNotNull("Unable to find the TextView", textView)
+ textView.click(LONG_PRESS_TIME_MS)
val copyBtn = device.wait(Until.findObject(By.text("Copy")), TIMEOUT_MS)
+ assertNotNull("Unable to find the copy button", copyBtn)
copyBtn.click()
// Paste text to destinationApp
@@ -364,9 +360,11 @@
Until.findObject(By.res(destinationApp.packageName, "plain_text_input")),
TIMEOUT_MS
)
- longPress(instrumentation, editText.visibleCenter)
+ assertNotNull("Unable to find the EditText", editText)
+ editText.click(LONG_PRESS_TIME_MS)
val pasteBtn = device.wait(Until.findObject(By.text("Paste")), TIMEOUT_MS)
+ assertNotNull("Unable to find the paste button", pasteBtn)
pasteBtn.click()
// Verify text
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 7896247..7eccbf4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -64,7 +64,6 @@
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.sysui.ShellSharedConstants;
-import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
import org.junit.Rule;
@@ -103,9 +102,6 @@
private IRemoteAnimationRunner mBackAnimationRunner;
@Mock
- private Transitions mTransitions;
-
- @Mock
private ShellController mShellController;
private BackAnimationController mController;
@@ -127,7 +123,7 @@
mController = new BackAnimationController(mShellInit, mShellController,
mShellExecutor, new Handler(mTestableLooper.getLooper()),
mActivityTaskManager, mContext,
- mContentResolver, mTransitions);
+ mContentResolver);
mController.setEnableUAnimation(true);
mShellInit.init();
mEventTime = 0;
@@ -225,7 +221,7 @@
mController = new BackAnimationController(shellInit, mShellController,
mShellExecutor, new Handler(mTestableLooper.getLooper()),
mActivityTaskManager, mContext,
- mContentResolver, mTransitions);
+ mContentResolver);
shellInit.init();
mController.setBackToLauncherCallback(mIOnBackInvokedCallback, mBackAnimationRunner);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/OWNERS b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/OWNERS
new file mode 100644
index 0000000..1e0f9bc
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/OWNERS
@@ -0,0 +1,5 @@
+# WM shell sub-module back navigation owners
+# Bug component: 1152663
+shanh@google.com
+arthurhung@google.com
+wilsonshih@google.com
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 f6d6c03..3d77948 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
@@ -105,7 +105,8 @@
@Test
public void testUpdateDivideBounds() {
mSplitLayout.updateDivideBounds(anyInt());
- verify(mSplitLayoutHandler).onLayoutSizeChanging(any(SplitLayout.class));
+ verify(mSplitLayoutHandler).onLayoutSizeChanging(any(SplitLayout.class), anyInt(),
+ anyInt());
}
@Test
@@ -140,7 +141,7 @@
DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */,
DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START);
- mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget);
+ mSplitLayout.snapToTarget(mSplitLayout.getDividePosition(), snapTarget);
waitDividerFlingFinished();
verify(mSplitLayoutHandler).onSnappedToDismiss(eq(false), anyInt());
}
@@ -152,7 +153,7 @@
DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */,
DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END);
- mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget);
+ mSplitLayout.snapToTarget(mSplitLayout.getDividePosition(), snapTarget);
waitDividerFlingFinished();
verify(mSplitLayoutHandler).onSnappedToDismiss(eq(true), anyInt());
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/floating/FloatingTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/floating/FloatingTasksControllerTest.java
deleted file mode 100644
index d378a17..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/floating/FloatingTasksControllerTest.java
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.floating;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.wm.shell.floating.FloatingTasksController.SMALLEST_SCREEN_WIDTH_DP_TO_BE_TABLET;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-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;
-
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.graphics.Insets;
-import android.graphics.Rect;
-import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-import android.view.WindowMetrics;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.TaskViewTransitions;
-import com.android.wm.shell.TestShellExecutor;
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.floating.views.FloatingTaskLayer;
-import com.android.wm.shell.sysui.ShellCommandHandler;
-import com.android.wm.shell.sysui.ShellController;
-import com.android.wm.shell.sysui.ShellInit;
-import com.android.wm.shell.sysui.ShellSharedConstants;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Optional;
-
-/**
- * Tests for the floating tasks controller.
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class FloatingTasksControllerTest extends ShellTestCase {
- // Some behavior in the controller constructor is dependent on this so we can only
- // validate if it's working for the real value for those things.
- private static final boolean FLOATING_TASKS_ACTUALLY_ENABLED =
- SystemProperties.getBoolean("persist.wm.debug.floating_tasks", false);
-
- @Mock private ShellInit mShellInit;
- @Mock private ShellController mShellController;
- @Mock private WindowManager mWindowManager;
- @Mock private ShellTaskOrganizer mTaskOrganizer;
- @Captor private ArgumentCaptor<FloatingTaskLayer> mFloatingTaskLayerCaptor;
-
- private FloatingTasksController mController;
-
- @Before
- public void setUp() throws RemoteException {
- MockitoAnnotations.initMocks(this);
-
- WindowMetrics windowMetrics = mock(WindowMetrics.class);
- WindowInsets windowInsets = mock(WindowInsets.class);
- Insets insets = Insets.of(0, 0, 0, 0);
- when(mWindowManager.getCurrentWindowMetrics()).thenReturn(windowMetrics);
- when(windowMetrics.getWindowInsets()).thenReturn(windowInsets);
- when(windowMetrics.getBounds()).thenReturn(new Rect(0, 0, 1000, 1000));
- when(windowInsets.getInsetsIgnoringVisibility(anyInt())).thenReturn(insets);
-
- // For the purposes of this test, just run everything synchronously
- ShellExecutor shellExecutor = new TestShellExecutor();
- when(mTaskOrganizer.getExecutor()).thenReturn(shellExecutor);
- }
-
- @After
- public void tearDown() {
- if (mController != null) {
- mController.removeTask();
- mController = null;
- }
- }
-
- private void setUpTabletConfig() {
- Configuration config = mock(Configuration.class);
- config.smallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_TO_BE_TABLET;
- mController.setConfig(config);
- }
-
- private void setUpPhoneConfig() {
- Configuration config = mock(Configuration.class);
- config.smallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_TO_BE_TABLET - 1;
- mController.setConfig(config);
- }
-
- private void createController() {
- mController = new FloatingTasksController(mContext,
- mShellInit,
- mShellController,
- mock(ShellCommandHandler.class),
- Optional.empty(),
- mWindowManager,
- mTaskOrganizer,
- mock(TaskViewTransitions.class),
- mock(ShellExecutor.class),
- mock(ShellExecutor.class),
- mock(SyncTransactionQueue.class));
- spyOn(mController);
- }
-
- //
- // Shell specific
- //
- @Test
- public void instantiateController_addInitCallback() {
- if (FLOATING_TASKS_ACTUALLY_ENABLED) {
- createController();
- setUpTabletConfig();
-
- verify(mShellInit, times(1)).addInitCallback(any(), any());
- }
- }
-
- @Test
- public void instantiateController_doesntAddInitCallback() {
- if (!FLOATING_TASKS_ACTUALLY_ENABLED) {
- createController();
-
- verify(mShellInit, never()).addInitCallback(any(), any());
- }
- }
-
- @Test
- public void onInit_registerConfigChangeListener() {
- if (FLOATING_TASKS_ACTUALLY_ENABLED) {
- createController();
- setUpTabletConfig();
- mController.onInit();
-
- verify(mShellController, times(1)).addConfigurationChangeListener(any());
- }
- }
-
- @Test
- public void onInit_addExternalInterface() {
- if (FLOATING_TASKS_ACTUALLY_ENABLED) {
- createController();
- setUpTabletConfig();
- mController.onInit();
-
- verify(mShellController, times(1)).addExternalInterface(
- ShellSharedConstants.KEY_EXTRA_SHELL_FLOATING_TASKS, any(), any());
- }
- }
-
- //
- // Tests for floating layer, which is only available for tablets.
- //
-
- @Test
- public void testIsFloatingLayerAvailable_true() {
- createController();
- setUpTabletConfig();
- assertThat(mController.isFloatingLayerAvailable()).isTrue();
- }
-
- @Test
- public void testIsFloatingLayerAvailable_false() {
- createController();
- setUpPhoneConfig();
- assertThat(mController.isFloatingLayerAvailable()).isFalse();
- }
-
- //
- // Tests for floating tasks being enabled, guarded by sysprop flag.
- //
-
- @Test
- public void testIsFloatingTasksEnabled_true() {
- createController();
- mController.setFloatingTasksEnabled(true);
- setUpTabletConfig();
- assertThat(mController.isFloatingTasksEnabled()).isTrue();
- }
-
- @Test
- public void testIsFloatingTasksEnabled_false() {
- createController();
- mController.setFloatingTasksEnabled(false);
- setUpTabletConfig();
- assertThat(mController.isFloatingTasksEnabled()).isFalse();
- }
-
- //
- // Tests for behavior depending on flags
- //
-
- @Test
- public void testShowTaskIntent_enabled() {
- createController();
- mController.setFloatingTasksEnabled(true);
- setUpTabletConfig();
-
- mController.showTask(mock(Intent.class));
- verify(mWindowManager).addView(mFloatingTaskLayerCaptor.capture(), any());
- assertThat(mFloatingTaskLayerCaptor.getValue().getTaskViewCount()).isEqualTo(1);
- }
-
- @Test
- public void testShowTaskIntent_notEnabled() {
- createController();
- mController.setFloatingTasksEnabled(false);
- setUpTabletConfig();
-
- mController.showTask(mock(Intent.class));
- verify(mWindowManager, never()).addView(any(), any());
- }
-
- @Test
- public void testRemoveTask() {
- createController();
- mController.setFloatingTasksEnabled(true);
- setUpTabletConfig();
-
- mController.showTask(mock(Intent.class));
- verify(mWindowManager).addView(mFloatingTaskLayerCaptor.capture(), any());
- assertThat(mFloatingTaskLayerCaptor.getValue().getTaskViewCount()).isEqualTo(1);
-
- mController.removeTask();
- verify(mWindowManager).removeView(mFloatingTaskLayerCaptor.capture());
- assertThat(mFloatingTaskLayerCaptor.getValue().getTaskViewCount()).isEqualTo(0);
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
index 5ee8bf3..1a1bebd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
@@ -61,7 +61,7 @@
@RunWith(AndroidJUnit4.class)
public final class StageTaskListenerTests extends ShellTestCase {
private static final boolean ENABLE_SHELL_TRANSITIONS =
- SystemProperties.getBoolean("persist.wm.debug.shell_transit", false);
+ SystemProperties.getBoolean("persist.wm.debug.shell_transit", true);
@Mock
private ShellTaskOrganizer mTaskOrganizer;
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 54f893e..099efd3 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -22,10 +22,18 @@
#include <SkBlendMode.h>
#include <SkCanvas.h>
#include <SkColor.h>
+#include <android-base/stringprintf.h>
#include <android-base/thread_annotations.h>
+#include <ftl/enum.h>
+
+#include <mutex>
#include "PointerControllerContext.h"
+#define INDENT " "
+#define INDENT2 " "
+#define INDENT3 " "
+
namespace android {
namespace {
@@ -223,7 +231,7 @@
}
void PointerController::clearSpotsLocked() {
- for (auto& [displayID, spotController] : mLocked.spotControllers) {
+ for (auto& [displayId, spotController] : mLocked.spotControllers) {
spotController.clearSpots();
}
}
@@ -235,7 +243,7 @@
void PointerController::reloadPointerResources() {
std::scoped_lock lock(getLock());
- for (auto& [displayID, spotController] : mLocked.spotControllers) {
+ for (auto& [displayId, spotController] : mLocked.spotControllers) {
spotController.reloadSpotResources();
}
@@ -286,13 +294,13 @@
std::scoped_lock lock(getLock());
for (auto it = mLocked.spotControllers.begin(); it != mLocked.spotControllers.end();) {
- int32_t displayID = it->first;
- if (!displayIdSet.count(displayID)) {
+ int32_t displayId = it->first;
+ if (!displayIdSet.count(displayId)) {
/*
* Ensures that an in-progress animation won't dereference
* a null pointer to TouchSpotController.
*/
- mContext.removeAnimationCallback(displayID);
+ mContext.removeAnimationCallback(displayId);
it = mLocked.spotControllers.erase(it);
} else {
++it;
@@ -313,4 +321,20 @@
return it != di.end() ? it->transform : kIdentityTransform;
}
+void PointerController::dump(std::string& dump) {
+ dump += INDENT "PointerController:\n";
+ std::scoped_lock lock(getLock());
+ dump += StringPrintf(INDENT2 "Presentation: %s\n",
+ ftl::enum_string(mLocked.presentation).c_str());
+ dump += StringPrintf(INDENT2 "Pointer Display ID: %" PRIu32 "\n", mLocked.pointerDisplayId);
+ dump += StringPrintf(INDENT2 "Viewports:\n");
+ for (const auto& info : mLocked.mDisplayInfos) {
+ info.dump(dump, INDENT3);
+ }
+ dump += INDENT2 "Spot Controllers:\n";
+ for (const auto& [_, spotController] : mLocked.spotControllers) {
+ spotController.dump(dump, INDENT3);
+ }
+}
+
} // namespace android
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 33480e8..48d5a57 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -27,6 +27,7 @@
#include <map>
#include <memory>
+#include <string>
#include <vector>
#include "MouseCursorController.h"
@@ -75,6 +76,8 @@
void onDisplayInfosChangedLocked(const std::vector<gui::DisplayInfo>& displayInfos)
REQUIRES(getLock());
+ void dump(std::string& dump);
+
protected:
using WindowListenerConsumer =
std::function<void(const sp<android::gui::WindowInfosListener>&)>;
diff --git a/libs/input/TouchSpotController.cpp b/libs/input/TouchSpotController.cpp
index 4ac66c4..d9fe599 100644
--- a/libs/input/TouchSpotController.cpp
+++ b/libs/input/TouchSpotController.cpp
@@ -21,8 +21,15 @@
#include "TouchSpotController.h"
+#include <android-base/stringprintf.h>
+#include <input/PrintTools.h>
#include <log/log.h>
+#include <mutex>
+
+#define INDENT " "
+#define INDENT2 " "
+
namespace {
// Time to spend fading out the spot completely.
const nsecs_t SPOT_FADE_DURATION = 200 * 1000000LL; // 200 ms
@@ -53,6 +60,12 @@
}
}
+void TouchSpotController::Spot::dump(std::string& out, const char* prefix) const {
+ out += prefix;
+ base::StringAppendF(&out, "Spot{id=%" PRIx32 ", alpha=%f, scale=%f, pos=[%f, %f]}\n", id, alpha,
+ scale, x, y);
+}
+
// --- TouchSpotController ---
TouchSpotController::TouchSpotController(int32_t displayId, PointerControllerContext& context)
@@ -255,4 +268,22 @@
mContext.addAnimationCallback(mDisplayId, func);
}
+void TouchSpotController::dump(std::string& out, const char* prefix) const {
+ using base::StringAppendF;
+ out += prefix;
+ out += "SpotController:\n";
+ out += prefix;
+ StringAppendF(&out, INDENT "DisplayId: %" PRId32 "\n", mDisplayId);
+ std::scoped_lock lock(mLock);
+ out += prefix;
+ StringAppendF(&out, INDENT "Animating: %s\n", toString(mLocked.animating));
+ out += prefix;
+ out += INDENT "Spots:\n";
+ std::string spotPrefix = prefix;
+ spotPrefix += INDENT2;
+ for (const auto& spot : mLocked.displaySpots) {
+ spot->dump(out, spotPrefix.c_str());
+ }
+}
+
} // namespace android
diff --git a/libs/input/TouchSpotController.h b/libs/input/TouchSpotController.h
index 703de36..5bbc75d 100644
--- a/libs/input/TouchSpotController.h
+++ b/libs/input/TouchSpotController.h
@@ -38,6 +38,8 @@
void reloadSpotResources();
bool doAnimations(nsecs_t timestamp);
+ void dump(std::string& out, const char* prefix = "") const;
+
private:
struct Spot {
static const uint32_t INVALID_ID = 0xffffffff;
@@ -58,6 +60,7 @@
mLastIcon(nullptr) {}
void updateSprite(const SpriteIcon* icon, float x, float y, int32_t displayId);
+ void dump(std::string& out, const char* prefix = "") const;
private:
const SpriteIcon* mLastIcon;
diff --git a/location/java/android/location/Country.java b/location/java/android/location/Country.java
index 8c40338..8e1bb1f0 100644
--- a/location/java/android/location/Country.java
+++ b/location/java/android/location/Country.java
@@ -16,6 +16,9 @@
package android.location;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -28,7 +31,8 @@
*
* @hide
*/
-public class Country implements Parcelable {
+@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+public final class Country implements Parcelable {
/**
* The country code came from the mobile network
*/
@@ -78,6 +82,8 @@
* <li>{@link #COUNTRY_SOURCE_SIM}</li>
* <li>{@link #COUNTRY_SOURCE_LOCALE}</li>
* </ul>
+ *
+ * @hide
*/
@UnsupportedAppUsage
public Country(final String countryIso, final int source) {
@@ -100,6 +106,7 @@
mTimestamp = timestamp;
}
+ /** @hide */
public Country(Country country) {
mCountryIso = country.mCountryIso;
mSource = country.mSource;
@@ -109,8 +116,8 @@
/**
* @return the ISO 3166-1 two letters country code
*/
- @UnsupportedAppUsage
- public final String getCountryIso() {
+ @NonNull
+ public String getCountryIso() {
return mCountryIso;
}
@@ -124,20 +131,22 @@
* <li>{@link #COUNTRY_SOURCE_LOCALE}</li>
* </ul>
*/
- @UnsupportedAppUsage
- public final int getSource() {
+ public int getSource() {
return mSource;
}
/**
* Returns the time that this object was created (which we assume to be the time that the source
* was consulted).
+ *
+ * @hide
*/
- public final long getTimestamp() {
+ public long getTimestamp() {
return mTimestamp;
}
- public static final @android.annotation.NonNull Parcelable.Creator<Country> CREATOR = new Parcelable.Creator<Country>() {
+ @android.annotation.NonNull
+ public static final Parcelable.Creator<Country> CREATOR = new Parcelable.Creator<Country>() {
public Country createFromParcel(Parcel in) {
return new Country(in.readString(), in.readInt(), in.readLong());
}
@@ -147,11 +156,13 @@
}
};
+ @Override
public int describeContents() {
return 0;
}
- public void writeToParcel(Parcel parcel, int flags) {
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
parcel.writeString(mCountryIso);
parcel.writeInt(mSource);
parcel.writeLong(mTimestamp);
@@ -161,9 +172,10 @@
* Returns true if this {@link Country} is equivalent to the given object. This ignores
* the timestamp value and just checks for equivalence of countryIso and source values.
* Returns false otherwise.
+ *
*/
@Override
- public boolean equals(Object object) {
+ public boolean equals(@Nullable Object object) {
if (object == this) {
return true;
}
@@ -194,12 +206,15 @@
* @param country the country to compare
* @return true if the specified country's countryIso field is equal to this
* country's, false otherwise.
+ *
+ * @hide
*/
public boolean equalsIgnoreSource(Country country) {
return country != null && mCountryIso.equals(country.getCountryIso());
}
@Override
+ @NonNull
public String toString() {
return "Country {ISO=" + mCountryIso + ", source=" + mSource + ", time=" + mTimestamp + "}";
}
diff --git a/location/java/android/location/CountryDetector.java b/location/java/android/location/CountryDetector.java
index e344b82..3a0edfc 100644
--- a/location/java/android/location/CountryDetector.java
+++ b/location/java/android/location/CountryDetector.java
@@ -16,6 +16,9 @@
package android.location;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -47,6 +50,7 @@
*
* @hide
*/
+@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
@SystemService(Context.COUNTRY_DETECTOR)
public class CountryDetector {
@@ -101,7 +105,7 @@
* @return the country if it is available immediately, otherwise null will
* be returned.
*/
- @UnsupportedAppUsage
+ @Nullable
public Country detectCountry() {
try {
return mService.detectCountry();
@@ -120,8 +124,7 @@
* implement the callback mechanism. If looper is null then the
* callbacks will be called on the main thread.
*/
- @UnsupportedAppUsage
- public void addCountryListener(CountryListener listener, Looper looper) {
+ public void addCountryListener(@NonNull CountryListener listener, @Nullable Looper looper) {
synchronized (mListeners) {
if (!mListeners.containsKey(listener)) {
ListenerTransport transport = new ListenerTransport(listener, looper);
@@ -138,8 +141,7 @@
/**
* Remove the listener
*/
- @UnsupportedAppUsage
- public void removeCountryListener(CountryListener listener) {
+ public void removeCountryListener(@NonNull CountryListener listener) {
synchronized (mListeners) {
ListenerTransport transport = mListeners.get(listener);
if (transport != null) {
diff --git a/location/java/android/location/CountryListener.java b/location/java/android/location/CountryListener.java
index eb67205..5c06d82 100644
--- a/location/java/android/location/CountryListener.java
+++ b/location/java/android/location/CountryListener.java
@@ -16,7 +16,8 @@
package android.location;
-import android.compat.annotation.UnsupportedAppUsage;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
/**
* The listener for receiving the notification when the country is detected or
@@ -24,10 +25,11 @@
*
* @hide
*/
+@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
public interface CountryListener {
/**
* @param country the changed or detected country.
+ *
*/
- @UnsupportedAppUsage
- void onCountryDetected(Country country);
+ void onCountryDetected(@NonNull Country country);
}
diff --git a/location/java/android/location/GnssCapabilities.java b/location/java/android/location/GnssCapabilities.java
index 98578b2..4f45602 100644
--- a/location/java/android/location/GnssCapabilities.java
+++ b/location/java/android/location/GnssCapabilities.java
@@ -138,6 +138,7 @@
@SubHalMeasurementCorrectionsCapabilityFlags int measurementCorrectionsFlags,
@SubHalPowerCapabilityFlags int powerFlags,
@NonNull List<GnssSignalType> gnssSignalTypes) {
+ Objects.requireNonNull(gnssSignalTypes);
mTopFlags = topFlags;
mMeasurementCorrectionsFlags = measurementCorrectionsFlags;
mPowerFlags = powerFlags;
@@ -190,6 +191,21 @@
}
/**
+ * Returns a new GnssCapabilities object with a list of GnssSignalType.
+ *
+ * @hide
+ */
+ public GnssCapabilities withSignalTypes(@NonNull List<GnssSignalType> gnssSignalTypes) {
+ Objects.requireNonNull(gnssSignalTypes);
+ if (mGnssSignalTypes.equals(gnssSignalTypes)) {
+ return this;
+ } else {
+ return new GnssCapabilities(mTopFlags, mMeasurementCorrectionsFlags, mPowerFlags,
+ new ArrayList<>(gnssSignalTypes));
+ }
+ }
+
+ /**
* Returns {@code true} if GNSS chipset supports scheduling, {@code false} otherwise.
*/
public boolean hasScheduling() {
@@ -452,7 +468,8 @@
GnssCapabilities that = (GnssCapabilities) o;
return mTopFlags == that.mTopFlags
&& mMeasurementCorrectionsFlags == that.mMeasurementCorrectionsFlags
- && mPowerFlags == that.mPowerFlags;
+ && mPowerFlags == that.mPowerFlags
+ && mGnssSignalTypes.equals(that.mGnssSignalTypes);
}
@Override
diff --git a/location/java/android/location/GnssSignalType.java b/location/java/android/location/GnssSignalType.java
index f9c6e72..16c3f2e 100644
--- a/location/java/android/location/GnssSignalType.java
+++ b/location/java/android/location/GnssSignalType.java
@@ -116,7 +116,7 @@
public String toString() {
StringBuilder s = new StringBuilder();
s.append("GnssSignalType[");
- s.append(" Constellation=").append(mConstellationType);
+ s.append("Constellation=").append(mConstellationType);
s.append(", CarrierFrequencyHz=").append(mCarrierFrequencyHz);
s.append(", CodeType=").append(mCodeType);
s.append(']');
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 6764890..ad6acea 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -438,6 +438,18 @@
return "AUDIO_FORMAT_APTX_TWSP";
case /* AUDIO_FORMAT_LC3 */ 0x2B000000:
return "AUDIO_FORMAT_LC3";
+ case /* AUDIO_FORMAT_MPEGH */ 0x2C000000:
+ return "AUDIO_FORMAT_MPEGH";
+ case /* AUDIO_FORMAT_IEC60958 */ 0x2D000000:
+ return "AUDIO_FORMAT_IEC60958";
+ case /* AUDIO_FORMAT_DTS_UHD */ 0x2E000000:
+ return "AUDIO_FORMAT_DTS_UHD";
+ case /* AUDIO_FORMAT_DRA */ 0x2F000000:
+ return "AUDIO_FORMAT_DRA";
+ case /* AUDIO_FORMAT_APTX_ADAPTIVE_QLEA */ 0x30000000:
+ return "AUDIO_FORMAT_APTX_ADAPTIVE_QLEA";
+ case /* AUDIO_FORMAT_APTX_ADAPTIVE_R4 */ 0x31000000:
+ return "AUDIO_FORMAT_APTX_ADAPTIVE_R4";
/* Aliases */
case /* AUDIO_FORMAT_PCM_16_BIT */ 0x1:
@@ -510,10 +522,14 @@
return "AUDIO_FORMAT_MAT_2_0"; // (MAT | MAT_SUB_2_0)
case /* AUDIO_FORMAT_MAT_2_1 */ 0x24000003:
return "AUDIO_FORMAT_MAT_2_1"; // (MAT | MAT_SUB_2_1)
- case /* AUDIO_FORMAT_DTS_UHD */ 0x2E000000:
- return "AUDIO_FORMAT_DTS_UHD";
- case /* AUDIO_FORMAT_DRA */ 0x2F000000:
- return "AUDIO_FORMAT_DRA";
+ case /* AUDIO_FORMAT_MPEGH_SUB_BL_L3 */ 0x2C000013:
+ return "AUDIO_FORMAT_MPEGH_SUB_BL_L3";
+ case /* AUDIO_FORMAT_MPEGH_SUB_BL_L4 */ 0x2C000014:
+ return "AUDIO_FORMAT_MPEGH_SUB_BL_L4";
+ case /* AUDIO_FORMAT_MPEGH_SUB_LC_L3 */ 0x2C000023:
+ return "AUDIO_FORMAT_MPEGH_SUB_LC_L3";
+ case /* AUDIO_FORMAT_MPEGH_SUB_LC_L4 */ 0x2C000024:
+ return "AUDIO_FORMAT_MPEGH_SUB_LC_L4";
default:
return "AUDIO_FORMAT_(" + audioFormat + ")";
}
@@ -2407,4 +2423,3 @@
*/
final static int NATIVE_EVENT_ROUTING_CHANGE = 1000;
}
-
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index b4b908d..161ea25 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -603,7 +603,7 @@
*/
public void transferTo(@NonNull MediaRoute2Info route) {
if (isSystemRouter()) {
- sManager.selectRoute(mClientPackageName, route);
+ sManager.transfer(mClientPackageName, route);
return;
}
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index b6f07f4..e403e24 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -448,14 +448,16 @@
}
/**
- * Selects media route for the specified package name.
+ * Transfers a {@link RoutingSessionInfo routing session} belonging to a specified package name
+ * to a {@link MediaRoute2Info media route}.
+ *
+ * <p>Same as {@link #transfer(RoutingSessionInfo, MediaRoute2Info)}, but resolves the routing
+ * session based on the provided package name.
*/
- public void selectRoute(@NonNull String packageName, @NonNull MediaRoute2Info route) {
+ public void transfer(@NonNull String packageName, @NonNull MediaRoute2Info route) {
Objects.requireNonNull(packageName, "packageName must not be null");
Objects.requireNonNull(route, "route must not be null");
- Log.v(TAG, "Selecting route. packageName= " + packageName + ", route=" + route);
-
List<RoutingSessionInfo> sessionInfos = getRoutingSessions(packageName);
RoutingSessionInfo targetSession = sessionInfos.get(sessionInfos.size() - 1);
transfer(targetSession, route);
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index 82c3139..538e64c 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -158,13 +158,15 @@
/**
* Creates a local media player for the ringtone using currently set attributes.
+ * @return true if media player creation succeeded or is deferred,
+ * false if it did not succeed and can't be tried remotely.
* @hide
*/
- public void createLocalMediaPlayer() {
+ public boolean createLocalMediaPlayer() {
Trace.beginSection("createLocalMediaPlayer");
if (mUri == null) {
Log.e(TAG, "Could not create media player as no URI was provided.");
- return;
+ return mAllowRemote && mRemotePlayer != null;
}
destroyLocalPlayer();
// try opening uri locally before delegating to remote player
@@ -195,6 +197,30 @@
}
}
Trace.endSection();
+ return mLocalPlayer != null || (mAllowRemote && mRemotePlayer != null);
+ }
+
+ /**
+ * Same as AudioManager.hasHapticChannels except it assumes an already created ringtone.
+ * If the ringtone has not been created, it will load based on URI provided at {@link #setUri}
+ * and if not URI has been set, it will assume no haptic channels are present.
+ * @hide
+ */
+ public boolean hasHapticChannels() {
+ // FIXME: support remote player, or internalize haptic channels support and remove entirely.
+ try {
+ android.os.Trace.beginSection("Ringtone.hasHapticChannels");
+ if (mLocalPlayer != null) {
+ for(MediaPlayer.TrackInfo trackInfo : mLocalPlayer.getTrackInfo()) {
+ if (trackInfo.hasHapticChannels()) {
+ return true;
+ }
+ }
+ }
+ } finally {
+ android.os.Trace.endSection();
+ }
+ return false;
}
/**
@@ -423,7 +449,6 @@
*/
public void setUri(Uri uri, @Nullable VolumeShaper.Configuration volumeShaperConfig) {
mVolumeShaperConfig = volumeShaperConfig;
-
mUri = uri;
if (mUri == null) {
destroyLocalPlayer();
@@ -443,10 +468,11 @@
if (mLocalPlayer != null) {
// Play ringtones if stream volume is over 0 or if it is a haptic-only ringtone
// (typically because ringer mode is vibrate).
- boolean isHapticOnly = AudioManager.hasHapticChannels(mContext, mUri)
- && !mAudioAttributes.areHapticChannelsMuted() && mVolume == 0;
- if (isHapticOnly || mAudioManager.getStreamVolume(
- AudioAttributes.toLegacyStreamType(mAudioAttributes)) != 0) {
+ if (mAudioManager.getStreamVolume(AudioAttributes.toLegacyStreamType(mAudioAttributes))
+ != 0) {
+ startLocalPlayer();
+ } else if (!mAudioAttributes.areHapticChannelsMuted() && hasHapticChannels()) {
+ // is haptic only ringtone
startLocalPlayer();
}
} else if (mAllowRemote && (mRemotePlayer != null) && (mUri != null)) {
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 27db41c..f15f443 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -720,11 +720,14 @@
@Nullable VolumeShaper.Configuration volumeShaperConfig,
AudioAttributes audioAttributes) {
// Don't set the stream type
- Ringtone ringtone =
- getRingtone(context, ringtoneUri, -1 /* streamType */, volumeShaperConfig, false);
+ Ringtone ringtone = getRingtone(context, ringtoneUri, -1 /* streamType */,
+ volumeShaperConfig, false);
if (ringtone != null) {
ringtone.setAudioAttributesField(audioAttributes);
- ringtone.createLocalMediaPlayer();
+ if (!ringtone.createLocalMediaPlayer()) {
+ Log.e(TAG, "Failed to open ringtone " + ringtoneUri);
+ return null;
+ }
}
return ringtone;
}
@@ -750,19 +753,6 @@
createLocalMediaPlayer);
}
- //FIXME bypass the notion of stream types within the class
- /**
- * Returns a {@link Ringtone} with {@link VolumeShaper} if required for a given sound URI on
- * the given stream type. Normally, if you change the stream type on the returned
- * {@link Ringtone}, it will re-create the {@link MediaPlayer}. This is just
- * an optimized route to avoid that.
- *
- * @param streamType The stream type for the ringtone, or -1 if it should
- * not be set (and the default used instead).
- * @param volumeShaperConfig config for volume shaper of the ringtone if applied.
- * @see #getRingtone(Context, Uri)
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private static Ringtone getRingtone(final Context context, Uri ringtoneUri, int streamType,
@Nullable VolumeShaper.Configuration volumeShaperConfig,
boolean createLocalMediaPlayer) {
@@ -776,7 +766,10 @@
r.setVolumeShaperConfig(volumeShaperConfig);
r.setUri(ringtoneUri, volumeShaperConfig);
if (createLocalMediaPlayer) {
- r.createLocalMediaPlayer();
+ if (!r.createLocalMediaPlayer()) {
+ Log.e(TAG, "Failed to open ringtone " + ringtoneUri);
+ return null;
+ }
}
return r;
} catch (Exception ex) {
@@ -1158,24 +1151,38 @@
}
// Try finding the scanned ringtone
- final String filename = getDefaultRingtoneFilename(type);
- final String whichAudio = getQueryStringForType(type);
- final String where = MediaColumns.DISPLAY_NAME + "=? AND " + whichAudio + "=?";
- final Uri baseUri = MediaStore.Audio.Media.INTERNAL_CONTENT_URI;
- try (Cursor cursor = context.getContentResolver().query(baseUri,
- new String[] { MediaColumns._ID },
- where,
- new String[] { filename, "1" }, null)) {
- if (cursor.moveToFirst()) {
- final Uri ringtoneUri = context.getContentResolver().canonicalizeOrElse(
- ContentUris.withAppendedId(baseUri, cursor.getLong(0)));
- RingtoneManager.setActualDefaultRingtoneUri(context, type, ringtoneUri);
- Settings.System.putInt(context.getContentResolver(), setting, 1);
- }
+ Uri ringtoneUri = computeDefaultRingtoneUri(context, type);
+ if (ringtoneUri != null) {
+ RingtoneManager.setActualDefaultRingtoneUri(context, type, ringtoneUri);
+ Settings.System.putInt(context.getContentResolver(), setting, 1);
}
}
}
+ /**
+ * @param type the type of ringtone (e.g {@link #TYPE_RINGTONE})
+ * @return the system default URI if found, null otherwise.
+ */
+ private static Uri computeDefaultRingtoneUri(@NonNull Context context, int type) {
+ // Try finding the scanned ringtone
+ final String filename = getDefaultRingtoneFilename(type);
+ final String whichAudio = getQueryStringForType(type);
+ final String where = MediaColumns.DISPLAY_NAME + "=? AND " + whichAudio + "=?";
+ final Uri baseUri = MediaStore.Audio.Media.INTERNAL_CONTENT_URI;
+ try (Cursor cursor = context.getContentResolver().query(baseUri,
+ new String[] { MediaColumns._ID },
+ where,
+ new String[] { filename, "1" }, null)) {
+ if (cursor.moveToFirst()) {
+ final Uri ringtoneUri = context.getContentResolver().canonicalizeOrElse(
+ ContentUris.withAppendedId(baseUri, cursor.getLong(0)));
+ return ringtoneUri;
+ }
+ }
+
+ return null;
+ }
+
private static String getDefaultRingtoneSetting(int type) {
switch (type) {
case TYPE_RINGTONE: return "ringtone_set";
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
index a27fd10..7d84fb2 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
@@ -1047,7 +1047,7 @@
}
}
- void notifyRecordingStarted(@Nullable String recordingId) {
+ void notifyRecordingStarted(String recordingId) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
index 3d65effa..2956a0a 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -456,9 +456,10 @@
/**
* Receives started recording's ID.
- * @hide
+ *
+ * @param recordingId The ID of the recording started
*/
- public void onRecordingStarted(@Nullable String recordingId) {
+ public void onRecordingStarted(@NonNull String recordingId) {
}
/**
@@ -914,7 +915,15 @@
/**
* Requests starting of recording
*
- * @hide
+ * <p> This is used to request the active {@link android.media.tv.TvRecordingClient} to
+ * call {@link android.media.tv.TvRecordingClient#startRecording(Uri)} with the provided
+ * {@code programUri}.
+ * A non-null {@code programUri} implies the started recording should be of that specific
+ * program, whereas null {@code programUri} does not impose such a requirement and the
+ * recording can span across multiple TV programs.
+ *
+ * @param programUri The URI for the TV program to record.
+ * @see android.media.tv.TvRecordingClient#startRecording(Uri)
*/
@CallSuper
public void requestStartRecording(@Nullable Uri programUri) {
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
index 76ba69c..1f270d0 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppView.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
@@ -583,11 +583,9 @@
/**
* Alerts the TV interactive app that a recording has been started with recordingId
*
- * @param recordingId The Id of the recording started
- *
- * @hide
+ * @param recordingId The ID of the recording started
*/
- public void notifyRecordingStarted(@Nullable String recordingId) {
+ public void notifyRecordingStarted(@NonNull String recordingId) {
if (DEBUG) {
Log.d(TAG, "notifyRecordingStarted");
}
@@ -859,10 +857,9 @@
/**
* This is called when {@link TvInteractiveAppService.Session#requestStartRecording(Uri)}
* is called.
+ *
* @param iAppServiceId The ID of the TV interactive app service bound to this view.
* @param programUri The program URI to record
- *
- * @hide
*/
public void onRequestStartRecording(
@NonNull String iAppServiceId,
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/MtpClient.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/MtpClient.java
index edb5e37..158e698 100644
--- a/media/tests/CameraBrowser/src/com/android/camerabrowser/MtpClient.java
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/MtpClient.java
@@ -158,7 +158,7 @@
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
filter.addAction(ACTION_USB_PERMISSION);
- context.registerReceiver(mUsbReceiver, filter);
+ context.registerReceiver(mUsbReceiver, filter, Context.RECEIVER_EXPORTED/*UNAUDITED*/);
}
/**
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
index 810b408..4193ffa 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
@@ -384,7 +384,7 @@
MediaRoute2Info routeToSelect = routes.get(ROUTE_ID1);
assertThat(routeToSelect).isNotNull();
- mManager.selectRoute(mPackageName, routeToSelect);
+ mManager.transfer(mPackageName, routeToSelect);
assertThat(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
assertThat(mManager.getRemoteSessions()).hasSize(1);
}
@@ -410,7 +410,7 @@
assertThat(mManager.getRoutingSessions(mPackageName)).hasSize(1);
- mManager.selectRoute(mPackageName, routeToSelect);
+ mManager.transfer(mPackageName, routeToSelect);
assertThat(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
List<RoutingSessionInfo> sessions = mManager.getRoutingSessions(mPackageName);
@@ -514,7 +514,7 @@
}
});
awaitOnRouteChangedManager(
- () -> mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1)),
+ () -> mManager.transfer(mPackageName, routes.get(ROUTE_ID1)),
ROUTE_ID1,
route -> TextUtils.equals(route.getClientPackageName(), mPackageName));
assertThat(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
@@ -525,7 +525,7 @@
RoutingSessionInfo sessionInfo = sessions.get(1);
awaitOnRouteChangedManager(
- () -> mManager.selectRoute(mPackageName, routes.get(ROUTE_ID5_TO_TRANSFER_TO)),
+ () -> mManager.transfer(mPackageName, routes.get(ROUTE_ID5_TO_TRANSFER_TO)),
ROUTE_ID5_TO_TRANSFER_TO,
route -> TextUtils.equals(route.getClientPackageName(), mPackageName));
@@ -583,9 +583,9 @@
assertThat(route1).isNotNull();
assertThat(route2).isNotNull();
- mManager.selectRoute(mPackageName, route1);
+ mManager.transfer(mPackageName, route1);
assertThat(successLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
- mManager.selectRoute(mPackageName, route2);
+ mManager.transfer(mPackageName, route2);
assertThat(successLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
// onTransferFailed/onSessionReleased should not be called.
@@ -703,7 +703,7 @@
}
});
- mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1));
+ mManager.transfer(mPackageName, routes.get(ROUTE_ID1));
assertThat(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
List<RoutingSessionInfo> sessions = mManager.getRoutingSessions(mPackageName);
@@ -858,7 +858,7 @@
});
mRouter2.setOnGetControllerHintsListener(listener);
- mManager.selectRoute(mPackageName, route);
+ mManager.transfer(mPackageName, route);
assertThat(hintLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
assertThat(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
@@ -903,7 +903,7 @@
}
});
- mManager.selectRoute(mPackageName, routes.get(ROUTE_ID4_TO_SELECT_AND_DESELECT));
+ mManager.transfer(mPackageName, routes.get(ROUTE_ID4_TO_SELECT_AND_DESELECT));
assertThat(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
}
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index cb0f22f..584d0ba2 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -330,6 +330,7 @@
APerformanceHint_updateTargetWorkDuration; # introduced=Tiramisu
APerformanceHint_reportActualWorkDuration; # introduced=Tiramisu
APerformanceHint_closeSession; # introduced=Tiramisu
+ APerformanceHint_sendHint; # introduced=UpsideDownCake
local:
*;
};
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index d627984..7863a7d 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -61,6 +61,7 @@
int updateTargetWorkDuration(int64_t targetDurationNanos);
int reportActualWorkDuration(int64_t actualDurationNanos);
+ int sendHint(int32_t hint);
private:
friend struct APerformanceHintManager;
@@ -159,7 +160,7 @@
}
binder::Status ret = mHintSession->updateTargetWorkDuration(targetDurationNanos);
if (!ret.isOk()) {
- ALOGE("%s: HintSessionn updateTargetWorkDuration failed: %s", __FUNCTION__,
+ ALOGE("%s: HintSession updateTargetWorkDuration failed: %s", __FUNCTION__,
ret.exceptionMessage().c_str());
return EPIPE;
}
@@ -205,6 +206,21 @@
return 0;
}
+int APerformanceHintSession::sendHint(int32_t hint) {
+ if (hint < 0) {
+ ALOGE("%s: session hint value must be greater than zero", __FUNCTION__);
+ return EINVAL;
+ }
+
+ binder::Status ret = mHintSession->sendHint(hint);
+
+ if (!ret.isOk()) {
+ ALOGE("%s: HintSession sendHint failed: %s", __FUNCTION__, ret.exceptionMessage().c_str());
+ return EPIPE;
+ }
+ return 0;
+}
+
// ===================================== C API
APerformanceHintManager* APerformanceHint_getManager() {
return APerformanceHintManager::getInstance();
@@ -230,6 +246,10 @@
return session->reportActualWorkDuration(actualDurationNanos);
}
+int APerformanceHint_sendHint(APerformanceHintSession* session, int32_t hint) {
+ return session->sendHint(hint);
+}
+
void APerformanceHint_closeSession(APerformanceHintSession* session) {
delete session;
}
diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
index b17850e..1881e60 100644
--- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
+++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
@@ -51,6 +51,7 @@
(const ::std::vector<int64_t>& actualDurationNanos,
const ::std::vector<int64_t>& timeStampNanos),
(override));
+ MOCK_METHOD(Status, sendHint, (int32_t hints), (override));
MOCK_METHOD(Status, close, (), (override));
MOCK_METHOD(IBinder*, onAsBinder, (), (override));
};
@@ -121,6 +122,15 @@
result = APerformanceHint_reportActualWorkDuration(session, -1L);
EXPECT_EQ(EINVAL, result);
+ // Send both valid and invalid session hints
+ int hintId = 2;
+ EXPECT_CALL(*iSession, sendHint(Eq(2))).Times(Exactly(1));
+ result = APerformanceHint_sendHint(session, hintId);
+ EXPECT_EQ(0, result);
+
+ result = APerformanceHint_sendHint(session, -1);
+ EXPECT_EQ(EINVAL, result);
+
EXPECT_CALL(*iSession, close()).Times(Exactly(1));
APerformanceHint_closeSession(session);
}
diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp
index 1709dfd..10c570b 100644
--- a/native/graphics/jni/Android.bp
+++ b/native/graphics/jni/Android.bp
@@ -93,7 +93,7 @@
],
static_libs: ["libarect"],
fuzz_config: {
- cc: ["scroggo@google.com"],
+ cc: ["dichenzhang@google.com","scroggo@google.com"],
asan_options: [
"detect_odr_violation=1",
],
diff --git a/packages/CarrierDefaultApp/Android.bp b/packages/CarrierDefaultApp/Android.bp
index 62ffe38..a216381 100644
--- a/packages/CarrierDefaultApp/Android.bp
+++ b/packages/CarrierDefaultApp/Android.bp
@@ -10,7 +10,7 @@
android_app {
name: "CarrierDefaultApp",
srcs: ["src/**/*.java"],
- libs: ["SliceStore"],
+ libs: ["SlicePurchaseController"],
platform_apis: true,
certificate: "platform",
optimize: {
diff --git a/packages/CarrierDefaultApp/AndroidManifest.xml b/packages/CarrierDefaultApp/AndroidManifest.xml
index a5b104b..4c22ee6 100644
--- a/packages/CarrierDefaultApp/AndroidManifest.xml
+++ b/packages/CarrierDefaultApp/AndroidManifest.xml
@@ -73,16 +73,16 @@
</intent-filter>
</activity-alias>
- <receiver android:name="com.android.carrierdefaultapp.SliceStoreBroadcastReceiver"
+ <receiver android:name="com.android.carrierdefaultapp.SlicePurchaseBroadcastReceiver"
android:exported="true">
<intent-filter>
- <action android:name="com.android.phone.slicestore.action.START_SLICE_STORE" />
- <action android:name="com.android.phone.slicestore.action.SLICE_STORE_RESPONSE_TIMEOUT" />
- <action android:name="com.android.phone.slicestore.action.NOTIFICATION_CANCELED" />
+ <action android:name="com.android.phone.slice.action.START_SLICE_PURCHASE_APP" />
+ <action android:name="com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_TIMEOUT" />
+ <action android:name="com.android.phone.slice.action.NOTIFICATION_CANCELED" />
</intent-filter>
</receiver>
- <activity android:name="com.android.carrierdefaultapp.SliceStoreActivity"
- android:label="@string/slice_store_label"
+ <activity android:name="com.android.carrierdefaultapp.SlicePurchaseActivity"
+ android:label="@string/slice_purchase_app_label"
android:exported="true"
android:configChanges="keyboardHidden|orientation|screenSize">
<intent-filter>
diff --git a/packages/CarrierDefaultApp/assets/slice_store_test.html b/packages/CarrierDefaultApp/assets/slice_purchase_test.html
similarity index 86%
rename from packages/CarrierDefaultApp/assets/slice_store_test.html
rename to packages/CarrierDefaultApp/assets/slice_purchase_test.html
index 7ddbd2d..67d2184 100644
--- a/packages/CarrierDefaultApp/assets/slice_store_test.html
+++ b/packages/CarrierDefaultApp/assets/slice_purchase_test.html
@@ -20,7 +20,8 @@
<meta charset="UTF-8">
<meta name="description" content="
This is a HTML page that calls and verifies responses from the @JavascriptInterface functions of
- SliceStoreWebInterface. Test SliceStore APIs using ADB shell commands and the APIs below:
+ SlicePurchaseWebInterface. Test slice purchase application behavior using ADB shell commands and
+ the APIs below:
FROM TERMINAL:
Allow device to override carrier configs:
@@ -29,7 +30,7 @@
$ adb shell cmd phone cc set-value -p supported_premium_capabilities_int_array 34
Set the carrier purchase URL to this test HTML file:
$ adb shell cmd phone cc set-value -p premium_capability_purchase_url_string \
- file:///android_asset/slice_store_test.html
+ file:///android_asset/slice_purchase_test.html
OPTIONAL: Allow premium capability purchase on LTE:
$ adb shell cmd phone cc set-value -p premium_capability_supported_on_lte_bool true
OPTIONAL: Override ServiceState to fake a NR SA connection:
@@ -43,7 +44,7 @@
this.getMainExecutor(), request::offer);
When the test application starts, this HTML will be loaded into the WebView along with the
- associated JavaScript functions in file:///android_asset/slice_store_test.js.
+ associated JavaScript functions in file:///android_asset/slice_purchase_test.js.
Click on the buttons in the HTML to call the corresponding @JavascriptInterface APIs.
RESET DEVICE STATE:
@@ -52,11 +53,11 @@
Clear ServiceState override that was set:
$ adb shell am broadcast -a com.android.internal.telephony.TestServiceState --es action reset
">
- <title>Test SliceStoreActivity</title>
- <script type="text/javascript" src="slice_store_test.js"></script>
+ <title>Test SlicePurchaseActivity</title>
+ <script type="text/javascript" src="slice_purchase_test.js"></script>
</head>
<body>
- <h1>Test SliceStoreActivity</h1>
+ <h1>Test SlicePurchaseActivity</h1>
<h2>Get requested premium capability</h2>
<button type="button" onclick="testGetRequestedCapability()">
Get requested premium capability
@@ -70,7 +71,7 @@
<p id="purchase_successful"></p>
<h2>Notify purchase failed</h2>
- <button type="button" onclick="testNotifyPurchaseFailed()">
+ <button type="button" onclick="testNotifyPurchaseFailed(2, 'FAILURE_CODE_SERVER_UNREACHABLE')">
Notify purchase failed
</button>
<p id="purchase_failed"></p>
diff --git a/packages/CarrierDefaultApp/assets/slice_store_test.js b/packages/CarrierDefaultApp/assets/slice_purchase_test.js
similarity index 76%
rename from packages/CarrierDefaultApp/assets/slice_store_test.js
rename to packages/CarrierDefaultApp/assets/slice_purchase_test.js
index f12a6da..02c4fea 100644
--- a/packages/CarrierDefaultApp/assets/slice_store_test.js
+++ b/packages/CarrierDefaultApp/assets/slice_purchase_test.js
@@ -15,19 +15,19 @@
*/
function testGetRequestedCapability() {
- let capability = SliceStoreWebInterface.getRequestedCapability();
+ let capability = SlicePurchaseWebInterface.getRequestedCapability();
document.getElementById("requested_capability").innerHTML =
"Premium capability requested: " + capability;
}
function testNotifyPurchaseSuccessful(duration_ms_long = 0) {
- SliceStoreWebInterface.notifyPurchaseSuccessful(duration);
+ SlicePurchaseWebInterface.notifyPurchaseSuccessful(duration_ms_long);
document.getElementById("purchase_successful").innerHTML =
- "Notified purchase success for duration: " + duration;
+ "Notified purchase success for duration: " + duration_ms_long;
}
-function testNotifyPurchaseFailed() {
- SliceStoreWebInterface.notifyPurchaseFailed();
+function testNotifyPurchaseFailed(failure_code = 0, failure_reason = "unknown") {
+ SlicePurchaseWebInterface.notifyPurchaseFailed(failure_code, failure_reason);
document.getElementById("purchase_failed").innerHTML =
"Notified purchase failed.";
}
diff --git a/packages/CarrierDefaultApp/res/values/strings.xml b/packages/CarrierDefaultApp/res/values/strings.xml
index ce88a40..8a19709 100644
--- a/packages/CarrierDefaultApp/res/values/strings.xml
+++ b/packages/CarrierDefaultApp/res/values/strings.xml
@@ -25,6 +25,6 @@
<!-- Notification button text to manage the network boost notification. -->
<string name="network_boost_notification_button_manage">Manage</string>
- <!-- Label to display when the slice store opens. -->
- <string name="slice_store_label">Purchase a network boost.</string>
+ <!-- Label to display when the slice purchase application opens. -->
+ <string name="slice_purchase_app_label">Purchase a network boost.</string>
</resources>
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java
new file mode 100644
index 0000000..e67ea7e
--- /dev/null
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.carrierdefaultapp;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.webkit.WebView;
+
+import com.android.phone.slice.SlicePurchaseController;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Activity that launches when the user clicks on the network boost notification.
+ * This will open a {@link WebView} for the carrier website to allow the user to complete the
+ * premium capability purchase.
+ * The carrier website can get the requested premium capability using the JavaScript interface
+ * method {@code SlicePurchaseWebInterface.getRequestedCapability()}.
+ * If the purchase is successful, the carrier website shall notify the slice purchase application
+ * using the JavaScript interface method
+ * {@code SlicePurchaseWebInterface.notifyPurchaseSuccessful(duration)}, where {@code duration} is
+ * the optional duration of the network boost.
+ * If the purchase was not successful, the carrier website shall notify the slice purchase
+ * application using the JavaScript interface method
+ * {@code SlicePurchaseWebInterface.notifyPurchaseFailed(code, reason)}, where {@code code} is the
+ * {@link SlicePurchaseController.FailureCode} indicating the reason for failure and {@code reason}
+ * is the human-readable reason for failure if the failure code is
+ * {@link SlicePurchaseController#FAILURE_CODE_UNKNOWN}.
+ * If either of these notification methods are not called, the purchase cannot be completed
+ * successfully and the purchase request will eventually time out.
+ */
+public class SlicePurchaseActivity extends Activity {
+ private static final String TAG = "SlicePurchaseActivity";
+
+ private @NonNull WebView mWebView;
+ private @NonNull Context mApplicationContext;
+ private int mSubId;
+ @TelephonyManager.PremiumCapability protected int mCapability;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Intent intent = getIntent();
+ mSubId = intent.getIntExtra(SlicePurchaseController.EXTRA_SUB_ID,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ mCapability = intent.getIntExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
+ SlicePurchaseController.PREMIUM_CAPABILITY_INVALID);
+ mApplicationContext = getApplicationContext();
+ URL url = getUrl();
+ logd("onCreate: subId=" + mSubId + ", capability="
+ + TelephonyManager.convertPremiumCapabilityToString(mCapability)
+ + ", url=" + url);
+
+ // Cancel network boost notification
+ mApplicationContext.getSystemService(NotificationManager.class)
+ .cancel(SlicePurchaseBroadcastReceiver.NETWORK_BOOST_NOTIFICATION_TAG, mCapability);
+
+ // Verify intent and values are valid
+ if (!SlicePurchaseBroadcastReceiver.isIntentValid(intent)) {
+ loge("Not starting SlicePurchaseActivity with an invalid Intent: " + intent);
+ SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponse(
+ intent, SlicePurchaseController.EXTRA_INTENT_REQUEST_FAILED);
+ finishAndRemoveTask();
+ return;
+ }
+ if (url == null) {
+ String error = "Unable to create a URL from carrier configs.";
+ loge(error);
+ Intent data = new Intent();
+ data.putExtra(SlicePurchaseController.EXTRA_FAILURE_CODE,
+ SlicePurchaseController.FAILURE_CODE_CARRIER_URL_UNAVAILABLE);
+ data.putExtra(SlicePurchaseController.EXTRA_FAILURE_REASON, error);
+ SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponseWithData(mApplicationContext,
+ getIntent(), SlicePurchaseController.EXTRA_INTENT_CARRIER_ERROR, data);
+ finishAndRemoveTask();
+ return;
+ }
+ if (mSubId != SubscriptionManager.getDefaultSubscriptionId()) {
+ loge("Unable to start the slice purchase application on the non-default data "
+ + "subscription: " + mSubId);
+ SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponse(
+ intent, SlicePurchaseController.EXTRA_INTENT_NOT_DEFAULT_DATA_SUB);
+ finishAndRemoveTask();
+ return;
+ }
+
+ // Create a reference to this activity in SlicePurchaseBroadcastReceiver
+ SlicePurchaseBroadcastReceiver.updateSlicePurchaseActivity(mCapability, this);
+
+ // Create and configure WebView
+ mWebView = new WebView(this);
+ // Enable JavaScript for the carrier purchase website to send results back to
+ // the slice purchase application.
+ mWebView.getSettings().setJavaScriptEnabled(true);
+ mWebView.addJavascriptInterface(
+ new SlicePurchaseWebInterface(this), "SlicePurchaseWebInterface");
+
+ // Display WebView
+ setContentView(mWebView);
+ mWebView.loadUrl(url.toString());
+ }
+
+ protected void onPurchaseSuccessful(long duration) {
+ logd("onPurchaseSuccessful: Carrier website indicated successfully purchased premium "
+ + "capability " + TelephonyManager.convertPremiumCapabilityToString(mCapability)
+ + (duration > 0 ? " for " + TimeUnit.MILLISECONDS.toMinutes(duration) + " minutes."
+ : "."));
+ Intent intent = new Intent();
+ intent.putExtra(SlicePurchaseController.EXTRA_PURCHASE_DURATION, duration);
+ SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponseWithData(mApplicationContext,
+ getIntent(), SlicePurchaseController.EXTRA_INTENT_SUCCESS, intent);
+ finishAndRemoveTask();
+ }
+
+ protected void onPurchaseFailed(@SlicePurchaseController.FailureCode int failureCode,
+ @Nullable String failureReason) {
+ logd("onPurchaseFailed: Carrier website indicated purchase failed for premium capability "
+ + TelephonyManager.convertPremiumCapabilityToString(mCapability) + " with code: "
+ + failureCode + " and reason: " + failureReason);
+ Intent data = new Intent();
+ data.putExtra(SlicePurchaseController.EXTRA_FAILURE_CODE, failureCode);
+ data.putExtra(SlicePurchaseController.EXTRA_FAILURE_REASON, failureReason);
+ SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponseWithData(mApplicationContext,
+ getIntent(), SlicePurchaseController.EXTRA_INTENT_CARRIER_ERROR, data);
+ finishAndRemoveTask();
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
+ // Pressing back in the WebView will go to the previous page instead of closing
+ // the slice purchase application.
+ if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) {
+ mWebView.goBack();
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ protected void onDestroy() {
+ logd("onDestroy: User canceled the purchase by closing the application.");
+ SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponse(
+ getIntent(), SlicePurchaseController.EXTRA_INTENT_CANCELED);
+ SlicePurchaseBroadcastReceiver.removeSlicePurchaseActivity(mCapability);
+ super.onDestroy();
+ }
+
+ @Nullable private URL getUrl() {
+ String url = mApplicationContext.getSystemService(CarrierConfigManager.class)
+ .getConfigForSubId(mSubId).getString(
+ CarrierConfigManager.KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING);
+ try {
+ return new URL(url);
+ } catch (MalformedURLException e) {
+ loge("Invalid URL: " + url);
+ }
+ return null;
+ }
+
+ private static void logd(@NonNull String s) {
+ Log.d(TAG, s);
+ }
+
+ private static void loge(@NonNull String s) {
+ Log.e(TAG, s);
+ }
+}
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SliceStoreBroadcastReceiver.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java
similarity index 68%
rename from packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SliceStoreBroadcastReceiver.java
rename to packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java
index 7867ef1..5761b3c 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SliceStoreBroadcastReceiver.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java
@@ -33,7 +33,7 @@
import android.util.Log;
import android.webkit.WebView;
-import com.android.phone.slicestore.SliceStore;
+import com.android.phone.slice.SlicePurchaseController;
import java.lang.ref.WeakReference;
import java.util.HashMap;
@@ -41,13 +41,14 @@
import java.util.UUID;
/**
- * The SliceStoreBroadcastReceiver listens for {@link SliceStore#ACTION_START_SLICE_STORE} from the
- * SliceStore in the phone process to start the SliceStore application. It displays the network
- * boost notification to the user and will start the {@link SliceStoreActivity} to display the
+ * The SlicePurchaseBroadcastReceiver listens for
+ * {@link SlicePurchaseController#ACTION_START_SLICE_PURCHASE_APP} from the SlicePurchaseController
+ * in the phone process to start the slice purchase application. It displays the network boost
+ * notification to the user and will start the {@link SlicePurchaseActivity} to display the
* {@link WebView} to purchase network boosts from the user's carrier.
*/
-public class SliceStoreBroadcastReceiver extends BroadcastReceiver{
- private static final String TAG = "SliceStoreBroadcastReceiver";
+public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{
+ private static final String TAG = "SlicePurchaseBroadcastReceiver";
/**
* UUID to report an anomaly when receiving a PendingIntent from an application or process
@@ -55,48 +56,49 @@
*/
private static final String UUID_BAD_PENDING_INTENT = "c360246e-95dc-4abf-9dc1-929a76cd7e53";
- /** Weak references to {@link SliceStoreActivity} for each capability, if it exists. */
- private static final Map<Integer, WeakReference<SliceStoreActivity>> sSliceStoreActivities =
- new HashMap<>();
+ /** Weak references to {@link SlicePurchaseActivity} for each capability, if it exists. */
+ private static final Map<Integer, WeakReference<SlicePurchaseActivity>>
+ sSlicePurchaseActivities = new HashMap<>();
/** Channel ID for the network boost notification. */
private static final String NETWORK_BOOST_NOTIFICATION_CHANNEL_ID = "network_boost";
/** Tag for the network boost notification. */
- public static final String NETWORK_BOOST_NOTIFICATION_TAG = "SliceStore.Notification";
+ public static final String NETWORK_BOOST_NOTIFICATION_TAG = "SlicePurchaseApp.Notification";
/** Action for when the user clicks the "Not now" button on the network boost notification. */
private static final String ACTION_NOTIFICATION_CANCELED =
- "com.android.phone.slicestore.action.NOTIFICATION_CANCELED";
+ "com.android.phone.slice.action.NOTIFICATION_CANCELED";
/**
- * Create a weak reference to {@link SliceStoreActivity}. The reference will be removed when
- * {@link SliceStoreActivity#onDestroy()} is called.
+ * Create a weak reference to {@link SlicePurchaseActivity}. The reference will be removed when
+ * {@link SlicePurchaseActivity#onDestroy()} is called.
*
* @param capability The premium capability requested.
- * @param sliceStoreActivity The instance of SliceStoreActivity.
+ * @param slicePurchaseActivity The instance of SlicePurchaseActivity.
*/
- public static void updateSliceStoreActivity(@TelephonyManager.PremiumCapability int capability,
- @NonNull SliceStoreActivity sliceStoreActivity) {
- sSliceStoreActivities.put(capability, new WeakReference<>(sliceStoreActivity));
+ public static void updateSlicePurchaseActivity(
+ @TelephonyManager.PremiumCapability int capability,
+ @NonNull SlicePurchaseActivity slicePurchaseActivity) {
+ sSlicePurchaseActivities.put(capability, new WeakReference<>(slicePurchaseActivity));
}
/**
- * Remove the weak reference to {@link SliceStoreActivity} when
- * {@link SliceStoreActivity#onDestroy()} is called.
+ * Remove the weak reference to {@link SlicePurchaseActivity} when
+ * {@link SlicePurchaseActivity#onDestroy()} is called.
*
* @param capability The premium capability requested.
*/
- public static void removeSliceStoreActivity(
+ public static void removeSlicePurchaseActivity(
@TelephonyManager.PremiumCapability int capability) {
- sSliceStoreActivities.remove(capability);
+ sSlicePurchaseActivities.remove(capability);
}
/**
- * Send the PendingIntent containing the corresponding SliceStore response.
+ * Send the PendingIntent containing the corresponding slice purchase application response.
*
* @param intent The Intent containing the PendingIntent extra.
* @param extra The extra to get the PendingIntent to send.
*/
- public static void sendSliceStoreResponse(@NonNull Intent intent, @NonNull String extra) {
+ public static void sendSlicePurchaseAppResponse(@NonNull Intent intent, @NonNull String extra) {
PendingIntent pendingIntent = intent.getParcelableExtra(extra, PendingIntent.class);
if (pendingIntent == null) {
loge("PendingIntent does not exist for extra: " + extra);
@@ -110,14 +112,15 @@
}
/**
- * Send the PendingIntent containing the corresponding SliceStore response with additional data.
+ * Send the PendingIntent containing the corresponding slice purchase application response
+ * with additional data.
*
* @param context The Context to use to send the PendingIntent.
* @param intent The Intent containing the PendingIntent extra.
* @param extra The extra to get the PendingIntent to send.
* @param data The Intent containing additional data to send with the PendingIntent.
*/
- public static void sendSliceStoreResponseWithData(@NonNull Context context,
+ public static void sendSlicePurchaseAppResponseWithData(@NonNull Context context,
@NonNull Intent intent, @NonNull String extra, @NonNull Intent data) {
PendingIntent pendingIntent = intent.getParcelableExtra(extra, PendingIntent.class);
if (pendingIntent == null) {
@@ -132,45 +135,46 @@
}
/**
- * Check whether the Intent is valid and can be used to complete purchases in the SliceStore.
- * This checks that all necessary extras exist and that the values are valid.
+ * Check whether the Intent is valid and can be used to complete purchases in the slice purchase
+ * application. This checks that all necessary extras exist and that the values are valid.
*
* @param intent The intent to check
* @return {@code true} if the intent is valid and {@code false} otherwise.
*/
public static boolean isIntentValid(@NonNull Intent intent) {
- int phoneId = intent.getIntExtra(SliceStore.EXTRA_PHONE_ID,
+ int phoneId = intent.getIntExtra(SlicePurchaseController.EXTRA_PHONE_ID,
SubscriptionManager.INVALID_PHONE_INDEX);
if (phoneId == SubscriptionManager.INVALID_PHONE_INDEX) {
loge("isIntentValid: invalid phone index: " + phoneId);
return false;
}
- int subId = intent.getIntExtra(SliceStore.EXTRA_SUB_ID,
+ int subId = intent.getIntExtra(SlicePurchaseController.EXTRA_SUB_ID,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
loge("isIntentValid: invalid subscription ID: " + subId);
return false;
}
- int capability = intent.getIntExtra(SliceStore.EXTRA_PREMIUM_CAPABILITY,
- SliceStore.PREMIUM_CAPABILITY_INVALID);
- if (capability == SliceStore.PREMIUM_CAPABILITY_INVALID) {
+ int capability = intent.getIntExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
+ SlicePurchaseController.PREMIUM_CAPABILITY_INVALID);
+ if (capability == SlicePurchaseController.PREMIUM_CAPABILITY_INVALID) {
loge("isIntentValid: invalid premium capability: " + capability);
return false;
}
- String appName = intent.getStringExtra(SliceStore.EXTRA_REQUESTING_APP_NAME);
+ String appName = intent.getStringExtra(SlicePurchaseController.EXTRA_REQUESTING_APP_NAME);
if (TextUtils.isEmpty(appName)) {
loge("isIntentValid: empty requesting application name: " + appName);
return false;
}
- return isPendingIntentValid(intent, SliceStore.EXTRA_INTENT_CANCELED)
- && isPendingIntentValid(intent, SliceStore.EXTRA_INTENT_CARRIER_ERROR)
- && isPendingIntentValid(intent, SliceStore.EXTRA_INTENT_REQUEST_FAILED)
- && isPendingIntentValid(intent, SliceStore.EXTRA_INTENT_NOT_DEFAULT_DATA)
- && isPendingIntentValid(intent, SliceStore.EXTRA_INTENT_SUCCESS);
+ return isPendingIntentValid(intent, SlicePurchaseController.EXTRA_INTENT_CANCELED)
+ && isPendingIntentValid(intent, SlicePurchaseController.EXTRA_INTENT_CARRIER_ERROR)
+ && isPendingIntentValid(intent, SlicePurchaseController.EXTRA_INTENT_REQUEST_FAILED)
+ && isPendingIntentValid(intent,
+ SlicePurchaseController.EXTRA_INTENT_NOT_DEFAULT_DATA_SUB)
+ && isPendingIntentValid(intent, SlicePurchaseController.EXTRA_INTENT_SUCCESS);
}
private static boolean isPendingIntentValid(@NonNull Intent intent, @NonNull String extra) {
@@ -197,11 +201,12 @@
@NonNull private static String getPendingIntentType(@NonNull String extra) {
switch (extra) {
- case SliceStore.EXTRA_INTENT_CANCELED: return "canceled";
- case SliceStore.EXTRA_INTENT_CARRIER_ERROR: return "carrier error";
- case SliceStore.EXTRA_INTENT_REQUEST_FAILED: return "request failed";
- case SliceStore.EXTRA_INTENT_NOT_DEFAULT_DATA: return "not default data";
- case SliceStore.EXTRA_INTENT_SUCCESS: return "success";
+ case SlicePurchaseController.EXTRA_INTENT_CANCELED: return "canceled";
+ case SlicePurchaseController.EXTRA_INTENT_CARRIER_ERROR: return "carrier error";
+ case SlicePurchaseController.EXTRA_INTENT_REQUEST_FAILED: return "request failed";
+ case SlicePurchaseController.EXTRA_INTENT_NOT_DEFAULT_DATA_SUB:
+ return "not default data sub";
+ case SlicePurchaseController.EXTRA_INTENT_SUCCESS: return "success";
default: {
loge("Unknown pending intent extra: " + extra);
return "unknown(" + extra + ")";
@@ -213,10 +218,10 @@
public void onReceive(@NonNull Context context, @NonNull Intent intent) {
logd("onReceive intent: " + intent.getAction());
switch (intent.getAction()) {
- case SliceStore.ACTION_START_SLICE_STORE:
+ case SlicePurchaseController.ACTION_START_SLICE_PURCHASE_APP:
onDisplayBoosterNotification(context, intent);
break;
- case SliceStore.ACTION_SLICE_STORE_RESPONSE_TIMEOUT:
+ case SlicePurchaseController.ACTION_SLICE_PURCHASE_APP_RESPONSE_TIMEOUT:
onTimeout(context, intent);
break;
case ACTION_NOTIFICATION_CANCELED:
@@ -229,7 +234,8 @@
private void onDisplayBoosterNotification(@NonNull Context context, @NonNull Intent intent) {
if (!isIntentValid(intent)) {
- sendSliceStoreResponse(intent, SliceStore.EXTRA_INTENT_REQUEST_FAILED);
+ sendSlicePurchaseAppResponse(intent,
+ SlicePurchaseController.EXTRA_INTENT_REQUEST_FAILED);
return;
}
@@ -243,13 +249,14 @@
new Notification.Builder(context, NETWORK_BOOST_NOTIFICATION_CHANNEL_ID)
.setContentTitle(String.format(context.getResources().getString(
R.string.network_boost_notification_title),
- intent.getStringExtra(SliceStore.EXTRA_REQUESTING_APP_NAME)))
+ intent.getStringExtra(
+ SlicePurchaseController.EXTRA_REQUESTING_APP_NAME)))
.setContentText(context.getResources().getString(
R.string.network_boost_notification_detail))
.setSmallIcon(R.drawable.ic_network_boost)
.setContentIntent(createContentIntent(context, intent, 1))
.setDeleteIntent(intent.getParcelableExtra(
- SliceStore.EXTRA_INTENT_CANCELED, PendingIntent.class))
+ SlicePurchaseController.EXTRA_INTENT_CANCELED, PendingIntent.class))
// Add an action for the "Not now" button, which has the same behavior as
// the user canceling or closing the notification.
.addAction(new Notification.Action.Builder(
@@ -266,8 +273,8 @@
createContentIntent(context, intent, 2)).build())
.build();
- int capability = intent.getIntExtra(SliceStore.EXTRA_PREMIUM_CAPABILITY,
- SliceStore.PREMIUM_CAPABILITY_INVALID);
+ int capability = intent.getIntExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
+ SlicePurchaseController.PREMIUM_CAPABILITY_INVALID);
logd("Display the booster notification for capability "
+ TelephonyManager.convertPremiumCapabilityToString(capability));
context.getSystemService(NotificationManager.class).notifyAsUser(
@@ -276,19 +283,19 @@
/**
* Create the intent for when the user clicks on the "Manage" button on the network boost
- * notification or the notification itself. This will open {@link SliceStoreActivity}.
+ * notification or the notification itself. This will open {@link SlicePurchaseActivity}.
*
* @param context The Context to create the intent for.
- * @param intent The source Intent used to launch the SliceStore application.
+ * @param intent The source Intent used to launch the slice purchase application.
* @param requestCode The request code for the PendingIntent.
*
- * @return The intent to start {@link SliceStoreActivity}.
+ * @return The intent to start {@link SlicePurchaseActivity}.
*/
@NonNull private PendingIntent createContentIntent(@NonNull Context context,
@NonNull Intent intent, int requestCode) {
- Intent i = new Intent(context, SliceStoreActivity.class);
+ Intent i = new Intent(context, SlicePurchaseActivity.class);
i.setComponent(ComponentName.unflattenFromString(
- "com.android.carrierdefaultapp/.SliceStoreActivity"));
+ "com.android.carrierdefaultapp/.SlicePurchaseActivity"));
i.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT
| Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
i.putExtras(intent);
@@ -303,7 +310,7 @@
* as if the user had canceled or removed the notification.
*
* @param context The Context to create the intent for.
- * @param intent The source Intent used to launch the SliceStore application.
+ * @param intent The source Intent used to launch the slice purchase application.
*
* @return The canceled intent.
*/
@@ -311,37 +318,37 @@
@NonNull Intent intent) {
Intent i = new Intent(ACTION_NOTIFICATION_CANCELED);
i.setComponent(ComponentName.unflattenFromString(
- "com.android.carrierdefaultapp/.SliceStoreBroadcastReceiver"));
+ "com.android.carrierdefaultapp/.SlicePurchaseBroadcastReceiver"));
i.putExtras(intent);
return PendingIntent.getBroadcast(context, 0, i,
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE);
}
private void onTimeout(@NonNull Context context, @NonNull Intent intent) {
- int capability = intent.getIntExtra(SliceStore.EXTRA_PREMIUM_CAPABILITY,
- SliceStore.PREMIUM_CAPABILITY_INVALID);
+ int capability = intent.getIntExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
+ SlicePurchaseController.PREMIUM_CAPABILITY_INVALID);
logd("Purchase capability " + TelephonyManager.convertPremiumCapabilityToString(capability)
+ " timed out.");
- if (sSliceStoreActivities.get(capability) == null) {
+ if (sSlicePurchaseActivities.get(capability) == null) {
// Notification is still active
logd("Closing booster notification since the user did not respond in time.");
context.getSystemService(NotificationManager.class).cancelAsUser(
NETWORK_BOOST_NOTIFICATION_TAG, capability, UserHandle.ALL);
} else {
- // Notification was dismissed but SliceStoreActivity is still active
- logd("Closing SliceStore WebView since the user did not complete the purchase "
- + "in time.");
- sSliceStoreActivities.get(capability).get().finishAndRemoveTask();
+ // Notification was dismissed but SlicePurchaseActivity is still active
+ logd("Closing slice purchase application WebView since the user did not complete the "
+ + "purchase in time.");
+ sSlicePurchaseActivities.get(capability).get().finishAndRemoveTask();
}
}
private void onUserCanceled(@NonNull Context context, @NonNull Intent intent) {
- int capability = intent.getIntExtra(SliceStore.EXTRA_PREMIUM_CAPABILITY,
- SliceStore.PREMIUM_CAPABILITY_INVALID);
+ int capability = intent.getIntExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
+ SlicePurchaseController.PREMIUM_CAPABILITY_INVALID);
logd("onUserCanceled: " + TelephonyManager.convertPremiumCapabilityToString(capability));
context.getSystemService(NotificationManager.class)
.cancelAsUser(NETWORK_BOOST_NOTIFICATION_TAG, capability, UserHandle.ALL);
- sendSliceStoreResponse(intent, SliceStore.EXTRA_INTENT_CANCELED);
+ sendSlicePurchaseAppResponse(intent, SlicePurchaseController.EXTRA_INTENT_CANCELED);
}
private static void logd(String s) {
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SliceStoreWebInterface.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseWebInterface.java
similarity index 67%
rename from packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SliceStoreWebInterface.java
rename to packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseWebInterface.java
index ab5d080..8547898 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SliceStoreWebInterface.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseWebInterface.java
@@ -21,18 +21,19 @@
import android.telephony.TelephonyManager;
import android.webkit.JavascriptInterface;
-import com.android.phone.slicestore.SliceStore;
+import com.android.phone.slice.SlicePurchaseController;
/**
- * SliceStore web interface class allowing carrier websites to send responses back to SliceStore
- * using JavaScript.
+ * Slice purchase web interface class allowing carrier websites to send responses back to the
+ * slice purchase application using JavaScript.
*/
-public class SliceStoreWebInterface {
- @NonNull SliceStoreActivity mActivity;
+public class SlicePurchaseWebInterface {
+ @NonNull SlicePurchaseActivity mActivity;
- public SliceStoreWebInterface(@NonNull SliceStoreActivity activity) {
+ public SlicePurchaseWebInterface(@NonNull SlicePurchaseActivity activity) {
mActivity = activity;
}
+
/**
* Interface method allowing the carrier website to get the premium capability
* that was requested to purchase.
@@ -40,7 +41,7 @@
* This can be called using the JavaScript below:
* <script type="text/javascript">
* function getRequestedCapability(duration) {
- * SliceStoreWebInterface.getRequestedCapability();
+ * SlicePurchaseWebInterface.getRequestedCapability();
* }
* </script>
*/
@@ -50,13 +51,14 @@
}
/**
- * Interface method allowing the carrier website to notify the SliceStore of a successful
- * premium capability purchase and the duration for which the premium capability is purchased.
+ * Interface method allowing the carrier website to notify the slice purchase application of
+ * a successful premium capability purchase and the duration for which the premium capability is
+ * purchased.
*
* This can be called using the JavaScript below:
* <script type="text/javascript">
* function notifyPurchaseSuccessful(duration_ms_long = 0) {
- * SliceStoreWebInterface.notifyPurchaseSuccessful(duration_ms_long);
+ * SlicePurchaseWebInterface.notifyPurchaseSuccessful(duration_ms_long);
* }
* </script>
*
@@ -68,22 +70,23 @@
}
/**
- * Interface method allowing the carrier website to notify the SliceStore of a failed
- * premium capability purchase.
+ * Interface method allowing the carrier website to notify the slice purchase application of
+ * a failed premium capability purchase.
*
* This can be called using the JavaScript below:
* <script type="text/javascript">
- * function notifyPurchaseFailed() {
- * SliceStoreWebInterface.notifyPurchaseFailed();
+ * function notifyPurchaseFailed(failure_code = 0, failure_reason = "unknown") {
+ * SlicePurchaseWebInterface.notifyPurchaseFailed();
* }
* </script>
*
* @param failureCode The failure code.
- * @param failureReason If the failure code is {@link SliceStore#FAILURE_CODE_UNKNOWN},
+ * @param failureReason If the failure code is
+ * {@link SlicePurchaseController#FAILURE_CODE_UNKNOWN},
* the human-readable reason for failure.
*/
@JavascriptInterface
- public void notifyPurchaseFailed(@SliceStore.FailureCode int failureCode,
+ public void notifyPurchaseFailed(@SlicePurchaseController.FailureCode int failureCode,
@Nullable String failureReason) {
mActivity.onPurchaseFailed(failureCode, failureReason);
}
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SliceStoreActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SliceStoreActivity.java
deleted file mode 100644
index 348e389..0000000
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SliceStoreActivity.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.carrierdefaultapp;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.Activity;
-import android.app.NotificationManager;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.telephony.CarrierConfigManager;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.webkit.WebView;
-
-import com.android.phone.slicestore.SliceStore;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Activity that launches when the user clicks on the network boost notification.
- * This will open a {@link WebView} for the carrier website to allow the user to complete the
- * premium capability purchase.
- * The carrier website can get the requested premium capability using the JavaScript interface
- * method {@code SliceStoreWebInterface.getRequestedCapability()}.
- * If the purchase is successful, the carrier website shall notify SliceStore using the JavaScript
- * interface method {@code SliceStoreWebInterface.notifyPurchaseSuccessful(duration)}, where
- * {@code duration} is the duration of the network boost.
- * If the purchase was not successful, the carrier website shall notify SliceStore using the
- * JavaScript interface method {@code SliceStoreWebInterface.notifyPurchaseFailed()}.
- * If either of these notification methods are not called, the purchase cannot be completed
- * successfully and the purchase request will eventually time out.
- */
-public class SliceStoreActivity extends Activity {
- private static final String TAG = "SliceStoreActivity";
-
- private @NonNull WebView mWebView;
- private @NonNull Context mApplicationContext;
- private int mSubId;
- @TelephonyManager.PremiumCapability protected int mCapability;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- Intent intent = getIntent();
- mSubId = intent.getIntExtra(SliceStore.EXTRA_SUB_ID,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- mCapability = intent.getIntExtra(SliceStore.EXTRA_PREMIUM_CAPABILITY,
- SliceStore.PREMIUM_CAPABILITY_INVALID);
- mApplicationContext = getApplicationContext();
- URL url = getUrl();
- logd("onCreate: subId=" + mSubId + ", capability="
- + TelephonyManager.convertPremiumCapabilityToString(mCapability)
- + ", url=" + url);
-
- // Cancel network boost notification
- mApplicationContext.getSystemService(NotificationManager.class)
- .cancel(SliceStoreBroadcastReceiver.NETWORK_BOOST_NOTIFICATION_TAG, mCapability);
-
- // Verify intent and values are valid
- if (!SliceStoreBroadcastReceiver.isIntentValid(intent)) {
- loge("Not starting SliceStoreActivity with an invalid Intent: " + intent);
- SliceStoreBroadcastReceiver.sendSliceStoreResponse(
- intent, SliceStore.EXTRA_INTENT_REQUEST_FAILED);
- finishAndRemoveTask();
- return;
- }
- if (url == null) {
- String error = "Unable to create a URL from carrier configs.";
- loge(error);
- Intent data = new Intent();
- data.putExtra(SliceStore.EXTRA_FAILURE_CODE,
- SliceStore.FAILURE_CODE_CARRIER_URL_UNAVAILABLE);
- data.putExtra(SliceStore.EXTRA_FAILURE_REASON, error);
- SliceStoreBroadcastReceiver.sendSliceStoreResponseWithData(
- mApplicationContext, getIntent(), SliceStore.EXTRA_INTENT_CARRIER_ERROR, data);
- finishAndRemoveTask();
- return;
- }
- if (mSubId != SubscriptionManager.getDefaultSubscriptionId()) {
- loge("Unable to start SliceStore on the non-default data subscription: " + mSubId);
- SliceStoreBroadcastReceiver.sendSliceStoreResponse(
- intent, SliceStore.EXTRA_INTENT_NOT_DEFAULT_DATA);
- finishAndRemoveTask();
- return;
- }
-
- // Create a reference to this activity in SliceStoreBroadcastReceiver
- SliceStoreBroadcastReceiver.updateSliceStoreActivity(mCapability, this);
-
- // Create and configure WebView
- mWebView = new WebView(this);
- // Enable JavaScript for the carrier purchase website to send results back to SliceStore
- mWebView.getSettings().setJavaScriptEnabled(true);
- mWebView.addJavascriptInterface(new SliceStoreWebInterface(this), "SliceStoreWebInterface");
-
- // Display WebView
- setContentView(mWebView);
- mWebView.loadUrl(url.toString());
- }
-
- protected void onPurchaseSuccessful(long duration) {
- logd("onPurchaseSuccessful: Carrier website indicated successfully purchased premium "
- + "capability " + TelephonyManager.convertPremiumCapabilityToString(mCapability)
- + " for " + TimeUnit.MILLISECONDS.toMinutes(duration) + " minutes.");
- Intent intent = new Intent();
- intent.putExtra(SliceStore.EXTRA_PURCHASE_DURATION, duration);
- SliceStoreBroadcastReceiver.sendSliceStoreResponseWithData(
- mApplicationContext, getIntent(), SliceStore.EXTRA_INTENT_SUCCESS, intent);
- finishAndRemoveTask();
- }
-
- protected void onPurchaseFailed(@SliceStore.FailureCode int failureCode,
- @Nullable String failureReason) {
- logd("onPurchaseFailed: Carrier website indicated purchase failed for premium capability "
- + TelephonyManager.convertPremiumCapabilityToString(mCapability) + " with code: "
- + SliceStore.convertFailureCodeToString(failureCode) + " and reason: "
- + failureReason);
- Intent data = new Intent();
- data.putExtra(SliceStore.EXTRA_FAILURE_CODE, failureCode);
- data.putExtra(SliceStore.EXTRA_FAILURE_REASON, failureReason);
- SliceStoreBroadcastReceiver.sendSliceStoreResponseWithData(
- mApplicationContext, getIntent(), SliceStore.EXTRA_INTENT_CARRIER_ERROR, data);
- finishAndRemoveTask();
- }
-
- @Override
- public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
- // Pressing back in the WebView will go to the previous page instead of closing SliceStore.
- if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) {
- mWebView.goBack();
- return true;
- }
- return super.onKeyDown(keyCode, event);
- }
-
- @Override
- protected void onDestroy() {
- logd("onDestroy: User canceled the purchase by closing the application.");
- SliceStoreBroadcastReceiver.sendSliceStoreResponse(
- getIntent(), SliceStore.EXTRA_INTENT_CANCELED);
- SliceStoreBroadcastReceiver.removeSliceStoreActivity(mCapability);
- super.onDestroy();
- }
-
- @Nullable private URL getUrl() {
- String url = mApplicationContext.getSystemService(CarrierConfigManager.class)
- .getConfigForSubId(mSubId).getString(
- CarrierConfigManager.KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING);
- try {
- return new URL(url);
- } catch (MalformedURLException e) {
- loge("Invalid URL: " + url);
- }
- return null;
- }
-
- private static void logd(@NonNull String s) {
- Log.d(TAG, s);
- }
-
- private static void loge(@NonNull String s) {
- Log.e(TAG, s);
- }
-}
diff --git a/packages/CredentialManager/Android.bp b/packages/CredentialManager/Android.bp
index 25529bb..d8577c3 100644
--- a/packages/CredentialManager/Android.bp
+++ b/packages/CredentialManager/Android.bp
@@ -17,7 +17,11 @@
static_libs: [
"androidx.activity_activity-compose",
"androidx.appcompat_appcompat",
- "androidx.compose.material_material",
+ "androidx.compose.animation_animation-core",
+ "androidx.compose.foundation_foundation",
+ "androidx.compose.material3_material3",
+ "androidx.compose.material_material-icons-core",
+ "androidx.compose.material_material-icons-extended",
"androidx.compose.runtime_runtime",
"androidx.compose.ui_ui",
"androidx.compose.ui_ui-tooling",
@@ -27,6 +31,7 @@
"androidx.lifecycle_lifecycle-runtime-ktx",
"androidx.lifecycle_lifecycle-viewmodel-compose",
"androidx.recyclerview_recyclerview",
+ "kotlinx-coroutines-core",
],
platform_apis: true,
diff --git a/packages/CredentialManager/res/drawable/ic_face.xml b/packages/CredentialManager/res/drawable/ic_face.xml
new file mode 100644
index 0000000..16fe144
--- /dev/null
+++ b/packages/CredentialManager/res/drawable/ic_face.xml
@@ -0,0 +1,30 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!--TODO: Testing only icon. Remove later. -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ tools:ignore="VectorPath"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="#808080"
+ android:pathData="M9.025,14.275Q8.5,14.275 8.125,13.9Q7.75,13.525 7.75,13Q7.75,12.475 8.125,12.1Q8.5,11.725 9.025,11.725Q9.575,11.725 9.938,12.1Q10.3,12.475 10.3,13Q10.3,13.525 9.938,13.9Q9.575,14.275 9.025,14.275ZM14.975,14.275Q14.425,14.275 14.062,13.9Q13.7,13.525 13.7,13Q13.7,12.475 14.062,12.1Q14.425,11.725 14.975,11.725Q15.5,11.725 15.875,12.1Q16.25,12.475 16.25,13Q16.25,13.525 15.875,13.9Q15.5,14.275 14.975,14.275ZM12,19.925Q15.325,19.925 17.625,17.625Q19.925,15.325 19.925,12Q19.925,11.4 19.85,10.85Q19.775,10.3 19.575,9.775Q19.05,9.9 18.538,9.962Q18.025,10.025 17.45,10.025Q15.2,10.025 13.188,9.062Q11.175,8.1 9.775,6.375Q8.975,8.3 7.5,9.712Q6.025,11.125 4.075,11.85Q4.075,11.9 4.075,11.925Q4.075,11.95 4.075,12Q4.075,15.325 6.375,17.625Q8.675,19.925 12,19.925ZM12,22.2Q9.9,22.2 8.038,21.4Q6.175,20.6 4.788,19.225Q3.4,17.85 2.6,15.988Q1.8,14.125 1.8,12Q1.8,9.875 2.6,8.012Q3.4,6.15 4.788,4.775Q6.175,3.4 8.038,2.6Q9.9,1.8 12,1.8Q14.125,1.8 15.988,2.6Q17.85,3.4 19.225,4.775Q20.6,6.15 21.4,8.012Q22.2,9.875 22.2,12Q22.2,14.125 21.4,15.988Q20.6,17.85 19.225,19.225Q17.85,20.6 15.988,21.4Q14.125,22.2 12,22.2Z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/CredentialManager/res/drawable/ic_manage_accounts.xml b/packages/CredentialManager/res/drawable/ic_manage_accounts.xml
new file mode 100644
index 0000000..adad2f1
--- /dev/null
+++ b/packages/CredentialManager/res/drawable/ic_manage_accounts.xml
@@ -0,0 +1,30 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!--TODO: Testing only icon. Remove later. -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ tools:ignore="VectorPath"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="#808080"
+ android:pathData="M16.1,21.2 L15.775,19.675Q15.5,19.55 15.25,19.425Q15,19.3 14.75,19.1L13.275,19.575L12.2,17.75L13.375,16.725Q13.325,16.4 13.325,16.112Q13.325,15.825 13.375,15.5L12.2,14.475L13.275,12.65L14.75,13.1Q15,12.925 15.25,12.787Q15.5,12.65 15.775,12.55L16.1,11.025H18.25L18.55,12.55Q18.825,12.65 19.075,12.8Q19.325,12.95 19.575,13.15L21.05,12.65L22.125,14.525L20.95,15.55Q21.025,15.825 21.013,16.137Q21,16.45 20.95,16.725L22.125,17.75L21.05,19.575L19.575,19.1Q19.325,19.3 19.075,19.425Q18.825,19.55 18.55,19.675L18.25,21.2ZM1.8,20.3V17.3Q1.8,16.375 2.275,15.613Q2.75,14.85 3.5,14.475Q4.775,13.825 6.425,13.362Q8.075,12.9 10,12.9Q10.2,12.9 10.4,12.9Q10.6,12.9 10.775,12.95Q9.925,14.85 10.062,16.738Q10.2,18.625 11.4,20.3ZM17.175,18.075Q17.975,18.075 18.55,17.487Q19.125,16.9 19.125,16.1Q19.125,15.3 18.55,14.725Q17.975,14.15 17.175,14.15Q16.375,14.15 15.788,14.725Q15.2,15.3 15.2,16.1Q15.2,16.9 15.788,17.487Q16.375,18.075 17.175,18.075ZM10,11.9Q8.25,11.9 7.025,10.662Q5.8,9.425 5.8,7.7Q5.8,5.95 7.025,4.725Q8.25,3.5 10,3.5Q11.75,3.5 12.975,4.725Q14.2,5.95 14.2,7.7Q14.2,9.425 12.975,10.662Q11.75,11.9 10,11.9Z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/CredentialManager/res/drawable/ic_other_devices.xml b/packages/CredentialManager/res/drawable/ic_other_devices.xml
new file mode 100644
index 0000000..754648c
--- /dev/null
+++ b/packages/CredentialManager/res/drawable/ic_other_devices.xml
@@ -0,0 +1,15 @@
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ tools:ignore="VectorPath"
+ android:name="vector"
+ android:width="20dp"
+ android:height="20dp"
+ android:viewportWidth="20"
+ android:viewportHeight="20">
+ <path
+ android:name="path"
+ android:pathData="M 7.6 4.72 L 7.6 7.6 L 4.72 7.6 L 4.72 4.72 L 7.6 4.72 Z M 9.04 3.28 L 3.28 3.28 L 3.28 9.04 L 9.04 9.04 L 9.04 3.28 Z M 7.6 12.4 L 7.6 15.28 L 4.72 15.28 L 4.72 12.4 L 7.6 12.4 Z M 9.04 10.96 L 3.28 10.96 L 3.28 16.72 L 9.04 16.72 L 9.04 10.96 Z M 15.28 4.72 L 15.28 7.6 L 12.4 7.6 L 12.4 4.72 L 15.28 4.72 Z M 16.72 3.28 L 10.96 3.28 L 10.96 9.04 L 16.72 9.04 L 16.72 3.28 Z M 10.96 10.96 L 12.4 10.96 L 12.4 12.4 L 10.96 12.4 L 10.96 10.96 Z M 12.4 12.4 L 13.84 12.4 L 13.84 13.84 L 12.4 13.84 L 12.4 12.4 Z M 13.84 10.96 L 15.28 10.96 L 15.28 12.4 L 13.84 12.4 L 13.84 10.96 Z M 10.96 13.84 L 12.4 13.84 L 12.4 15.28 L 10.96 15.28 L 10.96 13.84 Z M 12.4 15.28 L 13.84 15.28 L 13.84 16.72 L 12.4 16.72 L 12.4 15.28 Z M 13.84 13.84 L 15.28 13.84 L 15.28 15.28 L 13.84 15.28 L 13.84 13.84 Z M 15.28 12.4 L 16.72 12.4 L 16.72 13.84 L 15.28 13.84 L 15.28 12.4 Z M 15.28 15.28 L 16.72 15.28 L 16.72 16.72 L 15.28 16.72 L 15.28 15.28 Z M 19.6 5.2 L 17.68 5.2 L 17.68 2.32 L 14.8 2.32 L 14.8 0.4 L 19.6 0.4 L 19.6 5.2 Z M 19.6 19.6 L 19.6 14.8 L 17.68 14.8 L 17.68 17.68 L 14.8 17.68 L 14.8 19.6 L 19.6 19.6 Z M 0.4 19.6 L 5.2 19.6 L 5.2 17.68 L 2.32 17.68 L 2.32 14.8 L 0.4 14.8 L 0.4 19.6 Z M 0.4 0.4 L 0.4 5.2 L 2.32 5.2 L 2.32 2.32 L 5.2 2.32 L 5.2 0.4 L 0.4 0.4 Z"
+ android:fillColor="#000000"
+ android:strokeWidth="1"/>
+</vector>
\ No newline at end of file
diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml
index 08ab1b4..2f6d1b4 100644
--- a/packages/CredentialManager/res/values/strings.xml
+++ b/packages/CredentialManager/res/values/strings.xml
@@ -15,16 +15,51 @@
<string name="choose_create_option_password_title">Save your password to <xliff:g id="providerInfoDisplayName">%1$s</xliff:g>?</string>
<string name="choose_create_option_sign_in_title">Save your sign-in info to <xliff:g id="providerInfoDisplayName">%1$s</xliff:g>?</string>
<string name="choose_sign_in_title">Use saved sign in</string>
- <string name="create_passkey_at">Create passkey at</string>
+ <string name="create_passkey_in">Create passkey in</string>
+ <string name="save_password_to">Save password to</string>
+ <string name="save_sign_in_to">Save sign-in to</string>
<string name="use_provider_for_all_title">Use <xliff:g id="providerInfoDisplayName">%1$s</xliff:g> for all your sign-ins?</string>
<string name="set_as_default">Set as default</string>
<string name="use_once">Use once</string>
- <string name="choose_create_option_description">You can use saved <xliff:g id="type">%1$s</xliff:g> on any device. It will be saved to <xliff:g id="providerInfoDisplayName">%2$s</xliff:g> for <xliff:g id="createInfoDisplayName">%3$s</xliff:g></string>
- <string name="more_options_title_multiple_options"><xliff:g id="providerInfoDisplayName">%1$s</xliff:g> for <xliff:g id="createInfoTitle">%2$s</xliff:g></string>
- <string name="more_options_title_one_option"><xliff:g id="providerInfoDisplayName">%1$s</xliff:g></string>
- <string name="more_options_usage_data"><xliff:g id="passwordsNumber">%1$s</xliff:g> passwords and <xliff:g id="passkeyssNumber">%2$s</xliff:g> passkeys saved</string>
- <string name="passkeys">passkeys</string>
- <string name="passwords">passwords</string>
+ <string name="choose_create_option_description">You can use your <xliff:g id="appDomainName">%1$s</xliff:g> <xliff:g id="type">%2$s</xliff:g> on any device. It is saved to <xliff:g id="providerInfoDisplayName">%3$s</xliff:g> for <xliff:g id="createInfoDisplayName">%4$s</xliff:g></string>
+ <string name="more_options_usage_passwords_passkeys"><xliff:g id="passwordsNumber">%1$s</xliff:g> passwords, <xliff:g id="passkeysNumber">%2$s</xliff:g> passkeys</string>
+ <string name="more_options_usage_passwords"><xliff:g id="passwordsNumber">%1$s</xliff:g> passwords</string>
+ <string name="more_options_usage_passkeys"><xliff:g id="passkeysNumber">%1$s</xliff:g> passkeys</string>
+ <string name="passkey">passkey</string>
+ <string name="password">password</string>
<string name="sign_ins">sign-ins</string>
- <string name="createOptionInfo_icon_description">CreateOptionInfo credentialType icon</string>
+ <string name="another_device">Another device</string>
+ <string name="other_password_manager">Other password manager</string>
+ <!-- TODO: Check the wording here. -->
+ <string name="confirm_default_or_use_once_description">This password manager will store your passwords and passkeys to help you easily sign in.</string>
+ <!-- Spoken content description of an element which will close the sheet when clicked. -->
+ <string name="close_sheet">"Close sheet"</string>
+ <!-- Spoken content description of the back arrow button. -->
+ <string name="accessibility_back_arrow_button">"Go back to the previous page"</string>
+
+ <!-- Strings for the get flow. -->
+ <!-- This appears as the title of the modal bottom sheet asking for user confirmation to use the single previously saved passkey to sign in to the app. [CHAR LIMIT=200] -->
+ <string name="get_dialog_title_use_passkey_for">Use your saved passkey for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string>
+ <!-- This appears as the title of the dialog asking for user confirmation to use the single previously saved credential to sign in to the app. [CHAR LIMIT=200] -->
+ <string name="get_dialog_title_use_sign_in_for">Use your saved sign-in for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string>
+ <!-- This appears as the title of the dialog asking for user to make a choice from various previously saved credentials to sign in to the app. [CHAR LIMIT=200] -->
+ <string name="get_dialog_title_choose_sign_in_for">Choose a saved sign-in for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
+ <!-- Appears as an option row for viewing all the available sign-in options. [CHAR LIMIT=80] -->
+ <string name="get_dialog_use_saved_passkey_for">Sign in another way</string>
+ <!-- Button label to close the dialog when the user does not want to use any sign-in. [CHAR LIMIT=40] -->
+ <string name="get_dialog_button_label_no_thanks">No thanks</string>
+ <!-- Button label to continue with the selected sign-in. [CHAR LIMIT=40] -->
+ <string name="get_dialog_button_label_continue">Continue</string>
+ <!-- Separator for sign-in type and username in a sign-in entry. -->
+ <string name="get_dialog_sign_in_type_username_separator" translatable="false">" - "</string>
+ <!-- Modal bottom sheet title for displaying all the available sign-in options. [CHAR LIMIT=80] -->
+ <string name="get_dialog_title_sign_in_options">Sign-in options</string>
+ <!-- Column heading for displaying sign-ins for a specific username. [CHAR LIMIT=80] -->
+ <string name="get_dialog_heading_for_username">For <xliff:g id="username" example="becket@gmail.com">%1$s</xliff:g></string>
+ <!-- Column heading for displaying locked (that is, the user needs to first authenticate via pin, fingerprint, faceId, etc.) sign-ins. [CHAR LIMIT=80] -->
+ <string name="get_dialog_heading_locked_password_managers">Locked password managers</string>
+ <!-- Explanatory sub/body text for an option entry to use a locked (that is, the user needs to first authenticate via pin, fingerprint, faceId, etc.) sign-in. [CHAR LIMIT=120] -->
+ <string name="locked_credential_entry_label_subtext">Tap to unlock</string>
+ <!-- Column heading for displaying action chips for managing sign-ins from each credential provider. [CHAR LIMIT=80] -->
+ <string name="get_dialog_heading_manage_sign_ins">Manage sign-ins</string>
</resources>
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialEntryUi.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialEntryUi.kt
deleted file mode 100644
index ee4f4ca..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialEntryUi.kt
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.credentialmanager
-
-import android.app.slice.Slice
-import android.credentials.ui.Entry
-import android.graphics.drawable.Icon
-
-/**
- * UI representation for a credential entry used during the get credential flow.
- *
- * TODO: move to jetpack.
- */
-class CredentialEntryUi(
- val userName: CharSequence,
- val displayName: CharSequence?,
- val icon: Icon?,
- val usageData: CharSequence?,
- // TODO: add last used.
-) {
- companion object {
- fun fromSlice(slice: Slice): CredentialEntryUi {
- val items = slice.items
-
- var title: String? = null
- var subTitle: String? = null
- var icon: Icon? = null
- var usageData: String? = null
-
- items.forEach {
- if (it.hasHint(Entry.HINT_ICON)) {
- icon = it.icon
- } else if (it.hasHint(Entry.HINT_SUBTITLE) && it.subType == null) {
- subTitle = it.text.toString()
- } else if (it.hasHint(Entry.HINT_TITLE)) {
- title = it.text.toString()
- } else if (it.hasHint(Entry.HINT_SUBTITLE) && it.subType == Slice.SUBTYPE_MESSAGE) {
- usageData = it.text.toString()
- }
- }
- // TODO: fail NPE more elegantly.
- return CredentialEntryUi(title!!, subTitle, icon, usageData)
- }
- }
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index 3d1fc92..8bd7cf0 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -16,15 +16,19 @@
package com.android.credentialmanager
+import android.credentials.Credential.TYPE_PASSWORD_CREDENTIAL
import android.app.slice.Slice
import android.app.slice.SliceSpec
import android.content.Context
import android.content.Intent
import android.credentials.CreateCredentialRequest
+import android.credentials.GetCredentialOption
+import android.credentials.GetCredentialRequest
import android.credentials.ui.Constants
import android.credentials.ui.Entry
import android.credentials.ui.CreateCredentialProviderData
import android.credentials.ui.GetCredentialProviderData
+import android.credentials.ui.DisabledProviderData
import android.credentials.ui.ProviderData
import android.credentials.ui.RequestInfo
import android.credentials.ui.BaseDialogResult
@@ -36,11 +40,11 @@
import com.android.credentialmanager.createflow.ActiveEntry
import com.android.credentialmanager.createflow.CreatePasskeyUiState
import com.android.credentialmanager.createflow.CreateScreenState
-import com.android.credentialmanager.createflow.ProviderInfo
+import com.android.credentialmanager.createflow.EnabledProviderInfo
import com.android.credentialmanager.createflow.RequestDisplayInfo
import com.android.credentialmanager.getflow.GetCredentialUiState
import com.android.credentialmanager.getflow.GetScreenState
-import com.android.credentialmanager.jetpack.provider.CredentialEntryUi.Companion.TYPE_PUBLIC_KEY_CREDENTIAL
+import com.android.credentialmanager.jetpack.developer.PublicKeyCredential.Companion.TYPE_PUBLIC_KEY_CREDENTIAL
// Consider repo per screen, similar to view model?
class CredentialManagerRepo(
@@ -48,7 +52,8 @@
intent: Intent,
) {
private val requestInfo: RequestInfo
- private val providerList: List<ProviderData>
+ private val providerEnabledList: List<ProviderData>
+ private val providerDisabledList: List<DisabledProviderData>
// TODO: require non-null.
val resultReceiver: ResultReceiver?
@@ -56,18 +61,18 @@
requestInfo = intent.extras?.getParcelable(
RequestInfo.EXTRA_REQUEST_INFO,
RequestInfo::class.java
- ) ?: testRequestInfo()
+ ) ?: testCreateRequestInfo()
- providerList = when (requestInfo.type) {
+ providerEnabledList = when (requestInfo.type) {
RequestInfo.TYPE_CREATE ->
intent.extras?.getParcelableArrayList(
ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST,
CreateCredentialProviderData::class.java
- ) ?: testCreateCredentialProviderList()
+ ) ?: testCreateCredentialEnabledProviderList()
RequestInfo.TYPE_GET ->
intent.extras?.getParcelableArrayList(
ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST,
- GetCredentialProviderData::class.java
+ DisabledProviderData::class.java
) ?: testGetCredentialProviderList()
else -> {
// TODO: fail gracefully
@@ -75,6 +80,12 @@
}
}
+ providerDisabledList =
+ intent.extras?.getParcelableArrayList(
+ ProviderData.EXTRA_DISABLED_PROVIDER_DATA_LIST,
+ DisabledProviderData::class.java
+ ) ?: testDisabledProviderList()
+
resultReceiver = intent.getParcelableExtra(
Constants.EXTRA_RESULT_RECEIVER,
ResultReceiver::class.java
@@ -100,30 +111,29 @@
}
fun getCredentialInitialUiState(): GetCredentialUiState {
- val providerList = GetFlowUtils.toProviderList(
- // TODO: handle runtime cast error
- providerList as List<GetCredentialProviderData>, context)
+ val providerEnabledList = GetFlowUtils.toProviderList(
+ // TODO: handle runtime cast error
+ providerEnabledList as List<GetCredentialProviderData>, context)
// TODO: covert from real requestInfo
- val requestDisplayInfo = com.android.credentialmanager.getflow.RequestDisplayInfo(
- "Elisa Beckett",
- "beckett-bakert@gmail.com",
- TYPE_PUBLIC_KEY_CREDENTIAL,
- "tribank")
+ val requestDisplayInfo = com.android.credentialmanager.getflow.RequestDisplayInfo("tribank")
return GetCredentialUiState(
- providerList,
- GetScreenState.CREDENTIAL_SELECTION,
+ providerEnabledList,
+ GetScreenState.PRIMARY_SELECTION,
requestDisplayInfo,
- providerList.first()
)
}
fun createPasskeyInitialUiState(): CreatePasskeyUiState {
- val providerList = CreateFlowUtils.toProviderList(
+ val providerEnabledList = CreateFlowUtils.toEnabledProviderList(
// Handle runtime cast error
- providerList as List<CreateCredentialProviderData>, context)
+ providerEnabledList as List<CreateCredentialProviderData>, context)
+ val providerDisabledList = CreateFlowUtils.toDisabledProviderList(
+ // Handle runtime cast error
+ providerDisabledList as List<DisabledProviderData>, context)
var hasDefault = false
- var defaultProvider: ProviderInfo = providerList.first()
- providerList.forEach{providerInfo ->
+ var defaultProvider: EnabledProviderInfo = providerEnabledList.first()
+ providerEnabledList.forEach{providerInfo -> providerInfo.createOptions =
+ providerInfo.createOptions.sortedWith(compareBy { it.lastUsedTimeMillis }).reversed()
if (providerInfo.isDefault) {hasDefault = true; defaultProvider = providerInfo} }
// TODO: covert from real requestInfo
val requestDisplayInfo = RequestDisplayInfo(
@@ -132,7 +142,8 @@
TYPE_PUBLIC_KEY_CREDENTIAL,
"tribank")
return CreatePasskeyUiState(
- providers = providerList,
+ enabledProviders = providerEnabledList,
+ disabledProviders = providerDisabledList,
if (hasDefault)
{CreateScreenState.CREATION_OPTION_SELECTION} else {CreateScreenState.PASSKEY_INTRO},
requestDisplayInfo,
@@ -158,82 +169,175 @@
}
// TODO: below are prototype functionalities. To be removed for productionization.
- private fun testCreateCredentialProviderList(): List<CreateCredentialProviderData> {
+ private fun testCreateCredentialEnabledProviderList(): List<CreateCredentialProviderData> {
return listOf(
CreateCredentialProviderData
- .Builder("com.google/com.google.CredentialManagerService")
+ .Builder("io.enpass.app")
.setSaveEntries(
listOf<Entry>(
- newEntry("key1", "subkey-1", "elisa.beckett@gmail.com",
+ newCreateEntry("key1", "subkey-1", "elisa.beckett@gmail.com",
20, 7, 27, 10000),
- newEntry("key1", "subkey-2", "elisa.work@google.com",
+ newCreateEntry("key1", "subkey-2", "elisa.work@google.com",
20, 7, 27, 11000),
)
)
- .setActionChips(
- listOf<Entry>(
- newEntry("key2", "subkey-1", "Go to Settings",
- 20, 7, 27, 20000),
- newEntry("key2", "subkey-2", "Switch Account",
- 20, 7, 27, 21000),
- ),
+ .setRemoteEntry(
+ newRemoteEntry("key1", "subkey-1")
)
- .setIsDefaultProvider(false)
+ .setIsDefaultProvider(true)
.build(),
CreateCredentialProviderData
- .Builder("com.dashlane/com.dashlane.CredentialManagerService")
+ .Builder("com.dashlane")
.setSaveEntries(
listOf<Entry>(
- newEntry("key1", "subkey-3", "elisa.beckett@dashlane.com",
+ newCreateEntry("key1", "subkey-3", "elisa.beckett@dashlane.com",
20, 7, 27, 30000),
- newEntry("key1", "subkey-4", "elisa.work@dashlane.com",
+ newCreateEntry("key1", "subkey-4", "elisa.work@dashlane.com",
20, 7, 27, 31000),
)
- ).setActionChips(
- listOf<Entry>(
- newEntry("key2", "subkey-3", "Manage Accounts",
- 20, 7, 27, 32000),
- ),
- ).build(),
+ )
+ .build(),
+ )
+ }
+
+ private fun testDisabledProviderList(): List<DisabledProviderData> {
+ return listOf(
+ DisabledProviderData("com.lastpass.lpandroid"),
+ DisabledProviderData("com.google.android.youtube")
)
}
private fun testGetCredentialProviderList(): List<GetCredentialProviderData> {
return listOf(
- GetCredentialProviderData.Builder("com.google/com.google.CredentialManagerService")
+ GetCredentialProviderData.Builder("io.enpass.app")
.setCredentialEntries(
listOf<Entry>(
- newEntry("key1", "subkey-1", "elisa.beckett@gmail.com",
- 20, 7, 27, 10000),
- newEntry("key1", "subkey-2", "elisa.work@google.com",
- 20, 7, 27, 11000),
+ newGetEntry(
+ "key1", "subkey-1", TYPE_PUBLIC_KEY_CREDENTIAL, "Passkey",
+ "elisa.bakery@gmail.com", "Elisa Beckett", 300L
+ ),
+ newGetEntry(
+ "key1", "subkey-2", TYPE_PASSWORD_CREDENTIAL, "Password",
+ "elisa.bakery@gmail.com", null, 300L
+ ),
+ newGetEntry(
+ "key1", "subkey-3", TYPE_PASSWORD_CREDENTIAL, "Password",
+ "elisa.family@outlook.com", null, 100L
+ ),
)
+ ).setAuthenticationEntry(
+ newAuthenticationEntry("key2", "subkey-1", TYPE_PASSWORD_CREDENTIAL)
).setActionChips(
- listOf<Entry>(
- newEntry("key2", "subkey-1", "Go to Settings",
- 20, 7, 27, 20000),
- newEntry("key2", "subkey-2", "Switch Account",
- 20, 7, 27, 21000),
- ),
+ listOf(
+ newActionEntry(
+ "key3", "subkey-1", TYPE_PASSWORD_CREDENTIAL,
+ Icon.createWithResource(context, R.drawable.ic_manage_accounts),
+ "Open Google Password Manager", "elisa.beckett@gmail.com"
+ ),
+ newActionEntry(
+ "key3", "subkey-2", TYPE_PASSWORD_CREDENTIAL,
+ Icon.createWithResource(context, R.drawable.ic_manage_accounts),
+ "Open Google Password Manager", "beckett-family@gmail.com"
+ ),
+ )
).build(),
- GetCredentialProviderData.Builder("com.dashlane/com.dashlane.CredentialManagerService")
+ GetCredentialProviderData.Builder("com.dashlane")
.setCredentialEntries(
listOf<Entry>(
- newEntry("key1", "subkey-3", "elisa.beckett@dashlane.com",
- 20, 7, 27, 30000),
- newEntry("key1", "subkey-4", "elisa.work@dashlane.com",
- 20, 7, 27, 31000),
+ newGetEntry(
+ "key1", "subkey-1", TYPE_PASSWORD_CREDENTIAL, "Password",
+ "elisa.family@outlook.com", null, 600L
+ ),
+ newGetEntry(
+ "key1", "subkey-2", TYPE_PUBLIC_KEY_CREDENTIAL, "Passkey",
+ "elisa.family@outlook.com", null, 100L
+ ),
)
+ ).setAuthenticationEntry(
+ newAuthenticationEntry("key2", "subkey-1", TYPE_PASSWORD_CREDENTIAL)
).setActionChips(
- listOf<Entry>(
- newEntry("key2", "subkey-3", "Manage Accounts",
- 20, 7, 27, 40000),
- ),
+ listOf(
+ newActionEntry(
+ "key3", "subkey-1", TYPE_PASSWORD_CREDENTIAL,
+ Icon.createWithResource(context, R.drawable.ic_face),
+ "Open Enpass"
+ ),
+ )
).build(),
)
}
- private fun newEntry(
+ private fun newActionEntry(
+ key: String,
+ subkey: String,
+ credentialType: String,
+ icon: Icon,
+ text: String,
+ subtext: String? = null,
+ ): Entry {
+ val slice = Slice.Builder(
+ Entry.CREDENTIAL_MANAGER_ENTRY_URI, SliceSpec(credentialType, 1)
+ ).addText(
+ text, null, listOf(Entry.HINT_ACTION_TITLE)
+ ).addIcon(icon, null, listOf(Entry.HINT_ACTION_ICON))
+ if (subtext != null) {
+ slice.addText(subtext, null, listOf(Entry.HINT_ACTION_SUBTEXT))
+ }
+ return Entry(
+ key,
+ subkey,
+ slice.build()
+ )
+ }
+
+ private fun newAuthenticationEntry(
+ key: String,
+ subkey: String,
+ credentialType: String,
+ ): Entry {
+ val slice = Slice.Builder(
+ Entry.CREDENTIAL_MANAGER_ENTRY_URI, SliceSpec(credentialType, 1)
+ )
+ return Entry(
+ key,
+ subkey,
+ slice.build()
+ )
+ }
+
+ private fun newGetEntry(
+ key: String,
+ subkey: String,
+ credentialType: String,
+ credentialTypeDisplayName: String,
+ userName: String,
+ userDisplayName: String?,
+ lastUsedTimeMillis: Long?,
+ ): Entry {
+ val slice = Slice.Builder(
+ Entry.CREDENTIAL_MANAGER_ENTRY_URI, SliceSpec(credentialType, 1)
+ ).addText(
+ credentialTypeDisplayName, null, listOf(Entry.HINT_CREDENTIAL_TYPE_DISPLAY_NAME)
+ ).addText(
+ userName, null, listOf(Entry.HINT_USER_NAME)
+ ).addIcon(
+ Icon.createWithResource(context, R.drawable.ic_passkey),
+ null,
+ listOf(Entry.HINT_PROFILE_ICON))
+ if (userDisplayName != null) {
+ slice.addText(userDisplayName, null, listOf(Entry.HINT_PASSKEY_USER_DISPLAY_NAME))
+ }
+ if (lastUsedTimeMillis != null) {
+ slice.addLong(lastUsedTimeMillis, null, listOf(Entry.HINT_LAST_USED_TIME_MILLIS))
+ }
+ return Entry(
+ key,
+ subkey,
+ slice.build()
+ )
+ }
+
+ private fun newCreateEntry(
key: String,
subkey: String,
providerDisplayName: String,
@@ -270,12 +374,24 @@
)
}
- private fun testRequestInfo(): RequestInfo {
+ private fun newRemoteEntry(
+ key: String,
+ subkey: String,
+ ): Entry {
+ return Entry(
+ key,
+ subkey,
+ Slice.Builder(
+ Entry.CREDENTIAL_MANAGER_ENTRY_URI, SliceSpec(Entry.VERSION, 1)
+ ).build()
+ )
+ }
+
+ private fun testCreateRequestInfo(): RequestInfo {
val data = Bundle()
return RequestInfo.newCreateRequestInfo(
Binder(),
CreateCredentialRequest(
- // TODO: use the jetpack type and utils once defined.
TYPE_PUBLIC_KEY_CREDENTIAL,
data
),
@@ -283,4 +399,18 @@
"tribank.us"
)
}
+
+ private fun testGetRequestInfo(): RequestInfo {
+ val data = Bundle()
+ return RequestInfo.newGetRequestInfo(
+ Binder(),
+ GetCredentialRequest.Builder()
+ .addGetCredentialOption(
+ GetCredentialOption(TYPE_PUBLIC_KEY_CREDENTIAL, Bundle())
+ )
+ .build(),
+ /*isFirstUsage=*/false,
+ "tribank.us"
+ )
+ }
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index 3a8e975..33fb154 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -17,12 +17,20 @@
package com.android.credentialmanager
import android.content.Context
+import android.content.pm.PackageManager
import android.credentials.ui.Entry
import android.credentials.ui.GetCredentialProviderData
import android.credentials.ui.CreateCredentialProviderData
+import android.credentials.ui.DisabledProviderData
+import android.graphics.drawable.Drawable
import com.android.credentialmanager.createflow.CreateOptionInfo
-import com.android.credentialmanager.getflow.CredentialOptionInfo
+import com.android.credentialmanager.createflow.RemoteInfo
+import com.android.credentialmanager.getflow.ActionEntryInfo
+import com.android.credentialmanager.getflow.AuthenticationEntryInfo
+import com.android.credentialmanager.getflow.CredentialEntryInfo
import com.android.credentialmanager.getflow.ProviderInfo
+import com.android.credentialmanager.jetpack.provider.ActionUi
+import com.android.credentialmanager.jetpack.provider.CredentialEntryUi
import com.android.credentialmanager.jetpack.provider.SaveEntryUi
/** Utility functions for converting CredentialManager data structures to or from UI formats. */
@@ -33,36 +41,96 @@
providerDataList: List<GetCredentialProviderData>,
context: Context,
): List<ProviderInfo> {
+ val packageManager = context.packageManager
return providerDataList.map {
+ // TODO: get from the actual service info
+ val pkgInfo = packageManager
+ .getPackageInfo(it.providerFlattenedComponentName,
+ PackageManager.PackageInfoFlags.of(0))
+ val providerDisplayName = pkgInfo.applicationInfo.loadLabel(packageManager).toString()
+ // TODO: decide what to do when failed to load a provider icon
+ val providerIcon = pkgInfo.applicationInfo.loadIcon(packageManager)!!
ProviderInfo(
- // TODO: replace to extract from the service data structure when available
- icon = context.getDrawable(R.drawable.ic_passkey)!!,
- name = it.providerFlattenedComponentName,
- // TODO: get the service display name and icon from the component name.
- displayName = it.providerFlattenedComponentName,
- credentialTypeIcon = context.getDrawable(R.drawable.ic_passkey)!!,
- credentialOptions = toCredentialOptionInfoList(it.credentialEntries, context),
+ id = it.providerFlattenedComponentName,
+ // TODO: decide what to do when failed to load a provider icon
+ icon = providerIcon,
+ displayName = providerDisplayName,
+ credentialEntryList = getCredentialOptionInfoList(
+ it.providerFlattenedComponentName, it.credentialEntries, context),
+ authenticationEntry = getAuthenticationEntry(
+ it.providerFlattenedComponentName,
+ providerDisplayName,
+ providerIcon,
+ it.authenticationEntry),
+ actionEntryList = getActionEntryList(
+ it.providerFlattenedComponentName, it.actionChips, context),
)
}
}
/* From service data structure to UI credential entry list representation. */
- private fun toCredentialOptionInfoList(
+ private fun getCredentialOptionInfoList(
+ providerId: String,
credentialEntries: List<Entry>,
context: Context,
- ): List<CredentialOptionInfo> {
+ ): List<CredentialEntryInfo> {
return credentialEntries.map {
val credentialEntryUi = CredentialEntryUi.fromSlice(it.slice)
// Consider directly move the UI object into the class.
- return@map CredentialOptionInfo(
- // TODO: remove fallbacks
- icon = credentialEntryUi.icon?.loadDrawable(context)
- ?: context.getDrawable(R.drawable.ic_passkey)!!,
+ return@map CredentialEntryInfo(
+ providerId = providerId,
entryKey = it.key,
entrySubkey = it.subkey,
- usageData = credentialEntryUi.usageData?.toString() ?: "Unknown usageData",
+ credentialType = credentialEntryUi.credentialType.toString(),
+ credentialTypeDisplayName = credentialEntryUi.credentialTypeDisplayName.toString(),
+ userName = credentialEntryUi.userName.toString(),
+ displayName = credentialEntryUi.userDisplayName?.toString(),
+ // TODO: proper fallback
+ icon = credentialEntryUi.entryIcon.loadDrawable(context)
+ ?: context.getDrawable(R.drawable.ic_passkey)!!,
+ lastUsedTimeMillis = credentialEntryUi.lastUsedTimeMillis,
+ )
+ }
+ }
+
+ private fun getAuthenticationEntry(
+ providerId: String,
+ providerDisplayName: String,
+ providerIcon: Drawable,
+ authEntry: Entry?,
+ ): AuthenticationEntryInfo? {
+ // TODO: should also call fromSlice after getting the official jetpack code.
+
+ if (authEntry == null) {
+ return null
+ }
+ return AuthenticationEntryInfo(
+ providerId = providerId,
+ entryKey = authEntry.key,
+ entrySubkey = authEntry.subkey,
+ title = providerDisplayName,
+ icon = providerIcon,
+ )
+ }
+
+ private fun getActionEntryList(
+ providerId: String,
+ actionEntries: List<Entry>,
+ context: Context,
+ ): List<ActionEntryInfo> {
+ return actionEntries.map {
+ val actionEntryUi = ActionUi.fromSlice(it.slice)
+
+ return@map ActionEntryInfo(
+ providerId = providerId,
+ entryKey = it.key,
+ entrySubkey = it.subkey,
+ title = actionEntryUi.text.toString(),
+ // TODO: gracefully fail
+ icon = actionEntryUi.icon.loadDrawable(context)!!,
+ subTitle = actionEntryUi.subtext?.toString(),
)
}
}
@@ -72,18 +140,42 @@
class CreateFlowUtils {
companion object {
- fun toProviderList(
+ fun toEnabledProviderList(
providerDataList: List<CreateCredentialProviderData>,
context: Context,
- ): List<com.android.credentialmanager.createflow.ProviderInfo> {
+ ): List<com.android.credentialmanager.createflow.EnabledProviderInfo> {
+ // TODO: get from the actual service info
+ val packageManager = context.packageManager
return providerDataList.map {
- com.android.credentialmanager.createflow.ProviderInfo(
- // TODO: replace to extract from the service data structure when available
- icon = context.getDrawable(R.drawable.ic_passkey)!!,
+ val pkgInfo = packageManager
+ .getPackageInfo(it.providerFlattenedComponentName,
+ PackageManager.PackageInfoFlags.of(0))
+ com.android.credentialmanager.createflow.EnabledProviderInfo(
+ // TODO: decide what to do when failed to load a provider icon
+ icon = pkgInfo.applicationInfo.loadIcon(packageManager)!!,
name = it.providerFlattenedComponentName,
- displayName = it.providerFlattenedComponentName,
+ displayName = pkgInfo.applicationInfo.loadLabel(packageManager).toString(),
createOptions = toCreationOptionInfoList(it.saveEntries, context),
isDefault = it.isDefaultProvider,
+ remoteEntry = toRemoteInfo(it.remoteEntry),
+ )
+ }
+ }
+
+ fun toDisabledProviderList(
+ providerDataList: List<DisabledProviderData>,
+ context: Context,
+ ): List<com.android.credentialmanager.createflow.DisabledProviderInfo> {
+ // TODO: get from the actual service info
+ val packageManager = context.packageManager
+ return providerDataList.map {
+ val pkgInfo = packageManager
+ .getPackageInfo(it.providerFlattenedComponentName,
+ PackageManager.PackageInfoFlags.of(0))
+ com.android.credentialmanager.createflow.DisabledProviderInfo(
+ icon = pkgInfo.applicationInfo.loadIcon(packageManager)!!,
+ name = it.providerFlattenedComponentName,
+ displayName = pkgInfo.applicationInfo.loadLabel(packageManager).toString(),
)
}
}
@@ -111,5 +203,17 @@
)
}
}
+
+ private fun toRemoteInfo(
+ remoteEntry: Entry?,
+ ): RemoteInfo? {
+ // TODO: should also call fromSlice after getting the official jetpack code.
+ return if (remoteEntry != null) {
+ RemoteInfo(
+ entryKey = remoteEntry.key,
+ entrySubkey = remoteEntry.subkey,
+ )
+ } else null
+ }
}
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/material/ModalBottomSheet.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/material/ModalBottomSheet.kt
new file mode 100644
index 0000000..f1f453d
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/material/ModalBottomSheet.kt
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.common.material
+
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.TweenSpec
+import androidx.compose.animation.core.animateFloatAsState
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.detectTapGestures
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxWithConstraints
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.offset
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.contentColorFor
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.saveable.Saver
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.isSpecified
+import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.semantics.collapse
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.dismiss
+import androidx.compose.ui.semantics.expand
+import androidx.compose.ui.semantics.onClick
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.dp
+import com.android.credentialmanager.R
+import com.android.credentialmanager.common.material.ModalBottomSheetValue.Expanded
+import com.android.credentialmanager.common.material.ModalBottomSheetValue.HalfExpanded
+import com.android.credentialmanager.common.material.ModalBottomSheetValue.Hidden
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.launch
+import kotlin.math.max
+import kotlin.math.roundToInt
+
+/**
+ * Possible values of [ModalBottomSheetState].
+ */
+enum class ModalBottomSheetValue {
+ /**
+ * The bottom sheet is not visible.
+ */
+ Hidden,
+
+ /**
+ * The bottom sheet is visible at full height.
+ */
+ Expanded,
+
+ /**
+ * The bottom sheet is partially visible at 50% of the screen height. This state is only
+ * enabled if the height of the bottom sheet is more than 50% of the screen height.
+ */
+ HalfExpanded
+}
+
+/**
+ * State of the [ModalBottomSheetLayout] composable.
+ *
+ * @param initialValue The initial value of the state. <b>Must not be set to
+ * [ModalBottomSheetValue.HalfExpanded] if [isSkipHalfExpanded] is set to true.</b>
+ * @param animationSpec The default animation that will be used to animate to a new state.
+ * @param isSkipHalfExpanded Whether the half expanded state, if the sheet is tall enough, should
+ * be skipped. If true, the sheet will always expand to the [Expanded] state and move to the
+ * [Hidden] state when hiding the sheet, either programmatically or by user interaction.
+ * <b>Must not be set to true if the [initialValue] is [ModalBottomSheetValue.HalfExpanded].</b>
+ * If supplied with [ModalBottomSheetValue.HalfExpanded] for the [initialValue], an
+ * [IllegalArgumentException] will be thrown.
+ * @param confirmStateChange Optional callback invoked to confirm or veto a pending state change.
+ */
+class ModalBottomSheetState(
+ initialValue: ModalBottomSheetValue,
+ animationSpec: AnimationSpec<Float> = SwipeableDefaults.AnimationSpec,
+ internal val isSkipHalfExpanded: Boolean,
+ confirmStateChange: (ModalBottomSheetValue) -> Boolean = { true }
+) : SwipeableState<ModalBottomSheetValue>(
+ initialValue = initialValue,
+ animationSpec = animationSpec,
+ confirmStateChange = confirmStateChange
+) {
+ /**
+ * Whether the bottom sheet is visible.
+ */
+ val isVisible: Boolean
+ get() = currentValue != Hidden
+
+ internal val hasHalfExpandedState: Boolean
+ get() = anchors.values.contains(HalfExpanded)
+
+ constructor(
+ initialValue: ModalBottomSheetValue,
+ animationSpec: AnimationSpec<Float> = SwipeableDefaults.AnimationSpec,
+ confirmStateChange: (ModalBottomSheetValue) -> Boolean = { true }
+ ) : this(initialValue, animationSpec, isSkipHalfExpanded = false, confirmStateChange)
+
+ init {
+ if (isSkipHalfExpanded) {
+ require(initialValue != HalfExpanded) {
+ "The initial value must not be set to HalfExpanded if skipHalfExpanded is set to" +
+ " true."
+ }
+ }
+ }
+
+ /**
+ * Show the bottom sheet with animation and suspend until it's shown. If the sheet is taller
+ * than 50% of the parent's height, the bottom sheet will be half expanded. Otherwise it will be
+ * fully expanded.
+ *
+ * @throws [CancellationException] if the animation is interrupted
+ */
+ suspend fun show() {
+ val targetValue = when {
+ hasHalfExpandedState -> HalfExpanded
+ else -> Expanded
+ }
+ animateTo(targetValue = targetValue)
+ }
+
+ /**
+ * Half expand the bottom sheet if half expand is enabled with animation and suspend until it
+ * animation is complete or cancelled
+ *
+ * @throws [CancellationException] if the animation is interrupted
+ */
+ internal suspend fun halfExpand() {
+ if (!hasHalfExpandedState) {
+ return
+ }
+ animateTo(HalfExpanded)
+ }
+
+ /**
+ * Fully expand the bottom sheet with animation and suspend until it if fully expanded or
+ * animation has been cancelled.
+ * *
+ * @throws [CancellationException] if the animation is interrupted
+ */
+ internal suspend fun expand() = animateTo(Expanded)
+
+ /**
+ * Hide the bottom sheet with animation and suspend until it if fully hidden or animation has
+ * been cancelled.
+ *
+ * @throws [CancellationException] if the animation is interrupted
+ */
+ suspend fun hide() = animateTo(Hidden)
+
+ internal val nestedScrollConnection = this.PreUpPostDownNestedScrollConnection
+
+ companion object {
+ /**
+ * The default [Saver] implementation for [ModalBottomSheetState].
+ */
+ fun Saver(
+ animationSpec: AnimationSpec<Float>,
+ skipHalfExpanded: Boolean,
+ confirmStateChange: (ModalBottomSheetValue) -> Boolean
+ ): Saver<ModalBottomSheetState, *> = Saver(
+ save = { it.currentValue },
+ restore = {
+ ModalBottomSheetState(
+ initialValue = it,
+ animationSpec = animationSpec,
+ isSkipHalfExpanded = skipHalfExpanded,
+ confirmStateChange = confirmStateChange
+ )
+ }
+ )
+
+ /**
+ * The default [Saver] implementation for [ModalBottomSheetState].
+ */
+ @Deprecated(
+ message = "Please specify the skipHalfExpanded parameter",
+ replaceWith = ReplaceWith(
+ "ModalBottomSheetState.Saver(" +
+ "animationSpec = animationSpec," +
+ "skipHalfExpanded = ," +
+ "confirmStateChange = confirmStateChange" +
+ ")"
+ )
+ )
+ fun Saver(
+ animationSpec: AnimationSpec<Float>,
+ confirmStateChange: (ModalBottomSheetValue) -> Boolean
+ ): Saver<ModalBottomSheetState, *> = Saver(
+ animationSpec = animationSpec,
+ skipHalfExpanded = false,
+ confirmStateChange = confirmStateChange
+ )
+ }
+}
+
+/**
+ * Create a [ModalBottomSheetState] and [remember] it.
+ *
+ * @param initialValue The initial value of the state.
+ * @param animationSpec The default animation that will be used to animate to a new state.
+ * @param skipHalfExpanded Whether the half expanded state, if the sheet is tall enough, should
+ * be skipped. If true, the sheet will always expand to the [Expanded] state and move to the
+ * [Hidden] state when hiding the sheet, either programmatically or by user interaction.
+ * <b>Must not be set to true if the [initialValue] is [ModalBottomSheetValue.HalfExpanded].</b>
+ * If supplied with [ModalBottomSheetValue.HalfExpanded] for the [initialValue], an
+ * [IllegalArgumentException] will be thrown.
+ * @param confirmStateChange Optional callback invoked to confirm or veto a pending state change.
+ */
+@Composable
+fun rememberModalBottomSheetState(
+ initialValue: ModalBottomSheetValue,
+ animationSpec: AnimationSpec<Float> = SwipeableDefaults.AnimationSpec,
+ skipHalfExpanded: Boolean,
+ confirmStateChange: (ModalBottomSheetValue) -> Boolean = { true }
+): ModalBottomSheetState {
+ return rememberSaveable(
+ initialValue, animationSpec, skipHalfExpanded, confirmStateChange,
+ saver = ModalBottomSheetState.Saver(
+ animationSpec = animationSpec,
+ skipHalfExpanded = skipHalfExpanded,
+ confirmStateChange = confirmStateChange
+ )
+ ) {
+ ModalBottomSheetState(
+ initialValue = initialValue,
+ animationSpec = animationSpec,
+ isSkipHalfExpanded = skipHalfExpanded,
+ confirmStateChange = confirmStateChange
+ )
+ }
+}
+
+/**
+ * Create a [ModalBottomSheetState] and [remember] it.
+ *
+ * @param initialValue The initial value of the state.
+ * @param animationSpec The default animation that will be used to animate to a new state.
+ * @param confirmStateChange Optional callback invoked to confirm or veto a pending state change.
+ */
+@Composable
+fun rememberModalBottomSheetState(
+ initialValue: ModalBottomSheetValue,
+ animationSpec: AnimationSpec<Float> = SwipeableDefaults.AnimationSpec,
+ confirmStateChange: (ModalBottomSheetValue) -> Boolean = { true }
+): ModalBottomSheetState = rememberModalBottomSheetState(
+ initialValue = initialValue,
+ animationSpec = animationSpec,
+ skipHalfExpanded = false,
+ confirmStateChange = confirmStateChange
+)
+
+/**
+ * <a href="https://material.io/components/sheets-bottom#modal-bottom-sheet" class="external" target="_blank">Material Design modal bottom sheet</a>.
+ *
+ * Modal bottom sheets present a set of choices while blocking interaction with the rest of the
+ * screen. They are an alternative to inline menus and simple dialogs, providing
+ * additional room for content, iconography, and actions.
+ *
+ * 
+ *
+ * A simple example of a modal bottom sheet looks like this:
+ *
+ * @sample androidx.compose.material.samples.ModalBottomSheetSample
+ *
+ * @param sheetContent The content of the bottom sheet.
+ * @param modifier Optional [Modifier] for the entire component.
+ * @param sheetState The state of the bottom sheet.
+ * @param sheetShape The shape of the bottom sheet.
+ * @param sheetElevation The elevation of the bottom sheet.
+ * @param sheetBackgroundColor The background color of the bottom sheet.
+ * @param sheetContentColor The preferred content color provided by the bottom sheet to its
+ * children. Defaults to the matching content color for [sheetBackgroundColor], or if that is not
+ * a color from the theme, this will keep the same content color set above the bottom sheet.
+ * @param scrimColor The color of the scrim that is applied to the rest of the screen when the
+ * bottom sheet is visible. If the color passed is [Color.Unspecified], then a scrim will no
+ * longer be applied and the bottom sheet will not block interaction with the rest of the screen
+ * when visible.
+ * @param content The content of rest of the screen.
+ */
+@Composable
+fun ModalBottomSheetLayout(
+ sheetContent: @Composable ColumnScope.() -> Unit,
+ modifier: Modifier = Modifier,
+ sheetState: ModalBottomSheetState =
+ rememberModalBottomSheetState(Hidden),
+ sheetShape: Shape = MaterialTheme.shapes.large,
+ sheetElevation: Dp = ModalBottomSheetDefaults.Elevation,
+ sheetBackgroundColor: Color = MaterialTheme.colorScheme.surface,
+ sheetContentColor: Color = contentColorFor(sheetBackgroundColor),
+ scrimColor: Color = ModalBottomSheetDefaults.scrimColor,
+ content: @Composable () -> Unit
+) {
+ val scope = rememberCoroutineScope()
+ BoxWithConstraints(modifier) {
+ val fullHeight = constraints.maxHeight.toFloat()
+ val sheetHeightState = remember { mutableStateOf<Float?>(null) }
+ Box(Modifier.fillMaxSize()) {
+ content()
+ Scrim(
+ color = scrimColor,
+ onDismiss = {
+ if (sheetState.confirmStateChange(Hidden)) {
+ scope.launch { sheetState.hide() }
+ }
+ },
+ visible = sheetState.targetValue != Hidden
+ )
+ }
+ Surface(
+ Modifier
+ .fillMaxWidth()
+ .nestedScroll(sheetState.nestedScrollConnection)
+ .offset {
+ val y = if (sheetState.anchors.isEmpty()) {
+ // if we don't know our anchors yet, render the sheet as hidden
+ fullHeight.roundToInt()
+ } else {
+ // if we do know our anchors, respect them
+ sheetState.offset.value.roundToInt()
+ }
+ IntOffset(0, y)
+ }
+ .bottomSheetSwipeable(sheetState, fullHeight, sheetHeightState)
+ .onGloballyPositioned {
+ sheetHeightState.value = it.size.height.toFloat()
+ }
+ .semantics {
+ if (sheetState.isVisible) {
+ dismiss {
+ if (sheetState.confirmStateChange(Hidden)) {
+ scope.launch { sheetState.hide() }
+ }
+ true
+ }
+ if (sheetState.currentValue == HalfExpanded) {
+ expand {
+ if (sheetState.confirmStateChange(Expanded)) {
+ scope.launch { sheetState.expand() }
+ }
+ true
+ }
+ } else if (sheetState.hasHalfExpandedState) {
+ collapse {
+ if (sheetState.confirmStateChange(HalfExpanded)) {
+ scope.launch { sheetState.halfExpand() }
+ }
+ true
+ }
+ }
+ }
+ },
+ shape = sheetShape,
+ shadowElevation = sheetElevation,
+ color = sheetBackgroundColor,
+ contentColor = sheetContentColor
+ ) {
+ Column(content = sheetContent)
+ }
+ }
+}
+
+@Suppress("ModifierInspectorInfo")
+private fun Modifier.bottomSheetSwipeable(
+ sheetState: ModalBottomSheetState,
+ fullHeight: Float,
+ sheetHeightState: State<Float?>
+): Modifier {
+ val sheetHeight = sheetHeightState.value
+ val modifier = if (sheetHeight != null) {
+ val anchors = if (sheetHeight < fullHeight / 2 || sheetState.isSkipHalfExpanded) {
+ mapOf(
+ fullHeight to Hidden,
+ fullHeight - sheetHeight to Expanded
+ )
+ } else {
+ mapOf(
+ fullHeight to Hidden,
+ fullHeight / 2 to HalfExpanded,
+ max(0f, fullHeight - sheetHeight) to Expanded
+ )
+ }
+ Modifier.swipeable(
+ state = sheetState,
+ anchors = anchors,
+ orientation = Orientation.Vertical,
+ enabled = sheetState.currentValue != Hidden,
+ resistance = null
+ )
+ } else {
+ Modifier
+ }
+
+ return this.then(modifier)
+}
+
+@Composable
+private fun Scrim(
+ color: Color,
+ onDismiss: () -> Unit,
+ visible: Boolean
+) {
+ if (color.isSpecified) {
+ val alpha by animateFloatAsState(
+ targetValue = if (visible) 1f else 0f,
+ animationSpec = TweenSpec()
+ )
+ LocalConfiguration.current
+ val resources = LocalContext.current.resources
+ val closeSheet = resources.getString(R.string.close_sheet)
+ val dismissModifier = if (visible) {
+ Modifier
+ .pointerInput(onDismiss) { detectTapGestures { onDismiss() } }
+ .semantics(mergeDescendants = true) {
+ contentDescription = closeSheet
+ onClick { onDismiss(); true }
+ }
+ } else {
+ Modifier
+ }
+
+ Canvas(
+ Modifier
+ .fillMaxSize()
+ .then(dismissModifier)
+ ) {
+ drawRect(color = color, alpha = alpha)
+ }
+ }
+}
+
+/**
+ * Contains useful Defaults for [ModalBottomSheetLayout].
+ */
+object ModalBottomSheetDefaults {
+
+ /**
+ * The default elevation used by [ModalBottomSheetLayout].
+ */
+ val Elevation = 16.dp
+
+ /**
+ * The default scrim color used by [ModalBottomSheetLayout].
+ */
+ val scrimColor: Color
+ @Composable
+ get() = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.32f)
+}
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/material/Swipeable.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/material/Swipeable.kt
new file mode 100644
index 0000000..3e2de83
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/material/Swipeable.kt
@@ -0,0 +1,875 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.common.material
+
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.SpringSpec
+import androidx.compose.foundation.gestures.DraggableState
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.draggable
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.Saver
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshotFlow
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.composed
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.debugInspectorInfo
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.Velocity
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.lerp
+import com.android.credentialmanager.common.material.SwipeableDefaults.AnimationSpec
+import com.android.credentialmanager.common.material.SwipeableDefaults.StandardResistanceFactor
+import com.android.credentialmanager.common.material.SwipeableDefaults.VelocityThreshold
+import com.android.credentialmanager.common.material.SwipeableDefaults.resistanceConfig
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.take
+import kotlinx.coroutines.launch
+import kotlin.math.PI
+import kotlin.math.abs
+import kotlin.math.sign
+import kotlin.math.sin
+
+/**
+ * State of the [swipeable] modifier.
+ *
+ * This contains necessary information about any ongoing swipe or animation and provides methods
+ * to change the state either immediately or by starting an animation. To create and remember a
+ * [SwipeableState] with the default animation clock, use [rememberSwipeableState].
+ *
+ * @param initialValue The initial value of the state.
+ * @param animationSpec The default animation that will be used to animate to a new state.
+ * @param confirmStateChange Optional callback invoked to confirm or veto a pending state change.
+ */
+@Stable
+open class SwipeableState<T>(
+ initialValue: T,
+ internal val animationSpec: AnimationSpec<Float> = AnimationSpec,
+ internal val confirmStateChange: (newValue: T) -> Boolean = { true }
+) {
+ /**
+ * The current value of the state.
+ *
+ * If no swipe or animation is in progress, this corresponds to the anchor at which the
+ * [swipeable] is currently settled. If a swipe or animation is in progress, this corresponds
+ * the last anchor at which the [swipeable] was settled before the swipe or animation started.
+ */
+ var currentValue: T by mutableStateOf(initialValue)
+ private set
+
+ /**
+ * Whether the state is currently animating.
+ */
+ var isAnimationRunning: Boolean by mutableStateOf(false)
+ private set
+
+ /**
+ * The current position (in pixels) of the [swipeable].
+ *
+ * You should use this state to offset your content accordingly. The recommended way is to
+ * use `Modifier.offsetPx`. This includes the resistance by default, if resistance is enabled.
+ */
+ val offset: State<Float> get() = offsetState
+
+ /**
+ * The amount by which the [swipeable] has been swiped past its bounds.
+ */
+ val overflow: State<Float> get() = overflowState
+
+ // Use `Float.NaN` as a placeholder while the state is uninitialised.
+ private val offsetState = mutableStateOf(0f)
+ private val overflowState = mutableStateOf(0f)
+
+ // the source of truth for the "real"(non ui) position
+ // basically position in bounds + overflow
+ private val absoluteOffset = mutableStateOf(0f)
+
+ // current animation target, if animating, otherwise null
+ private val animationTarget = mutableStateOf<Float?>(null)
+
+ internal var anchors by mutableStateOf(emptyMap<Float, T>())
+
+ private val latestNonEmptyAnchorsFlow: Flow<Map<Float, T>> =
+ snapshotFlow { anchors }
+ .filter { it.isNotEmpty() }
+ .take(1)
+
+ internal var minBound = Float.NEGATIVE_INFINITY
+ internal var maxBound = Float.POSITIVE_INFINITY
+
+ internal fun ensureInit(newAnchors: Map<Float, T>) {
+ if (anchors.isEmpty()) {
+ // need to do initial synchronization synchronously :(
+ val initialOffset = newAnchors.getOffset(currentValue)
+ requireNotNull(initialOffset) {
+ "The initial value must have an associated anchor."
+ }
+ offsetState.value = initialOffset
+ absoluteOffset.value = initialOffset
+ }
+ }
+
+ internal suspend fun processNewAnchors(
+ oldAnchors: Map<Float, T>,
+ newAnchors: Map<Float, T>
+ ) {
+ if (oldAnchors.isEmpty()) {
+ // If this is the first time that we receive anchors, then we need to initialise
+ // the state so we snap to the offset associated to the initial value.
+ minBound = newAnchors.keys.minOrNull()!!
+ maxBound = newAnchors.keys.maxOrNull()!!
+ val initialOffset = newAnchors.getOffset(currentValue)
+ requireNotNull(initialOffset) {
+ "The initial value must have an associated anchor."
+ }
+ snapInternalToOffset(initialOffset)
+ } else if (newAnchors != oldAnchors) {
+ // If we have received new anchors, then the offset of the current value might
+ // have changed, so we need to animate to the new offset. If the current value
+ // has been removed from the anchors then we animate to the closest anchor
+ // instead. Note that this stops any ongoing animation.
+ minBound = Float.NEGATIVE_INFINITY
+ maxBound = Float.POSITIVE_INFINITY
+ val animationTargetValue = animationTarget.value
+ // if we're in the animation already, let's find it a new home
+ val targetOffset = if (animationTargetValue != null) {
+ // first, try to map old state to the new state
+ val oldState = oldAnchors[animationTargetValue]
+ val newState = newAnchors.getOffset(oldState)
+ // return new state if exists, or find the closes one among new anchors
+ newState ?: newAnchors.keys.minByOrNull { abs(it - animationTargetValue) }!!
+ } else {
+ // we're not animating, proceed by finding the new anchors for an old value
+ val actualOldValue = oldAnchors[offset.value]
+ val value = if (actualOldValue == currentValue) currentValue else actualOldValue
+ newAnchors.getOffset(value) ?: newAnchors
+ .keys.minByOrNull { abs(it - offset.value) }!!
+ }
+ try {
+ animateInternalToOffset(targetOffset, animationSpec)
+ } catch (c: CancellationException) {
+ // If the animation was interrupted for any reason, snap as a last resort.
+ snapInternalToOffset(targetOffset)
+ } finally {
+ currentValue = newAnchors.getValue(targetOffset)
+ minBound = newAnchors.keys.minOrNull()!!
+ maxBound = newAnchors.keys.maxOrNull()!!
+ }
+ }
+ }
+
+ internal var thresholds: (Float, Float) -> Float by mutableStateOf({ _, _ -> 0f })
+
+ internal var velocityThreshold by mutableStateOf(0f)
+
+ internal var resistance: ResistanceConfig? by mutableStateOf(null)
+
+ internal val draggableState = DraggableState {
+ val newAbsolute = absoluteOffset.value + it
+ val clamped = newAbsolute.coerceIn(minBound, maxBound)
+ val overflow = newAbsolute - clamped
+ val resistanceDelta = resistance?.computeResistance(overflow) ?: 0f
+ offsetState.value = clamped + resistanceDelta
+ overflowState.value = overflow
+ absoluteOffset.value = newAbsolute
+ }
+
+ private suspend fun snapInternalToOffset(target: Float) {
+ draggableState.drag {
+ dragBy(target - absoluteOffset.value)
+ }
+ }
+
+ private suspend fun animateInternalToOffset(target: Float, spec: AnimationSpec<Float>) {
+ draggableState.drag {
+ var prevValue = absoluteOffset.value
+ animationTarget.value = target
+ isAnimationRunning = true
+ try {
+ Animatable(prevValue).animateTo(target, spec) {
+ dragBy(this.value - prevValue)
+ prevValue = this.value
+ }
+ } finally {
+ animationTarget.value = null
+ isAnimationRunning = false
+ }
+ }
+ }
+
+ /**
+ * The target value of the state.
+ *
+ * If a swipe is in progress, this is the value that the [swipeable] would animate to if the
+ * swipe finished. If an animation is running, this is the target value of that animation.
+ * Finally, if no swipe or animation is in progress, this is the same as the [currentValue].
+ */
+ val targetValue: T
+ get() {
+ // TODO(calintat): Track current velocity (b/149549482) and use that here.
+ val target = animationTarget.value ?: computeTarget(
+ offset = offset.value,
+ lastValue = anchors.getOffset(currentValue) ?: offset.value,
+ anchors = anchors.keys,
+ thresholds = thresholds,
+ velocity = 0f,
+ velocityThreshold = Float.POSITIVE_INFINITY
+ )
+ return anchors[target] ?: currentValue
+ }
+
+ /**
+ * Information about the ongoing swipe or animation, if any. See [SwipeProgress] for details.
+ *
+ * If no swipe or animation is in progress, this returns `SwipeProgress(value, value, 1f)`.
+ */
+ val progress: SwipeProgress<T>
+ get() {
+ val bounds = findBounds(offset.value, anchors.keys)
+ val from: T
+ val to: T
+ val fraction: Float
+ when (bounds.size) {
+ 0 -> {
+ from = currentValue
+ to = currentValue
+ fraction = 1f
+ }
+ 1 -> {
+ from = anchors.getValue(bounds[0])
+ to = anchors.getValue(bounds[0])
+ fraction = 1f
+ }
+ else -> {
+ val (a, b) =
+ if (direction > 0f) {
+ bounds[0] to bounds[1]
+ } else {
+ bounds[1] to bounds[0]
+ }
+ from = anchors.getValue(a)
+ to = anchors.getValue(b)
+ fraction = (offset.value - a) / (b - a)
+ }
+ }
+ return SwipeProgress(from, to, fraction)
+ }
+
+ /**
+ * The direction in which the [swipeable] is moving, relative to the current [currentValue].
+ *
+ * This will be either 1f if it is is moving from left to right or top to bottom, -1f if it is
+ * moving from right to left or bottom to top, or 0f if no swipe or animation is in progress.
+ */
+ val direction: Float
+ get() = anchors.getOffset(currentValue)?.let { sign(offset.value - it) } ?: 0f
+
+ /**
+ * Set the state without any animation and suspend until it's set
+ *
+ * @param targetValue The new target value to set [currentValue] to.
+ */
+ suspend fun snapTo(targetValue: T) {
+ latestNonEmptyAnchorsFlow.collect { anchors ->
+ val targetOffset = anchors.getOffset(targetValue)
+ requireNotNull(targetOffset) {
+ "The target value must have an associated anchor."
+ }
+ snapInternalToOffset(targetOffset)
+ currentValue = targetValue
+ }
+ }
+
+ /**
+ * Set the state to the target value by starting an animation.
+ *
+ * @param targetValue The new value to animate to.
+ * @param anim The animation that will be used to animate to the new value.
+ */
+ suspend fun animateTo(targetValue: T, anim: AnimationSpec<Float> = animationSpec) {
+ latestNonEmptyAnchorsFlow.collect { anchors ->
+ try {
+ val targetOffset = anchors.getOffset(targetValue)
+ requireNotNull(targetOffset) {
+ "The target value must have an associated anchor."
+ }
+ animateInternalToOffset(targetOffset, anim)
+ } finally {
+ val endOffset = absoluteOffset.value
+ val endValue = anchors
+ // fighting rounding error once again, anchor should be as close as 0.5 pixels
+ .filterKeys { anchorOffset -> abs(anchorOffset - endOffset) < 0.5f }
+ .values.firstOrNull() ?: currentValue
+ currentValue = endValue
+ }
+ }
+ }
+
+ /**
+ * Perform fling with settling to one of the anchors which is determined by the given
+ * [velocity]. Fling with settling [swipeable] will always consume all the velocity provided
+ * since it will settle at the anchor.
+ *
+ * In general cases, [swipeable] flings by itself when being swiped. This method is to be
+ * used for nested scroll logic that wraps the [swipeable]. In nested scroll developer may
+ * want to trigger settling fling when the child scroll container reaches the bound.
+ *
+ * @param velocity velocity to fling and settle with
+ *
+ * @return the reason fling ended
+ */
+ suspend fun performFling(velocity: Float) {
+ latestNonEmptyAnchorsFlow.collect { anchors ->
+ val lastAnchor = anchors.getOffset(currentValue)!!
+ val targetValue = computeTarget(
+ offset = offset.value,
+ lastValue = lastAnchor,
+ anchors = anchors.keys,
+ thresholds = thresholds,
+ velocity = velocity,
+ velocityThreshold = velocityThreshold
+ )
+ val targetState = anchors[targetValue]
+ if (targetState != null && confirmStateChange(targetState)) animateTo(targetState)
+ // If the user vetoed the state change, rollback to the previous state.
+ else animateInternalToOffset(lastAnchor, animationSpec)
+ }
+ }
+
+ /**
+ * Force [swipeable] to consume drag delta provided from outside of the regular [swipeable]
+ * gesture flow.
+ *
+ * Note: This method performs generic drag and it won't settle to any particular anchor, *
+ * leaving swipeable in between anchors. When done dragging, [performFling] must be
+ * called as well to ensure swipeable will settle at the anchor.
+ *
+ * In general cases, [swipeable] drags by itself when being swiped. This method is to be
+ * used for nested scroll logic that wraps the [swipeable]. In nested scroll developer may
+ * want to force drag when the child scroll container reaches the bound.
+ *
+ * @param delta delta in pixels to drag by
+ *
+ * @return the amount of [delta] consumed
+ */
+ fun performDrag(delta: Float): Float {
+ val potentiallyConsumed = absoluteOffset.value + delta
+ val clamped = potentiallyConsumed.coerceIn(minBound, maxBound)
+ val deltaToConsume = clamped - absoluteOffset.value
+ if (abs(deltaToConsume) > 0) {
+ draggableState.dispatchRawDelta(deltaToConsume)
+ }
+ return deltaToConsume
+ }
+
+ companion object {
+ /**
+ * The default [Saver] implementation for [SwipeableState].
+ */
+ fun <T : Any> Saver(
+ animationSpec: AnimationSpec<Float>,
+ confirmStateChange: (T) -> Boolean
+ ) = Saver<SwipeableState<T>, T>(
+ save = { it.currentValue },
+ restore = { SwipeableState(it, animationSpec, confirmStateChange) }
+ )
+ }
+}
+
+/**
+ * Collects information about the ongoing swipe or animation in [swipeable].
+ *
+ * To access this information, use [SwipeableState.progress].
+ *
+ * @param from The state corresponding to the anchor we are moving away from.
+ * @param to The state corresponding to the anchor we are moving towards.
+ * @param fraction The fraction that the current position represents between [from] and [to].
+ * Must be between `0` and `1`.
+ */
+@Immutable
+class SwipeProgress<T>(
+ val from: T,
+ val to: T,
+ /*@FloatRange(from = 0.0, to = 1.0)*/
+ val fraction: Float
+) {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is SwipeProgress<*>) return false
+
+ if (from != other.from) return false
+ if (to != other.to) return false
+ if (fraction != other.fraction) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = from?.hashCode() ?: 0
+ result = 31 * result + (to?.hashCode() ?: 0)
+ result = 31 * result + fraction.hashCode()
+ return result
+ }
+
+ override fun toString(): String {
+ return "SwipeProgress(from=$from, to=$to, fraction=$fraction)"
+ }
+}
+
+/**
+ * Create and [remember] a [SwipeableState] with the default animation clock.
+ *
+ * @param initialValue The initial value of the state.
+ * @param animationSpec The default animation that will be used to animate to a new state.
+ * @param confirmStateChange Optional callback invoked to confirm or veto a pending state change.
+ */
+@Composable
+fun <T : Any> rememberSwipeableState(
+ initialValue: T,
+ animationSpec: AnimationSpec<Float> = AnimationSpec,
+ confirmStateChange: (newValue: T) -> Boolean = { true }
+): SwipeableState<T> {
+ return rememberSaveable(
+ saver = SwipeableState.Saver(
+ animationSpec = animationSpec,
+ confirmStateChange = confirmStateChange
+ )
+ ) {
+ SwipeableState(
+ initialValue = initialValue,
+ animationSpec = animationSpec,
+ confirmStateChange = confirmStateChange
+ )
+ }
+}
+
+/**
+ * Create and [remember] a [SwipeableState] which is kept in sync with another state, i.e.:
+ * 1. Whenever the [value] changes, the [SwipeableState] will be animated to that new value.
+ * 2. Whenever the value of the [SwipeableState] changes (e.g. after a swipe), the owner of the
+ * [value] will be notified to update their state to the new value of the [SwipeableState] by
+ * invoking [onValueChange]. If the owner does not update their state to the provided value for
+ * some reason, then the [SwipeableState] will perform a rollback to the previous, correct value.
+ */
+@Composable
+internal fun <T : Any> rememberSwipeableStateFor(
+ value: T,
+ onValueChange: (T) -> Unit,
+ animationSpec: AnimationSpec<Float> = AnimationSpec
+): SwipeableState<T> {
+ val swipeableState = remember {
+ SwipeableState(
+ initialValue = value,
+ animationSpec = animationSpec,
+ confirmStateChange = { true }
+ )
+ }
+ val forceAnimationCheck = remember { mutableStateOf(false) }
+ LaunchedEffect(value, forceAnimationCheck.value) {
+ if (value != swipeableState.currentValue) {
+ swipeableState.animateTo(value)
+ }
+ }
+ DisposableEffect(swipeableState.currentValue) {
+ if (value != swipeableState.currentValue) {
+ onValueChange(swipeableState.currentValue)
+ forceAnimationCheck.value = !forceAnimationCheck.value
+ }
+ onDispose { }
+ }
+ return swipeableState
+}
+
+/**
+ * Enable swipe gestures between a set of predefined states.
+ *
+ * To use this, you must provide a map of anchors (in pixels) to states (of type [T]).
+ * Note that this map cannot be empty and cannot have two anchors mapped to the same state.
+ *
+ * When a swipe is detected, the offset of the [SwipeableState] will be updated with the swipe
+ * delta. You should use this offset to move your content accordingly (see `Modifier.offsetPx`).
+ * When the swipe ends, the offset will be animated to one of the anchors and when that anchor is
+ * reached, the value of the [SwipeableState] will also be updated to the state corresponding to
+ * the new anchor. The target anchor is calculated based on the provided positional [thresholds].
+ *
+ * Swiping is constrained between the minimum and maximum anchors. If the user attempts to swipe
+ * past these bounds, a resistance effect will be applied by default. The amount of resistance at
+ * each edge is specified by the [resistance] config. To disable all resistance, set it to `null`.
+ *
+ * For an example of a [swipeable] with three states, see:
+ *
+ * @sample androidx.compose.material.samples.SwipeableSample
+ *
+ * @param T The type of the state.
+ * @param state The state of the [swipeable].
+ * @param anchors Pairs of anchors and states, used to map anchors to states and vice versa.
+ * @param thresholds Specifies where the thresholds between the states are. The thresholds will be
+ * used to determine which state to animate to when swiping stops. This is represented as a lambda
+ * that takes two states and returns the threshold between them in the form of a [ThresholdConfig].
+ * Note that the order of the states corresponds to the swipe direction.
+ * @param orientation The orientation in which the [swipeable] can be swiped.
+ * @param enabled Whether this [swipeable] is enabled and should react to the user's input.
+ * @param reverseDirection Whether to reverse the direction of the swipe, so a top to bottom
+ * swipe will behave like bottom to top, and a left to right swipe will behave like right to left.
+ * @param interactionSource Optional [MutableInteractionSource] that will passed on to
+ * the internal [Modifier.draggable].
+ * @param resistance Controls how much resistance will be applied when swiping past the bounds.
+ * @param velocityThreshold The threshold (in dp per second) that the end velocity has to exceed
+ * in order to animate to the next state, even if the positional [thresholds] have not been reached.
+ */
+fun <T> Modifier.swipeable(
+ state: SwipeableState<T>,
+ anchors: Map<Float, T>,
+ orientation: Orientation,
+ enabled: Boolean = true,
+ reverseDirection: Boolean = false,
+ interactionSource: MutableInteractionSource? = null,
+ thresholds: (from: T, to: T) -> ThresholdConfig = { _, _ -> FixedThreshold(56.dp) },
+ resistance: ResistanceConfig? = resistanceConfig(anchors.keys),
+ velocityThreshold: Dp = VelocityThreshold
+) = composed(
+ inspectorInfo = debugInspectorInfo {
+ name = "swipeable"
+ properties["state"] = state
+ properties["anchors"] = anchors
+ properties["orientation"] = orientation
+ properties["enabled"] = enabled
+ properties["reverseDirection"] = reverseDirection
+ properties["interactionSource"] = interactionSource
+ properties["thresholds"] = thresholds
+ properties["resistance"] = resistance
+ properties["velocityThreshold"] = velocityThreshold
+ }
+) {
+ require(anchors.isNotEmpty()) {
+ "You must have at least one anchor."
+ }
+ require(anchors.values.distinct().count() == anchors.size) {
+ "You cannot have two anchors mapped to the same state."
+ }
+ val density = LocalDensity.current
+ state.ensureInit(anchors)
+ LaunchedEffect(anchors, state) {
+ val oldAnchors = state.anchors
+ state.anchors = anchors
+ state.resistance = resistance
+ state.thresholds = { a, b ->
+ val from = anchors.getValue(a)
+ val to = anchors.getValue(b)
+ with(thresholds(from, to)) { density.computeThreshold(a, b) }
+ }
+ with(density) {
+ state.velocityThreshold = velocityThreshold.toPx()
+ }
+ state.processNewAnchors(oldAnchors, anchors)
+ }
+
+ Modifier.draggable(
+ orientation = orientation,
+ enabled = enabled,
+ reverseDirection = reverseDirection,
+ interactionSource = interactionSource,
+ startDragImmediately = state.isAnimationRunning,
+ onDragStopped = { velocity -> launch { state.performFling(velocity) } },
+ state = state.draggableState
+ )
+}
+
+/**
+ * Interface to compute a threshold between two anchors/states in a [swipeable].
+ *
+ * To define a [ThresholdConfig], consider using [FixedThreshold] and [FractionalThreshold].
+ */
+@Stable
+interface ThresholdConfig {
+ /**
+ * Compute the value of the threshold (in pixels), once the values of the anchors are known.
+ */
+ fun Density.computeThreshold(fromValue: Float, toValue: Float): Float
+}
+
+/**
+ * A fixed threshold will be at an [offset] away from the first anchor.
+ *
+ * @param offset The offset (in dp) that the threshold will be at.
+ */
+@Immutable
+data class FixedThreshold(private val offset: Dp) : ThresholdConfig {
+ override fun Density.computeThreshold(fromValue: Float, toValue: Float): Float {
+ return fromValue + offset.toPx() * sign(toValue - fromValue)
+ }
+}
+
+/**
+ * A fractional threshold will be at a [fraction] of the way between the two anchors.
+ *
+ * @param fraction The fraction (between 0 and 1) that the threshold will be at.
+ */
+@Immutable
+data class FractionalThreshold(
+ /*@FloatRange(from = 0.0, to = 1.0)*/
+ private val fraction: Float
+) : ThresholdConfig {
+ override fun Density.computeThreshold(fromValue: Float, toValue: Float): Float {
+ return lerp(fromValue, toValue, fraction)
+ }
+}
+
+/**
+ * Specifies how resistance is calculated in [swipeable].
+ *
+ * There are two things needed to calculate resistance: the resistance basis determines how much
+ * overflow will be consumed to achieve maximum resistance, and the resistance factor determines
+ * the amount of resistance (the larger the resistance factor, the stronger the resistance).
+ *
+ * The resistance basis is usually either the size of the component which [swipeable] is applied
+ * to, or the distance between the minimum and maximum anchors. For a constructor in which the
+ * resistance basis defaults to the latter, consider using [resistanceConfig].
+ *
+ * You may specify different resistance factors for each bound. Consider using one of the default
+ * resistance factors in [SwipeableDefaults]: `StandardResistanceFactor` to convey that the user
+ * has run out of things to see, and `StiffResistanceFactor` to convey that the user cannot swipe
+ * this right now. Also, you can set either factor to 0 to disable resistance at that bound.
+ *
+ * @param basis Specifies the maximum amount of overflow that will be consumed. Must be positive.
+ * @param factorAtMin The factor by which to scale the resistance at the minimum bound.
+ * Must not be negative.
+ * @param factorAtMax The factor by which to scale the resistance at the maximum bound.
+ * Must not be negative.
+ */
+@Immutable
+class ResistanceConfig(
+ /*@FloatRange(from = 0.0, fromInclusive = false)*/
+ val basis: Float,
+ /*@FloatRange(from = 0.0)*/
+ val factorAtMin: Float = StandardResistanceFactor,
+ /*@FloatRange(from = 0.0)*/
+ val factorAtMax: Float = StandardResistanceFactor
+) {
+ fun computeResistance(overflow: Float): Float {
+ val factor = if (overflow < 0) factorAtMin else factorAtMax
+ if (factor == 0f) return 0f
+ val progress = (overflow / basis).coerceIn(-1f, 1f)
+ return basis / factor * sin(progress * PI.toFloat() / 2)
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is ResistanceConfig) return false
+
+ if (basis != other.basis) return false
+ if (factorAtMin != other.factorAtMin) return false
+ if (factorAtMax != other.factorAtMax) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = basis.hashCode()
+ result = 31 * result + factorAtMin.hashCode()
+ result = 31 * result + factorAtMax.hashCode()
+ return result
+ }
+
+ override fun toString(): String {
+ return "ResistanceConfig(basis=$basis, factorAtMin=$factorAtMin, factorAtMax=$factorAtMax)"
+ }
+}
+
+/**
+ * Given an offset x and a set of anchors, return a list of anchors:
+ * 1. [ ] if the set of anchors is empty,
+ * 2. [ x' ] if x is equal to one of the anchors, accounting for a small rounding error, where x'
+ * is x rounded to the exact value of the matching anchor,
+ * 3. [ min ] if min is the minimum anchor and x < min,
+ * 4. [ max ] if max is the maximum anchor and x > max, or
+ * 5. [ a , b ] if a and b are anchors such that a < x < b and b - a is minimal.
+ */
+private fun findBounds(
+ offset: Float,
+ anchors: Set<Float>
+): List<Float> {
+ // Find the anchors the target lies between with a little bit of rounding error.
+ val a = anchors.filter { it <= offset + 0.001 }.maxOrNull()
+ val b = anchors.filter { it >= offset - 0.001 }.minOrNull()
+
+ return when {
+ a == null ->
+ // case 1 or 3
+ listOfNotNull(b)
+ b == null ->
+ // case 4
+ listOf(a)
+ a == b ->
+ // case 2
+ // Can't return offset itself here since it might not be exactly equal
+ // to the anchor, despite being considered an exact match.
+ listOf(a)
+ else ->
+ // case 5
+ listOf(a, b)
+ }
+}
+
+private fun computeTarget(
+ offset: Float,
+ lastValue: Float,
+ anchors: Set<Float>,
+ thresholds: (Float, Float) -> Float,
+ velocity: Float,
+ velocityThreshold: Float
+): Float {
+ val bounds = findBounds(offset, anchors)
+ return when (bounds.size) {
+ 0 -> lastValue
+ 1 -> bounds[0]
+ else -> {
+ val lower = bounds[0]
+ val upper = bounds[1]
+ if (lastValue <= offset) {
+ // Swiping from lower to upper (positive).
+ if (velocity >= velocityThreshold) {
+ return upper
+ } else {
+ val threshold = thresholds(lower, upper)
+ if (offset < threshold) lower else upper
+ }
+ } else {
+ // Swiping from upper to lower (negative).
+ if (velocity <= -velocityThreshold) {
+ return lower
+ } else {
+ val threshold = thresholds(upper, lower)
+ if (offset > threshold) upper else lower
+ }
+ }
+ }
+ }
+}
+
+private fun <T> Map<Float, T>.getOffset(state: T): Float? {
+ return entries.firstOrNull { it.value == state }?.key
+}
+
+/**
+ * Contains useful defaults for [swipeable] and [SwipeableState].
+ */
+object SwipeableDefaults {
+ /**
+ * The default animation used by [SwipeableState].
+ */
+ val AnimationSpec = SpringSpec<Float>()
+
+ /**
+ * The default velocity threshold (1.8 dp per millisecond) used by [swipeable].
+ */
+ val VelocityThreshold = 125.dp
+
+ /**
+ * A stiff resistance factor which indicates that swiping isn't available right now.
+ */
+ const val StiffResistanceFactor = 20f
+
+ /**
+ * A standard resistance factor which indicates that the user has run out of things to see.
+ */
+ const val StandardResistanceFactor = 10f
+
+ /**
+ * The default resistance config used by [swipeable].
+ *
+ * This returns `null` if there is one anchor. If there are at least two anchors, it returns
+ * a [ResistanceConfig] with the resistance basis equal to the distance between the two bounds.
+ */
+ fun resistanceConfig(
+ anchors: Set<Float>,
+ factorAtMin: Float = StandardResistanceFactor,
+ factorAtMax: Float = StandardResistanceFactor
+ ): ResistanceConfig? {
+ return if (anchors.size <= 1) {
+ null
+ } else {
+ val basis = anchors.maxOrNull()!! - anchors.minOrNull()!!
+ ResistanceConfig(basis, factorAtMin, factorAtMax)
+ }
+ }
+}
+
+// temp default nested scroll connection for swipeables which desire as an opt in
+// revisit in b/174756744 as all types will have their own specific connection probably
+internal val <T> SwipeableState<T>.PreUpPostDownNestedScrollConnection: NestedScrollConnection
+ get() = object : NestedScrollConnection {
+ override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+ val delta = available.toFloat()
+ return if (delta < 0 && source == NestedScrollSource.Drag) {
+ performDrag(delta).toOffset()
+ } else {
+ Offset.Zero
+ }
+ }
+
+ override fun onPostScroll(
+ consumed: Offset,
+ available: Offset,
+ source: NestedScrollSource
+ ): Offset {
+ return if (source == NestedScrollSource.Drag) {
+ performDrag(available.toFloat()).toOffset()
+ } else {
+ Offset.Zero
+ }
+ }
+
+ override suspend fun onPreFling(available: Velocity): Velocity {
+ val toFling = Offset(available.x, available.y).toFloat()
+ return if (toFling < 0 && offset.value > minBound) {
+ performFling(velocity = toFling)
+ // since we go to the anchor with tween settling, consume all for the best UX
+ available
+ } else {
+ Velocity.Zero
+ }
+ }
+
+ override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
+ performFling(velocity = Offset(available.x, available.y).toFloat())
+ return available
+ }
+
+ private fun Float.toOffset(): Offset = Offset(0f, this)
+
+ private fun Offset.toFloat(): Float = this.y
+ }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/IFloatingTasks.aidl b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/CancelButton.kt
similarity index 66%
copy from libs/WindowManager/Shell/src/com/android/wm/shell/floating/IFloatingTasks.aidl
copy to packages/CredentialManager/src/com/android/credentialmanager/common/ui/CancelButton.kt
index f79ca10..177d0e0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/IFloatingTasks.aidl
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/CancelButton.kt
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.floating;
+package com.android.credentialmanager.common.ui
-import android.content.Intent;
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.runtime.Composable
-/**
- * Interface that is exposed to remote callers to manipulate floating task features.
- */
-interface IFloatingTasks {
-
- void showTask(in Intent intent) = 1;
-
-}
+@Composable
+fun CancelButton(text: String, onClick: () -> Unit) {
+ TextButton(onClick = onClick) {
+ Text(text = text)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/ChooserSelectorResourceHelper.java b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ConfirmButton.kt
similarity index 65%
rename from packages/SystemUI/src/com/android/systemui/ChooserSelectorResourceHelper.java
rename to packages/CredentialManager/src/com/android/credentialmanager/common/ui/ConfirmButton.kt
index 7a2de7b..b2b0bdc 100644
--- a/packages/SystemUI/src/com/android/systemui/ChooserSelectorResourceHelper.java
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ConfirmButton.kt
@@ -14,18 +14,15 @@
* limitations under the License.
*/
-package com.android.systemui;
+package com.android.credentialmanager.common.ui
-import androidx.annotation.StringRes;
+import androidx.compose.material3.FilledTonalButton
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
-import com.android.internal.R;
-
-/** Helper class for referencing resources */
-class ChooserSelectorResourceHelper {
-
- private ChooserSelectorResourceHelper() {
+@Composable
+fun ConfirmButton(text: String, onClick: () -> Unit) {
+ FilledTonalButton(onClick = onClick) {
+ Text(text = text)
}
-
- @StringRes
- static final int CONFIG_CHOOSER_ACTIVITY = R.string.config_chooserActivity;
-}
+}
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
index d7e5ee8..123c3d4 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
@@ -18,14 +18,27 @@
import android.graphics.drawable.Drawable
-data class ProviderInfo(
+open class ProviderInfo(
val icon: Drawable,
val name: String,
val displayName: String,
- val createOptions: List<CreateOptionInfo>,
- val isDefault: Boolean,
)
+class EnabledProviderInfo(
+ icon: Drawable,
+ name: String,
+ displayName: String,
+ var createOptions: List<CreateOptionInfo>,
+ val isDefault: Boolean,
+ var remoteEntry: RemoteInfo?,
+) : ProviderInfo(icon, name, displayName)
+
+class DisabledProviderInfo(
+ icon: Drawable,
+ name: String,
+ displayName: String,
+) : ProviderInfo(icon, name, displayName)
+
open class EntryInfo (
val entryKey: String,
val entrySubkey: String,
@@ -34,15 +47,20 @@
class CreateOptionInfo(
entryKey: String,
entrySubkey: String,
- val userProviderDisplayName: String,
+ val userProviderDisplayName: String?,
val credentialTypeIcon: Drawable,
val profileIcon: Drawable,
- val passwordCount: Int,
- val passkeyCount: Int,
- val totalCredentialCount: Int,
+ val passwordCount: Int?,
+ val passkeyCount: Int?,
+ val totalCredentialCount: Int?,
val lastUsedTimeMillis: Long?,
) : EntryInfo(entryKey, entrySubkey)
+class RemoteInfo(
+ entryKey: String,
+ entrySubkey: String,
+) : EntryInfo(entryKey, entrySubkey)
+
data class RequestDisplayInfo(
val userName: String,
val displayName: String,
@@ -55,7 +73,7 @@
* user selects a different entry on the more option page.
*/
data class ActiveEntry (
- val activeProvider: ProviderInfo,
+ val activeProvider: EnabledProviderInfo,
val activeEntryInfo: EntryInfo,
)
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyComponents.kt
index 13a892f..67b704f 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyComponents.kt
@@ -1,6 +1,6 @@
package com.android.credentialmanager.createflow
-import androidx.compose.foundation.BorderStroke
+import android.credentials.Credential.TYPE_PASSWORD_CREDENTIAL
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -9,24 +9,20 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.material.Button
-import androidx.compose.material.ButtonColors
-import androidx.compose.material.ButtonDefaults
-import androidx.compose.material.Card
-import androidx.compose.material.Chip
-import androidx.compose.material.ChipDefaults
-import androidx.compose.material.Divider
-import androidx.compose.material.ExperimentalMaterialApi
-import androidx.compose.material.Icon
-import androidx.compose.material.IconButton
-import androidx.compose.material.ModalBottomSheetLayout
-import androidx.compose.material.ModalBottomSheetValue
-import androidx.compose.material.Text
-import androidx.compose.material.TextButton
-import androidx.compose.material.TopAppBar
+import androidx.compose.material3.Card
+import androidx.compose.material3.Divider
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.SuggestionChip
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.material3.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
-import androidx.compose.material.rememberModalBottomSheetState
+import androidx.compose.material.icons.outlined.NewReleases
+import androidx.compose.material.icons.filled.Add
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
@@ -39,16 +35,14 @@
import androidx.compose.ui.unit.dp
import androidx.core.graphics.drawable.toBitmap
import com.android.credentialmanager.R
-import com.android.credentialmanager.jetpack.provider.CredentialEntryUi.Companion.TYPE_PASSWORD_CREDENTIAL
-import com.android.credentialmanager.jetpack.provider.CredentialEntryUi.Companion.TYPE_PUBLIC_KEY_CREDENTIAL
-import com.android.credentialmanager.ui.theme.Grey100
-import com.android.credentialmanager.ui.theme.Shapes
-import com.android.credentialmanager.ui.theme.Typography
-import com.android.credentialmanager.ui.theme.lightBackgroundColor
-import com.android.credentialmanager.ui.theme.lightColorAccentSecondary
-import com.android.credentialmanager.ui.theme.lightSurface1
+import com.android.credentialmanager.common.material.ModalBottomSheetLayout
+import com.android.credentialmanager.common.material.ModalBottomSheetValue
+import com.android.credentialmanager.common.material.rememberModalBottomSheetState
+import com.android.credentialmanager.common.ui.CancelButton
+import com.android.credentialmanager.common.ui.ConfirmButton
+import com.android.credentialmanager.jetpack.developer.PublicKeyCredential.Companion.TYPE_PUBLIC_KEY_CREDENTIAL
-@ExperimentalMaterialApi
+@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CreatePasskeyScreen(
viewModel: CreatePasskeyViewModel,
@@ -67,7 +61,7 @@
onCancel = viewModel::onCancel,
)
CreateScreenState.PROVIDER_SELECTION -> ProviderSelectionCard(
- providerList = uiState.providers,
+ enabledProviderList = uiState.enabledProviders,
onCancel = viewModel::onCancel,
onProviderSelected = viewModel::onProviderSelected
)
@@ -78,13 +72,17 @@
onOptionSelected = viewModel::onPrimaryCreateOptionInfoSelected,
onConfirm = viewModel::onPrimaryCreateOptionInfoSelected,
onCancel = viewModel::onCancel,
- multiProvider = uiState.providers.size > 1,
+ multiProvider = uiState.enabledProviders.size > 1,
onMoreOptionsSelected = viewModel::onMoreOptionsSelected
)
CreateScreenState.MORE_OPTIONS_SELECTION -> MoreOptionsSelectionCard(
- providerList = uiState.providers,
+ requestDisplayInfo = uiState.requestDisplayInfo,
+ enabledProviderList = uiState.enabledProviders,
+ disabledProviderList = uiState.disabledProviders,
onBackButtonSelected = viewModel::onBackButtonSelected,
- onOptionSelected = viewModel::onMoreOptionsRowSelected
+ onOptionSelected = viewModel::onMoreOptionsRowSelected,
+ onDisabledPasswordManagerSelected = viewModel::onDisabledPasswordManagerSelected,
+ onRemoteEntrySelected = viewModel::onRemoteEntrySelected
)
CreateScreenState.MORE_OPTIONS_ROW_INTRO -> MoreOptionsRowIntroCard(
providerInfo = uiState.activeEntry?.activeProvider!!,
@@ -92,8 +90,8 @@
)
}
},
- scrimColor = Color.Transparent,
- sheetShape = Shapes.medium,
+ scrimColor = MaterialTheme.colorScheme.scrim,
+ sheetShape = MaterialTheme.shapes.medium,
) {}
LaunchedEffect(state.currentValue) {
if (state.currentValue == ModalBottomSheetValue.Hidden) {
@@ -107,9 +105,7 @@
onConfirm: () -> Unit,
onCancel: () -> Unit,
) {
- Card(
- backgroundColor = lightBackgroundColor,
- ) {
+ Card() {
Column() {
Icon(
painter = painterResource(R.drawable.ic_passkey),
@@ -119,7 +115,7 @@
)
Text(
text = stringResource(R.string.passkey_creation_intro_title),
- style = Typography.subtitle1,
+ style = MaterialTheme.typography.titleMedium,
modifier = Modifier
.padding(horizontal = 24.dp)
.align(alignment = Alignment.CenterHorizontally)
@@ -130,7 +126,7 @@
)
Text(
text = stringResource(R.string.passkey_creation_intro_body),
- style = Typography.body1,
+ style = MaterialTheme.typography.bodyLarge,
modifier = Modifier.padding(horizontal = 28.dp)
)
Divider(
@@ -143,11 +139,11 @@
) {
CancelButton(
stringResource(R.string.string_cancel),
- onclick = onCancel
+ onClick = onCancel
)
ConfirmButton(
stringResource(R.string.string_continue),
- onclick = onConfirm
+ onClick = onConfirm
)
}
Divider(
@@ -159,25 +155,23 @@
}
}
-@ExperimentalMaterialApi
+@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ProviderSelectionCard(
- providerList: List<ProviderInfo>,
+ enabledProviderList: List<EnabledProviderInfo>,
onProviderSelected: (String) -> Unit,
onCancel: () -> Unit
) {
- Card(
- backgroundColor = lightBackgroundColor,
- ) {
+ Card() {
Column() {
Text(
text = stringResource(R.string.choose_provider_title),
- style = Typography.subtitle1,
+ style = MaterialTheme.typography.titleMedium,
modifier = Modifier.padding(all = 24.dp).align(alignment = Alignment.CenterHorizontally)
)
Text(
text = stringResource(R.string.choose_provider_body),
- style = Typography.body1,
+ style = MaterialTheme.typography.bodyLarge,
modifier = Modifier.padding(horizontal = 28.dp)
)
Divider(
@@ -185,15 +179,15 @@
color = Color.Transparent
)
Card(
- shape = Shapes.medium,
+ shape = MaterialTheme.shapes.large,
modifier = Modifier
.padding(horizontal = 24.dp)
- .align(alignment = Alignment.CenterHorizontally)
+ .align(alignment = Alignment.CenterHorizontally),
) {
LazyColumn(
verticalArrangement = Arrangement.spacedBy(2.dp)
) {
- providerList.forEach {
+ enabledProviderList.forEach {
item {
ProviderRow(providerInfo = it, onProviderSelected = onProviderSelected)
}
@@ -219,43 +213,44 @@
}
}
-@ExperimentalMaterialApi
+@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MoreOptionsSelectionCard(
- providerList: List<ProviderInfo>,
+ requestDisplayInfo: RequestDisplayInfo,
+ enabledProviderList: List<EnabledProviderInfo>,
+ disabledProviderList: List<DisabledProviderInfo>?,
onBackButtonSelected: () -> Unit,
- onOptionSelected: (ActiveEntry) -> Unit
+ onOptionSelected: (ActiveEntry) -> Unit,
+ onDisabledPasswordManagerSelected: () -> Unit,
+ onRemoteEntrySelected: () -> Unit,
) {
- Card(
- backgroundColor = lightBackgroundColor,
- ) {
+ Card() {
Column() {
TopAppBar(
title = {
- Text(text = stringResource(R.string.string_more_options), style = Typography.subtitle1)
+ Text(
+ text = when (requestDisplayInfo.type) {
+ TYPE_PUBLIC_KEY_CREDENTIAL -> stringResource(R.string.create_passkey_in)
+ TYPE_PASSWORD_CREDENTIAL -> stringResource(R.string.save_password_to)
+ else -> stringResource(R.string.save_sign_in_to)
+ },
+ style = MaterialTheme.typography.titleMedium
+ )
},
- backgroundColor = lightBackgroundColor,
- elevation = 0.dp,
- navigationIcon =
- {
+ navigationIcon = {
IconButton(onClick = onBackButtonSelected) {
- Icon(Icons.Filled.ArrowBack, "backIcon"
- )
+ Icon(
+ Icons.Filled.ArrowBack,
+ stringResource(R.string.accessibility_back_arrow_button))
}
}
)
Divider(
- thickness = 24.dp,
+ thickness = 8.dp,
color = Color.Transparent
)
- Text(
- text = stringResource(R.string.create_passkey_at),
- style = Typography.body1,
- modifier = Modifier.padding(horizontal = 28.dp),
- textAlign = TextAlign.Center
- )
Card(
- shape = Shapes.medium,
+ shape = MaterialTheme.shapes.large,
modifier = Modifier
.padding(horizontal = 24.dp)
.align(alignment = Alignment.CenterHorizontally)
@@ -263,19 +258,39 @@
LazyColumn(
verticalArrangement = Arrangement.spacedBy(2.dp)
) {
- // TODO: change the order according to usage frequency
- providerList.forEach { providerInfo ->
- providerInfo.createOptions.forEach { createOptionInfo ->
+ enabledProviderList.forEach { enabledProviderInfo ->
+ enabledProviderInfo.createOptions.forEach { createOptionInfo ->
item {
MoreOptionsInfoRow(
- providerInfo = providerInfo,
+ providerInfo = enabledProviderInfo,
createOptionInfo = createOptionInfo,
onOptionSelected = {
- onOptionSelected(ActiveEntry(providerInfo, createOptionInfo))
+ onOptionSelected(ActiveEntry(enabledProviderInfo, createOptionInfo))
})
}
}
}
+ if (disabledProviderList != null) {
+ item {
+ MoreOptionsDisabledProvidersRow(
+ disabledProviders = disabledProviderList,
+ onDisabledPasswordManagerSelected = onDisabledPasswordManagerSelected,
+ )
+ }
+ }
+ var hasRemoteInfo = false
+ enabledProviderList.forEach {
+ if (it.remoteEntry != null) {
+ hasRemoteInfo = true
+ }
+ }
+ if (hasRemoteInfo) {
+ item {
+ RemoteEntryRow(
+ onRemoteEntrySelected = onRemoteEntrySelected,
+ )
+ }
+ }
}
}
Divider(
@@ -287,19 +302,29 @@
}
}
-@ExperimentalMaterialApi
+@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MoreOptionsRowIntroCard(
- providerInfo: ProviderInfo,
+ providerInfo: EnabledProviderInfo,
onDefaultOrNotSelected: () -> Unit,
) {
- Card(
- backgroundColor = lightBackgroundColor,
- ) {
+ Card() {
Column() {
+ Icon(
+ Icons.Outlined.NewReleases,
+ contentDescription = null,
+ modifier = Modifier.align(alignment = Alignment.CenterHorizontally).padding(all = 24.dp)
+ )
Text(
text = stringResource(R.string.use_provider_for_all_title, providerInfo.displayName),
- style = Typography.subtitle1,
+ style = MaterialTheme.typography.titleMedium,
+ modifier = Modifier.padding(horizontal = 24.dp)
+ .align(alignment = Alignment.CenterHorizontally),
+ textAlign = TextAlign.Center,
+ )
+ Text(
+ text = stringResource(R.string.confirm_default_or_use_once_description),
+ style = MaterialTheme.typography.bodyLarge,
modifier = Modifier.padding(all = 24.dp).align(alignment = Alignment.CenterHorizontally)
)
Row(
@@ -308,11 +333,11 @@
) {
CancelButton(
stringResource(R.string.use_once),
- onclick = onDefaultOrNotSelected
+ onClick = onDefaultOrNotSelected
)
ConfirmButton(
stringResource(R.string.set_as_default),
- onclick = onDefaultOrNotSelected
+ onClick = onDefaultOrNotSelected
)
}
Divider(
@@ -324,74 +349,31 @@
}
}
-@ExperimentalMaterialApi
+@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ProviderRow(providerInfo: ProviderInfo, onProviderSelected: (String) -> Unit) {
- Chip(
+ SuggestionChip(
modifier = Modifier.fillMaxWidth(),
onClick = {onProviderSelected(providerInfo.name)},
- leadingIcon = {
+ icon = {
Image(modifier = Modifier.size(24.dp, 24.dp).padding(start = 10.dp),
bitmap = providerInfo.icon.toBitmap().asImageBitmap(),
// painter = painterResource(R.drawable.ic_passkey),
// TODO: add description.
contentDescription = "")
},
- colors = ChipDefaults.chipColors(
- backgroundColor = Grey100,
- leadingIconContentColor = Grey100
- ),
- shape = Shapes.large
- ) {
- Text(
- text = providerInfo.displayName,
- style = Typography.button,
- modifier = Modifier.padding(vertical = 18.dp)
- )
- }
-}
-
-@Composable
-fun CancelButton(text: String, onclick: () -> Unit) {
- val colors = ButtonDefaults.buttonColors(
- backgroundColor = lightBackgroundColor
+ shape = MaterialTheme.shapes.large,
+ label = {
+ Text(
+ text = providerInfo.displayName,
+ style = MaterialTheme.typography.labelLarge,
+ modifier = Modifier.padding(vertical = 18.dp)
+ )
+ }
)
- NavigationButton(
- border = BorderStroke(1.dp, lightSurface1),
- colors = colors,
- text = text,
- onclick = onclick)
}
-@Composable
-fun ConfirmButton(text: String, onclick: () -> Unit) {
- val colors = ButtonDefaults.buttonColors(
- backgroundColor = lightColorAccentSecondary
- )
- NavigationButton(
- colors = colors,
- text = text,
- onclick = onclick)
-}
-
-@Composable
-fun NavigationButton(
- border: BorderStroke? = null,
- colors: ButtonColors,
- text: String,
- onclick: () -> Unit
-) {
- Button(
- onClick = onclick,
- shape = Shapes.small,
- colors = colors,
- border = border
- ) {
- Text(text = text, style = Typography.button)
- }
-}
-
-@ExperimentalMaterialApi
+@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CreationSelectionCard(
requestDisplayInfo: RequestDisplayInfo,
@@ -403,12 +385,10 @@
multiProvider: Boolean,
onMoreOptionsSelected: () -> Unit,
) {
- Card(
- backgroundColor = lightBackgroundColor,
- ) {
+ Card() {
Column() {
Icon(
- bitmap = createOptionInfo.credentialTypeIcon.toBitmap().asImageBitmap(),
+ bitmap = providerInfo.icon.toBitmap().asImageBitmap(),
contentDescription = null,
tint = Color.Unspecified,
modifier = Modifier.align(alignment = Alignment.CenterHorizontally).padding(all = 24.dp)
@@ -422,34 +402,33 @@
else -> stringResource(R.string.choose_create_option_sign_in_title,
providerInfo.displayName)
},
- style = Typography.subtitle1,
+ style = MaterialTheme.typography.titleMedium,
modifier = Modifier.padding(horizontal = 24.dp)
.align(alignment = Alignment.CenterHorizontally),
textAlign = TextAlign.Center,
)
- Text(
- text = requestDisplayInfo.appDomainName,
- style = Typography.body2,
- modifier = Modifier.align(alignment = Alignment.CenterHorizontally)
- )
- Text(
- text = stringResource(
- R.string.choose_create_option_description,
- when (requestDisplayInfo.type) {
- TYPE_PUBLIC_KEY_CREDENTIAL -> stringResource(R.string.passkeys)
- TYPE_PASSWORD_CREDENTIAL -> stringResource(R.string.passwords)
- else -> stringResource(R.string.sign_ins)
- },
- providerInfo.displayName,
- createOptionInfo.userProviderDisplayName),
- style = Typography.body1,
- modifier = Modifier.padding(all = 24.dp).align(alignment = Alignment.CenterHorizontally)
- )
+ if (createOptionInfo.userProviderDisplayName != null) {
+ Text(
+ text = stringResource(
+ R.string.choose_create_option_description,
+ requestDisplayInfo.appDomainName,
+ when (requestDisplayInfo.type) {
+ TYPE_PUBLIC_KEY_CREDENTIAL -> stringResource(R.string.passkey)
+ TYPE_PASSWORD_CREDENTIAL -> stringResource(R.string.password)
+ else -> stringResource(R.string.sign_ins)
+ },
+ providerInfo.displayName,
+ createOptionInfo.userProviderDisplayName
+ ),
+ style = MaterialTheme.typography.bodyLarge,
+ modifier = Modifier.padding(all = 24.dp).align(alignment = Alignment.CenterHorizontally)
+ )
+ }
Card(
- shape = Shapes.medium,
+ shape = MaterialTheme.shapes.large,
modifier = Modifier
.padding(horizontal = 24.dp)
- .align(alignment = Alignment.CenterHorizontally)
+ .align(alignment = Alignment.CenterHorizontally),
) {
LazyColumn(
verticalArrangement = Arrangement.spacedBy(2.dp)
@@ -489,11 +468,11 @@
) {
CancelButton(
stringResource(R.string.string_cancel),
- onclick = onCancel
+ onClick = onCancel
)
ConfirmButton(
stringResource(R.string.string_continue),
- onclick = onConfirm
+ onClick = onConfirm
)
}
Divider(
@@ -505,82 +484,169 @@
}
}
-@ExperimentalMaterialApi
+@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PrimaryCreateOptionRow(
requestDisplayInfo: RequestDisplayInfo,
createOptionInfo: CreateOptionInfo,
onOptionSelected: () -> Unit
) {
- Chip(
+ SuggestionChip(
modifier = Modifier.fillMaxWidth(),
onClick = onOptionSelected,
- leadingIcon = {
+ icon = {
Image(modifier = Modifier.size(24.dp, 24.dp).padding(start = 10.dp),
bitmap = createOptionInfo.credentialTypeIcon.toBitmap().asImageBitmap(),
- contentDescription = stringResource(R.string.createOptionInfo_icon_description))
+ contentDescription = null)
},
- colors = ChipDefaults.chipColors(
- backgroundColor = Grey100,
- leadingIconContentColor = Grey100
- ),
- shape = Shapes.large
- ) {
- Column() {
- Text(
- text = requestDisplayInfo.userName,
- style = Typography.h6,
- modifier = Modifier.padding(top = 16.dp)
- )
- Text(
- text = requestDisplayInfo.displayName,
- style = Typography.body2,
- modifier = Modifier.padding(bottom = 16.dp)
- )
+ shape = MaterialTheme.shapes.large,
+ label = {
+ Column() {
+ Text(
+ text = requestDisplayInfo.userName,
+ style = MaterialTheme.typography.titleLarge,
+ modifier = Modifier.padding(top = 16.dp)
+ )
+ Text(
+ text = requestDisplayInfo.displayName,
+ style = MaterialTheme.typography.bodyMedium,
+ modifier = Modifier.padding(bottom = 16.dp)
+ )
+ }
}
- }
+ )
}
-@ExperimentalMaterialApi
+@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MoreOptionsInfoRow(
- providerInfo: ProviderInfo,
+ providerInfo: EnabledProviderInfo,
createOptionInfo: CreateOptionInfo,
onOptionSelected: () -> Unit
) {
- Chip(
+ SuggestionChip(
modifier = Modifier.fillMaxWidth(),
onClick = onOptionSelected,
- leadingIcon = {
- Image(modifier = Modifier.size(24.dp, 24.dp).padding(start = 10.dp),
- bitmap = createOptionInfo.profileIcon.toBitmap().asImageBitmap(),
- // painter = painterResource(R.drawable.ic_passkey),
- // TODO: add description.
- contentDescription = "")
+ icon = {
+ Image(modifier = Modifier.size(32.dp, 32.dp).padding(start = 16.dp),
+ bitmap = providerInfo.icon.toBitmap().asImageBitmap(),
+ contentDescription = null)
},
- colors = ChipDefaults.chipColors(
- backgroundColor = Grey100,
- leadingIconContentColor = Grey100
- ),
- shape = Shapes.large
- ) {
- Column() {
- Text(
+ shape = MaterialTheme.shapes.large,
+ label = {
+ Column() {
+ Text(
+ text = providerInfo.displayName,
+ style = MaterialTheme.typography.titleLarge,
+ modifier = Modifier.padding(top = 16.dp, start = 16.dp)
+ )
+ if (createOptionInfo.userProviderDisplayName != null) {
+ Text(
+ text = createOptionInfo.userProviderDisplayName,
+ style = MaterialTheme.typography.bodyMedium,
+ modifier = Modifier.padding(start = 16.dp)
+ )
+ }
+ if (createOptionInfo.passwordCount != null && createOptionInfo.passkeyCount != null) {
+ Text(
text =
- if (providerInfo.createOptions.size > 1)
- {stringResource(R.string.more_options_title_multiple_options,
- providerInfo.displayName, createOptionInfo.userProviderDisplayName)} else {
- stringResource(R.string.more_options_title_one_option,
- providerInfo.displayName)},
- style = Typography.h6,
- modifier = Modifier.padding(top = 16.dp)
- )
- Text(
- text = stringResource(R.string.more_options_usage_data,
- createOptionInfo.passwordCount, createOptionInfo.passkeyCount),
- style = Typography.body2,
- modifier = Modifier.padding(bottom = 16.dp)
- )
+ stringResource(
+ R.string.more_options_usage_passwords_passkeys,
+ createOptionInfo.passwordCount,
+ createOptionInfo.passkeyCount
+ ),
+ style = MaterialTheme.typography.bodyMedium,
+ modifier = Modifier.padding(bottom = 16.dp, start = 16.dp)
+ )
+ } else if (createOptionInfo.passwordCount != null) {
+ Text(
+ text =
+ stringResource(
+ R.string.more_options_usage_passwords,
+ createOptionInfo.passwordCount
+ ),
+ style = MaterialTheme.typography.bodyMedium,
+ modifier = Modifier.padding(bottom = 16.dp, start = 16.dp)
+ )
+ } else if (createOptionInfo.passkeyCount != null) {
+ Text(
+ text =
+ stringResource(
+ R.string.more_options_usage_passkeys,
+ createOptionInfo.passkeyCount
+ ),
+ style = MaterialTheme.typography.bodyMedium,
+ modifier = Modifier.padding(bottom = 16.dp, start = 16.dp)
+ )
+ } else if (createOptionInfo.totalCredentialCount != null) {
+ // TODO: Handle the case when there is total count
+ // but no passwords and passkeys after design is set
+ }
+ }
}
+ )
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun MoreOptionsDisabledProvidersRow(
+ disabledProviders: List<ProviderInfo>,
+ onDisabledPasswordManagerSelected: () -> Unit,
+) {
+ SuggestionChip(
+ modifier = Modifier.fillMaxWidth(),
+ onClick = onDisabledPasswordManagerSelected,
+ icon = {
+ Icon(
+ Icons.Filled.Add,
+ contentDescription = null,
+ modifier = Modifier.padding(start = 16.dp)
+ )
+ },
+ shape = MaterialTheme.shapes.large,
+ label = {
+ Column() {
+ Text(
+ text = stringResource(R.string.other_password_manager),
+ style = MaterialTheme.typography.titleLarge,
+ modifier = Modifier.padding(top = 16.dp, start = 16.dp)
+ )
+ Text(
+ text = disabledProviders.joinToString(separator = ", "){ it.displayName },
+ style = MaterialTheme.typography.bodyMedium,
+ modifier = Modifier.padding(bottom = 16.dp, start = 16.dp)
+ )
+ }
}
+ )
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun RemoteEntryRow(
+ onRemoteEntrySelected: () -> Unit,
+) {
+ SuggestionChip(
+ modifier = Modifier.fillMaxWidth(),
+ onClick = onRemoteEntrySelected,
+ icon = {
+ Icon(
+ painter = painterResource(R.drawable.ic_other_devices),
+ contentDescription = null,
+ tint = Color.Unspecified,
+ modifier = Modifier.padding(start = 18.dp)
+ )
+ },
+ shape = MaterialTheme.shapes.large,
+ label = {
+ Column() {
+ Text(
+ text = stringResource(R.string.another_device),
+ style = MaterialTheme.typography.titleLarge,
+ modifier = Modifier.padding(start = 16.dp, top = 18.dp, bottom = 18.dp)
+ .align(alignment = Alignment.CenterHorizontally)
+ )
+ }
+ }
+ )
}
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyViewModel.kt
index 2e9758a..af74b8e 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyViewModel.kt
@@ -28,7 +28,8 @@
import com.android.credentialmanager.common.ResultState
data class CreatePasskeyUiState(
- val providers: List<ProviderInfo>,
+ val enabledProviders: List<EnabledProviderInfo>,
+ val disabledProviders: List<DisabledProviderInfo>? = null,
val currentScreenState: CreateScreenState,
val requestDisplayInfo: RequestDisplayInfo,
val activeEntry: ActiveEntry? = null,
@@ -50,15 +51,15 @@
}
fun onConfirmIntro() {
- if (uiState.providers.size > 1) {
+ if (uiState.enabledProviders.size > 1) {
uiState = uiState.copy(
currentScreenState = CreateScreenState.PROVIDER_SELECTION
)
- } else if (uiState.providers.size == 1){
+ } else if (uiState.enabledProviders.size == 1){
uiState = uiState.copy(
currentScreenState = CreateScreenState.CREATION_OPTION_SELECTION,
- activeEntry = ActiveEntry(uiState.providers.first(),
- uiState.providers.first().createOptions.first())
+ activeEntry = ActiveEntry(uiState.enabledProviders.first(),
+ uiState.enabledProviders.first().createOptions.first())
)
} else {
throw java.lang.IllegalStateException("Empty provider list.")
@@ -73,8 +74,8 @@
)
}
- fun getProviderInfoByName(providerName: String): ProviderInfo {
- return uiState.providers.single {
+ fun getProviderInfoByName(providerName: String): EnabledProviderInfo {
+ return uiState.enabledProviders.single {
it.name.equals(providerName)
}
}
@@ -98,6 +99,14 @@
)
}
+ fun onDisabledPasswordManagerSelected() {
+ // TODO: Complete this function
+ }
+
+ fun onRemoteEntrySelected() {
+ // TODO: Complete this function
+ }
+
fun onCancel() {
CredentialManagerRepo.getInstance().onCancel()
dialogResult.value = DialogResult(ResultState.CANCELED)
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index 8b81083..dcdd71a 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -16,25 +16,29 @@
package com.android.credentialmanager.getflow
+import android.text.TextUtils
+
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.material.Card
-import androidx.compose.material.Chip
-import androidx.compose.material.ChipDefaults
-import androidx.compose.material.Divider
-import androidx.compose.material.ExperimentalMaterialApi
-import androidx.compose.material.Icon
-import androidx.compose.material.ModalBottomSheetLayout
-import androidx.compose.material.ModalBottomSheetValue
-import androidx.compose.material.Text
-import androidx.compose.material.rememberModalBottomSheetState
+import androidx.compose.foundation.lazy.items
+import androidx.compose.material3.Card
+import androidx.compose.material3.Divider
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.SuggestionChip
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBar
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
@@ -45,13 +49,12 @@
import androidx.compose.ui.unit.dp
import androidx.core.graphics.drawable.toBitmap
import com.android.credentialmanager.R
-import com.android.credentialmanager.createflow.CancelButton
-import com.android.credentialmanager.ui.theme.Grey100
-import com.android.credentialmanager.ui.theme.Shapes
-import com.android.credentialmanager.ui.theme.Typography
-import com.android.credentialmanager.ui.theme.lightBackgroundColor
+import com.android.credentialmanager.common.material.ModalBottomSheetLayout
+import com.android.credentialmanager.common.material.ModalBottomSheetValue
+import com.android.credentialmanager.common.material.rememberModalBottomSheetState
+import com.android.credentialmanager.common.ui.CancelButton
+import com.android.credentialmanager.jetpack.developer.PublicKeyCredential
-@ExperimentalMaterialApi
@Composable
fun GetCredentialScreen(
viewModel: GetCredentialViewModel,
@@ -65,18 +68,23 @@
sheetContent = {
val uiState = viewModel.uiState
when (uiState.currentScreenState) {
- GetScreenState.CREDENTIAL_SELECTION -> CredentialSelectionCard(
+ GetScreenState.PRIMARY_SELECTION -> PrimarySelectionCard(
requestDisplayInfo = uiState.requestDisplayInfo,
- providerInfo = uiState.selectedProvider!!,
+ providerDisplayInfo = uiState.providerDisplayInfo,
+ onEntrySelected = viewModel::onEntrySelected,
onCancel = viewModel::onCancel,
- onOptionSelected = viewModel::onCredentailSelected,
- multiProvider = uiState.providers.size > 1,
onMoreOptionSelected = viewModel::onMoreOptionSelected,
)
+ GetScreenState.ALL_SIGN_IN_OPTIONS -> AllSignInOptionCard(
+ providerInfoList = uiState.providerInfoList,
+ providerDisplayInfo = uiState.providerDisplayInfo,
+ onEntrySelected = viewModel::onEntrySelected,
+ onBackButtonClicked = viewModel::onBackToPrimarySelectionScreen,
+ )
}
},
scrimColor = Color.Transparent,
- sheetShape = Shapes.medium,
+ sheetShape = MaterialTheme.shapes.medium,
) {}
LaunchedEffect(state.currentValue) {
if (state.currentValue == ModalBottomSheetValue.Hidden) {
@@ -85,44 +93,36 @@
}
}
-@ExperimentalMaterialApi
+/** Draws the primary credential selection page. */
@Composable
-fun CredentialSelectionCard(
+fun PrimarySelectionCard(
requestDisplayInfo: RequestDisplayInfo,
- providerInfo: ProviderInfo,
- onOptionSelected: (String, String) -> Unit,
+ providerDisplayInfo: ProviderDisplayInfo,
+ onEntrySelected: (EntryInfo) -> Unit,
onCancel: () -> Unit,
- multiProvider: Boolean,
onMoreOptionSelected: () -> Unit,
) {
- Card(
- backgroundColor = lightBackgroundColor,
- ) {
+ val sortedUserNameToCredentialEntryList = providerDisplayInfo.sortedUserNameToCredentialEntryList
+ val authenticationEntryList = providerDisplayInfo.authenticationEntryList
+ Card() {
Column() {
- Icon(
- bitmap = providerInfo.credentialTypeIcon.toBitmap().asImageBitmap(),
- contentDescription = null,
- tint = Color.Unspecified,
- modifier = Modifier.align(alignment = Alignment.CenterHorizontally).padding(top = 24.dp)
- )
Text(
- text = stringResource(R.string.choose_sign_in_title),
- style = Typography.subtitle1,
- modifier = Modifier
- .padding(all = 24.dp)
- .align(alignment = Alignment.CenterHorizontally)
+ text = stringResource(
+ if (sortedUserNameToCredentialEntryList.size == 1) {
+ if (sortedUserNameToCredentialEntryList.first().sortedCredentialEntryList
+ .first().credentialType == PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL
+ )
+ R.string.get_dialog_title_use_passkey_for
+ else R.string.get_dialog_title_use_sign_in_for
+ } else R.string.get_dialog_title_choose_sign_in_for,
+ requestDisplayInfo.appDomainName
+ ),
+ style = MaterialTheme.typography.titleMedium,
+ modifier = Modifier.padding(all = 24.dp).align(alignment = Alignment.CenterHorizontally)
)
- Text(
- text = requestDisplayInfo.appDomainName,
- style = Typography.body2,
- modifier = Modifier.padding(horizontal = 28.dp)
- )
- Divider(
- thickness = 24.dp,
- color = Color.Transparent
- )
+
Card(
- shape = Shapes.medium,
+ shape = MaterialTheme.shapes.large,
modifier = Modifier
.padding(horizontal = 24.dp)
.align(alignment = Alignment.CenterHorizontally)
@@ -130,15 +130,20 @@
LazyColumn(
verticalArrangement = Arrangement.spacedBy(2.dp)
) {
- providerInfo.credentialOptions.forEach {
- item {
- CredentialOptionRow(credentialOptionInfo = it, onOptionSelected = onOptionSelected)
- }
+ items(sortedUserNameToCredentialEntryList) {
+ CredentialEntryRow(
+ credentialEntryInfo = it.sortedCredentialEntryList.first(),
+ onEntrySelected = onEntrySelected,
+ )
}
- if (multiProvider) {
- item {
- MoreOptionRow(onSelect = onMoreOptionSelected)
- }
+ items(authenticationEntryList) {
+ AuthenticationEntryRow(
+ authenticationEntryInfo = it,
+ onEntrySelected = onEntrySelected,
+ )
+ }
+ item {
+ SignInAnotherWayRow(onSelect = onMoreOptionSelected)
}
}
}
@@ -161,57 +166,252 @@
}
}
-@ExperimentalMaterialApi
+/** Draws the secondary credential selection page, where all sign-in options are listed. */
+@OptIn(ExperimentalMaterial3Api::class)
@Composable
-fun CredentialOptionRow(
- credentialOptionInfo: CredentialOptionInfo,
- onOptionSelected: (String, String) -> Unit,
+fun AllSignInOptionCard(
+ providerInfoList: List<ProviderInfo>,
+ providerDisplayInfo: ProviderDisplayInfo,
+ onEntrySelected: (EntryInfo) -> Unit,
+ onBackButtonClicked: () -> Unit,
) {
- Chip(
- modifier = Modifier.fillMaxWidth(),
- onClick = {onOptionSelected(credentialOptionInfo.entryKey, credentialOptionInfo.entrySubkey)},
- leadingIcon = {
- Image(modifier = Modifier.size(24.dp, 24.dp).padding(start = 10.dp),
- bitmap = credentialOptionInfo.icon.toBitmap().asImageBitmap(),
- // TODO: add description.
- contentDescription = "")
- },
- colors = ChipDefaults.chipColors(
- backgroundColor = Grey100,
- leadingIconContentColor = Grey100
- ),
- shape = Shapes.large
- ) {
+ val sortedUserNameToCredentialEntryList = providerDisplayInfo.sortedUserNameToCredentialEntryList
+ val authenticationEntryList = providerDisplayInfo.authenticationEntryList
+ Card() {
Column() {
- Text(
- text = credentialOptionInfo.entryKey,
- style = Typography.h6,
- modifier = Modifier.padding(top = 16.dp)
+ TopAppBar(
+ colors = TopAppBarDefaults.smallTopAppBarColors(
+ containerColor = Color.Transparent,
+ ),
+ title = {
+ Text(
+ text = stringResource(R.string.get_dialog_title_sign_in_options),
+ style = MaterialTheme.typography.titleMedium
+ )
+ },
+ navigationIcon = {
+ IconButton(onClick = onBackButtonClicked) {
+ Icon(
+ Icons.Filled.ArrowBack,
+ contentDescription = stringResource(R.string.accessibility_back_arrow_button))
+ }
+ },
+ modifier = Modifier.padding(top = 12.dp)
)
- Text(
- text = credentialOptionInfo.entrySubkey,
- style = Typography.body2,
- modifier = Modifier.padding(bottom = 16.dp)
- )
+
+ Card(
+ shape = MaterialTheme.shapes.large,
+ modifier = Modifier
+ .padding(start = 24.dp, end = 24.dp, bottom = 24.dp)
+ .align(alignment = Alignment.CenterHorizontally)
+ ) {
+ LazyColumn(
+ verticalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ // For username
+ items(sortedUserNameToCredentialEntryList) { item ->
+ PerUserNameCredentials(
+ perUserNameCredentialEntryList = item,
+ onEntrySelected = onEntrySelected,
+ )
+ }
+ // Locked password manager
+ item {
+ if (!authenticationEntryList.isEmpty()) {
+ LockedCredentials(
+ authenticationEntryList = authenticationEntryList,
+ onEntrySelected = onEntrySelected,
+ )
+ }
+ }
+ // TODO: Remote action
+ // Manage sign-ins
+ item {
+ ActionChips(providerInfoList = providerInfoList, onEntrySelected = onEntrySelected)
+ }
+ }
+ }
}
}
}
-@ExperimentalMaterialApi
+// TODO: create separate rows for primary and secondary pages.
+// TODO: reuse rows and columns across types.
+
@Composable
-fun MoreOptionRow(onSelect: () -> Unit) {
- Chip(
- modifier = Modifier.fillMaxWidth().height(52.dp),
- onClick = onSelect,
- colors = ChipDefaults.chipColors(
- backgroundColor = Grey100,
- leadingIconContentColor = Grey100
- ),
- shape = Shapes.large
- ) {
- Text(
- text = stringResource(R.string.string_more_options),
- style = Typography.h6,
- )
+fun ActionChips(
+ providerInfoList: List<ProviderInfo>,
+ onEntrySelected: (EntryInfo) -> Unit,
+) {
+ val actionChips = providerInfoList.flatMap { it.actionEntryList }
+ if (actionChips.isEmpty()) {
+ return
}
+
+ Text(
+ text = stringResource(R.string.get_dialog_heading_manage_sign_ins),
+ style = MaterialTheme.typography.labelLarge,
+ modifier = Modifier.padding(vertical = 8.dp)
+ )
+ // TODO: tweak padding.
+ Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
+ actionChips.forEach {
+ ActionEntryRow(it, onEntrySelected)
+ }
+ }
+}
+
+@Composable
+fun LockedCredentials(
+ authenticationEntryList: List<AuthenticationEntryInfo>,
+ onEntrySelected: (EntryInfo) -> Unit,
+) {
+ Text(
+ text = stringResource(R.string.get_dialog_heading_locked_password_managers),
+ style = MaterialTheme.typography.labelLarge,
+ modifier = Modifier.padding(vertical = 8.dp)
+ )
+ authenticationEntryList.forEach {
+ AuthenticationEntryRow(it, onEntrySelected)
+ }
+}
+
+@Composable
+fun PerUserNameCredentials(
+ perUserNameCredentialEntryList: PerUserNameCredentialEntryList,
+ onEntrySelected: (EntryInfo) -> Unit,
+) {
+ Text(
+ text = stringResource(
+ R.string.get_dialog_heading_for_username, perUserNameCredentialEntryList.userName),
+ style = MaterialTheme.typography.labelLarge,
+ modifier = Modifier.padding(vertical = 8.dp)
+ )
+ perUserNameCredentialEntryList.sortedCredentialEntryList.forEach {
+ CredentialEntryRow(it, onEntrySelected)
+ }
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun CredentialEntryRow(
+ credentialEntryInfo: CredentialEntryInfo,
+ onEntrySelected: (EntryInfo) -> Unit,
+) {
+ SuggestionChip(
+ modifier = Modifier.fillMaxWidth(),
+ onClick = {onEntrySelected(credentialEntryInfo)},
+ icon = {
+ Image(modifier = Modifier.size(24.dp, 24.dp).padding(start = 10.dp),
+ bitmap = credentialEntryInfo.icon.toBitmap().asImageBitmap(),
+ // TODO: add description.
+ contentDescription = "")
+ },
+ shape = MaterialTheme.shapes.large,
+ label = {
+ Column() {
+ // TODO: fix the text values.
+ Text(
+ text = credentialEntryInfo.userName,
+ style = MaterialTheme.typography.titleLarge,
+ modifier = Modifier.padding(top = 16.dp)
+ )
+ Text(
+ text =
+ if (TextUtils.isEmpty(credentialEntryInfo.displayName))
+ credentialEntryInfo.credentialTypeDisplayName
+ else
+ credentialEntryInfo.credentialTypeDisplayName +
+ stringResource(R.string.get_dialog_sign_in_type_username_separator) +
+ credentialEntryInfo.displayName,
+ style = MaterialTheme.typography.bodyMedium,
+ modifier = Modifier.padding(bottom = 16.dp)
+ )
+ }
+ }
+ )
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun AuthenticationEntryRow(
+ authenticationEntryInfo: AuthenticationEntryInfo,
+ onEntrySelected: (EntryInfo) -> Unit,
+) {
+ SuggestionChip(
+ modifier = Modifier.fillMaxWidth(),
+ onClick = {onEntrySelected(authenticationEntryInfo)},
+ icon = {
+ Image(modifier = Modifier.size(24.dp, 24.dp).padding(start = 10.dp),
+ bitmap = authenticationEntryInfo.icon.toBitmap().asImageBitmap(),
+ // TODO: add description.
+ contentDescription = "")
+ },
+ shape = MaterialTheme.shapes.large,
+ label = {
+ Column() {
+ // TODO: fix the text values.
+ Text(
+ text = authenticationEntryInfo.title,
+ style = MaterialTheme.typography.titleLarge,
+ modifier = Modifier.padding(top = 16.dp)
+ )
+ Text(
+ text = stringResource(R.string.locked_credential_entry_label_subtext),
+ style = MaterialTheme.typography.bodyMedium,
+ modifier = Modifier.padding(bottom = 16.dp)
+ )
+ }
+ }
+ )
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun ActionEntryRow(
+ actionEntryInfo: ActionEntryInfo,
+ onEntrySelected: (EntryInfo) -> Unit,
+) {
+ SuggestionChip(
+ modifier = Modifier.fillMaxWidth(),
+ onClick = { onEntrySelected(actionEntryInfo) },
+ icon = {
+ Image(modifier = Modifier.size(24.dp, 24.dp).padding(start = 10.dp),
+ bitmap = actionEntryInfo.icon.toBitmap().asImageBitmap(),
+ // TODO: add description.
+ contentDescription = "")
+ },
+ shape = MaterialTheme.shapes.large,
+ label = {
+ Column() {
+ Text(
+ text = actionEntryInfo.title,
+ style = MaterialTheme.typography.titleLarge,
+ )
+ if (actionEntryInfo.subTitle != null) {
+ Text(
+ text = actionEntryInfo.subTitle,
+ style = MaterialTheme.typography.bodyMedium,
+ )
+ }
+ }
+ }
+ )
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun SignInAnotherWayRow(onSelect: () -> Unit) {
+ SuggestionChip(
+ modifier = Modifier.fillMaxWidth(),
+ onClick = onSelect,
+ shape = MaterialTheme.shapes.large,
+ label = {
+ Text(
+ text = stringResource(R.string.get_dialog_use_saved_passkey_for),
+ style = MaterialTheme.typography.titleLarge,
+ modifier = Modifier.padding(vertical = 16.dp)
+ )
+ }
+ )
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt
index 7b6c30a..f78456a 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt
@@ -26,12 +26,13 @@
import com.android.credentialmanager.CredentialManagerRepo
import com.android.credentialmanager.common.DialogResult
import com.android.credentialmanager.common.ResultState
+import com.android.credentialmanager.jetpack.developer.PublicKeyCredential
data class GetCredentialUiState(
- val providers: List<ProviderInfo>,
+ val providerInfoList: List<ProviderInfo>,
val currentScreenState: GetScreenState,
val requestDisplayInfo: RequestDisplayInfo,
- val selectedProvider: ProviderInfo? = null,
+ val providerDisplayInfo: ProviderDisplayInfo = toProviderDisplayInfo(providerInfoList),
)
class GetCredentialViewModel(
@@ -49,20 +50,28 @@
return dialogResult
}
- fun onCredentailSelected(entryKey: String, entrySubkey: String) {
- Log.d("Account Selector", "credential selected: {key=$entryKey,subkey=$entrySubkey}")
+ fun onEntrySelected(entry: EntryInfo) {
+ Log.d("Account Selector", "credential selected:" +
+ " {provider=${entry.providerId}, key=${entry.entryKey}, subkey=${entry.entrySubkey}}")
CredentialManagerRepo.getInstance().onOptionSelected(
- uiState.selectedProvider!!.name,
- entryKey,
- entrySubkey
+ entry.providerId,
+ entry.entryKey,
+ entry.entrySubkey
)
- dialogResult.value = DialogResult(
- ResultState.COMPLETE,
- )
+ dialogResult.value = DialogResult(ResultState.COMPLETE)
}
fun onMoreOptionSelected() {
Log.d("Account Selector", "More Option selected")
+ uiState = uiState.copy(
+ currentScreenState = GetScreenState.ALL_SIGN_IN_OPTIONS
+ )
+ }
+
+ fun onBackToPrimarySelectionScreen() {
+ uiState = uiState.copy(
+ currentScreenState = GetScreenState.PRIMARY_SELECTION
+ )
}
fun onCancel() {
@@ -70,3 +79,75 @@
dialogResult.value = DialogResult(ResultState.CANCELED)
}
}
+
+private fun toProviderDisplayInfo(
+ providerInfoList: List<ProviderInfo>
+): ProviderDisplayInfo {
+
+ val userNameToCredentialEntryMap = mutableMapOf<String, MutableList<CredentialEntryInfo>>()
+ val authenticationEntryList = mutableListOf<AuthenticationEntryInfo>()
+ providerInfoList.forEach { providerInfo ->
+ if (providerInfo.authenticationEntry != null) {
+ authenticationEntryList.add(providerInfo.authenticationEntry)
+ }
+
+ providerInfo.credentialEntryList.forEach {
+ userNameToCredentialEntryMap.compute(
+ it.userName
+ ) {
+ _, v ->
+ if (v == null) {
+ mutableListOf(it)
+ } else {
+ v.add(it)
+ v
+ }
+ }
+ }
+ }
+
+ // Compose sortedUserNameToCredentialEntryList
+ val comparator = CredentialEntryInfoComparator()
+ // Sort per username
+ userNameToCredentialEntryMap.values.forEach {
+ it.sortWith(comparator)
+ }
+ // Transform to list of PerUserNameCredentialEntryLists and then sort across usernames
+ val sortedUserNameToCredentialEntryList = userNameToCredentialEntryMap.map {
+ PerUserNameCredentialEntryList(it.key, it.value)
+ }.sortedWith(
+ compareBy(comparator) { it.sortedCredentialEntryList.first() }
+ )
+
+ return ProviderDisplayInfo(
+ sortedUserNameToCredentialEntryList = sortedUserNameToCredentialEntryList,
+ authenticationEntryList = authenticationEntryList,
+ )
+}
+
+internal class CredentialEntryInfoComparator : Comparator<CredentialEntryInfo> {
+ override fun compare(p0: CredentialEntryInfo, p1: CredentialEntryInfo): Int {
+ // First order by last used timestamp
+ if (p0.lastUsedTimeMillis != null && p1.lastUsedTimeMillis != null) {
+ if (p0.lastUsedTimeMillis < p1.lastUsedTimeMillis) {
+ return 1
+ } else if (p0.lastUsedTimeMillis > p1.lastUsedTimeMillis) {
+ return -1
+ }
+ } else if (p0.lastUsedTimeMillis != null && p0.lastUsedTimeMillis > 0) {
+ return -1
+ } else if (p1.lastUsedTimeMillis != null && p1.lastUsedTimeMillis > 0) {
+ return 1
+ }
+
+ // Then prefer passkey type for its security benefits
+ if (p0.credentialType != p1.credentialType) {
+ if (PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL == p0.credentialType) {
+ return -1
+ } else if (PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL == p1.credentialType) {
+ return 1
+ }
+ }
+ return 0
+ }
+}
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
index b427de6..c1d9ea9 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
@@ -19,34 +19,88 @@
import android.graphics.drawable.Drawable
data class ProviderInfo(
+ /**
+ * Unique id (component name) of this provider.
+ * Not for display purpose - [displayName] should be used for ui rendering.
+ */
+ val id: String,
val icon: Drawable,
- val name: String,
val displayName: String,
- val credentialTypeIcon: Drawable,
- val credentialOptions: List<CredentialOptionInfo>,
- // TODO: Add the authenticationOption
+ val credentialEntryList: List<CredentialEntryInfo>,
+ val authenticationEntry: AuthenticationEntryInfo?,
+ val actionEntryList: List<ActionEntryInfo>,
+ // TODO: add remote entry
)
-open class EntryInfo (
+/** Display-centric data structure derived from the [ProviderInfo]. This abstraction is not grouping
+ * by the provider id but instead focuses on structures convenient for display purposes. */
+data class ProviderDisplayInfo(
+ /**
+ * The credential entries grouped by userName, derived from all entries of the [providerInfoList].
+ * Note that the list order matters to the display order.
+ */
+ val sortedUserNameToCredentialEntryList: List<PerUserNameCredentialEntryList>,
+ val authenticationEntryList: List<AuthenticationEntryInfo>,
+)
+
+abstract class EntryInfo (
+ /** Unique id combination of this entry. Not for display purpose. */
+ val providerId: String,
val entryKey: String,
val entrySubkey: String,
)
-class CredentialOptionInfo(
+class CredentialEntryInfo(
+ providerId: String,
entryKey: String,
entrySubkey: String,
+ /** Type of this credential used for sorting. Not localized so must not be directly displayed. */
+ val credentialType: String,
+ /** Localized type value of this credential used for display purpose. */
+ val credentialTypeDisplayName: String,
+ val userName: String,
+ val displayName: String?,
val icon: Drawable,
- val usageData: String,
-) : EntryInfo(entryKey, entrySubkey)
+ val lastUsedTimeMillis: Long?,
+) : EntryInfo(providerId, entryKey, entrySubkey)
+
+// TODO: handle sub credential type values like password obfuscation.
+
+class AuthenticationEntryInfo(
+ providerId: String,
+ entryKey: String,
+ entrySubkey: String,
+ val title: String,
+ val icon: Drawable,
+) : EntryInfo(providerId, entryKey, entrySubkey)
+
+class ActionEntryInfo(
+ providerId: String,
+ entryKey: String,
+ entrySubkey: String,
+ val title: String,
+ val icon: Drawable,
+ val subTitle: String?,
+) : EntryInfo(providerId, entryKey, entrySubkey)
data class RequestDisplayInfo(
- val userName: String,
- val displayName: String,
- val type: String,
val appDomainName: String,
)
+/**
+ * @property userName the userName that groups all the entries in this list
+ * @property sortedCredentialEntryList the credential entries associated with the [userName] sorted
+ * by last used timestamps and then by credential types
+ */
+data class PerUserNameCredentialEntryList(
+ val userName: String,
+ val sortedCredentialEntryList: List<CredentialEntryInfo>,
+)
+
/** The name of the current screen. */
enum class GetScreenState {
- CREDENTIAL_SELECTION,
+ /** The primary credential selection page. */
+ PRIMARY_SELECTION,
+ /** The secondary credential selection page, where all sign-in options are listed. */
+ ALL_SIGN_IN_OPTIONS,
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialEntryUi.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialEntryUi.kt
index 12ab436..dfbcae1 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialEntryUi.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialEntryUi.kt
@@ -17,6 +17,7 @@
package com.android.credentialmanager.jetpack.provider
import android.app.slice.Slice
+import android.credentials.ui.Entry
import android.graphics.drawable.Icon
/**
@@ -24,23 +25,46 @@
*
* TODO: move to jetpack.
*/
-abstract class CredentialEntryUi(
- val credentialTypeIcon: Icon,
- val profileIcon: Icon?,
+class CredentialEntryUi(
+ val credentialType: CharSequence,
+ val credentialTypeDisplayName: CharSequence,
+ val userName: CharSequence,
+ val userDisplayName: CharSequence?,
+ val entryIcon: Icon,
val lastUsedTimeMillis: Long?,
val note: CharSequence?,
) {
companion object {
fun fromSlice(slice: Slice): CredentialEntryUi {
- return when (slice.spec?.type) {
- TYPE_PUBLIC_KEY_CREDENTIAL -> PasskeyCredentialEntryUi.fromSlice(slice)
- TYPE_PASSWORD_CREDENTIAL -> PasswordCredentialEntryUi.fromSlice(slice)
- else -> throw IllegalArgumentException("Unexpected type: ${slice.spec?.type}")
- }
- }
+ var credentialType = slice.spec!!.type
+ var credentialTypeDisplayName: CharSequence? = null
+ var userName: CharSequence? = null
+ var userDisplayName: CharSequence? = null
+ var entryIcon: Icon? = null
+ var lastUsedTimeMillis: Long? = null
+ var note: CharSequence? = null
- const val TYPE_PUBLIC_KEY_CREDENTIAL: String =
- "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL"
- const val TYPE_PASSWORD_CREDENTIAL: String = "androidx.credentials.TYPE_PASSWORD"
+ val items = slice.items
+ items.forEach {
+ if (it.hasHint(Entry.HINT_CREDENTIAL_TYPE_DISPLAY_NAME)) {
+ credentialTypeDisplayName = it.text
+ } else if (it.hasHint(Entry.HINT_USER_NAME)) {
+ userName = it.text
+ } else if (it.hasHint(Entry.HINT_PASSKEY_USER_DISPLAY_NAME)) {
+ userDisplayName = it.text
+ } else if (it.hasHint(Entry.HINT_PROFILE_ICON)) {
+ entryIcon = it.icon
+ } else if (it.hasHint(Entry.HINT_LAST_USED_TIME_MILLIS)) {
+ lastUsedTimeMillis = it.long
+ } else if (it.hasHint(Entry.HINT_NOTE)) {
+ note = it.text
+ }
+ }
+
+ return CredentialEntryUi(
+ credentialType, credentialTypeDisplayName!!, userName!!, userDisplayName, entryIcon!!,
+ lastUsedTimeMillis, note,
+ )
+ }
}
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/PasskeyCredentialEntryUi.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/PasskeyCredentialEntryUi.kt
deleted file mode 100644
index c5dbe66..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/PasskeyCredentialEntryUi.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.credentialmanager.jetpack.provider
-
-import android.app.slice.Slice
-import android.credentials.ui.Entry
-import android.graphics.drawable.Icon
-
-class PasskeyCredentialEntryUi(
- val userName: CharSequence,
- val userDisplayName: CharSequence?,
- credentialTypeIcon: Icon,
- profileIcon: Icon?,
- lastUsedTimeMillis: Long?,
- note: CharSequence?,
-) : CredentialEntryUi(credentialTypeIcon, profileIcon, lastUsedTimeMillis, note) {
- companion object {
- fun fromSlice(slice: Slice): CredentialEntryUi {
- var userName: CharSequence? = null
- var userDisplayName: CharSequence? = null
- var credentialTypeIcon: Icon? = null
- var profileIcon: Icon? = null
- var lastUsedTimeMillis: Long? = null
- var note: CharSequence? = null
-
- val items = slice.items
- items.forEach {
- if (it.hasHint(Entry.HINT_USER_NAME)) {
- userName = it.text
- } else if (it.hasHint(Entry.HINT_PASSKEY_USER_DISPLAY_NAME)) {
- userDisplayName = it.text
- } else if (it.hasHint(Entry.HINT_CREDENTIAL_TYPE_ICON)) {
- credentialTypeIcon = it.icon
- } else if (it.hasHint(Entry.HINT_PROFILE_ICON)) {
- profileIcon = it.icon
- } else if (it.hasHint(Entry.HINT_LAST_USED_TIME_MILLIS)) {
- lastUsedTimeMillis = it.long
- } else if (it.hasHint(Entry.HINT_NOTE)) {
- note = it.text
- }
- }
- // TODO: fail NPE more elegantly.
- return PasskeyCredentialEntryUi(
- userName!!, userDisplayName, credentialTypeIcon!!,
- profileIcon, lastUsedTimeMillis, note,
- )
- }
- }
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/PasswordCredentialEntryUi.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/PasswordCredentialEntryUi.kt
deleted file mode 100644
index 5049503..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/PasswordCredentialEntryUi.kt
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.credentialmanager.jetpack.provider
-
-import android.app.slice.Slice
-import android.credentials.ui.Entry
-import android.graphics.drawable.Icon
-
-/**
- * UI representation for a password credential entry used during the get credential flow.
- *
- * TODO: move to jetpack.
- */
-class PasswordCredentialEntryUi(
- val userName: CharSequence,
- val password: CharSequence,
- credentialTypeIcon: Icon,
- profileIcon: Icon?,
- lastUsedTimeMillis: Long?,
- note: CharSequence?,
-) : CredentialEntryUi(credentialTypeIcon, profileIcon, lastUsedTimeMillis, note) {
- companion object {
- fun fromSlice(slice: Slice): CredentialEntryUi {
- var userName: CharSequence? = null
- var password: CharSequence? = null
- var credentialTypeIcon: Icon? = null
- var profileIcon: Icon? = null
- var lastUsedTimeMillis: Long? = null
- var note: CharSequence? = null
-
- val items = slice.items
- items.forEach {
- if (it.hasHint(Entry.HINT_USER_NAME)) {
- userName = it.text
- } else if (it.hasHint(Entry.HINT_PASSWORD_VALUE)) {
- password = it.text
- } else if (it.hasHint(Entry.HINT_CREDENTIAL_TYPE_ICON)) {
- credentialTypeIcon = it.icon
- } else if (it.hasHint(Entry.HINT_PROFILE_ICON)) {
- profileIcon = it.icon
- } else if (it.hasHint(Entry.HINT_LAST_USED_TIME_MILLIS)) {
- lastUsedTimeMillis = it.long
- } else if (it.hasHint(Entry.HINT_NOTE)) {
- note = it.text
- }
- }
- // TODO: fail NPE more elegantly.
- return PasswordCredentialEntryUi(
- userName!!, password!!, credentialTypeIcon!!,
- profileIcon, lastUsedTimeMillis, note,
- )
- }
- }
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/SaveEntryUi.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/SaveEntryUi.kt
index b260cf6..bcc0531 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/SaveEntryUi.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/SaveEntryUi.kt
@@ -26,7 +26,7 @@
* TODO: move to jetpack.
*/
class SaveEntryUi(
- val userProviderAccountName: CharSequence,
+ val userProviderAccountName: CharSequence?,
val credentialTypeIcon: Icon?,
val profileIcon: Icon?,
val passwordCount: Int?,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Shape.kt b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Shape.kt
index cba8658..5ea6993 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Shape.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Shape.kt
@@ -1,7 +1,7 @@
package com.android.credentialmanager.ui.theme
import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.Shapes
+import androidx.compose.material3.Shapes
import androidx.compose.ui.unit.dp
val Shapes = Shapes(
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Theme.kt b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Theme.kt
index a9d20ae..248df92 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Theme.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Theme.kt
@@ -1,30 +1,21 @@
package com.android.credentialmanager.ui.theme
import androidx.compose.foundation.isSystemInDarkTheme
-import androidx.compose.material.MaterialTheme
-import androidx.compose.material.darkColors
-import androidx.compose.material.lightColors
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.lightColorScheme
+import androidx.compose.material3.darkColorScheme
import androidx.compose.runtime.Composable
-private val DarkColorPalette = darkColors(
+private val AppDarkColorScheme = darkColorScheme(
primary = Purple200,
- primaryVariant = Purple700,
- secondary = Teal200
+ secondary = Purple700,
+ tertiary = Teal200
)
-private val LightColorPalette = lightColors(
+private val AppLightColorScheme = lightColorScheme(
primary = Purple500,
- primaryVariant = Purple700,
- secondary = Teal200
-
- /* Other default colors to override
- background = Color.White,
- surface = Color.White,
- onPrimary = Color.White,
- onSecondary = Color.Black,
- onBackground = Color.Black,
- onSurface = Color.Black,
- */
+ secondary = Purple700,
+ tertiary = Teal200
)
@Composable
@@ -32,14 +23,14 @@
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
- val colors = if (darkTheme) {
- DarkColorPalette
+ val AppColorScheme = if (darkTheme) {
+ AppDarkColorScheme
} else {
- LightColorPalette
+ AppLightColorScheme
}
MaterialTheme(
- colors = colors,
+ colorScheme = AppColorScheme,
typography = Typography,
shapes = Shapes,
content = content
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Type.kt b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Type.kt
index d8fb01c..e09abbb 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Type.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Type.kt
@@ -1,6 +1,6 @@
package com.android.credentialmanager.ui.theme
-import androidx.compose.material.Typography
+import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
@@ -8,32 +8,32 @@
// Set of Material typography styles to start with
val Typography = Typography(
- subtitle1 = TextStyle(
+ titleMedium = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 24.sp,
lineHeight = 32.sp,
),
- body1 = TextStyle(
+ bodyLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 14.sp,
lineHeight = 20.sp,
),
- body2 = TextStyle(
+ bodyMedium = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 14.sp,
lineHeight = 20.sp,
color = textColorSecondary
),
- button = TextStyle(
+ labelLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 14.sp,
lineHeight = 20.sp,
),
- h6 = TextStyle(
+ titleLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 16.sp,
diff --git a/packages/PackageInstaller/res/values-te/strings.xml b/packages/PackageInstaller/res/values-te/strings.xml
index c016bfc..3344d4d 100644
--- a/packages/PackageInstaller/res/values-te/strings.xml
+++ b/packages/PackageInstaller/res/values-te/strings.xml
@@ -73,8 +73,8 @@
<string name="uninstall_all_blocked_profile_owner" msgid="2009393666026751501">"ఈ యాప్ కొందరు వినియోగదారులకు లేదా కొన్ని ప్రొఫైళ్లకు అవసరం, ఇతరులకు అన్ఇన్స్టాల్ చేయబడింది"</string>
<string name="uninstall_blocked_profile_owner" msgid="6373897407002404848">"మీ ప్రొఫైల్ కోసం ఈ యాప్ అవసరం, అందువల్ల దీన్ని అన్ఇన్స్టాల్ చేయడం కుదరదు."</string>
<string name="uninstall_blocked_device_owner" msgid="6724602931761073901">"మీ పరికర నిర్వాహకులకు ఈ యాప్ అవసరం, అందువల్ల దీన్ని అన్ఇన్స్టాల్ చేయడం కుదరదు."</string>
- <string name="manage_device_administrators" msgid="3092696419363842816">"పరికర నిర్వాహక యాప్లను నిర్వహించు"</string>
- <string name="manage_users" msgid="1243995386982560813">"వినియోగదారులను నిర్వహించు"</string>
+ <string name="manage_device_administrators" msgid="3092696419363842816">"పరికర నిర్వాహక యాప్లను మేనేజ్ చేయండి"</string>
+ <string name="manage_users" msgid="1243995386982560813">"వినియోగదారులను మేనేజ్ చేయండి"</string>
<string name="uninstall_failed_msg" msgid="2176744834786696012">"<xliff:g id="APP_NAME">%1$s</xliff:g>ని అన్ఇన్స్టాల్ చేయడం సాధ్యపడలేదు."</string>
<string name="Parse_error_dlg_text" msgid="1661404001063076789">"ప్యాకేజీని అన్వయించడంలో సమస్య ఏర్పడింది."</string>
<string name="wear_not_allowed_dlg_title" msgid="8664785993465117517">"Android Wear"</string>
diff --git a/packages/PackageInstaller/res/values/strings.xml b/packages/PackageInstaller/res/values/strings.xml
index 688d116..6a3b239 100644
--- a/packages/PackageInstaller/res/values/strings.xml
+++ b/packages/PackageInstaller/res/values/strings.xml
@@ -189,6 +189,9 @@
<!-- Text to show in warning dialog on the tv when the app source is not trusted [CHAR LIMIT=NONE] -->
<string name="untrusted_external_source_warning" product="tv">For your security, your TV currently isn’t allowed to install unknown apps from this source. You can change this in Settings.</string>
+ <!-- Text to show in warning dialog on the wear when the app source is not trusted [CHAR LIMIT=NONE] -->
+ <string name="untrusted_external_source_warning" product="watch">For your security, your watch currently isn’t allowed to install unknown apps from this source. You can change this in Settings.</string>
+
<!-- Text to show in warning dialog on the phone when the app source is not trusted [CHAR LIMIT=NONE] -->
<string name="untrusted_external_source_warning" product="default">For your security, your phone currently isn’t allowed to install unknown apps from this source. You can change this in Settings.</string>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index de76632..fa93670 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -124,9 +124,8 @@
private static final int DLG_INSTALL_ERROR = DLG_BASE + 4;
private static final int DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER = DLG_BASE + 5;
private static final int DLG_ANONYMOUS_SOURCE = DLG_BASE + 6;
- private static final int DLG_NOT_SUPPORTED_ON_WEAR = DLG_BASE + 7;
- private static final int DLG_EXTERNAL_SOURCE_BLOCKED = DLG_BASE + 8;
- private static final int DLG_INSTALL_APPS_RESTRICTED_FOR_USER = DLG_BASE + 9;
+ private static final int DLG_EXTERNAL_SOURCE_BLOCKED = DLG_BASE + 7;
+ private static final int DLG_INSTALL_APPS_RESTRICTED_FOR_USER = DLG_BASE + 8;
// If unknown sources are temporary allowed
private boolean mAllowUnknownSources;
@@ -189,8 +188,6 @@
case DLG_INSTALL_ERROR:
return InstallErrorDialog.newInstance(
mPm.getApplicationLabel(mPkgInfo.applicationInfo));
- case DLG_NOT_SUPPORTED_ON_WEAR:
- return NotSupportedOnWearDialog.newInstance();
case DLG_INSTALL_APPS_RESTRICTED_FOR_USER:
return SimpleErrorDialog.newInstance(
R.string.install_apps_user_restriction_dlg_text);
@@ -379,12 +376,8 @@
return;
}
- if (DeviceUtils.isWear(this)) {
- showDialogInner(DLG_NOT_SUPPORTED_ON_WEAR);
- return;
- }
-
final boolean wasSetUp = processAppSnippet(packageSource);
+
if (mLocalLOGV) Log.i(TAG, "wasSetUp: " + wasSetUp);
if (!wasSetUp) {
@@ -779,21 +772,6 @@
}
/**
- * An error dialog shown when the app is not supported on wear
- */
- public static class NotSupportedOnWearDialog extends SimpleErrorDialog {
- static SimpleErrorDialog newInstance() {
- return SimpleErrorDialog.newInstance(R.string.wear_not_allowed_dlg_text);
- }
-
- @Override
- public void onCancel(DialogInterface dialog) {
- getActivity().setResult(RESULT_OK);
- getActivity().finish();
- }
- }
-
- /**
* An error dialog shown when the device is out of space
*/
public static class OutOfSpaceDialog extends AppErrorDialog {
diff --git a/packages/SettingsLib/Spa/gallery/AndroidManifest.xml b/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
index f1a24af..50cab84 100644
--- a/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
+++ b/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
@@ -35,8 +35,8 @@
</activity>
<provider
- android:name=".GalleryEntryProvider"
- android:authorities="com.android.spa.gallery.provider"
+ android:name="com.android.settingslib.spa.framework.SpaSearchProvider"
+ android:authorities="com.android.spa.gallery.search.provider"
android:enabled="true"
android:exported="false">
</provider>
@@ -51,7 +51,7 @@
</activity>
<provider
android:name="com.android.settingslib.spa.framework.debug.DebugProvider"
- android:authorities="com.android.spa.gallery.debug"
+ android:authorities="com.android.spa.gallery.debug.provider"
android:enabled="true"
android:exported="false">
</provider>
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index 742e271..016b27f 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -80,7 +80,7 @@
override val browseActivityClass = GalleryMainActivity::class.java
- override val entryProviderAuthorities = "com.android.spa.gallery.provider"
+ override val searchProviderAuthorities = "com.android.spa.gallery.search.provider"
override val logger = LocalLogger()
}
diff --git a/packages/SettingsLib/Spa/settings.gradle b/packages/SettingsLib/Spa/settings.gradle
index cef79c1..b627a70 100644
--- a/packages/SettingsLib/Spa/settings.gradle
+++ b/packages/SettingsLib/Spa/settings.gradle
@@ -32,4 +32,5 @@
rootProject.name = "SpaLib"
include ':spa'
include ':gallery'
+include ':testutils'
include ':tests'
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/SpaSearchProvider.kt
similarity index 98%
rename from packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt
rename to packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/SpaSearchProvider.kt
index 38f41bc..35b9c0f 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/SpaSearchProvider.kt
@@ -33,7 +33,7 @@
import com.android.settingslib.spa.framework.common.addUri
import com.android.settingslib.spa.framework.common.getColumns
-private const val TAG = "EntryProvider"
+private const val TAG = "SpaSearchProvider"
/**
* The content provider to return entry related data, which can be used for search and hierarchy.
@@ -47,7 +47,7 @@
* $ adb shell content query --uri content://<AuthorityPath>/search_mutable_status
* $ adb shell content query --uri content://<AuthorityPath>/search_immutable_status
*/
-open class EntryProvider : ContentProvider() {
+class SpaSearchProvider : ContentProvider() {
private val spaEnvironment get() = SpaEnvironmentFactory.instance
private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt
index b831043..a9cb041 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt
@@ -64,7 +64,7 @@
open val browseActivityClass: Class<out Activity>? = null
- open val entryProviderAuthorities: String? = null
+ open val searchProviderAuthorities: String? = null
open val logger: SpaLogger = object : SpaLogger {}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
new file mode 100644
index 0000000..7db1ca1
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
@@ -0,0 +1,604 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.scaffold
+
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.AnimationState
+import androidx.compose.animation.core.CubicBezierEasing
+import androidx.compose.animation.core.DecayAnimationSpec
+import androidx.compose.animation.core.FastOutLinearInEasing
+import androidx.compose.animation.core.animateDecay
+import androidx.compose.animation.core.animateTo
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.draggable
+import androidx.compose.foundation.gestures.rememberDraggableState
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.asPaddingValues
+import androidx.compose.foundation.layout.navigationBars
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.windowInsetsPadding
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.LocalContentColor
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.ProvideTextStyle
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.material3.TopAppBarScrollBehavior
+import androidx.compose.material3.TopAppBarState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.SideEffect
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clipToBounds
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.graphics.lerp
+import androidx.compose.ui.layout.AlignmentLine
+import androidx.compose.ui.layout.LastBaseline
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.layoutId
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.semantics.clearAndSetSemantics
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.Velocity
+import androidx.compose.ui.unit.dp
+import com.android.settingslib.spa.framework.compose.horizontalValues
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import kotlin.math.abs
+import kotlin.math.max
+import kotlin.math.roundToInt
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+internal fun CustomizedTopAppBar(
+ title: @Composable () -> Unit,
+ navigationIcon: @Composable () -> Unit = {},
+ actions: @Composable RowScope.() -> Unit = {},
+) {
+ SingleRowTopAppBar(
+ title = title,
+ titleTextStyle = MaterialTheme.typography.titleMedium,
+ navigationIcon = navigationIcon,
+ actions = actions,
+ windowInsets = TopAppBarDefaults.windowInsets,
+ colors = topAppBarColors(),
+ )
+}
+
+/**
+ * The customized LargeTopAppBar for Settings.
+ */
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+internal fun CustomizedLargeTopAppBar(
+ title: String,
+ modifier: Modifier = Modifier,
+ navigationIcon: @Composable () -> Unit = {},
+ actions: @Composable RowScope.() -> Unit = {},
+ scrollBehavior: TopAppBarScrollBehavior? = null,
+) {
+ TwoRowsTopAppBar(
+ title = { Title(title = title, maxLines = 2) },
+ titleTextStyle = MaterialTheme.typography.displaySmall,
+ smallTitleTextStyle = MaterialTheme.typography.titleMedium,
+ titleBottomPadding = LargeTitleBottomPadding,
+ smallTitle = { Title(title = title, maxLines = 1) },
+ modifier = modifier,
+ navigationIcon = navigationIcon,
+ actions = actions,
+ colors = topAppBarColors(),
+ windowInsets = TopAppBarDefaults.windowInsets,
+ maxHeight = 176.dp,
+ pinnedHeight = ContainerHeight,
+ scrollBehavior = scrollBehavior,
+ )
+}
+
+@Composable
+private fun Title(title: String, maxLines: Int = Int.MAX_VALUE) {
+ Text(
+ text = title,
+ modifier = Modifier
+ .padding(
+ WindowInsets.navigationBars
+ .asPaddingValues()
+ .horizontalValues()
+ )
+ .padding(horizontal = SettingsDimension.itemPaddingAround),
+ overflow = TextOverflow.Ellipsis,
+ maxLines = maxLines,
+ )
+}
+
+@Composable
+private fun topAppBarColors() = TopAppBarColors(
+ containerColor = MaterialTheme.colorScheme.background,
+ scrolledContainerColor = SettingsTheme.colorScheme.surfaceHeader,
+ navigationIconContentColor = MaterialTheme.colorScheme.onSurface,
+ titleContentColor = MaterialTheme.colorScheme.onSurface,
+ actionIconContentColor = MaterialTheme.colorScheme.onSurfaceVariant,
+)
+
+/**
+ * Represents the colors used by a top app bar in different states.
+ * This implementation animates the container color according to the top app bar scroll state. It
+ * does not animate the leading, headline, or trailing colors.
+ */
+@Stable
+private class TopAppBarColors(
+ val containerColor: Color,
+ val scrolledContainerColor: Color,
+ val navigationIconContentColor: Color,
+ val titleContentColor: Color,
+ val actionIconContentColor: Color,
+) {
+
+ /**
+ * Represents the container color used for the top app bar.
+ *
+ * A [colorTransitionFraction] provides a percentage value that can be used to generate a color.
+ * Usually, an app bar implementation will pass in a [colorTransitionFraction] read from
+ * the [TopAppBarState.collapsedFraction] or the [TopAppBarState.overlappedFraction].
+ *
+ * @param colorTransitionFraction a `0.0` to `1.0` value that represents a color transition
+ * percentage
+ */
+ @Composable
+ fun containerColor(colorTransitionFraction: Float): Color {
+ return lerp(
+ containerColor,
+ scrolledContainerColor,
+ FastOutLinearInEasing.transform(colorTransitionFraction)
+ )
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || other !is TopAppBarColors) return false
+
+ if (containerColor != other.containerColor) return false
+ if (scrolledContainerColor != other.scrolledContainerColor) return false
+ if (navigationIconContentColor != other.navigationIconContentColor) return false
+ if (titleContentColor != other.titleContentColor) return false
+ if (actionIconContentColor != other.actionIconContentColor) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = containerColor.hashCode()
+ result = 31 * result + scrolledContainerColor.hashCode()
+ result = 31 * result + navigationIconContentColor.hashCode()
+ result = 31 * result + titleContentColor.hashCode()
+ result = 31 * result + actionIconContentColor.hashCode()
+
+ return result
+ }
+}
+
+/**
+ * A single-row top app bar that is designed to be called by the small and center aligned top app
+ * bar composables.
+ */
+@Composable
+private fun SingleRowTopAppBar(
+ title: @Composable () -> Unit,
+ titleTextStyle: TextStyle,
+ navigationIcon: @Composable () -> Unit,
+ actions: @Composable (RowScope.() -> Unit),
+ windowInsets: WindowInsets,
+ colors: TopAppBarColors,
+) {
+ // Wrap the given actions in a Row.
+ val actionsRow = @Composable {
+ Row(
+ horizontalArrangement = Arrangement.End,
+ verticalAlignment = Alignment.CenterVertically,
+ content = actions
+ )
+ }
+
+ // Compose a Surface with a TopAppBarLayout content.
+ Surface(color = colors.scrolledContainerColor) {
+ val height = LocalDensity.current.run { ContainerHeight.toPx() }
+ TopAppBarLayout(
+ modifier = Modifier
+ .windowInsetsPadding(windowInsets)
+ // clip after padding so we don't show the title over the inset area
+ .clipToBounds(),
+ heightPx = height,
+ navigationIconContentColor = colors.navigationIconContentColor,
+ titleContentColor = colors.titleContentColor,
+ actionIconContentColor = colors.actionIconContentColor,
+ title = title,
+ titleTextStyle = titleTextStyle,
+ titleAlpha = 1f,
+ titleVerticalArrangement = Arrangement.Center,
+ titleHorizontalArrangement = Arrangement.Start,
+ titleBottomPadding = 0,
+ hideTitleSemantics = false,
+ navigationIcon = navigationIcon,
+ actions = actionsRow,
+ )
+ }
+}
+
+/**
+ * A two-rows top app bar that is designed to be called by the Large and Medium top app bar
+ * composables.
+ *
+ * @throws [IllegalArgumentException] if the given [maxHeight] is equal or smaller than the
+ * [pinnedHeight]
+ */
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+private fun TwoRowsTopAppBar(
+ modifier: Modifier = Modifier,
+ title: @Composable () -> Unit,
+ titleTextStyle: TextStyle,
+ titleBottomPadding: Dp,
+ smallTitle: @Composable () -> Unit,
+ smallTitleTextStyle: TextStyle,
+ navigationIcon: @Composable () -> Unit,
+ actions: @Composable RowScope.() -> Unit,
+ windowInsets: WindowInsets,
+ colors: TopAppBarColors,
+ maxHeight: Dp,
+ pinnedHeight: Dp,
+ scrollBehavior: TopAppBarScrollBehavior?
+) {
+ if (maxHeight <= pinnedHeight) {
+ throw IllegalArgumentException(
+ "A TwoRowsTopAppBar max height should be greater than its pinned height"
+ )
+ }
+ val pinnedHeightPx: Float
+ val maxHeightPx: Float
+ val titleBottomPaddingPx: Int
+ LocalDensity.current.run {
+ pinnedHeightPx = pinnedHeight.toPx()
+ maxHeightPx = maxHeight.toPx()
+ titleBottomPaddingPx = titleBottomPadding.roundToPx()
+ }
+
+ // Sets the app bar's height offset limit to hide just the bottom title area and keep top title
+ // visible when collapsed.
+ SideEffect {
+ if (scrollBehavior?.state?.heightOffsetLimit != pinnedHeightPx - maxHeightPx) {
+ scrollBehavior?.state?.heightOffsetLimit = pinnedHeightPx - maxHeightPx
+ }
+ }
+
+ // Obtain the container Color from the TopAppBarColors using the `collapsedFraction`, as the
+ // bottom part of this TwoRowsTopAppBar changes color at the same rate the app bar expands or
+ // collapse.
+ // This will potentially animate or interpolate a transition between the container color and the
+ // container's scrolled color according to the app bar's scroll state.
+ val colorTransitionFraction = scrollBehavior?.state?.collapsedFraction ?: 0f
+ val appBarContainerColor by rememberUpdatedState(colors.containerColor(colorTransitionFraction))
+
+ // Wrap the given actions in a Row.
+ val actionsRow = @Composable {
+ Row(
+ horizontalArrangement = Arrangement.End,
+ verticalAlignment = Alignment.CenterVertically,
+ content = actions
+ )
+ }
+ val topTitleAlpha = TopTitleAlphaEasing.transform(colorTransitionFraction)
+ val bottomTitleAlpha = 1f - colorTransitionFraction
+ // Hide the top row title semantics when its alpha value goes below 0.5 threshold.
+ // Hide the bottom row title semantics when the top title semantics are active.
+ val hideTopRowSemantics = colorTransitionFraction < 0.5f
+ val hideBottomRowSemantics = !hideTopRowSemantics
+
+ // Set up support for resizing the top app bar when vertically dragging the bar itself.
+ val appBarDragModifier = if (scrollBehavior != null && !scrollBehavior.isPinned) {
+ Modifier.draggable(
+ orientation = Orientation.Vertical,
+ state = rememberDraggableState { delta ->
+ scrollBehavior.state.heightOffset = scrollBehavior.state.heightOffset + delta
+ },
+ onDragStopped = { velocity ->
+ settleAppBar(
+ scrollBehavior.state,
+ velocity,
+ scrollBehavior.flingAnimationSpec,
+ scrollBehavior.snapAnimationSpec
+ )
+ }
+ )
+ } else {
+ Modifier
+ }
+
+ Surface(modifier = modifier.then(appBarDragModifier), color = appBarContainerColor) {
+ Column {
+ TopAppBarLayout(
+ modifier = Modifier
+ .windowInsetsPadding(windowInsets)
+ // clip after padding so we don't show the title over the inset area
+ .clipToBounds(),
+ heightPx = pinnedHeightPx,
+ navigationIconContentColor = colors.navigationIconContentColor,
+ titleContentColor = colors.titleContentColor,
+ actionIconContentColor = colors.actionIconContentColor,
+ title = smallTitle,
+ titleTextStyle = smallTitleTextStyle,
+ titleAlpha = topTitleAlpha,
+ titleVerticalArrangement = Arrangement.Center,
+ titleHorizontalArrangement = Arrangement.Start,
+ titleBottomPadding = 0,
+ hideTitleSemantics = hideTopRowSemantics,
+ navigationIcon = navigationIcon,
+ actions = actionsRow,
+ )
+ TopAppBarLayout(
+ modifier = Modifier.clipToBounds(),
+ heightPx = maxHeightPx - pinnedHeightPx + (scrollBehavior?.state?.heightOffset
+ ?: 0f),
+ navigationIconContentColor = colors.navigationIconContentColor,
+ titleContentColor = colors.titleContentColor,
+ actionIconContentColor = colors.actionIconContentColor,
+ title = title,
+ titleTextStyle = titleTextStyle,
+ titleAlpha = bottomTitleAlpha,
+ titleVerticalArrangement = Arrangement.Bottom,
+ titleHorizontalArrangement = Arrangement.Start,
+ titleBottomPadding = titleBottomPaddingPx,
+ hideTitleSemantics = hideBottomRowSemantics,
+ navigationIcon = {},
+ actions = {}
+ )
+ }
+ }
+}
+
+/**
+ * The base [Layout] for all top app bars. This function lays out a top app bar navigation icon
+ * (leading icon), a title (header), and action icons (trailing icons). Note that the navigation and
+ * the actions are optional.
+ *
+ * @param heightPx the total height this layout is capped to
+ * @param navigationIconContentColor the content color that will be applied via a
+ * [LocalContentColor] when composing the navigation icon
+ * @param titleContentColor the color that will be applied via a [LocalContentColor] when composing
+ * the title
+ * @param actionIconContentColor the content color that will be applied via a [LocalContentColor]
+ * when composing the action icons
+ * @param title the top app bar title (header)
+ * @param titleTextStyle the title's text style
+ * @param modifier a [Modifier]
+ * @param titleAlpha the title's alpha
+ * @param titleVerticalArrangement the title's vertical arrangement
+ * @param titleHorizontalArrangement the title's horizontal arrangement
+ * @param titleBottomPadding the title's bottom padding
+ * @param hideTitleSemantics hides the title node from the semantic tree. Apply this
+ * boolean when this layout is part of a [TwoRowsTopAppBar] to hide the title's semantics
+ * from accessibility services. This is needed to avoid having multiple titles visible to
+ * accessibility services at the same time, when animating between collapsed / expanded states.
+ * @param navigationIcon a navigation icon [Composable]
+ * @param actions actions [Composable]
+ */
+@Composable
+private fun TopAppBarLayout(
+ modifier: Modifier,
+ heightPx: Float,
+ navigationIconContentColor: Color,
+ titleContentColor: Color,
+ actionIconContentColor: Color,
+ title: @Composable () -> Unit,
+ titleTextStyle: TextStyle,
+ titleAlpha: Float,
+ titleVerticalArrangement: Arrangement.Vertical,
+ titleHorizontalArrangement: Arrangement.Horizontal,
+ titleBottomPadding: Int,
+ hideTitleSemantics: Boolean,
+ navigationIcon: @Composable () -> Unit,
+ actions: @Composable () -> Unit,
+) {
+ Layout(
+ {
+ Box(
+ Modifier
+ .layoutId("navigationIcon")
+ .padding(start = TopAppBarHorizontalPadding)
+ ) {
+ CompositionLocalProvider(
+ LocalContentColor provides navigationIconContentColor,
+ content = navigationIcon
+ )
+ }
+ Box(
+ Modifier
+ .layoutId("title")
+ .padding(horizontal = TopAppBarHorizontalPadding)
+ .then(if (hideTitleSemantics) Modifier.clearAndSetSemantics { } else Modifier)
+ .graphicsLayer(alpha = titleAlpha)
+ ) {
+ ProvideTextStyle(value = titleTextStyle) {
+ CompositionLocalProvider(
+ LocalContentColor provides titleContentColor,
+ content = title
+ )
+ }
+ }
+ Box(
+ Modifier
+ .layoutId("actionIcons")
+ .padding(end = TopAppBarHorizontalPadding)
+ ) {
+ CompositionLocalProvider(
+ LocalContentColor provides actionIconContentColor,
+ content = actions
+ )
+ }
+ },
+ modifier = modifier
+ ) { measurables, constraints ->
+ val navigationIconPlaceable =
+ measurables.first { it.layoutId == "navigationIcon" }
+ .measure(constraints.copy(minWidth = 0))
+ val actionIconsPlaceable =
+ measurables.first { it.layoutId == "actionIcons" }
+ .measure(constraints.copy(minWidth = 0))
+
+ val maxTitleWidth = if (constraints.maxWidth == Constraints.Infinity) {
+ constraints.maxWidth
+ } else {
+ (constraints.maxWidth - navigationIconPlaceable.width - actionIconsPlaceable.width)
+ .coerceAtLeast(0)
+ }
+ val titlePlaceable =
+ measurables.first { it.layoutId == "title" }
+ .measure(constraints.copy(minWidth = 0, maxWidth = maxTitleWidth))
+
+ // Locate the title's baseline.
+ val titleBaseline =
+ if (titlePlaceable[LastBaseline] != AlignmentLine.Unspecified) {
+ titlePlaceable[LastBaseline]
+ } else {
+ 0
+ }
+
+ val layoutHeight = heightPx.roundToInt()
+
+ layout(constraints.maxWidth, layoutHeight) {
+ // Navigation icon
+ navigationIconPlaceable.placeRelative(
+ x = 0,
+ y = (layoutHeight - navigationIconPlaceable.height) / 2
+ )
+
+ // Title
+ titlePlaceable.placeRelative(
+ x = when (titleHorizontalArrangement) {
+ Arrangement.Center -> (constraints.maxWidth - titlePlaceable.width) / 2
+ Arrangement.End ->
+ constraints.maxWidth - titlePlaceable.width - actionIconsPlaceable.width
+ // Arrangement.Start.
+ // A TopAppBarTitleInset will make sure the title is offset in case the
+ // navigation icon is missing.
+ else -> max(TopAppBarTitleInset.roundToPx(), navigationIconPlaceable.width)
+ },
+ y = when (titleVerticalArrangement) {
+ Arrangement.Center -> (layoutHeight - titlePlaceable.height) / 2
+ // Apply bottom padding from the title's baseline only when the Arrangement is
+ // "Bottom".
+ Arrangement.Bottom ->
+ if (titleBottomPadding == 0) layoutHeight - titlePlaceable.height
+ else layoutHeight - titlePlaceable.height - max(
+ 0,
+ titleBottomPadding - titlePlaceable.height + titleBaseline
+ )
+ // Arrangement.Top
+ else -> 0
+ }
+ )
+
+ // Action icons
+ actionIconsPlaceable.placeRelative(
+ x = constraints.maxWidth - actionIconsPlaceable.width,
+ y = (layoutHeight - actionIconsPlaceable.height) / 2
+ )
+ }
+ }
+}
+
+
+/**
+ * Settles the app bar by flinging, in case the given velocity is greater than zero, and snapping
+ * after the fling settles.
+ */
+@OptIn(ExperimentalMaterial3Api::class)
+private suspend fun settleAppBar(
+ state: TopAppBarState,
+ velocity: Float,
+ flingAnimationSpec: DecayAnimationSpec<Float>?,
+ snapAnimationSpec: AnimationSpec<Float>?
+): Velocity {
+ // Check if the app bar is completely collapsed/expanded. If so, no need to settle the app bar,
+ // and just return Zero Velocity.
+ // Note that we don't check for 0f due to float precision with the collapsedFraction
+ // calculation.
+ if (state.collapsedFraction < 0.01f || state.collapsedFraction == 1f) {
+ return Velocity.Zero
+ }
+ var remainingVelocity = velocity
+ // In case there is an initial velocity that was left after a previous user fling, animate to
+ // continue the motion to expand or collapse the app bar.
+ if (flingAnimationSpec != null && abs(velocity) > 1f) {
+ var lastValue = 0f
+ AnimationState(
+ initialValue = 0f,
+ initialVelocity = velocity,
+ )
+ .animateDecay(flingAnimationSpec) {
+ val delta = value - lastValue
+ val initialHeightOffset = state.heightOffset
+ state.heightOffset = initialHeightOffset + delta
+ val consumed = abs(initialHeightOffset - state.heightOffset)
+ lastValue = value
+ remainingVelocity = this.velocity
+ // avoid rounding errors and stop if anything is unconsumed
+ if (abs(delta - consumed) > 0.5f) this.cancelAnimation()
+ }
+ }
+ // Snap if animation specs were provided.
+ if (snapAnimationSpec != null) {
+ if (state.heightOffset < 0 &&
+ state.heightOffset > state.heightOffsetLimit
+ ) {
+ AnimationState(initialValue = state.heightOffset).animateTo(
+ if (state.collapsedFraction < 0.5f) {
+ 0f
+ } else {
+ state.heightOffsetLimit
+ },
+ animationSpec = snapAnimationSpec
+ ) { state.heightOffset = value }
+ }
+ }
+
+ return Velocity(0f, remainingVelocity)
+}
+
+// An easing function used to compute the alpha value that is applied to the top title part of a
+// Medium or Large app bar.
+private val TopTitleAlphaEasing = CubicBezierEasing(.8f, 0f, .8f, .15f)
+
+private val ContainerHeight = 56.dp
+private val LargeTitleBottomPadding = 28.dp
+private val TopAppBarHorizontalPadding = 4.dp
+
+// A title inset when the App-Bar is a Medium or Large one. Also used to size a spacer when the
+// navigation icon is missing.
+private val TopAppBarTitleInset = 16.dp - TopAppBarHorizontalPadding
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt
index efc623a..b4852e4 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt
@@ -24,17 +24,14 @@
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
-import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
-import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
@@ -136,7 +133,6 @@
}
}
-@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun SearchTopAppBar(
query: TextFieldValue,
@@ -144,20 +140,16 @@
onClose: () -> Unit,
actions: @Composable RowScope.() -> Unit = {},
) {
- Surface(color = SettingsTheme.colorScheme.surfaceHeader) {
- TopAppBar(
- title = { SearchBox(query, onQueryChange) },
- modifier = Modifier.statusBarsPadding(),
- navigationIcon = { CollapseAction(onClose) },
- actions = {
- if (query.text.isNotEmpty()) {
- ClearAction { onQueryChange(TextFieldValue()) }
- }
- actions()
- },
- colors = TopAppBarDefaults.smallTopAppBarColors(containerColor = Color.Transparent),
- )
- }
+ CustomizedTopAppBar(
+ title = { SearchBox(query, onQueryChange) },
+ navigationIcon = { CollapseAction(onClose) },
+ actions = {
+ if (query.text.isNotEmpty()) {
+ ClearAction { onQueryChange(TextFieldValue()) }
+ }
+ actions()
+ },
+ )
BackHandler { onClose() }
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsTopAppBar.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsTopAppBar.kt
index f7cb035..3311792 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsTopAppBar.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsTopAppBar.kt
@@ -17,24 +17,9 @@
package com.android.settingslib.spa.widget.scaffold
import androidx.compose.foundation.layout.RowScope
-import androidx.compose.foundation.layout.WindowInsets
-import androidx.compose.foundation.layout.asPaddingValues
-import androidx.compose.foundation.layout.navigationBars
-import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.LargeTopAppBar
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
-import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.text.style.TextOverflow
-import com.android.settingslib.spa.framework.compose.horizontalValues
-import com.android.settingslib.spa.framework.theme.SettingsDimension
-import com.android.settingslib.spa.framework.theme.SettingsTheme
-import com.android.settingslib.spa.framework.theme.rememberSettingsTypography
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -43,20 +28,12 @@
scrollBehavior: TopAppBarScrollBehavior,
actions: @Composable RowScope.() -> Unit,
) {
- val colorScheme = MaterialTheme.colorScheme
- // TODO: Remove MaterialTheme() after top app bar color fixed in AndroidX.
- MaterialTheme(
- colorScheme = remember { colorScheme.copy(surface = colorScheme.background) },
- typography = rememberSettingsTypography(),
- ) {
- LargeTopAppBar(
- title = { Title(title) },
- navigationIcon = { NavigateBack() },
- actions = actions,
- colors = largeTopAppBarColors(),
- scrollBehavior = scrollBehavior,
- )
- }
+ CustomizedLargeTopAppBar(
+ title = title,
+ navigationIcon = { NavigateBack() },
+ actions = actions,
+ scrollBehavior = scrollBehavior,
+ )
}
@OptIn(ExperimentalMaterial3Api::class)
@@ -65,22 +42,3 @@
heightOffset = heightOffsetLimit
}
}
-
-@Composable
-private fun Title(title: String) {
- Text(
- text = title,
- modifier = Modifier
- .padding(WindowInsets.navigationBars.asPaddingValues().horizontalValues())
- .padding(SettingsDimension.itemPaddingAround),
- overflow = TextOverflow.Ellipsis,
- maxLines = 1,
- )
-}
-
-@OptIn(ExperimentalMaterial3Api::class)
-@Composable
-private fun largeTopAppBarColors() = TopAppBarDefaults.largeTopAppBarColors(
- containerColor = MaterialTheme.colorScheme.background,
- scrolledContainerColor = SettingsTheme.colorScheme.surfaceHeader,
-)
diff --git a/packages/SettingsLib/Spa/tests/Android.bp b/packages/SettingsLib/Spa/tests/Android.bp
index 2449dec..dcfc171 100644
--- a/packages/SettingsLib/Spa/tests/Android.bp
+++ b/packages/SettingsLib/Spa/tests/Android.bp
@@ -26,6 +26,7 @@
static_libs: [
"SpaLib",
+ "SpaLibTestUtils",
"androidx.test.runner",
"androidx.test.ext.junit",
"androidx.compose.runtime_runtime",
diff --git a/packages/SettingsLib/Spa/tests/build.gradle b/packages/SettingsLib/Spa/tests/build.gradle
index 5261091..529a201 100644
--- a/packages/SettingsLib/Spa/tests/build.gradle
+++ b/packages/SettingsLib/Spa/tests/build.gradle
@@ -59,6 +59,7 @@
dependencies {
androidTestImplementation project(":spa")
+ androidTestImplementation project(":testutils")
androidTestImplementation "androidx.test.ext:junit-ktx:1.1.3"
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$jetpack_compose_version"
androidTestImplementation "com.google.truth:truth:1.1.3"
diff --git a/packages/SettingsLib/Spa/testutils/Android.bp b/packages/SettingsLib/Spa/testutils/Android.bp
new file mode 100644
index 0000000..68ad414
--- /dev/null
+++ b/packages/SettingsLib/Spa/testutils/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_library {
+ name: "SpaLibTestUtils",
+
+ srcs: ["src/**/*.kt"],
+
+ static_libs: [
+ "mockito-target-minus-junit4",
+ ],
+ kotlincflags: [
+ "-Xjvm-default=all",
+ ],
+ min_sdk_version: "31",
+}
diff --git a/packages/SettingsLib/Spa/testutils/AndroidManifest.xml b/packages/SettingsLib/Spa/testutils/AndroidManifest.xml
new file mode 100644
index 0000000..1aa7782
--- /dev/null
+++ b/packages/SettingsLib/Spa/testutils/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.spa.testutils">
+ <uses-sdk android:minSdkVersion="21"/>
+</manifest>
diff --git a/packages/SettingsLib/Spa/testutils/build.gradle b/packages/SettingsLib/Spa/testutils/build.gradle
new file mode 100644
index 0000000..71d7d8a
--- /dev/null
+++ b/packages/SettingsLib/Spa/testutils/build.gradle
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+ id 'com.android.library'
+ id 'kotlin-android'
+}
+
+android {
+ compileSdk spa_target_sdk
+
+ defaultConfig {
+ minSdk spa_min_sdk
+ targetSdk spa_target_sdk
+ }
+
+ sourceSets {
+ main {
+ kotlin {
+ srcDir "src"
+ }
+ manifest.srcFile "AndroidManifest.xml"
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ freeCompilerArgs = ["-Xjvm-default=all", "-opt-in=kotlin.RequiresOptIn"]
+ }
+}
+
+dependencies {
+ api "org.mockito:mockito-android:3.4.6"
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/IFloatingTasks.aidl b/packages/SettingsLib/Spa/testutils/src/MockitoHelper.kt
similarity index 63%
copy from libs/WindowManager/Shell/src/com/android/wm/shell/floating/IFloatingTasks.aidl
copy to packages/SettingsLib/Spa/testutils/src/MockitoHelper.kt
index f79ca10..5ba54c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/IFloatingTasks.aidl
+++ b/packages/SettingsLib/Spa/testutils/src/MockitoHelper.kt
@@ -14,15 +14,16 @@
* limitations under the License.
*/
-package com.android.wm.shell.floating;
+package com.android.settingslib.spa.testutils
-import android.content.Intent;
+import org.mockito.Mockito
/**
- * Interface that is exposed to remote callers to manipulate floating task features.
+ * Returns Mockito.any() as nullable type to avoid java.lang.IllegalStateException when null is
+ * returned.
+ *
+ * Generic T is nullable because implicitly bounded by Any?.
*/
-interface IFloatingTasks {
+fun <T> any(type: Class<T>): T = Mockito.any(type)
- void showTask(in Intent intent) = 1;
-
-}
+inline fun <reified T> any(): T = any(T::class.java)
diff --git a/packages/SettingsLib/Spa/testutils/src/SpaTest.kt b/packages/SettingsLib/Spa/testutils/src/SpaTest.kt
new file mode 100644
index 0000000..a397bb4
--- /dev/null
+++ b/packages/SettingsLib/Spa/testutils/src/SpaTest.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.testutils
+
+import java.util.concurrent.TimeoutException
+
+/**
+ * Blocks until the given condition is satisfied.
+ */
+fun waitUntil(timeoutMillis: Long = 1000, condition: () -> Boolean) {
+ val startTime = System.currentTimeMillis()
+ while (!condition()) {
+ // Let Android run measure, draw and in general any other async operations.
+ Thread.sleep(10)
+ if (System.currentTimeMillis() - startTime > timeoutMillis) {
+ throw TimeoutException("Condition still not satisfied after $timeoutMillis ms")
+ }
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUser.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUser.kt
index a7de4ce..b2ea4a0 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUser.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUser.kt
@@ -23,7 +23,6 @@
import android.os.UserHandle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.lifecycle.Lifecycle
@@ -34,24 +33,25 @@
*/
@Composable
fun DisposableBroadcastReceiverAsUser(
- userId: Int,
intentFilter: IntentFilter,
+ userHandle: UserHandle,
+ onStart: () -> Unit = {},
onReceive: (Intent) -> Unit,
) {
- val broadcastReceiver = remember {
- object : BroadcastReceiver() {
+ val context = LocalContext.current
+ val lifecycleOwner = LocalLifecycleOwner.current
+ DisposableEffect(lifecycleOwner) {
+ val broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
onReceive(intent)
}
}
- }
- val context = LocalContext.current
- val lifecycleOwner = LocalLifecycleOwner.current
- DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_START) {
context.registerReceiverAsUser(
- broadcastReceiver, UserHandle.of(userId), intentFilter, null, null)
+ broadcastReceiver, userHandle, intentFilter, null, null
+ )
+ onStart()
} else if (event == Lifecycle.Event.ON_STOP) {
context.unregisterReceiver(broadcastReceiver)
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
index ee89003..487dbcb 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
@@ -21,13 +21,10 @@
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
-import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
/**
* The config used to load the App List.
@@ -40,36 +37,42 @@
/**
* The repository to load the App List data.
*/
-internal class AppListRepository(context: Context) {
+internal interface AppListRepository {
+ /** Loads the list of [ApplicationInfo]. */
+ suspend fun loadApps(config: AppListConfig): List<ApplicationInfo>
+
+ /** Gets the flow of predicate that could used to filter system app. */
+ fun showSystemPredicate(
+ userIdFlow: Flow<Int>,
+ showSystemFlow: Flow<Boolean>,
+ ): Flow<(app: ApplicationInfo) -> Boolean>
+}
+
+
+internal class AppListRepositoryImpl(context: Context) : AppListRepository {
private val packageManager = context.packageManager
- fun loadApps(configFlow: Flow<AppListConfig>): Flow<List<ApplicationInfo>> = configFlow
- .map { loadApps(it) }
- .flowOn(Dispatchers.Default)
+ override suspend fun loadApps(config: AppListConfig): List<ApplicationInfo> = coroutineScope {
+ val hiddenSystemModulesDeferred = async {
+ packageManager.getInstalledModules(0)
+ .filter { it.isHidden }
+ .map { it.packageName }
+ .toSet()
+ }
+ val flags = PackageManager.ApplicationInfoFlags.of(
+ (PackageManager.MATCH_DISABLED_COMPONENTS or
+ PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS).toLong()
+ )
+ val installedApplicationsAsUser =
+ packageManager.getInstalledApplicationsAsUser(flags, config.userId)
- private suspend fun loadApps(config: AppListConfig): List<ApplicationInfo> {
- return coroutineScope {
- val hiddenSystemModulesDeferred = async {
- packageManager.getInstalledModules(0)
- .filter { it.isHidden }
- .map { it.packageName }
- .toSet()
- }
- val flags = PackageManager.ApplicationInfoFlags.of(
- (PackageManager.MATCH_DISABLED_COMPONENTS or
- PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS).toLong()
- )
- val installedApplicationsAsUser =
- packageManager.getInstalledApplicationsAsUser(flags, config.userId)
-
- val hiddenSystemModules = hiddenSystemModulesDeferred.await()
- installedApplicationsAsUser.filter { app ->
- app.isInAppList(config.showInstantApps, hiddenSystemModules)
- }
+ val hiddenSystemModules = hiddenSystemModulesDeferred.await()
+ installedApplicationsAsUser.filter { app ->
+ app.isInAppList(config.showInstantApps, hiddenSystemModules)
}
}
- fun showSystemPredicate(
+ override fun showSystemPredicate(
userIdFlow: Flow<Int>,
showSystemFlow: Flow<Boolean>,
): Flow<(app: ApplicationInfo) -> Boolean> =
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt
index 1e487da..650b278 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt
@@ -17,6 +17,7 @@
package com.android.settingslib.spaprivileged.model.app
import android.app.Application
+import android.content.Context
import android.content.pm.ApplicationInfo
import android.icu.text.Collator
import androidx.lifecycle.AndroidViewModel
@@ -27,12 +28,16 @@
import java.util.concurrent.ConcurrentHashMap
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.launch
import kotlinx.coroutines.plus
internal data class AppListData<T : AppRecord>(
@@ -43,9 +48,15 @@
AppListData(appEntries.filter(predicate), option)
}
-@OptIn(ExperimentalCoroutinesApi::class)
internal class AppListViewModel<T : AppRecord>(
application: Application,
+) : AppListViewModelImpl<T>(application)
+
+@OptIn(ExperimentalCoroutinesApi::class)
+internal open class AppListViewModelImpl<T : AppRecord>(
+ application: Application,
+ appListRepositoryFactory: (Context) -> AppListRepository = ::AppListRepositoryImpl,
+ appRepositoryFactory: (Context) -> AppRepository = ::AppRepositoryImpl,
) : AndroidViewModel(application) {
val appListConfig = StateFlowBridge<AppListConfig>()
val listModel = StateFlowBridge<AppListModel<T>>()
@@ -53,16 +64,18 @@
val option = StateFlowBridge<Int>()
val searchQuery = StateFlowBridge<String>()
- private val appListRepository = AppListRepository(application)
- private val appRepository = AppRepositoryImpl(application)
+ private val appListRepository = appListRepositoryFactory(application)
+ private val appRepository = appRepositoryFactory(application)
private val collator = Collator.getInstance().freeze()
private val labelMap = ConcurrentHashMap<String, String>()
- private val scope = viewModelScope + Dispatchers.Default
+ private val scope = viewModelScope + Dispatchers.IO
private val userIdFlow = appListConfig.flow.map { it.userId }
+ private val appsStateFlow = MutableStateFlow<List<ApplicationInfo>?>(null)
+
private val recordListFlow = listModel.flow
- .flatMapLatest { it.transform(userIdFlow, appListRepository.loadApps(appListConfig.flow)) }
+ .flatMapLatest { it.transform(userIdFlow, appsStateFlow.filterNotNull()) }
.shareIn(scope = scope, started = SharingStarted.Eagerly, replay = 1)
private val systemFilteredFlow =
@@ -83,6 +96,12 @@
scheduleOnFirstLoaded()
}
+ fun reloadApps() {
+ viewModelScope.launch {
+ appsStateFlow.value = appListRepository.loadApps(appListConfig.flow.first())
+ }
+ }
+
private fun filterAndSort(option: Int) = listModel.flow.flatMapLatest { listModel ->
listModel.filter(userIdFlow, option, systemFilteredFlow)
.asyncMapItem { record ->
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppRepository.kt
index 34f12af..90710db 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppRepository.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppRepository.kt
@@ -22,8 +22,10 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.produceState
+import androidx.compose.ui.res.stringResource
import com.android.settingslib.Utils
import com.android.settingslib.spa.framework.compose.rememberContext
+import com.android.settingslib.spaprivileged.R
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@@ -34,7 +36,12 @@
fun loadLabel(app: ApplicationInfo): String
@Composable
- fun produceLabel(app: ApplicationInfo): State<String>
+ fun produceLabel(app: ApplicationInfo) =
+ produceState(initialValue = stringResource(R.string.summary_placeholder), app) {
+ withContext(Dispatchers.IO) {
+ value = loadLabel(app)
+ }
+ }
@Composable
fun produceIcon(app: ApplicationInfo): State<Drawable?>
@@ -46,13 +53,6 @@
override fun loadLabel(app: ApplicationInfo): String = app.loadLabel(packageManager).toString()
@Composable
- override fun produceLabel(app: ApplicationInfo) = produceState(initialValue = "", app) {
- withContext(Dispatchers.Default) {
- value = app.loadLabel(packageManager).toString()
- }
- }
-
- @Composable
override fun produceIcon(app: ApplicationInfo) =
produceState<Drawable?>(initialValue = null, app) {
withContext(Dispatchers.Default) {
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
index 9d6b311..7f5fe9f 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
@@ -16,6 +16,9 @@
package com.android.settingslib.spaprivileged.template.app
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.UserHandle
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
@@ -33,6 +36,7 @@
import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.widget.ui.PlaceholderTitle
import com.android.settingslib.spaprivileged.R
+import com.android.settingslib.spaprivileged.framework.compose.DisposableBroadcastReceiverAsUser
import com.android.settingslib.spaprivileged.model.app.AppListConfig
import com.android.settingslib.spaprivileged.model.app.AppListData
import com.android.settingslib.spaprivileged.model.app.AppListModel
@@ -120,5 +124,15 @@
viewModel.option.Sync(state.option)
viewModel.searchQuery.Sync(state.searchQuery)
- return viewModel.appListDataFlow.collectAsState(null, Dispatchers.Default)
+ DisposableBroadcastReceiverAsUser(
+ intentFilter = IntentFilter(Intent.ACTION_PACKAGE_ADDED).apply {
+ addAction(Intent.ACTION_PACKAGE_REMOVED)
+ addAction(Intent.ACTION_PACKAGE_CHANGED)
+ addDataScheme("package")
+ },
+ userHandle = UserHandle.of(config.userId),
+ onStart = { viewModel.reloadApps() },
+ ) { viewModel.reloadApps() }
+
+ return viewModel.appListDataFlow.collectAsState(null, Dispatchers.IO)
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/Android.bp b/packages/SettingsLib/SpaPrivileged/tests/Android.bp
index 5afe21e..12955c8 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/Android.bp
+++ b/packages/SettingsLib/SpaPrivileged/tests/Android.bp
@@ -31,6 +31,7 @@
],
static_libs: [
+ "SpaLibTestUtils",
"androidx.compose.ui_ui-test-junit4",
"androidx.compose.ui_ui-test-manifest",
"androidx.test.ext.junit",
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt
index 5d5a24e..bc6925b 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt
@@ -24,9 +24,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.toList
-import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
@@ -36,11 +33,9 @@
import org.mockito.Mockito.any
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.eq
-import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
-
-private const val USER_ID = 0
+import org.mockito.Mockito.`when` as whenever
@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
@@ -80,36 +75,28 @@
packageManager.queryIntentActivitiesAsUser(any(), any<ResolveInfoFlags>(), eq(USER_ID))
).thenReturn(emptyList())
- repository = AppListRepository(context)
+ repository = AppListRepositoryImpl(context)
}
@Test
fun notShowInstantApps() = runTest {
val appListConfig = AppListConfig(userId = USER_ID, showInstantApps = false)
- val appListFlow = repository.loadApps(flowOf(appListConfig))
+ val appListFlow = repository.loadApps(appListConfig)
- launch {
- val flowValues = mutableListOf<List<ApplicationInfo>>()
- appListFlow.toList(flowValues)
- assertThat(flowValues).hasSize(1)
-
- assertThat(flowValues[0]).containsExactly(normalApp)
- }
+ assertThat(appListFlow).containsExactly(normalApp)
}
@Test
fun showInstantApps() = runTest {
val appListConfig = AppListConfig(userId = USER_ID, showInstantApps = true)
- val appListFlow = repository.loadApps(flowOf(appListConfig))
+ val appListFlow = repository.loadApps(appListConfig)
- launch {
- val flowValues = mutableListOf<List<ApplicationInfo>>()
- appListFlow.toList(flowValues)
- assertThat(flowValues).hasSize(1)
+ assertThat(appListFlow).containsExactly(normalApp, instantApp)
+ }
- assertThat(flowValues[0]).containsExactly(normalApp, instantApp)
- }
+ private companion object {
+ const val USER_ID = 0
}
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListViewModelTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListViewModelTest.kt
new file mode 100644
index 0000000..b570815
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListViewModelTest.kt
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spaprivileged.model.app
+
+import android.app.Application
+import android.content.pm.ApplicationInfo
+import androidx.compose.runtime.Composable
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spa.framework.util.asyncMapItem
+import com.android.settingslib.spa.testutils.waitUntil
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+class AppListViewModelTest {
+ @JvmField
+ @Rule
+ val mockito: MockitoRule = MockitoJUnit.rule()
+
+ @Mock
+ private lateinit var application: Application
+
+ private val listModel = TestAppListModel()
+
+ private fun createViewModel(): AppListViewModelImpl<TestAppRecord> {
+ val viewModel = AppListViewModelImpl<TestAppRecord>(
+ application = application,
+ appListRepositoryFactory = { FakeAppListRepository },
+ appRepositoryFactory = { FakeAppRepository },
+ )
+ viewModel.appListConfig.setIfAbsent(CONFIG)
+ viewModel.listModel.setIfAbsent(listModel)
+ viewModel.showSystem.setIfAbsent(false)
+ viewModel.option.setIfAbsent(0)
+ viewModel.searchQuery.setIfAbsent("")
+ viewModel.reloadApps()
+ return viewModel
+ }
+
+ @Test
+ fun appListDataFlow() = runTest {
+ val viewModel = createViewModel()
+
+ val (appEntries, option) = viewModel.appListDataFlow.first()
+
+ assertThat(appEntries).hasSize(1)
+ assertThat(appEntries[0].record.app).isSameInstanceAs(APP)
+ assertThat(appEntries[0].label).isEqualTo(LABEL)
+ assertThat(option).isEqualTo(0)
+ }
+
+ @Test
+ fun onFirstLoaded_calledWhenLoaded() = runTest {
+ val viewModel = createViewModel()
+
+ viewModel.appListDataFlow.first()
+
+ waitUntil { listModel.onFirstLoadedCalled }
+ }
+
+ private object FakeAppListRepository : AppListRepository {
+ override suspend fun loadApps(config: AppListConfig) = listOf(APP)
+
+ override fun showSystemPredicate(
+ userIdFlow: Flow<Int>,
+ showSystemFlow: Flow<Boolean>,
+ ): Flow<(app: ApplicationInfo) -> Boolean> = flowOf { true }
+ }
+
+ private object FakeAppRepository : AppRepository {
+ override fun loadLabel(app: ApplicationInfo) = LABEL
+
+ @Composable
+ override fun produceIcon(app: ApplicationInfo) = stateOf(null)
+ }
+
+ private companion object {
+ const val USER_ID = 0
+ const val PACKAGE_NAME = "package.name"
+ const val LABEL = "Label"
+ val CONFIG = AppListConfig(userId = USER_ID, showInstantApps = false)
+ val APP = ApplicationInfo().apply {
+ packageName = PACKAGE_NAME
+ }
+ }
+}
+
+private data class TestAppRecord(override val app: ApplicationInfo) : AppRecord
+
+private class TestAppListModel : AppListModel<TestAppRecord> {
+ var onFirstLoadedCalled = false
+
+ override fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>) =
+ appListFlow.asyncMapItem { TestAppRecord(it) }
+
+ @Composable
+ override fun getSummary(option: Int, record: TestAppRecord) = null
+
+ override fun filter(
+ userIdFlow: Flow<Int>,
+ option: Int,
+ recordListFlow: Flow<List<TestAppRecord>>,
+ ) = recordListFlow
+
+ override suspend fun onFirstLoaded(recordList: List<TestAppRecord>) {
+ onFirstLoadedCalled = true
+ }
+}
diff --git a/packages/SettingsLib/res/drawable/ic_5g_plus_mobiledata_default.xml b/packages/SettingsLib/res/drawable/ic_5g_plus_mobiledata_default.xml
new file mode 100644
index 0000000..46abff8
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_5g_plus_mobiledata_default.xml
@@ -0,0 +1,33 @@
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:viewportWidth="22"
+ android:viewportHeight="17"
+ android:width="22dp"
+ android:height="17dp">
+ <group>
+ <group>
+ <path android:fillColor="#FF000000"
+ android:pathData="M1.03 8.47l0.43-4.96h4.33v1.17H2.48L2.25 7.39C2.66 7.1 3.1 6.96 3.57 6.96c0.77 0 1.38 0.3 1.83 0.9 s0.66 1.41 0.66 2.43c0 1.03-0.24 1.84-0.72 2.43S4.2 13.6 3.36 13.6c-0.75 0-1.36-0.24-1.83-0.73s-0.74-1.16-0.81-2.02h1.13 c0.07 0.57 0.23 1 0.49 1.29s0.59 0.43 1.01 0.43c0.47 0 0.84-0.2 1.1-0.61c0.26-0.41 0.4-0.96 0.4-1.65 c0-0.65-0.14-1.18-0.43-1.59S3.76 8.09 3.28 8.09c-0.4 0-0.72 0.1-0.96 0.31L1.99 8.73L1.03 8.47z"/>
+ </group>
+ <group>
+ <path android:fillColor="#FF000000"
+ android:pathData="M 18.93,5.74 L 18.93,3.39 L 17.63,3.39 L 17.63,5.74 L 15.28,5.74 L 15.28,7.04 L 17.63,7.04 L 17.63,9.39 L 18.93,9.39 L 18.93,7.04 L 21.28,7.04 L 21.28,5.74 z"/>
+ </group>
+ <path android:fillColor="#FF000000"
+ android:pathData="M13.78 12.24l-0.22 0.27c-0.63 0.73-1.55 1.1-2.76 1.1c-1.08 0-1.92-0.36-2.53-1.07s-0.93-1.72-0.94-3.02V7.56 c0-1.39 0.28-2.44 0.84-3.13s1.39-1.04 2.51-1.04c0.95 0 1.69 0.26 2.23 0.79s0.83 1.28 0.89 2.26h-1.25 c-0.05-0.62-0.22-1.1-0.52-1.45s-0.74-0.52-1.34-0.52c-0.72 0-1.24 0.23-1.57 0.7S8.6 6.37 8.59 7.4v2.03c0 1 0.19 1.77 0.57 2.31 c0.38 0.54 0.93 0.8 1.65 0.8c0.67 0 1.19-0.16 1.54-0.49l0.18-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+ </group>
+</vector>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 29d7d95..cd1e7d1 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -610,7 +610,7 @@
<string name="user_image_photo_selector" msgid="433658323306627093">"Hautatu argazki bat"</string>
<string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Saiakera oker gehiegi egin dituzu. Gailu honetako datuak ezabatu egingo dira."</string>
<string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Saiakera oker gehiegi egin dituzu. Erabiltzailea ezabatu egingo da."</string>
- <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Saiakera oker gehiegi egin dituzu. Laneko profila eta bertako datuak ezabatu egingo dira."</string>
+ <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Saiakera oker gehiegi egin dituzu. Laneko profila eta bertako datuak ezabatuko dira."</string>
<string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Baztertu"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Gailuaren balio lehenetsia"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desgaituta"</string>
diff --git a/packages/SettingsLib/res/values/carrierid_icon_overrides.xml b/packages/SettingsLib/res/values/carrierid_icon_overrides.xml
new file mode 100644
index 0000000..d2ae52d
--- /dev/null
+++ b/packages/SettingsLib/res/values/carrierid_icon_overrides.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<!--
+ ~ This resource file exists to enumerate all network type icon overrides on a
+ ~ per-carrierId basis
+-->
+<resources>
+ <!--
+ Network type (RAT) icon overrides can be configured here on a per-carrierId basis.
+ 1. Add a new TypedArray here, using the naming scheme below
+ 2. The entries are (NetworkType, drawable ID) pairs
+ 3. Add this array's ID to the MAPPING field of MobileIconCarrierIdOverrides.kt
+ -->
+ <array name="carrierId_2032_iconOverrides">
+ <item>5G_PLUS</item>
+ <item>@drawable/ic_5g_plus_mobiledata_default</item>
+ </array>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 7913c16..65c94ce 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -36,8 +36,10 @@
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
+import android.content.pm.UserProperties;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -1577,8 +1579,8 @@
public long internalSize;
public long externalSize;
public String labelDescription;
-
public boolean mounted;
+ public boolean showInPersonalTab;
/**
* Setting this to {@code true} prevents the entry to be filtered by
@@ -1635,6 +1637,33 @@
ThreadUtils.postOnBackgroundThread(
() -> this.ensureLabelDescriptionLocked(context));
}
+ this.showInPersonalTab = shouldShowInPersonalTab(context, info.uid);
+ }
+
+ /**
+ * Checks if the user that the app belongs to have the property
+ * {@link UserProperties#SHOW_IN_SETTINGS_WITH_PARENT} set.
+ */
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ boolean shouldShowInPersonalTab(Context context, int uid) {
+ UserManager userManager = UserManager.get(context);
+ int userId = UserHandle.getUserId(uid);
+
+ // Regardless of apk version, if the app belongs to the current user then return true.
+ if (userId == ActivityManager.getCurrentUser()) {
+ return true;
+ }
+
+ // For sdk version < 34, if the app doesn't belong to the current user,
+ // then as per earlier behaviour the app shouldn't be displayed in personal tab.
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ return false;
+ }
+
+ UserProperties userProperties = userManager.getUserProperties(
+ UserHandle.of(userId));
+ return userProperties.getShowInSettings()
+ == UserProperties.SHOW_IN_SETTINGS_WITH_PARENT;
}
public void ensureLabel(Context context) {
@@ -1784,7 +1813,7 @@
@Override
public boolean filterApp(AppEntry entry) {
- return UserHandle.getUserId(entry.info.uid) == mCurrentUser;
+ return entry.showInPersonalTab;
}
};
@@ -1811,7 +1840,7 @@
@Override
public boolean filterApp(AppEntry entry) {
- return UserHandle.getUserId(entry.info.uid) != mCurrentUser;
+ return !entry.showInPersonalTab;
}
};
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index c829bc3..3ba51d2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -293,7 +293,7 @@
return false;
}
setConnectedRecord();
- mRouterManager.selectRoute(mPackageName, mRouteInfo);
+ mRouterManager.transfer(mPackageName, mRouteInfo);
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileIconCarrierIdOverrides.kt b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileIconCarrierIdOverrides.kt
new file mode 100644
index 0000000..a0395b5
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileIconCarrierIdOverrides.kt
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.mobile
+
+import android.annotation.DrawableRes
+import android.content.res.Resources
+import android.content.res.TypedArray
+import android.util.Log
+import androidx.annotation.VisibleForTesting
+import com.android.settingslib.R
+import com.android.settingslib.SignalIcon.MobileIconGroup
+
+/**
+ * This class defines a network type (3G, 4G, etc.) override mechanism on a per-carrierId basis.
+ *
+ * Traditionally, carrier-customized network type iconography was achieved using the `MCC/MNC`
+ * resource qualifiers, and swapping out the drawable resource by name. It would look like this:
+ *
+ * res/
+ * drawable/
+ * 3g_mobiledata_icon.xml
+ * drawable-MCC-MNC/
+ * 3g_mobiledata_icon.xml
+ *
+ * This would mean that, provided a context created with this MCC/MNC configuration set, loading
+ * the network type icon through [MobileIconGroup] would provide a carrier-defined network type
+ * icon rather than the AOSP-defined default.
+ *
+ * The MCC/MNC mechanism no longer can fully define carrier-specific network type icons, because
+ * there is no longer a 1:1 mapping between MCC/MNC and carrier. With the advent of MVNOs, multiple
+ * carriers can have the same MCC/MNC value, but wish to differentiate based on their carrier ID.
+ * CarrierId is a newer concept than MCC/MNC, and provides more granularity when it comes to
+ * determining the carrier (e.g. MVNOs can share MCC/MNC values with the network owner), therefore
+ * it can fit all of the same use cases currently handled by `MCC/MNC`, without the need to apply a
+ * configuration context in order to get the proper UI for a given SIM icon.
+ *
+ * NOTE: CarrierId icon overrides will always take precedence over those defined using `MCC/MNC`
+ * resource qualifiers.
+ *
+ * [MAPPING] encodes the relationship between CarrierId and the corresponding override array
+ * that exists in the config.xml. An alternative approach could be to generate the resource name
+ * by string concatenation at run-time:
+ *
+ * val resName = "carrierId_$carrierId_iconOverrides"
+ * val override = resources.getResourceIdentifier(resName)
+ *
+ * However, that's going to be far less efficient until MAPPING grows to a sufficient size. For now,
+ * given a relatively small number of entries, we should just maintain the mapping here.
+ */
+interface MobileIconCarrierIdOverrides {
+ @DrawableRes
+ fun getOverrideFor(carrierId: Int, networkType: String, resources: Resources): Int
+ fun carrierIdEntryExists(carrierId: Int): Boolean
+}
+
+class MobileIconCarrierIdOverridesImpl : MobileIconCarrierIdOverrides {
+ @DrawableRes
+ override fun getOverrideFor(carrierId: Int, networkType: String, resources: Resources): Int {
+ val resId = MAPPING[carrierId] ?: return 0
+ val ta = resources.obtainTypedArray(resId)
+ val map = parseNetworkIconOverrideTypedArray(ta)
+ ta.recycle()
+ return map[networkType] ?: 0
+ }
+
+ override fun carrierIdEntryExists(carrierId: Int) =
+ overrideExists(carrierId, MAPPING)
+
+ companion object {
+ private const val TAG = "MobileIconOverrides"
+ /**
+ * This map maintains the lookup from the canonical carrier ID (see below link) to the
+ * corresponding overlay resource. New overrides should add an entry below in order to
+ * change the network type icon resources based on carrier ID
+ *
+ * Refer to the link below for the canonical mapping maintained in AOSP:
+ * https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/latest_carrier_id/carrier_list.textpb
+ */
+ private val MAPPING = mapOf(
+ // 2032 == Xfinity Mobile
+ 2032 to R.array.carrierId_2032_iconOverrides,
+ )
+
+ /**
+ * Parse `carrierId_XXXX_iconOverrides` for a particular network type. The resource file
+ * "carrierid_icon_overrides.xml" defines a TypedArray format for overriding specific
+ * network type icons (a.k.a. RAT icons) for a particular carrier ID. The format is defined
+ * as an array of (network type name, drawable) pairs:
+ * <array name="carrierId_XXXX_iconOverrides>
+ * <item>NET_TYPE_1</item>
+ * <item>@drawable/net_type_1_override</item>
+ * <item>NET_TYPE_2</item>
+ * <item>@drawable/net_type_2_override</item>
+ * </array>
+ *
+ * @param ta the [TypedArray] defined in carrierid_icon_overrides.xml
+ * @return the overridden drawable resource ID if it exists, or 0 if it does not
+ */
+ @VisibleForTesting
+ @JvmStatic
+ fun parseNetworkIconOverrideTypedArray(ta: TypedArray): Map<String, Int> {
+ if (ta.length() % 2 != 0) {
+ Log.w(TAG,
+ "override must contain an even number of (key, value) entries. skipping")
+
+ return mapOf()
+ }
+
+ val result = mutableMapOf<String, Int>()
+ // The array is defined as Pair(String, resourceId), so walk by 2
+ for (i in 0 until ta.length() step 2) {
+ val key = ta.getString(i)
+ val override = ta.getResourceId(i + 1, 0)
+ if (key == null || override == 0) {
+ Log.w(TAG, "Invalid override found. Skipping")
+ continue
+ }
+ result[key] = override
+ }
+
+ return result
+ }
+
+ @JvmStatic
+ private fun overrideExists(carrierId: Int, mapping: Map<Int, Int>): Boolean =
+ mapping.containsKey(carrierId)
+ }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
index f1e1e7d..c5598bf 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
@@ -293,4 +293,15 @@
assertThat(ApplicationsState.FILTER_MOVIES.filterApp(mEntry)).isFalse();
}
+
+ @Test
+ public void testPersonalAndWorkFiltersDisplaysCorrectApps() {
+ mEntry.showInPersonalTab = true;
+ assertThat(ApplicationsState.FILTER_PERSONAL.filterApp(mEntry)).isTrue();
+ assertThat(ApplicationsState.FILTER_WORK.filterApp(mEntry)).isFalse();
+
+ mEntry.showInPersonalTab = false;
+ assertThat(ApplicationsState.FILTER_PERSONAL.filterApp(mEntry)).isFalse();
+ assertThat(ApplicationsState.FILTER_WORK.filterApp(mEntry)).isTrue();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
index fc2bf0a..39875f7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
@@ -17,6 +17,7 @@
package com.android.settingslib.applications;
import static android.os.UserHandle.MU_ENABLED;
+import static android.os.UserHandle.USER_SYSTEM;
import static com.google.common.truth.Truth.assertThat;
@@ -48,9 +49,11 @@
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
+import android.content.pm.UserProperties;
import android.content.res.Resources;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -58,6 +61,8 @@
import android.text.TextUtils;
import android.util.IconDrawableFactory;
+import androidx.test.core.app.ApplicationProvider;
+
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.applications.ApplicationsState.Callbacks;
import com.android.settingslib.applications.ApplicationsState.Session;
@@ -71,6 +76,7 @@
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
@@ -79,6 +85,7 @@
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowContextImpl;
import org.robolectric.shadows.ShadowLooper;
+import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.Arrays;
@@ -95,6 +102,7 @@
private final static String LAUNCHABLE_PACKAGE_NAME = "com.android.launchable";
private static final int PROFILE_USERID = 10;
+ private static final int PROFILE_USERID2 = 11;
private static final String PKG_1 = "PKG1";
private static final int OWNER_UID_1 = 1001;
@@ -106,6 +114,10 @@
private static final String PKG_3 = "PKG3";
private static final int OWNER_UID_3 = 1003;
+ private static final int PROFILE_UID_3 = UserHandle.getUid(PROFILE_USERID2, OWNER_UID_3);
+
+ private static final String CLONE_USER = "clone_user";
+ private static final String RANDOM_USER = "random_user";
/** Class under test */
private ApplicationsState mApplicationsState;
@@ -113,6 +125,8 @@
private Application mApplication;
+ @Spy
+ Context mContext = ApplicationProvider.getApplicationContext();
@Mock
private Callbacks mCallbacks;
@Captor
@@ -738,4 +752,51 @@
when(configChanges.applyNewConfig(any(Resources.class))).thenReturn(false);
mApplicationsState.setInterestingConfigChanges(configChanges);
}
+
+ @Test
+ public void shouldShowInPersonalTab_forCurrentUser_returnsTrue() {
+ ApplicationInfo appInfo = createApplicationInfo(PKG_1);
+ AppEntry primaryUserApp = createAppEntry(appInfo, 1);
+
+ assertThat(primaryUserApp.shouldShowInPersonalTab(mContext, appInfo.uid)).isTrue();
+ }
+
+ @Test
+ public void shouldShowInPersonalTab_userProfilePreU_returnsFalse() {
+ ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT",
+ Build.VERSION_CODES.TIRAMISU);
+ // Create an app (and subsequent AppEntry) in a non-primary user profile.
+ ApplicationInfo appInfo1 = createApplicationInfo(PKG_1, PROFILE_UID_1);
+ AppEntry nonPrimaryUserApp = createAppEntry(appInfo1, 1);
+
+ assertThat(nonPrimaryUserApp.shouldShowInPersonalTab(mContext, appInfo1.uid)).isFalse();
+ }
+
+ @Test
+ public void shouldShowInPersonalTab_currentUserIsParent_returnsAsPerUserPropertyOfProfile1() {
+ // Mark system user as parent for both profile users.
+ ShadowUserManager shadowUserManager = Shadow
+ .extract(RuntimeEnvironment.application.getSystemService(UserManager.class));
+ shadowUserManager.addProfile(USER_SYSTEM, PROFILE_USERID,
+ CLONE_USER, 0);
+ shadowUserManager.addProfile(USER_SYSTEM, PROFILE_USERID2,
+ RANDOM_USER, 0);
+ shadowUserManager.setupUserProperty(PROFILE_USERID,
+ /*showInSettings*/ UserProperties.SHOW_IN_SETTINGS_WITH_PARENT);
+ shadowUserManager.setupUserProperty(PROFILE_USERID2,
+ /*showInSettings*/ UserProperties.SHOW_IN_SETTINGS_NO);
+
+ ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT",
+ Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
+
+ // Treat PROFILE_USERID as a clone user profile and create an app PKG_1 in it.
+ ApplicationInfo appInfo1 = createApplicationInfo(PKG_1, PROFILE_UID_1);
+ // Treat PROFILE_USERID2 as a random non-primary profile and create an app PKG_3 in it.
+ ApplicationInfo appInfo2 = createApplicationInfo(PKG_3, PROFILE_UID_3);
+ AppEntry nonPrimaryUserApp1 = createAppEntry(appInfo1, 1);
+ AppEntry nonPrimaryUserApp2 = createAppEntry(appInfo2, 2);
+
+ assertThat(nonPrimaryUserApp1.shouldShowInPersonalTab(mContext, appInfo1.uid)).isTrue();
+ assertThat(nonPrimaryUserApp2.shouldShowInPersonalTab(mContext, appInfo2.uid)).isFalse();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
index 5399f8a..c058a61 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
@@ -461,7 +461,7 @@
public void connect_shouldSelectRoute() {
mInfoMediaDevice1.connect();
- verify(mMediaRouter2Manager).selectRoute(TEST_PACKAGE_NAME, mRouteInfo1);
+ verify(mMediaRouter2Manager).transfer(TEST_PACKAGE_NAME, mRouteInfo1);
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/mobile/MobileIconCarrierIdOverridesTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/mobile/MobileIconCarrierIdOverridesTest.java
new file mode 100644
index 0000000..740261d
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/mobile/MobileIconCarrierIdOverridesTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.mobile;
+
+import static com.android.settingslib.mobile.MobileIconCarrierIdOverridesImpl.parseNetworkIconOverrideTypedArray;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.res.TypedArray;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.Map;
+
+@RunWith(RobolectricTestRunner.class)
+public final class MobileIconCarrierIdOverridesTest {
+ private static final String OVERRIDE_ICON_1_NAME = "name_1";
+ private static final int OVERRIDE_ICON_1_RES = 1;
+
+ private static final String OVERRIDE_ICON_2_NAME = "name_2";
+ private static final int OVERRIDE_ICON_2_RES = 2;
+
+ NetworkOverrideTypedArrayMock mResourceMock;
+
+ @Before
+ public void setUp() {
+ mResourceMock = new NetworkOverrideTypedArrayMock(
+ new String[] { OVERRIDE_ICON_1_NAME, OVERRIDE_ICON_2_NAME },
+ new int[] { OVERRIDE_ICON_1_RES, OVERRIDE_ICON_2_RES }
+ );
+ }
+
+ @Test
+ public void testParse_singleOverride() {
+ mResourceMock.setOverrides(
+ new String[] { OVERRIDE_ICON_1_NAME },
+ new int[] { OVERRIDE_ICON_1_RES }
+ );
+
+ Map<String, Integer> parsed = parseNetworkIconOverrideTypedArray(mResourceMock.getMock());
+
+ assertThat(parsed.get(OVERRIDE_ICON_1_NAME)).isEqualTo(OVERRIDE_ICON_1_RES);
+ }
+
+ @Test
+ public void testParse_multipleOverrides() {
+ mResourceMock.setOverrides(
+ new String[] { OVERRIDE_ICON_1_NAME, OVERRIDE_ICON_2_NAME },
+ new int[] { OVERRIDE_ICON_1_RES, OVERRIDE_ICON_2_RES }
+ );
+
+ Map<String, Integer> parsed = parseNetworkIconOverrideTypedArray(mResourceMock.getMock());
+
+ assertThat(parsed.get(OVERRIDE_ICON_2_NAME)).isEqualTo(OVERRIDE_ICON_2_RES);
+ assertThat(parsed.get(OVERRIDE_ICON_1_NAME)).isEqualTo(OVERRIDE_ICON_1_RES);
+ }
+
+ @Test
+ public void testParse_nonexistentKey_isNull() {
+ mResourceMock.setOverrides(
+ new String[] { OVERRIDE_ICON_1_NAME },
+ new int[] { OVERRIDE_ICON_1_RES }
+ );
+
+ Map<String, Integer> parsed = parseNetworkIconOverrideTypedArray(mResourceMock.getMock());
+
+ assertThat(parsed.get(OVERRIDE_ICON_2_NAME)).isNull();
+ }
+
+ static class NetworkOverrideTypedArrayMock {
+ private Object[] mInterleaved;
+
+ private final TypedArray mMockTypedArray = mock(TypedArray.class);
+
+ NetworkOverrideTypedArrayMock(
+ String[] networkTypes,
+ int[] iconOverrides) {
+
+ mInterleaved = interleaveTypes(networkTypes, iconOverrides);
+
+ doAnswer(invocation -> {
+ return mInterleaved[(int) invocation.getArgument(0)];
+ }).when(mMockTypedArray).getString(/* index */ anyInt());
+
+ doAnswer(invocation -> {
+ return mInterleaved[(int) invocation.getArgument(0)];
+ }).when(mMockTypedArray).getResourceId(/* index */ anyInt(), /* default */ anyInt());
+
+ when(mMockTypedArray.length()).thenAnswer(invocation -> {
+ return mInterleaved.length;
+ });
+ }
+
+ TypedArray getMock() {
+ return mMockTypedArray;
+ }
+
+ void setOverrides(String[] types, int[] resIds) {
+ mInterleaved = interleaveTypes(types, resIds);
+ }
+
+ private Object[] interleaveTypes(String[] strs, int[] ints) {
+ assertThat(strs.length).isEqualTo(ints.length);
+
+ Object[] ret = new Object[strs.length * 2];
+
+ // Keep track of where we are in the interleaved array, but iterate the overrides
+ int interleavedIndex = 0;
+ for (int i = 0; i < strs.length; i++) {
+ ret[interleavedIndex] = strs[i];
+ interleavedIndex += 1;
+ ret[interleavedIndex] = ints[i];
+ interleavedIndex += 1;
+ }
+ return ret;
+ }
+ }
+}
diff --git a/packages/Shell/tests/src/com/android/shell/ActionSendMultipleConsumerActivity.java b/packages/Shell/tests/src/com/android/shell/ActionSendMultipleConsumerActivity.java
index e34f5c8..d866653 100644
--- a/packages/Shell/tests/src/com/android/shell/ActionSendMultipleConsumerActivity.java
+++ b/packages/Shell/tests/src/com/android/shell/ActionSendMultipleConsumerActivity.java
@@ -104,7 +104,7 @@
final IntentFilter filter = new IntentFilter();
filter.addAction(CUSTOM_ACTION_SEND_MULTIPLE_INTENT);
- context.registerReceiver(receiver, filter);
+ context.registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED_UNAUDITED);
}
/**
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 7a4a93f..71ad886 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -114,6 +114,7 @@
"androidx.dynamicanimation_dynamicanimation",
"androidx-constraintlayout_constraintlayout",
"androidx.exifinterface_exifinterface",
+ "androidx.test.ext.junit",
"com.google.android.material_material",
"kotlinx_coroutines_android",
"kotlinx_coroutines",
@@ -224,6 +225,7 @@
"androidx.test.rules",
"androidx.test.uiautomator_uiautomator",
"mockito-target-extended-minus-junit4",
+ "androidx.test.ext.junit",
"testables",
"truth-prebuilt",
"monet",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 07e37d3..11237dc 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -195,6 +195,9 @@
<permission android:name="com.android.systemui.permission.FLAGS"
android:protectionLevel="signature" />
+ <permission android:name="android.permission.ACCESS_KEYGUARD_QUICK_AFFORDANCES"
+ android:protectionLevel="signature|privileged" />
+
<!-- Adding Quick Settings tiles -->
<uses-permission android:name="android.permission.BIND_QUICK_SETTINGS_TILE" />
@@ -971,10 +974,11 @@
<receiver android:name=".media.dialog.MediaOutputDialogReceiver"
android:exported="true">
- <intent-filter>
+ <intent-filter android:priority="1">
<action android:name="com.android.systemui.action.LAUNCH_MEDIA_OUTPUT_DIALOG" />
<action android:name="com.android.systemui.action.LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG" />
<action android:name="com.android.systemui.action.DISMISS_MEDIA_OUTPUT_DIALOG" />
+ <action android:name="android.intent.action.SHOW_OUTPUT_SWITCHER" />
</intent-filter>
</receiver>
@@ -986,5 +990,18 @@
<action android:name="com.android.systemui.action.DISMISS_VOLUME_PANEL_DIALOG" />
</intent-filter>
</receiver>
+
+ <activity android:name=".logcat.LogAccessDialogActivity"
+ android:theme="@android:style/Theme.Translucent.NoTitleBar"
+ android:excludeFromRecents="true"
+ android:exported="false">
+ </activity>
+
+ <provider
+ android:authorities="com.android.systemui.keyguard.quickaffordance"
+ android:name="com.android.systemui.keyguard.KeyguardQuickAffordanceProvider"
+ android:exported="true"
+ android:permission="android.permission.ACCESS_KEYGUARD_QUICK_AFFORDANCES"
+ />
</application>
</manifest>
diff --git a/packages/SystemUI/compose/features/AndroidManifest.xml b/packages/SystemUI/compose/features/AndroidManifest.xml
index eada40e..278a89f 100644
--- a/packages/SystemUI/compose/features/AndroidManifest.xml
+++ b/packages/SystemUI/compose/features/AndroidManifest.xml
@@ -34,6 +34,11 @@
android:enabled="false"
tools:replace="android:authorities"
tools:node="remove" />
+ <provider android:name="com.android.systemui.keyguard.KeyguardQuickAffordanceProvider"
+ android:authorities="com.android.systemui.test.keyguard.quickaffordance.disabled"
+ android:enabled="false"
+ tools:replace="android:authorities"
+ tools:node="remove" />
<provider android:name="com.android.keyguard.clock.ClockOptionsProvider"
android:authorities="com.android.systemui.test.keyguard.clock.disabled"
android:enabled="false"
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
index c50340c..e52a57f 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
@@ -82,6 +82,18 @@
boolean isFalseTap(@Penalty int penalty);
/**
+ * Returns true if the FalsingManager thinks the last gesture was not a valid long tap.
+ *
+ * Use this method to validate a long tap for launching an action, like long press on a UMO
+ *
+ * The only parameter, penalty, indicates how much this should affect future gesture
+ * classifications if this long tap looks like a false.
+ * As long taps are hard to confirm as false or otherwise,
+ * a low penalty value is encouraged unless context indicates otherwise.
+ */
+ boolean isFalseLongTap(@Penalty int penalty);
+
+ /**
* Returns true if the last two gestures do not look like a double tap.
*
* Only works on data that has already been reported to the FalsingManager. Be sure that
diff --git a/packages/SystemUI/res-keyguard/values-land/dimens.xml b/packages/SystemUI/res-keyguard/values-land/dimens.xml
index a4e7a5f..f1aa544 100644
--- a/packages/SystemUI/res-keyguard/values-land/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values-land/dimens.xml
@@ -27,4 +27,6 @@
<integer name="scaled_password_text_size">26</integer>
<dimen name="bouncer_user_switcher_y_trans">@dimen/status_bar_height</dimen>
+ <dimen name="bouncer_user_switcher_view_mode_user_switcher_bottom_margin">0dp</dimen>
+ <dimen name="bouncer_user_switcher_view_mode_view_flipper_bottom_margin">0dp</dimen>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 0a55cf7..3861d98 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -124,6 +124,8 @@
<dimen name="bouncer_user_switcher_item_padding_horizontal">12dp</dimen>
<dimen name="bouncer_user_switcher_header_padding_end">44dp</dimen>
<dimen name="bouncer_user_switcher_y_trans">0dp</dimen>
+ <dimen name="bouncer_user_switcher_view_mode_user_switcher_bottom_margin">0dp</dimen>
+ <dimen name="bouncer_user_switcher_view_mode_view_flipper_bottom_margin">0dp</dimen>
<!-- 2 * the margin + size should equal the plus_margin -->
<dimen name="user_switcher_icon_large_margin">16dp</dimen>
diff --git a/packages/SystemUI/res/drawable/brightness_progress_drawable.xml b/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
index 88d8f78f..569ee76 100644
--- a/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
+++ b/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
@@ -30,7 +30,7 @@
</item>
<item android:id="@android:id/progress"
android:gravity="center_vertical|fill_horizontal">
- <com.android.systemui.util.RoundedCornerProgressDrawable
+ <com.android.systemui.util.BrightnessProgressDrawable
android:drawable="@drawable/brightness_progress_full_drawable"
/>
</item>
diff --git a/core/res/res/drawable/grant_permissions_buttons_bottom.xml b/packages/SystemUI/res/drawable/grant_permissions_buttons_bottom.xml
similarity index 100%
rename from core/res/res/drawable/grant_permissions_buttons_bottom.xml
rename to packages/SystemUI/res/drawable/grant_permissions_buttons_bottom.xml
diff --git a/core/res/res/drawable/grant_permissions_buttons_top.xml b/packages/SystemUI/res/drawable/grant_permissions_buttons_top.xml
similarity index 100%
rename from core/res/res/drawable/grant_permissions_buttons_top.xml
rename to packages/SystemUI/res/drawable/grant_permissions_buttons_top.xml
diff --git a/packages/SystemUI/res/drawable/ic_doc_document.xml b/packages/SystemUI/res/drawable/ic_doc_document.xml
new file mode 100644
index 0000000..df9ddab
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_doc_document.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF4883F3"
+ android:pathData="M19 3H5c-1.1 0,-2 .9,-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2,-.9 2,-2V5c0,-1.1,-.9,-2,-2,-2zm-1.99 6H7V7h10.01v2zm0 4H7v-2h10.01v2zm-3 4H7v-2h7.01v2z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/user_switcher_fullscreen_button_bg.xml b/packages/SystemUI/res/drawable/user_switcher_fullscreen_button_bg.xml
new file mode 100644
index 0000000..ae0f4b2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/user_switcher_fullscreen_button_bg.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:insetTop="@dimen/dialog_button_vertical_inset"
+ android:insetBottom="@dimen/dialog_button_vertical_inset">
+ <ripple android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="@android:color/white"/>
+ <corners android:radius="20dp"/>
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="rectangle">
+ <corners android:radius="20dp"/>
+ <solid android:color="@android:color/transparent"/>
+ <stroke android:color="?androidprv:attr/colorAccentPrimaryVariant"
+ android:width="1dp"
+ />
+ <padding android:left="@dimen/dialog_button_horizontal_padding"
+ android:top="@dimen/dialog_button_vertical_padding"
+ android:right="@dimen/dialog_button_horizontal_padding"
+ android:bottom="@dimen/dialog_button_vertical_padding"/>
+ </shape>
+ </item>
+ </ripple>
+</inset>
diff --git a/core/res/res/layout/log_access_user_consent_dialog_permission.xml b/packages/SystemUI/res/layout/log_access_user_consent_dialog_permission.xml
similarity index 96%
rename from core/res/res/layout/log_access_user_consent_dialog_permission.xml
rename to packages/SystemUI/res/layout/log_access_user_consent_dialog_permission.xml
index 3da14c8..89e36ac 100644
--- a/core/res/res/layout/log_access_user_consent_dialog_permission.xml
+++ b/packages/SystemUI/res/layout/log_access_user_consent_dialog_permission.xml
@@ -75,7 +75,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/log_access_confirmation_allow"
- style="@style/PermissionGrantButtonTop"
+ style="?permissionGrantButtonTopStyle"
android:textAppearance="@style/PermissionGrantButtonTextAppearance"
android:layout_marginBottom="5dp"
android:layout_centerHorizontal="true"
@@ -89,7 +89,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/log_access_confirmation_deny"
- style="@style/PermissionGrantButtonBottom"
+ style="?permissionGrantButtonBottomStyle"
android:textAppearance="@style/PermissionGrantButtonTextAppearance"
android:layout_centerHorizontal="true"
android:layout_alignParentTop="true"
diff --git a/packages/SystemUI/res/layout/screen_record_options.xml b/packages/SystemUI/res/layout/screen_record_options.xml
new file mode 100644
index 0000000..d6c9e98
--- /dev/null
+++ b/packages/SystemUI/res/layout/screen_record_options.xml
@@ -0,0 +1,87 @@
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <ImageView
+ android:layout_width="@dimen/screenrecord_option_icon_size"
+ android:layout_height="@dimen/screenrecord_option_icon_size"
+ android:src="@drawable/ic_mic_26dp"
+ android:tint="?android:attr/textColorSecondary"
+ android:layout_gravity="center_vertical"
+ android:layout_weight="0"
+ android:layout_marginRight="@dimen/screenrecord_option_padding"
+ android:importantForAccessibility="no"/>
+ <Spinner
+ android:id="@+id/screen_recording_options"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:minHeight="48dp"
+ android:layout_weight="1"
+ android:popupBackground="@drawable/screenrecord_spinner_background"
+ android:dropDownWidth="274dp"
+ android:importantForAccessibility="yes"/>
+ <Switch
+ android:layout_width="wrap_content"
+ android:minWidth="48dp"
+ android:layout_height="48dp"
+ android:layout_weight="0"
+ android:layout_gravity="end"
+ android:id="@+id/screenrecord_audio_switch"
+ style="@style/ScreenRecord.Switch"
+ android:importantForAccessibility="yes"/>
+ </LinearLayout>
+ <LinearLayout
+ android:id="@+id/show_taps"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginTop="@dimen/screenrecord_option_padding">
+ <ImageView
+ android:layout_width="@dimen/screenrecord_option_icon_size"
+ android:layout_height="@dimen/screenrecord_option_icon_size"
+ android:layout_weight="0"
+ android:src="@drawable/ic_touch"
+ android:tint="?android:attr/textColorSecondary"
+ android:layout_gravity="center_vertical"
+ android:layout_marginRight="@dimen/screenrecord_option_padding"
+ android:importantForAccessibility="no"/>
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:minHeight="48dp"
+ android:layout_weight="1"
+ android:gravity="center_vertical"
+ android:text="@string/screenrecord_taps_label"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:fontFamily="@*android:string/config_headlineFontFamily"
+ android:textColor="?android:attr/textColorPrimary"
+ android:contentDescription="@string/screenrecord_taps_label"/>
+ <Switch
+ android:layout_width="wrap_content"
+ android:minWidth="48dp"
+ android:layout_height="48dp"
+ android:layout_weight="0"
+ android:id="@+id/screenrecord_taps_switch"
+ style="@style/ScreenRecord.Switch"
+ android:importantForAccessibility="yes"/>
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/screen_share_dialog.xml b/packages/SystemUI/res/layout/screen_share_dialog.xml
new file mode 100644
index 0000000..ac46cdb
--- /dev/null
+++ b/packages/SystemUI/res/layout/screen_share_dialog.xml
@@ -0,0 +1,94 @@
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<!-- Scrollview is necessary to fit everything in landscape layout -->
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/screen_share_permission_dialog"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="@dimen/dialog_side_padding"
+ android:paddingEnd="@dimen/dialog_side_padding"
+ android:paddingTop="@dimen/dialog_top_padding"
+ android:paddingBottom="@dimen/dialog_bottom_padding"
+ android:orientation="vertical"
+ android:gravity="center_horizontal">
+
+ <ImageView
+ android:layout_width="@dimen/screenrecord_logo_size"
+ android:layout_height="@dimen/screenrecord_logo_size"
+ android:src="@drawable/ic_screenrecord"
+ android:tint="@color/screenrecord_icon_color"
+ android:importantForAccessibility="no"/>
+ <TextView
+ android:id="@+id/screen_share_dialog_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:fontFamily="@*android:string/config_headlineFontFamily"
+ android:layout_marginTop="22dp"
+ android:layout_marginBottom="15dp"/>
+ <Spinner
+ android:id="@+id/screen_share_mode_spinner"
+ android:layout_width="320dp"
+ android:layout_height="72dp"
+ android:layout_marginTop="24dp"
+ android:layout_marginBottom="24dp" />
+ <ViewStub
+ android:id="@+id/options_stub"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ <TextView
+ android:id="@+id/text_warning"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/screenrecord_description"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:textColorSecondary"
+ android:gravity="start"
+ android:layout_marginBottom="20dp"/>
+
+ <!-- Buttons -->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginTop="36dp">
+ <TextView
+ android:id="@+id/button_cancel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:text="@string/cancel"
+ style="@style/Widget.Dialog.Button.BorderButton" />
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"/>
+ <TextView
+ android:id="@+id/button_start"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:text="@string/screenrecord_start"
+ style="@style/Widget.Dialog.Button" />
+ </LinearLayout>
+ </LinearLayout>
+</ScrollView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/screenshot_static.xml b/packages/SystemUI/res/layout/screenshot_static.xml
index 1ac78d4..8842992 100644
--- a/packages/SystemUI/res/layout/screenshot_static.xml
+++ b/packages/SystemUI/res/layout/screenshot_static.xml
@@ -44,7 +44,7 @@
app:layout_constraintHorizontal_bias="0"
app:layout_constraintWidth_percent="1.0"
app:layout_constraintWidth_max="wrap"
- app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/screenshot_message_container"
app:layout_constraintStart_toEndOf="@+id/screenshot_preview_border"
app:layout_constraintEnd_toEndOf="parent">
<LinearLayout
@@ -70,7 +70,7 @@
android:alpha="0"
android:background="@drawable/overlay_border"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/screenshot_message_container"
app:layout_constraintEnd_toEndOf="@id/screenshot_preview_end"
app:layout_constraintTop_toTopOf="@id/screenshot_preview_top"/>
<androidx.constraintlayout.widget.Barrier
@@ -142,4 +142,41 @@
app:layout_constraintStart_toStartOf="@id/screenshot_preview"
app:layout_constraintTop_toTopOf="@id/screenshot_preview"
android:elevation="7dp"/>
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/screenshot_message_container"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="@dimen/overlay_action_container_margin_horizontal"
+ android:layout_marginVertical="4dp"
+ android:paddingHorizontal="@dimen/overlay_action_container_padding_right"
+ android:paddingVertical="@dimen/overlay_action_container_padding_vertical"
+ android:elevation="4dp"
+ android:background="@drawable/action_chip_container_background"
+ android:visibility="gone"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent">
+
+ <ImageView
+ android:id="@+id/screenshot_message_icon"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:paddingEnd="4dp"
+ android:src="@drawable/ic_work_app_badge"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"/>
+
+ <TextView
+ android:id="@+id/screenshot_message_content"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toEndOf="@id/screenshot_message_icon"
+ app:layout_constraintEnd_toEndOf="parent"/>
+
+ </androidx.constraintlayout.widget.ConstraintLayout>
</com.android.systemui.screenshot.DraggableConstraintLayout>
diff --git a/packages/SystemUI/res/layout/user_switcher_fullscreen.xml b/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
index 78884ff..fa9d739 100644
--- a/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
+++ b/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
@@ -80,7 +80,7 @@
<TextView
android:id="@+id/add"
- style="@style/Widget.Dialog.Button.BorderButton"
+ android:background="@drawable/user_switcher_fullscreen_button_bg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
diff --git a/packages/SystemUI/res/raw/biometricprompt_folded_base_bottomright.json b/packages/SystemUI/res/raw/biometricprompt_folded_base_bottomright.json
new file mode 100644
index 0000000..2797996
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_folded_base_bottomright.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_portrait_base_bottomright","ddd":0,"assets":[{"id":"comp_0","nm":"biometricprompt_landscape_base","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 16","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"Null_Circle","parent":1,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[70.333,-88.75,0],"to":[-11.722,17.639,0],"ti":[11.722,-17.639,0]},{"t":-48,"s":[0,17.083,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".grey600","cl":"grey600","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle mask 3","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Finger","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-60,"s":[55]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":110,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":140,"s":[10]},{"t":170,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-60,"s":[92.146,-65.896,0],"to":[1.361,6.667,0],"ti":[-1.361,-6.667,0]},{"i":{"x":0.2,"y":0.2},"o":{"x":0.167,"y":0.167},"t":0,"s":[100.313,-25.896,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":0.2},"o":{"x":0.7,"y":0.7},"t":110,"s":[100.313,-25.896,0],"to":[0,0,0],"ti":[0,0,0]},{"t":170,"s":[100.313,-25.896,0]}],"ix":2,"l":2},"a":{"a":0,"k":[160.315,58.684,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-11.013,2.518],[5.251,5.023],[8.982,-2.829],[-0.264,-5.587]],"o":[[12.768,-2.854],[-14.961,2.071],[-6.004,1.89],[8.052,1.403]],"v":[[5.115,7.499],[19.814,-10.087],[-16.489,-3.588],[-24.801,8.684]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.760784373564,0.478431402468,0.400000029919,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[34.67,28.053],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[22.231,-7],[-27.395,-1.197],[-26.792,4.092],[14.179,15.736]],"o":[[-17.931,5.646],[56.062,2.45],[-1.765,-22.396],[-51.819,17.744]],"v":[[-62.102,-8.314],[-39.958,30.079],[80.033,25.905],[54.879,-32.529]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.678431372549,0.403921598547,0.305882352941,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[80.283,32.779],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"circle mask 7","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".grey600","cl":"grey600","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.25,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":36.9,"ix":2},"o":{"a":0,"k":114.2,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"circle mask","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".grey800","cl":"grey800","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.5,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"circle mask 6","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".grey900","cl":"grey900","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":377,"s":[-180]},{"t":417,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":377,"s":[-1.137,1.771,0],"to":[0.375,0,0],"ti":[-0.375,0,0]},{"t":417,"s":[1.113,1.771,0]}],"ix":2,"l":2},"a":{"a":0,"k":[6.238,5.063,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":77,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":107,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":137,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":167,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":197,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.7,"y":0},"t":232,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":562,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"t":602,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-4.546,-0.421],[-5.988,1.021],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[6.238,5.063],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"circle mask 2","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue400","cl":"blue400","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,8.308,0],"ix":2,"l":2},"a":{"a":0,"k":[41.706,20.979,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[18.645,0],[0,18.645]],"o":[[0,18.645],[-18.644,0],[0,0]],"v":[[33.76,-16.88],[-0.001,16.88],[-33.76,-16.88]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.706,17.13],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[22.896,0],[0,22.896]],"o":[[0,22.896],[-22.896,0],[0,0]],"v":[[41.457,-20.729],[-0.001,20.729],[-41.457,-20.729]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.706,20.979],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"circle mask 4","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":15,"ty":1,"nm":".grey900","cl":"grey900","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,66,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[52,52,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#202124","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":"circle mask 5","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":17,"ty":1,"nm":".black","cl":"black","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-17.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[72,72,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#000000","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":".grey800","cl":"grey800","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[-192.25,99.933,0],"to":[5,3.333,0],"ti":[-5,-3.333,0]},{"t":-48,"s":[-162.25,119.933,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-163,100.85,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":-108,"s":[100,100,100]},{"t":-48,"s":[59,59,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":".grey900","cl":"grey900","parent":23,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[0,18.167,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[0,10.667,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-171,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-141,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.512],[0,0.512],[3,3.512]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-111,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"t":-81,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.967],[0,0.967],[3,3.967]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-199,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":"Shape Layer 4","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-116.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":-199,"s":[71,-101.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":365,"s":[71,-101.083,0],"to":[0,0,0],"ti":[16.833,-14.361,0]},{"t":405,"s":[-30,-14.917,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-239,"s":[29,29]},{"i":{"x":[0.833,0.833],"y":[1,0.833]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-199,"s":[29,38]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":365,"s":[29,36]},{"t":405,"s":[83,83]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":365,"s":[50]},{"t":405,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":21,"ty":4,"nm":".grey900","cl":"grey900","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-82.917,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[71,-90.417,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":-199,"st":-255,"bm":0},{"ddd":0,"ind":22,"ty":4,"nm":"device frame mask","parent":24,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,1.167,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":23,"ty":4,"nm":".blue400","cl":"blue400","parent":18,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[100.25,-115.167,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-199,"s":[100.25,-100.167,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":-159,"s":[100.25,-105.667,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":365,"s":[100.25,-100.167,0],"to":[0,0,0],"ti":[16.833,-14.361,0]},{"t":405,"s":[-0.75,-14,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-239,"s":[29,29]},{"i":{"x":[0.833,0.833],"y":[1,0.833]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-199,"s":[29,38]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":365,"s":[29,36]},{"t":405,"s":[83,83]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":365,"s":[50]},{"t":405,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":24,"ty":3,"nm":"device frame mask 5","parent":18,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":-165,"op":6.00000000000001,"st":-271,"bm":0},{"ddd":0,"ind":28,"ty":4,"nm":"device frame mask 9","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-181,"op":-62,"st":-181,"bm":0},{"ddd":0,"ind":29,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-145,"s":[50]},{"t":-75,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-165,"s":[0,0]},{"t":-75,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":73,"s":[50]},{"t":113,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-181,"op":-62,"st":-181,"bm":0},{"ddd":0,"ind":30,"ty":4,"nm":"device frame mask 8","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-211,"op":-92,"st":-211,"bm":0},{"ddd":0,"ind":31,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-165,"s":[50]},{"t":-95,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-195,"s":[0,0]},{"t":-105,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":43,"s":[50]},{"t":83,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-211,"op":-92,"st":-211,"bm":0},{"ddd":0,"ind":32,"ty":4,"nm":"device frame mask 7","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-241,"op":-122,"st":-241,"bm":0},{"ddd":0,"ind":33,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-195,"s":[50]},{"t":-125,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-225,"s":[0,0]},{"t":-135,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":13,"s":[50]},{"t":53,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-241,"op":-122,"st":-241,"bm":0},{"ddd":0,"ind":34,"ty":4,"nm":"device frame mask 6","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-271,"op":-152,"st":-271,"bm":0},{"ddd":0,"ind":35,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-225,"s":[50]},{"t":-155,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-255,"s":[0,0]},{"t":-165,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-17,"s":[50]},{"t":23,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-271,"op":-152,"st":-271,"bm":0}]}],"layers":[{"ddd":0,"ind":6,"ty":0,"nm":"biometricprompt_landscape_base","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[170,170,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":340,"h":340,"ip":0,"op":900,"st":0,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
diff --git a/packages/SystemUI/res/raw/biometricprompt_folded_base_default.json b/packages/SystemUI/res/raw/biometricprompt_folded_base_default.json
new file mode 100644
index 0000000..bf65b34
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_folded_base_default.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_landscape_base","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 16","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"Null_Circle","parent":1,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[70.333,-88.75,0],"to":[-11.722,17.639,0],"ti":[11.722,-17.639,0]},{"t":-48,"s":[0,17.083,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".grey600","cl":"grey600","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle mask 3","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Finger","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-60,"s":[55]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":110,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":140,"s":[10]},{"t":170,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-60,"s":[92.146,-65.896,0],"to":[1.361,6.667,0],"ti":[-1.361,-6.667,0]},{"i":{"x":0.2,"y":0.2},"o":{"x":0.167,"y":0.167},"t":0,"s":[100.313,-25.896,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":0.2},"o":{"x":0.7,"y":0.7},"t":110,"s":[100.313,-25.896,0],"to":[0,0,0],"ti":[0,0,0]},{"t":170,"s":[100.313,-25.896,0]}],"ix":2,"l":2},"a":{"a":0,"k":[160.315,58.684,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-11.013,2.518],[5.251,5.023],[8.982,-2.829],[-0.264,-5.587]],"o":[[12.768,-2.854],[-14.961,2.071],[-6.004,1.89],[8.052,1.403]],"v":[[5.115,7.499],[19.814,-10.087],[-16.489,-3.588],[-24.801,8.684]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.760784373564,0.478431402468,0.400000029919,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[34.67,28.053],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[22.231,-7],[-27.395,-1.197],[-26.792,4.092],[14.179,15.736]],"o":[[-17.931,5.646],[56.062,2.45],[-1.765,-22.396],[-51.819,17.744]],"v":[[-62.102,-8.314],[-39.958,30.079],[80.033,25.905],[54.879,-32.529]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.678431372549,0.403921598547,0.305882352941,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[80.283,32.779],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"circle mask 7","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".grey600","cl":"grey600","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.25,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":36.9,"ix":2},"o":{"a":0,"k":114.2,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"circle mask","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".grey800","cl":"grey800","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.5,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"circle mask 6","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".grey900","cl":"grey900","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":377,"s":[-180]},{"t":417,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":377,"s":[-1.137,1.771,0],"to":[0.375,0,0],"ti":[-0.375,0,0]},{"t":417,"s":[1.113,1.771,0]}],"ix":2,"l":2},"a":{"a":0,"k":[6.238,5.063,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":77,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":107,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":137,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":167,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":197,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.7,"y":0},"t":232,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":562,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"t":602,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-4.546,-0.421],[-5.988,1.021],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[6.238,5.063],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"circle mask 2","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue400","cl":"blue400","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,8.308,0],"ix":2,"l":2},"a":{"a":0,"k":[41.706,20.979,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[18.645,0],[0,18.645]],"o":[[0,18.645],[-18.644,0],[0,0]],"v":[[33.76,-16.88],[-0.001,16.88],[-33.76,-16.88]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.706,17.13],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[22.896,0],[0,22.896]],"o":[[0,22.896],[-22.896,0],[0,0]],"v":[[41.457,-20.729],[-0.001,20.729],[-41.457,-20.729]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.706,20.979],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"circle mask 4","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":15,"ty":1,"nm":".grey900","cl":"grey900","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,66,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[52,52,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#202124","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":"circle mask 5","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":17,"ty":1,"nm":".black","cl":"black","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-17.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[72,72,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#000000","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":".grey800","cl":"grey800","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[-192.25,99.933,0],"to":[5,3.333,0],"ti":[-5,-3.333,0]},{"t":-48,"s":[-162.25,119.933,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-163,100.85,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":-108,"s":[100,100,100]},{"t":-48,"s":[59,59,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":".grey900","cl":"grey900","parent":23,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[0,18.167,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[0,10.667,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-171,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-141,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.512],[0,0.512],[3,3.512]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-111,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"t":-81,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.967],[0,0.967],[3,3.967]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-199,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":"Shape Layer 4","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-116.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":-199,"s":[71,-101.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":365,"s":[71,-101.083,0],"to":[0,0,0],"ti":[16.833,-14.361,0]},{"t":405,"s":[-30,-14.917,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-239,"s":[29,29]},{"i":{"x":[0.833,0.833],"y":[1,0.833]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-199,"s":[29,38]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":365,"s":[29,36]},{"t":405,"s":[83,83]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":365,"s":[50]},{"t":405,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":21,"ty":4,"nm":".grey900","cl":"grey900","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-82.917,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[71,-90.417,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":-199,"st":-255,"bm":0},{"ddd":0,"ind":22,"ty":4,"nm":"device frame mask","parent":24,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,1.167,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":23,"ty":4,"nm":".blue400","cl":"blue400","parent":18,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[100.25,-115.167,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-199,"s":[100.25,-100.167,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":-159,"s":[100.25,-105.667,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":365,"s":[100.25,-100.167,0],"to":[0,0,0],"ti":[16.833,-14.361,0]},{"t":405,"s":[-0.75,-14,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-239,"s":[29,29]},{"i":{"x":[0.833,0.833],"y":[1,0.833]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-199,"s":[29,38]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":365,"s":[29,36]},{"t":405,"s":[83,83]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":365,"s":[50]},{"t":405,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":24,"ty":3,"nm":"device frame mask 5","parent":18,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":-165,"op":6.00000000000001,"st":-271,"bm":0},{"ddd":0,"ind":28,"ty":4,"nm":"device frame mask 9","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-181,"op":-62,"st":-181,"bm":0},{"ddd":0,"ind":29,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-145,"s":[50]},{"t":-75,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-165,"s":[0,0]},{"t":-75,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":73,"s":[50]},{"t":113,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-181,"op":-62,"st":-181,"bm":0},{"ddd":0,"ind":30,"ty":4,"nm":"device frame mask 8","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-211,"op":-92,"st":-211,"bm":0},{"ddd":0,"ind":31,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-165,"s":[50]},{"t":-95,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-195,"s":[0,0]},{"t":-105,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":43,"s":[50]},{"t":83,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-211,"op":-92,"st":-211,"bm":0},{"ddd":0,"ind":32,"ty":4,"nm":"device frame mask 7","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-241,"op":-122,"st":-241,"bm":0},{"ddd":0,"ind":33,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-195,"s":[50]},{"t":-125,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-225,"s":[0,0]},{"t":-135,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":13,"s":[50]},{"t":53,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-241,"op":-122,"st":-241,"bm":0},{"ddd":0,"ind":34,"ty":4,"nm":"device frame mask 6","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-271,"op":-152,"st":-271,"bm":0},{"ddd":0,"ind":35,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-225,"s":[50]},{"t":-155,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-255,"s":[0,0]},{"t":-165,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-17,"s":[50]},{"t":23,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-271,"op":-152,"st":-271,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
diff --git a/packages/SystemUI/res/raw/biometricprompt_folded_base_topleft.json b/packages/SystemUI/res/raw/biometricprompt_folded_base_topleft.json
new file mode 100644
index 0000000..7351d7c
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_folded_base_topleft.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Portrait_Base_TopLeft","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":6,"ty":3,"nm":"Null 16","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null_Circle","parent":6,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[70.333,-88.75,0],"to":[-11.722,17.639,0],"ti":[11.722,-17.639,0]},{"t":-48,"s":[0,17.083,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".grey600","cl":"grey600","parent":7,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"circle mask 3","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Finger_Flipped","parent":6,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[-24.98,-35.709,0],"ix":2,"l":2},"a":{"a":0,"k":[31.791,75.23,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[5.03,5.25],[-2.83,8.98],[-5.59,-0.26],[2.52,-11.02]],"o":[[-2.85,12.77],[2.07,-14.96],[1.9,-6],[1.4,8.05],[0,0]],"v":[[7.5,4.99],[-10.09,19.69],[-3.59,-16.61],[8.69,-24.92],[7.5,5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.760784373564,0.478431402468,0.400000029919,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27.8,24.94],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-7.01,22.23],[-1.2,-27.39],[4.09,-26.79],[15.73,14.18]],"o":[[5.64,-17.93],[2.45,56.06],[-22.4,-1.77],[17.73,-51.82]],"v":[[-7.57,-66.9],[30.82,-44.76],[26.65,75.23],[-31.78,50.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.678431372549,0.403921598547,0.305882352941,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[31.79,75.23],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"circle mask 7","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".grey600","cl":"grey600","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.25,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":36.9,"ix":2},"o":{"a":0,"k":114.2,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"circle mask","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".grey900","cl":"grey900","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.5,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":"circle mask 6","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":".grey900","cl":"grey900","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":377,"s":[-180]},{"t":417,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":377,"s":[-1.137,1.771,0],"to":[0.375,0,0],"ti":[-0.375,0,0]},{"t":417,"s":[1.113,1.771,0]}],"ix":2,"l":2},"a":{"a":0,"k":[6.238,5.063,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":77,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":107,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":137,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":167,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":197,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.7,"y":0},"t":232,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":562,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"t":602,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-4.546,-0.421],[-5.988,1.021],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[6.238,5.063],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"circle mask 2","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":".blue400","cl":"blue400","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,8.308,0],"ix":2,"l":2},"a":{"a":0,"k":[41.706,20.979,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[18.645,0],[0,18.645]],"o":[[0,18.645],[-18.644,0],[0,0]],"v":[[33.76,-16.88],[-0.001,16.88],[-33.76,-16.88]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.706,17.13],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[22.896,0],[0,22.896]],"o":[[0,22.896],[-22.896,0],[0,0]],"v":[[41.457,-20.729],[-0.001,20.729],[-41.457,-20.729]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.706,20.979],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":"circle mask 4","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":20,"ty":1,"nm":".grey900","cl":"grey900","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,66,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[52,52,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#202124","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":21,"ty":4,"nm":"circle mask 5","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":22,"ty":1,"nm":".black","cl":"black","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-17.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[72,72,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#000000","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":23,"ty":4,"nm":".grey800","cl":"grey800","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[-192.25,99.933,0],"to":[5,3.333,0],"ti":[-5,-3.333,0]},{"t":-48,"s":[-162.25,119.933,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-163,100.85,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":-108,"s":[100,100,100]},{"t":-48,"s":[59,59,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":24,"ty":4,"nm":".grey900","cl":"grey900","parent":23,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[100.25,-87.156,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[100.25,-94.656,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-171,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-141,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.512],[0,0.512],[3,3.512]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-111,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"t":-81,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.967],[0,0.967],[3,3.967]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-199,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":25,"ty":4,"nm":"Shape Layer 4","parent":6,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-116.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":-199,"s":[71,-101.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":365,"s":[71,-101.083,0],"to":[0,0,0],"ti":[16.833,-14.361,0]},{"t":405,"s":[-30,-14.917,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-239,"s":[29,29]},{"i":{"x":[0.833,0.833],"y":[1,0.833]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-199,"s":[29,38]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":365,"s":[29,36]},{"t":405,"s":[83,83]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":365,"s":[50]},{"t":405,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":26,"ty":4,"nm":".grey900","cl":"grey900","parent":6,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-82.917,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[71,-90.417,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":-199,"st":-255,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 2f2780b..afa9818 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Kan nie gesig herken nie"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Gebruik eerder vingerafdruk"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth gekoppel."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Batterypersentasie is onbekend."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Gekoppel aan <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Helderheid"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Kleuromkering"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Kleurregstelling"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Bestuur gebruikers"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Klaar"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Maak toe"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Gekoppel"</string>
@@ -304,6 +305,28 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Mikrofoon beskikbaar"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Kamera beskikbaar"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Mikrofoon en kamera beskikbaar"</string>
+ <!-- no translation found for sensor_privacy_mic_turned_on_dialog_title (6348853159838376513) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_turned_off_dialog_title (5760464281790732849) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_unblocked_dialog_content (4889961886199270224) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_blocked_no_exception_dialog_content (5864898470772965394) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_blocked_with_exception_dialog_content (810289713700437896) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_turned_on_dialog_title (8039095295100075952) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_turned_off_dialog_title (1936603903120742696) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_unblocked_dialog_content (7847190103011782278) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_blocked_dialog_content (3182428709314874616) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_htt_blocked_dialog_content (3333321592997666441) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_dialog_open_settings (1503088305279285048) -->
+ <skip />
<string name="media_seamless_other_device" msgid="4654849800789196737">"Ander toestel"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Wissel oorsig"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Jy sal nie deur geluide en vibrasies gepla word nie, behalwe deur wekkers, herinneringe, geleenthede en bellers wat jy spesifiseer. Jy sal steeds enigiets hoor wat jy kies om te speel, insluitend musiek, video\'s en speletjies."</string>
@@ -374,6 +397,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Wanneer jy ’n program deel, opneem of uitsaai, het <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> toegang tot enigiets wat in daardie program sigbaar is of daarin gespeel word. Wees dus versigtig met wagwoorde, betalingbesonderhede, boodskappe of ander sensitiewe inligting."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Gaan voort"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Deel of neem ’n program op"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Vee alles uit"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Bestuur"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Geskiedenis"</string>
@@ -728,14 +761,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Skakel mobiele data af?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Jy sal nie deur <xliff:g id="CARRIER">%s</xliff:g> toegang tot data of die internet hê nie. Internet sal net deur Wi-Fi beskikbaar wees."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"jou diensverskaffer"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Skakel weer oor na <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Mobiele data sal nie outomaties op grond van beskikbaarheid oorskakel nie"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Nee, dankie"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Ja, skakel oor"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Instellings kan nie jou antwoord verifieer nie omdat \'n program \'n toestemmingversoek verberg."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Laat <xliff:g id="APP_0">%1$s</xliff:g> toe om <xliff:g id="APP_2">%2$s</xliff:g>-skyfies te wys?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"– Dit kan inligting van <xliff:g id="APP">%1$s</xliff:g> af lees"</string>
@@ -794,18 +823,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Vergrootglasvensterinstellings"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tik om toeganklikheidkenmerke oop te maak Pasmaak of vervang knoppie in Instellings.\n\n"<annotation id="link">"Bekyk instellings"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Skuif knoppie na kant om dit tydelik te versteek"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Ontdoen"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} kortpad is verwyder}other{# kortpaaie is verwyder}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Beweeg na links bo"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Beweeg na regs bo"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Beweeg na links onder"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Beweeg na regs onder"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Beweeg na rand en versteek"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Beweeg weg van rand en wys"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Verwyder"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"wissel"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Toestelkontroles"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Kies program om kontroles by te voeg"</string>
@@ -948,10 +974,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiele data"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Gekoppel"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Tydelik gekoppel"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Swak verbinding"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobiele data sal nie outomaties koppel nie"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Geen verbinding nie"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Geen ander netwerke beskikbaar nie"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 3ccb686..5c4e766 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"መልክን መለየት አልተቻለም"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"በምትኩ የጣት አሻራን ይጠቀሙ"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"ብሉቱዝ ተያይዟል።"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"የባትሪ መቶኛ አይታወቅም።"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"ከ<xliff:g id="BLUETOOTH">%s</xliff:g> ጋር ተገናኝቷል።"</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ብሩህነት"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"ተቃራኒ ቀለም"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"የቀለም ማስተካከያ"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"ተጠቃሚዎችን ያስተዳድሩ"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"ተከናውኗል"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"ዝጋ"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"ተገናኝቷል"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"ማይክሮፎን አለ"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"ካሜራ አለ"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"ማይክሮፎን እና ካሜራ አለ"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"ማይክሮፎን በርቷል"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"ማይክሮፎን ጠፍቷል"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"ማይክሮፎን ለሁሉም መተግበሪያዎች እና አገልግሎቶች ነቅቷል።"</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"የማይክሮፎን መዳረሻ ለሁሉም መተግበሪያዎች እና አገልግሎቶች ተሰናክሏል። የማይክሮፎን መዳረሻን በቅንብሮች > ግላዊነት > ማይክሮፎን ውስጥ ማንቃት ይችላሉ።"</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"የማይክሮፎን መዳረሻ ለሁሉም መተግበሪያዎች እና አገልግሎቶች ተሰናክሏል። ይህን በቅንብሮች > ግላዊነት > ማይክሮፎን ውስጥ መቀየር ይችላሉ።"</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"ካሜራ በርቷል"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"ካሜራ ጠፍቷል"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"ካሜራ ለሁሉም መተግበሪያዎች እና አገልግሎቶች ነቅቷል።"</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"የካሜራ መዳረሻ ለሁሉም መተግበሪያዎች እና አገልግሎቶች ተሰናክሏል።"</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"የማይክሮፎን አዝራርን ለመጠቀም በቅንብሮች ውስጥ የማይክሮፎን መዳረሻን ያንቁ።"</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"ቅንብሮችን ክፈት።"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"ሌላ መሣሪያ"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"አጠቃላይ እይታን ቀያይር"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"እርስዎ ከወሰንዋቸው ማንቂያዎች፣ አስታዋሾች፣ ክስተቶች እና ደዋዮች በስተቀር፣ በድምጾች እና ንዝረቶች አይረበሹም። ሙዚቃ፣ ቪዲዮዎች እና ጨዋታዎች ጨምሮ ለመጫወት የሚመርጡትን ማንኛውም ነገር አሁንም ይሰማሉ።"</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"አንድን መተግበሪያ ሲያጋሩ፣ ሲቀርጹ ወይም cast ሲያደርጉ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> በዚያ መተግበሪያ ላይ ለሚታይ ወይም ለሚጫወት ማንኛውም ነገር መዳረሻ አለው። ስለዚህ በይለፍ ቃላት፣ በክፍያ ዝርዝሮች፣ በመልዕክቶች ወይም በሌሎች ልዩ ጥንቃቄ የሚያስፈልጋቸው መረጃዎች ላይ ጥንቃቄ ያድርጉ።"</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"ቀጥል"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"መተግበሪያ ያጋሩ ወይም ይቅረጹ"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"ሁሉንም አጽዳ"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"ያቀናብሩ"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ታሪክ"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"የተንቀሳቃሽ ስልክ ውሂብ ይጥፋ?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"በ<xliff:g id="CARRIER">%s</xliff:g> በኩል የውሂብ ወይም የበይነመረቡ መዳረሻ አይኖረዎትም። በይነመረብ በWi-Fi በኩል ብቻ ነው የሚገኝ የሚሆነው።"</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"የእርስዎ አገልግሎት አቅራቢ"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"ወደ <xliff:g id="CARRIER">%s</xliff:g> ተመልሶ ይቀየር?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"የተንቀሳቃሽ ስልክ ውሂብ በተገኝነት መሰረት በራስ ሰር አይቀይርም"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"አይ አመሰግናለሁ"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"አዎ፣ ቀይር"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"አንድ መተግበሪያ የፍቃድ ጥያቄ እያገደ ስለሆነ ቅንብሮች ጥያቄዎን ማረጋገጥ አይችሉም።"</string>
<string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> የ<xliff:g id="APP_2">%2$s</xliff:g> ቁራጮችን እንዲያሳይ ይፈቀድለት?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- ከ<xliff:g id="APP">%1$s</xliff:g> የመጣ መረጃን ማንበብ ይችላል"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"የማጉያ መስኮት ቅንብሮች"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"የተደራሽነት ባህሪያትን ለመክፈት መታ ያድርጉ። ይህንን አዝራር በቅንብሮች ውስጥ ያብጁ ወይም ይተኩ።\n\n"<annotation id="link">"ቅንብሮችን አሳይ"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ለጊዜው ለመደበቅ አዝራሩን ወደ ጠርዝ ያንቀሳቅሱ"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"ቀልብስ"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} አቋራጭ ተወግዷል}one{# አቋራጭ ተወግዷል}other{# አቋራጮች ተወግደዋል}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ወደ ላይኛው ግራ አንቀሳቅስ"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ወደ ላይኛው ቀኝ አንቀሳቅስ"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"የግርጌውን ግራ አንቀሳቅስ"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"ታችኛውን ቀኝ አንቀሳቅስ"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"ወደ ጠርዝ አንቀሳቅስ እና ደደብቅ"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"ጠርዙን ወደ ውጭ አንቀሳቅስ እና አሳይ"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"አስወግድ"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ቀያይር"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"የመሣሪያ መቆጣጠሪያዎች"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"መቆጣጠሪያዎችን ለማከል መተግበሪያ ይምረጡ"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"የተንቀሳቃሽ ስልክ ውሂብ"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"ተገናኝቷል"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"በጊዜያዊነት ተገናኝቷል"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"ደካማ ግንኙነት"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"የተንቀሳቃሽ ስልክ ውሂብ በራስ-ሰር አይገናኝም"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"ግንኙነት የለም"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"ሌላ አውታረ መረብ የሉም"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index c90fb25..f9b866d 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"يتعذّر التعرّف على الوجه."</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"يمكنك استخدام بصمة إصبعك."</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"تم توصيل البلوتوث."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"نسبة شحن البطارية غير معروفة."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"متصل بـ <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"السطوع"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"قلب الألوان"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"تصحيح الألوان"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"إدارة المستخدمين"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"تم"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"إغلاق"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"متصل"</string>
@@ -304,6 +305,28 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"يمكنك الوصول إلى الميكروفون الآن."</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"يمكنك الوصول إلى الكاميرا الآن."</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"يمكنك الوصول إلى الميكروفون والكاميرا الآن."</string>
+ <!-- no translation found for sensor_privacy_mic_turned_on_dialog_title (6348853159838376513) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_turned_off_dialog_title (5760464281790732849) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_unblocked_dialog_content (4889961886199270224) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_blocked_no_exception_dialog_content (5864898470772965394) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_blocked_with_exception_dialog_content (810289713700437896) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_turned_on_dialog_title (8039095295100075952) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_turned_off_dialog_title (1936603903120742696) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_unblocked_dialog_content (7847190103011782278) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_blocked_dialog_content (3182428709314874616) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_htt_blocked_dialog_content (3333321592997666441) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_dialog_open_settings (1503088305279285048) -->
+ <skip />
<string name="media_seamless_other_device" msgid="4654849800789196737">"جهاز آخر"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"تبديل \"النظرة العامة\""</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"لن يتم إزعاجك بالأصوات والاهتزاز، باستثناء المُنبِّهات والتذكيرات والأحداث والمتصلين الذين تحددهم. وسيظل بإمكانك سماع أي عناصر أخرى تختار تشغيلها، بما في ذلك الموسيقى والفيديوهات والألعاب."</string>
@@ -374,6 +397,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"أثناء مشاركة محتوى تطبيق أو تسجيله أو بثه، يمكن لتطبيق <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> الوصول إلى كل العناصر المعروضة أو التي يتم تشغيلها في ذلك التطبيق، لذا يُرجى توخي الحذر بشأن كلمات المرور أو تفاصيل الدفع أو الرسائل أو المعلومات الحساسة الأخرى."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"متابعة"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"مشاركة محتوى تطبيق أو تسجيله"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"محو الكل"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"إدارة"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"السجلّ"</string>
@@ -728,14 +761,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"هل تريد إيقاف بيانات الجوّال؟"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"لن تتمكّن من استخدام البيانات أو الإنترنت من خلال <xliff:g id="CARRIER">%s</xliff:g>. ولن يتوفر اتصال الإنترنت إلا عبر Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"مشغّل شبكة الجوّال"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"هل تريد التبديل مرة أخرى إلى \"<xliff:g id="CARRIER">%s</xliff:g>\"؟"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"لن يتم تلقائيًا تبديل بيانات الجوّال بناءً على التوفّر."</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"لا، شكرًا"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"نعم، أريد التبديل"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"لا يمكن للإعدادات التحقق من ردك لأن هناك تطبيقًا يحجب طلب الإذن."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"هل تريد السماح لتطبيق <xliff:g id="APP_0">%1$s</xliff:g> بعرض شرائح <xliff:g id="APP_2">%2$s</xliff:g>؟"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- يستطيع قراءة المعلومات من <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +823,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"إعدادات نافذة مكبّر الشاشة"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"انقر لفتح ميزات تسهيل الاستخدام. يمكنك تخصيص هذا الزر أو استبداله من الإعدادات.\n\n"<annotation id="link">"عرض الإعدادات"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"يمكنك نقل الزر إلى الحافة لإخفائه مؤقتًا."</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"تراجع"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{تمت إزالة اختصار واحد ({label}).}zero{تمت إزالة # اختصار.}two{تمت إزالة اختصارَين.}few{تمت إزالة # اختصارات.}many{تمت إزالة # اختصارًا.}other{تمت إزالة # اختصار.}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"نقل إلى أعلى يمين الشاشة"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"نقل إلى أعلى يسار الشاشة"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"نقل إلى أسفل يمين الشاشة"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"نقل إلى أسفل يسار الشاشة"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"نقله إلى الحافة وإخفاؤه"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"نقله إلى خارج الحافة وإظهاره"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"إزالة"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"إيقاف/تفعيل"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"التحكم بالجهاز"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"اختيار تطبيق لإضافة عناصر التحكّم"</string>
@@ -948,10 +974,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"بيانات الجوّال"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"متصلة بالإنترنت"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"متصلة مؤقتًا"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"الاتصال ضعيف"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"لن يتم تلقائيًا الاتصال ببيانات الجوّال."</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"لا يتوفّر اتصال بالإنترنت"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"لا تتوفّر شبكات أخرى."</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index c363eee..a341820 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"মুখাৱয়ব চিনিব নোৱাৰি"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"ইয়াৰ সলনি ফিংগাৰপ্ৰিণ্ট ব্যৱহাৰ কৰক"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"ব্লুটুথ সংযোগ হ’ল।"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"বেটাৰীৰ চাৰ্জৰ শতাংশ অজ্ঞাত।"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>ৰ লগত সংযোগ কৰা হ’ল।"</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"উজ্জ্বলতা"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"ৰং বিপৰীতকৰণ"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ৰং শুধৰণী"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"ব্যৱহাৰকাৰী পৰিচালনা কৰক"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"সম্পন্ন কৰা হ’ল"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"বন্ধ কৰক"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"সংযোগ কৰা হ’ল"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"মাইক্ৰ’ফ’ন উপলব্ধ"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"কেমেৰা উপলব্ধ"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"মাইক্ৰ’ফ’ন আৰু কেমেৰা উপলব্ধ"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"মাইক্ৰ’ফ’ন অন কৰা হ’ল"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"মাইক্ৰ’ফ’ন অফ কৰা হ’ল"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"আটাইবোৰ এপ্ আৰু সেৱাৰ বাবে মাইক্ৰ’ফ’ন সক্ষম কৰা হৈছে।"</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"আটাইবোৰ এপ্ আৰু সেৱাৰ বাবে মাইক্ৰ’ফ’নৰ এক্সেছ অক্ষম কৰা হৈছে। আপুনি ছেটিং > গোপনীয়তা > মাইক্ৰ’ফ’নত মাইক্ৰ’ফ’নৰ এক্সেছ সক্ষম কৰিব পাৰে।"</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"আটাইবোৰ এপ্ আৰু সেৱাৰ বাবে মাইক্ৰ’ফ’নৰ এক্সেছ অক্ষম কৰা হৈছে। আপুনি এইটো ছেটিং > গোপনীয়তা > মাইক্ৰ’ফ’নত সলনি কৰিব পাৰে।"</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"কেমেৰা অন কৰা হ’ল"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"কেমেৰা অফ কৰা হ’ল"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"আটাইবোৰ এপ্ আৰু সেৱাৰ বাবে কেমেৰা সক্ষম কৰা হৈছে।"</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"আটাইবোৰ এপ্ আৰু সেৱাৰ বাবে কেমেৰাৰ এক্সেছ অক্ষম কৰা হৈছে।"</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"মাইক্ৰ’ফ’নৰ বুটামটো ব্যৱহাৰ কৰিবলৈ, ছেটিঙত মাইক্ৰ’ফ’নৰ এক্সেছ সক্ষম কৰক।"</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"ছেটিং খোলক।"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"অন্য ডিভাইচ"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"অৱলোকন ট’গল কৰক"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"আপুনি নিৰ্দিষ্ট কৰা এলাৰ্ম, ৰিমাইণ্ডাৰ, ইভেন্ট আৰু কল কৰোঁতাৰ বাহিৰে আন কোনো শব্দৰ পৰা আপুনি অসুবিধা নাপাব। কিন্তু, সংগীত, ভিডিঅ\' আৰু খেলসমূহকে ধৰি আপুনি প্লে কৰিব খোজা যিকোনো বস্তু তথাপি শুনিব পাৰিব।"</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"আপুনি শ্বেয়াৰ কৰা, ৰেকৰ্ড কৰা অথবা কাষ্ট কৰাৰ সময়ত, সেইটো এপত দৃশ্যমান যিকোনো বস্তু অথবা আপোনাৰ ডিভাইচত প্লে’ কৰা যিকোনো সমললৈ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ৰ এক্সেছ থাকে। গতিকে, পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা অথবা অন্য সংবেদনশীল তথ্যৰ ক্ষেত্ৰত সাৱধান হওক।"</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"অব্যাহত ৰাখক"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"এটা এপ্ শ্বেয়াৰ অথবা ৰেকৰ্ড কৰক"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"আটাইবোৰ মচক"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"পৰিচালনা"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ইতিহাস"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"ম’বাইল ডেটা অফ কৰিবনে?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"আপুনি <xliff:g id="CARRIER">%s</xliff:g>ৰ জৰিয়তে ডেটা সংযোগ বা ইণ্টাৰনেট সংযোগ নাপাব। কেৱল ৱাই-ফাইৰ যোগেৰে ইণ্টাৰনেট উপলব্ধ হ\'ব।"</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"আপোনাৰ বাহক"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"আকৌ <xliff:g id="CARRIER">%s</xliff:g>লৈ সলনি কৰিবনে?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"ম’বাইলৰ ডেটা উপলব্ধতাৰ ওপৰত ভিত্তি কৰি স্বয়ংক্ৰিয়ভাৱে সলনি কৰা নহ’ব"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"নালাগে, ধন্যবাদ"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"হয়, সলনি কৰক"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"এটা এপে অনুমতি বিচাৰি কৰা অনুৰোধ এটা ঢাকি ধৰা বাবে ছেটিঙৰ পৰা আপোনাৰ উত্তৰ সত্যাপন কৰিব পৰা নাই।"</string>
<string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g>ক <xliff:g id="APP_2">%2$s</xliff:g>ৰ অংশ দেখুওৱাবলৈ অনুমতি দিবনে?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- ই <xliff:g id="APP">%1$s</xliff:g>ৰ তথ্য পঢ়িব পাৰে"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"বিবৰ্ধকৰ ৱিণ্ড’ৰ ছেটিং"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"সাধ্য সুবিধাসমূহ খুলিবলৈ টিপক। ছেটিঙত এই বুটামটো কাষ্টমাইজ অথবা সলনি কৰক।\n\n"<annotation id="link">"ছেটিং চাওক"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"বুটামটোক সাময়িকভাৱে লুকুৱাবলৈ ইয়াক একেবাৰে কাষলৈ লৈ যাওক"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"আনডু কৰক"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} টা শ্বৰ্টকাট আঁতৰোৱা হ’ল}one{# টা শ্বৰ্টকাট আঁতৰোৱা হ’ল}other{# টা শ্বৰ্টকাট আঁতৰোৱা হ’ল}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"শীৰ্ষৰ বাওঁফালে নিয়ক"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"শীৰ্ষৰ সোঁফালে নিয়ক"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"তলৰ বাওঁফালে নিয়ক"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"তলৰ সোঁফালে নিয়ক"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"কাষলৈ নিয়ক আৰু লুকুৱাওক"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"কাষৰ বাহিৰলৈ নিয়ক আৰু দেখুৱাওক"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"আঁতৰাওক"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ট’গল কৰক"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ডিভাইচৰ নিয়ন্ত্ৰণসমূহ"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"নিয়ন্ত্ৰণসমূহ যোগ কৰিবলৈ এপ্ বাছনি কৰক"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"ম’বাইল ডেটা"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"সংযোজিত হৈ আছে"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"অস্থায়ীভাৱে সংযোগ কৰা হৈছে"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"বেয়া সংযোগ"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"ম’বাইল ডেটা স্বয়ংক্ৰিয়ভাৱে সংযুক্ত নহ’ব"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"সংযোগ নাই"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"অন্য কোনো নেটৱৰ্ক উপলব্ধ নহয়"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index c5f143b..ed95d80 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Üzü tanımaq olmur"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Barmaq izi istifadə edin"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth qoşulub."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Batareyanın faizi naməlumdur."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> üzərindən qoşuldu."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Parlaqlıq"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Rəng inversiyası"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Rəng korreksiyası"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"İstifadəçiləri idarə edin"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Hazır"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Bağlayın"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Qoşulu"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Mikrofon əlçatandır"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Kamera əlçatandır"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Mikrofon və kamera əlçatandır"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Mikrofon aktiv edilib"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Mikrofon deaktiv edilib"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Mikrofon bütün tətbiqlər və xidmətlər üçün aktiv edilib."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Mikrofon girişi bütün tətbiqlər və xidmətlər üçün deaktiv edilib. Mikrofon girişini Ayarlar > Məxfilik > Mikrofon bölməsində aktiv edə bilərsiniz."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Mikrofon girişi bütün tətbiqlər və xidmətlər üçün deaktiv edilib. Bunu Ayarlar > Məxfilik > Mikrofon bölməsində dəyişə bilərsiniz."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Kamera aktivdir"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Kamera deaktivdir"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Kamera bütün tətbiqlər və xidmətlər üçün aktiv edilib."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Kamera girişi bütün tətbiqlər və xidmətlər üçün deaktiv edilib."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Mikrofon düyməsini istifadə etmək üçün Ayarlarda mikrofona girişi aktiv edin."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Ayarları açın."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Digər cihaz"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"İcmala Keçin"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Seçdiyiniz siqnal, xatırladıcı, tədbir və zənglər istisna olmaqla səslər və vibrasiyalar Sizi narahat etməyəcək. Musiqi, video və oyunlar da daxil olmaqla oxutmaq istədiyiniz hər şeyi eşidəcəksiniz."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Paylaşdığınız, qeydə aldığınız və ya yayımladığınız zaman <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tətbiqi həmin tətbiqdə göstərilən və ya oxudulan hər şeyə giriş edə bilir. Odur ki, parollar, ödəniş detalları, mesajlar və ya digər həssas məlumatlarla bağlı diqqətli olun."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Davam edin"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Tətbiqi paylaşın və ya qeydə alın"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Hamısını silin"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"İdarə edin"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Tarixçə"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Mobil data söndürülsün?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"<xliff:g id="CARRIER">%s</xliff:g> ilə data və ya internetə daxil ola bilməyəcəksiniz. İnternet yalnız Wi-Fi ilə əlçatan olacaq."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"operatorunuz"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"<xliff:g id="CARRIER">%s</xliff:g> operatoruna keçirilsin?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Mobil data əlçatımlıq əsasında avtomatik olaraq keçirilməyəcək"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Xeyr, təşəkkürlər"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Bəli, keçirin"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Tətbiq icazə sorğusunu gizlətdiyi üçün Ayarlar cavabınızı doğrulaya bilməz."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> tətbiqinə <xliff:g id="APP_2">%2$s</xliff:g> hissələrini göstərmək üçün icazə verilsin?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- <xliff:g id="APP">%1$s</xliff:g> tətbiqindən məlumat oxuya bilər"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Böyüdücü pəncərə ayarları"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Əlçatımlılıq funksiyalarını açmaq üçün toxunun. Ayarlarda bu düyməni fərdiləşdirin və ya dəyişdirin.\n\n"<annotation id="link">"Ayarlara baxın"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Düyməni müvəqqəti gizlətmək üçün kənara çəkin"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Geri qaytarın"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} qısayol silindi}other{# qısayol silindi}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Yuxarıya sola köçürün"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Yuxarıya sağa köçürün"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Aşağıya sola köçürün"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Aşağıya sağa köçürün"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"İçəri keçirib gizlədin"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Kənara daşıyıb göstərin"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Silin"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"keçirin"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Cihaz kontrolları"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Kontrol əlavə etmək üçün tətbiq seçin"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobil data"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Qoşulub"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Müvəqqəti qoşulub"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Zəif bağlantı"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobil data avtomatik qoşulmayacaq"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Bağlantı yoxdur"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Heç bir başqa şəbəkə əlçatan deyil"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index e61b692..a2cbce79 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Lice nije prepoznato"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Koristite otisak prsta"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth je priključen."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Procenat napunjenosti baterije nije poznat."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Povezani ste sa <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Osvetljenost"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inverzija boja"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Korekcija boja"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Upravljajte korisnicima"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Gotovo"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Zatvori"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Povezan"</string>
@@ -304,6 +305,28 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Mikrofon je dostupan"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Kamera je dostupna"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Mikrofon i kamera su dostupni"</string>
+ <!-- no translation found for sensor_privacy_mic_turned_on_dialog_title (6348853159838376513) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_turned_off_dialog_title (5760464281790732849) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_unblocked_dialog_content (4889961886199270224) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_blocked_no_exception_dialog_content (5864898470772965394) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_blocked_with_exception_dialog_content (810289713700437896) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_turned_on_dialog_title (8039095295100075952) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_turned_off_dialog_title (1936603903120742696) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_unblocked_dialog_content (7847190103011782278) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_blocked_dialog_content (3182428709314874616) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_htt_blocked_dialog_content (3333321592997666441) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_dialog_open_settings (1503088305279285048) -->
+ <skip />
<string name="media_seamless_other_device" msgid="4654849800789196737">"Drugi uređaj"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Uključi/isključi pregled"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Neće vas uznemiravati zvukovi i vibracije osim za alarme, podsetnike, događaje i pozivaoce koje navedete. I dalje ćete čuti sve što odaberete da pustite, uključujući muziku, video snimke i igre."</string>
@@ -374,6 +397,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Kada delite, snimate ili prebacujete aplikaciju, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ima pristup kompletnom sadržaju koji je vidljiv ili se pušta u toj aplikaciji. Budite pažljivi sa lozinkama, informacijama o plaćanju, porukama ili drugim osetljivim informacijama."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Nastavi"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Delite ili snimite aplikaciju"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Obriši sve"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Upravljajte"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Istorija"</string>
@@ -728,14 +761,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Želite da isključite mobilne podatke?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Nećete imati pristup podacima ili internetu preko mobilnog operatera <xliff:g id="CARRIER">%s</xliff:g>. Internet će biti dostupan samo preko WiFi veze."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"mobilni operater"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Želite da se vratite na mobilnog operatera <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Mobilni podaci se neće automatski promeniti na osnovu dostupnosti"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Ne, hvala"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Da, pređi"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Podešavanja ne mogu da verifikuju vaš odgovor jer aplikacija skriva zahtev za dozvolu."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Želite li da dozvolite aplikaciji <xliff:g id="APP_0">%1$s</xliff:g> da prikazuje isečke iz aplikacije <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"– Može da čita podatke iz aplikacije <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +823,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Podešavanja prozora za uvećanje"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Dodirnite za funkcije pristupačnosti. Prilagodite ili zamenite ovo dugme u Podešavanjima.\n\n"<annotation id="link">"Podešavanja"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Pomerite dugme do ivice da biste ga privremeno sakrili"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Opozovite"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} prečica je uklonjena}one{# prečica je uklonjena}few{# prečice su uklonjene}other{# prečica je uklonjeno}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Premesti gore levo"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Premesti gore desno"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Premesti dole levo"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Premesti dole desno"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Premesti do ivice i sakrij"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Premesti izvan ivice i prikaži"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Uklonite"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"uključite/isključite"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Kontrole uređaja"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Odaberite aplikaciju za dodavanje kontrola"</string>
@@ -948,10 +974,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilni podaci"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Povezano"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Privremeno povezano"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Veza je loša"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Nije uspelo autom. povezivanje preko mob. podataka"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Veza nije uspostavljena"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nije dostupna nijedna druga mreža"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 827370f..3d05c86 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Твар не распазнаны"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Скарыстайце адбітак пальца"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth-сувязь."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Працэнт зараду акумулятара невядомы."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Падлучаны да <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Яркасць"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Інверсія колераў"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Карэкцыя колераў"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Кіраваць карыстальнікамі"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Гатова"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Закрыць"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Падлучана"</string>
@@ -304,6 +305,28 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Мікрафон можна выкарыстоўваць"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Камеру можна выкарыстоўваць"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Мікрафон і камеру можна выкарыстоўваць"</string>
+ <!-- no translation found for sensor_privacy_mic_turned_on_dialog_title (6348853159838376513) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_turned_off_dialog_title (5760464281790732849) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_unblocked_dialog_content (4889961886199270224) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_blocked_no_exception_dialog_content (5864898470772965394) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_blocked_with_exception_dialog_content (810289713700437896) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_turned_on_dialog_title (8039095295100075952) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_turned_off_dialog_title (1936603903120742696) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_unblocked_dialog_content (7847190103011782278) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_blocked_dialog_content (3182428709314874616) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_htt_blocked_dialog_content (3333321592997666441) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_dialog_open_settings (1503088305279285048) -->
+ <skip />
<string name="media_seamless_other_device" msgid="4654849800789196737">"Іншая прылада"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Уключыць/выключыць агляд"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Вас не будуць турбаваць гукі і вібрацыя, за выключэннем будзільнікаў, напамінаў, падзей і выбраных вамі абанентаў. Вы будзеце чуць усё, што ўключыце, у тым ліку музыку, відэа і гульні."</string>
@@ -374,6 +397,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Калі пачынаецца абагульванне, запіс ці трансляцыя змесціва праграмы, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> атрымлівае доступ да ўсяго змесціва, якое паказваецца ці прайграецца ў праграме. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў і іншай канфідэнцыяльнай інфармацыі."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Далей"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Абагульванне або запіс праграмы"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Ачысціць усё"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Кіраваць"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Гісторыя"</string>
@@ -728,14 +761,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Выключыць мабільную перадачу даных?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"У вас не будзе доступу да даных ці інтэрнэту праз аператара <xliff:g id="CARRIER">%s</xliff:g>. Інтэрнэт будзе даступны толькі праз Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"ваш аператар"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Зноў пераключыцца на аператара \"<xliff:g id="CARRIER">%s</xliff:g>\"?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Мабільны інтэрнэт не будзе аўтаматычна пераключацца ў залежнасці ад даступнасці"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Не, дзякуй"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Так, пераключыцца"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Праграма хавае запыт на дазвол, таму ваш адказ немагчыма спраўдзіць у Наладах."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Дазволіць праграме <xliff:g id="APP_0">%1$s</xliff:g> паказваць зрэзы праграмы <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Можа счытваць інфармацыю з праграмы <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +823,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Налады акна лупы"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Націсніце, каб адкрыць спецыяльныя магчымасці. Рэгулюйце ці замяняйце кнопку ў Наладах.\n\n"<annotation id="link">"Прагляд налад"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Каб часова схаваць кнопку, перамясціце яе на край"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Адрабіць"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{Выдалены {label} ярлык}one{Выдалены # ярлык}few{Выдалена # ярлыкі}many{Выдалена # ярлыкоў}other{Выдалена # ярлыка}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Перамясціць лявей і вышэй"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Перамясціць правей і вышэй"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Перамясціць лявей і ніжэй"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Перамясціць правей і ніжэй"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Перамясціць на край і схаваць"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Перамясціць за край і паказаць"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Выдаліць"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"уключыць/выключыць"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Элементы кіравання прыладай"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Выберыце праграму для дадавання элементаў кіравання"</string>
@@ -948,10 +974,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Мабільная перадача даных"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Падключана"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Падключана часова"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Нестабільнае падключэнне"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Мабільная перадача даных не ўключаецца аўтаматычна"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Няма падключэння"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Больш няма даступных сетак"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 880445b..7253b498 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Лицето не е разпознато"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Използвайте отпечатък"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth е включен."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Процентът на батерията е неизвестен."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Има връзка с <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Яркост"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Цветове: инверт."</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Корекция на цветове"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Управление на потребителите"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Готово"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Затваряне"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Установена е връзка"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Микрофонът е налице"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Камерата е налице"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Микрофонът и камерата са налице"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Микрофонът е включен"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Микрофонът е изключен"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Достъпът до микрофона е активиран за всички приложения и услуги."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Достъпът до микрофона е деактивиран за всички приложения и услуги. Можете да го активирате от „Настройки > Поверителност > Микрофон“."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Достъпът до микрофона е деактивиран за всички приложения и услуги. Можете да промените това от „Настройки > Поверителност > Микрофон“."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Камерата е включена"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Камерата е изключена"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Достъпът до камерата е активиран за всички приложения и услуги."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Достъпът до камерата е деактивиран за всички приложения и услуги."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Активирайте достъпа до микрофона, за да използвате съответния бутон."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Отваряне на настройките."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Друго устройство"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Превключване на общия преглед"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Няма да бъдете обезпокоявани от звуци и вибрирания освен от будилници, напомняния, събития и обаждания от посочени от вас контакти. Пак ще чувате всичко, което изберете да се пусне, включително музика, видеоклипове и игри."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Когато споделяте, записвате или предавате, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> има достъп до всичко, което се показва или възпроизвежда в това приложение, затова бъдете внимателни с пароли, подробности за начини на плащане, съобщения или друга поверителна информация."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Напред"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Споделяне или записване на приложение"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Изчистване на всички"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Управление"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"История"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Да се изключат ли мобилните данни?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Няма да можете да използвате данни или интернет чрез <xliff:g id="CARRIER">%s</xliff:g>. Ще имате достъп до интернет само през Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"оператора си"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Искате ли да се върнете към <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Мрежата за мобилни данни няма да се превключва автоматично въз основа на наличността"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Не, благодаря"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Да, превключване"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"От Настройки не може да се получи потвърждение за отговора ви, защото заявката за разрешение се прикрива от приложение."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Искате ли да разрешите на <xliff:g id="APP_0">%1$s</xliff:g> да показва части от <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"– Може да чете информация от <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Настройки за инструмента за увеличаване на прозорци"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Докоснете, за да отворите функциите за достъпност. Персон./заменете бутона от настройките.\n\n"<annotation id="link">"Преглед на настройките"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Преместете бутона до края, за да го скриете временно"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Отмяна"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} пряк път бе премахнат}other{# преки пътища бяха премахнати}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Преместване горе вляво"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Преместване горе вдясно"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Преместване долу вляво"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Преместване долу вдясно"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Преместване в края и скриване"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Преместване в края и показване"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Премахване"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"превключване"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Контроли за устройството"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Изберете приложение, за да добавите контроли"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобилни данни"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Свързано"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Установена е временна връзка"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Слаба връзка"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Връзката за мобилни данни няма да е автоматична"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Няма връзка"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Няма други налични мрежи"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index c74fc69..915d0eb 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"ফেস শনাক্ত করা যায়নি"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"পরিবর্তে ফিঙ্গারপ্রিন্ট ব্যবহার করুন"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"ব্লুটুথ সংযুক্ত হয়েছে৷"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ব্যাটারি কত শতাংশ আছে তা জানা যায়নি।"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>এ সংযুক্ত হয়ে আছে।"</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"উজ্জ্বলতা"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"কালার ইনভার্সন"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"রঙ সংশোধন"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"ব্যবহারকারীদের ম্যানেজ করুন"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"সম্পন্ন হয়েছে"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"বন্ধ করুন"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"সংযুক্ত হয়েছে"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"মাইক্রোফোন উপলভ্য"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"ক্যামেরা উপলভ্য"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"মাইক্রোফোন ও ক্যামেরা উপলভ্য"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"মাইক্রোফোন চালু করা আছে"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"মাইক্রোফোন বন্ধ করা আছে"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"সব অ্যাপ ও পরিষেবার জন্য মাইক্রোফোনের অ্যাক্সেস চালু করা হয়েছে।"</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"সব অ্যাপ ও পরিষেবার জন্য মাইক্রোফোনের অ্যাক্সেস বন্ধ করা হয়েছে। \'সেটিংস > গোপনীয়তা > মাইক্রোফোন\' বিকল্প থেকে আপনি মাইক্রোফোনের অ্যাক্সেস চালু করতে পারবেন।"</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"সব অ্যাপ ও পরিষেবার জন্য মাইক্রোফোনের অ্যাক্সেস বন্ধ করা হয়েছে। \'সেটিংস > গোপনীয়তা > মাইক্রোফোন\' বিকল্প থেকে আপনি এটি পরিবর্তন করতে পারবেন।"</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"ক্যামেরা চালু করা হয়েছে"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"ক্যামেরা বন্ধ করা হয়েছে"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"সব অ্যাপ ও পরিষেবার জন্য ক্যামেরার অ্যাক্সেস চালু করা হয়েছে।"</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"সব অ্যাপ ও পরিষেবার জন্য ক্যামেরার অ্যাক্সেস বন্ধ করা হয়েছে।"</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"মাইক্রোফোনের বোতাম ব্যবহার করতে, সেটিংস থেকে মাইক্রোফোনের অ্যাক্সেস চালু করুন।"</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"সেটিংস খুলুন।"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"অন্য ডিভাইস"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"\'এক নজরে\' বৈশিষ্ট্যটি চালু বা বন্ধ করুন"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"অ্যালার্ম, রিমাইন্ডার, ইভেন্ট, এবং আপনার নির্দিষ্ট করে দেওয়া ব্যক্তিদের কল ছাড়া অন্য কোনও আওয়াজ বা ভাইব্রেশন হবে না। তবে সঙ্গীত, ভিডিও, এবং গেম সহ আপনি যা কিছু চালাবেন তার আওয়াজ শুনতে পাবেন।"</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"কোনও অ্যাপ আপনার শেয়ার করা, রেকর্ড করা বা কাস্ট করার সময়, সেই অ্যাপে দেখা যায় বা খেলা হয় এমন সব কিছু অ্যাক্সেস করার অনুমতি <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-এর আছে। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ বা অন্য সংবেদনশীল তথ্য সম্পর্কে সতর্ক থাকুন।"</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"চালিয়ে যান"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"অ্যাপ শেয়ার বা রেকর্ড করা"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"সবকিছু সাফ করুন"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"পরিচালনা করুন"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ইতিহাস"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"মোবাইল ডেটা বন্ধ করবেন?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"আপনি \'<xliff:g id="CARRIER">%s</xliff:g>\'-এর মাধ্যমে ডেটা অথবা ইন্টারনেট অ্যাক্সেস করতে পারবেন না। শুধুমাত্র ওয়াই-ফাইয়ের মাধ্যমেই ইন্টারনেট অ্যাক্সেস করা যাবে।"</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"আপনার পরিষেবা প্রদানকারী"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"আবার <xliff:g id="CARRIER">%s</xliff:g>-এর ডেটায় পরিবর্তন করবেন?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"উপলভ্যতার উপরে ভিত্তি করে অটোমেটিক মোবাইল ডেটায় পরিবর্তন করা হবে না"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"না থাক"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"হ্যাঁ, পাল্টান"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"একটি অ্যাপ কোনও অনুমোদনের অনুরোধকে ঢেকে দিচ্ছে, তাই সেটিংস থেকে আপনার প্রতিক্রিয়া যাচাই করা যাচ্ছে না।"</string>
<string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> অ্যাপটিকে <xliff:g id="APP_2">%2$s</xliff:g> এর অংশ দেখানোর অনুমতি দেবেন?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- এটি <xliff:g id="APP">%1$s</xliff:g> এর তথ্য অ্যাক্সেস করতে পারবে"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"\'ম্যাগনিফায়ার উইন্ডো\' সেটিংস"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"অ্যাক্সেসিবিলিটি ফিচার খুলতে ট্যাপ করুন। কাস্টমাইজ করুন বা সেটিংসে এই বোতামটি সরিয়ে দিন।\n\n"<annotation id="link">"সেটিংস দেখুন"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"এটি অস্থায়ীভাবে লুকাতে বোতামটি কোণে সরান"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"আগের অবস্থায় ফিরুন"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label}টি শর্টকাট সরানো হয়েছে}one{#টি শর্টকাট সরানো হয়েছে}other{#টি শর্টকাট সরানো হয়েছে}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"উপরে বাঁদিকে সরান"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"উপরে ডানদিকে সরান"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"নিচে বাঁদিকে সরান"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"নিচে ডান দিকে সরান"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"প্রান্তে যান ও আড়াল করুন"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"প্রান্ত থেকে সরান এবং দেখুন"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"সরিয়ে দিন"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"টগল করুন"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ডিভাইস কন্ট্রোল"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"কন্ট্রোল যোগ করতে অ্যাপ বেছে নিন"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"মোবাইল ডেটা"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"কানেক্ট করা আছে"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"সাময়িকভাবে কানেক্ট করা হয়েছে"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"খারাপ কানেকশন"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"মোবাইল ডেটা নিজে থেকে কানেক্ট হবে না"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"কানেকশন নেই"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"অন্য কোনও নেটওয়ার্ক উপলভ্য নেই"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 5356d74..bdb8638 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Nije moguće prepoznati lice"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Koristite otisak prsta"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth je povezan."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Postotak napunjenosti baterije nije poznat"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Povezan na <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -303,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Mikrofon je dostupan"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Kamera je dostupna"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Mikrofon i kamera su dostuni"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Mikrofon je uključen"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Mikrofon je isključen"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Mikrofon je omogućen za sve aplikacije i usluge."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Pristup mikrofonu je onemogućen za sve aplikacije i usluge. Možete omogućiti pristup mikrofonu u Postavkama > Privatnost > Mikrofon."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Pristup mikrofonu je onemogućen za sve aplikacije i usluge. To možete promijeniti u Postavkama > Privatnost > Mikrofon."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Kamera je uključena"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Kamera je isključena"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Kamera je omogućena za sve aplikacije i usluge."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Pristup kamere je onemogućen za sve aplikacije i usluge."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Da koristite dugme za mikrofon, omogućite pristup mikrofonu u Postavkama."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Otvori postavke."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Drugi uređaj"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Pregled uključivanja/isključivanja"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Neće vas ometati zvukovi i vibracije, osim alarma, podsjetnika, događaja i pozivalaca koje odredite. I dalje ćete čuti sve što ste odabrali za reprodukciju, uključujući muziku, videozapise i igre."</string>
@@ -373,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Kada aplikaciju dijelite, snimate ili emitirate, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ima pristup svemu što se prikazuje ili reproducira u toj aplikaciji. Zato budite oprezni s lozinkama, detaljima o plaćanju, porukama i drugim osjetljivim informacijama."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Nastavi"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Dijelite ili snimite aplikaciju"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Očisti sve"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Upravljajte"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historija"</string>
@@ -727,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Isključiti prijenos podataka na mobilnoj mreži?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Nećete imati pristup podacima ni internetu putem mobilnog operatera <xliff:g id="CARRIER">%s</xliff:g>. Internet će biti dostupan samo putem WiFi-ja."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"vaš operater"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Vratiti na operatera <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Prijenos podataka na mobilnoj mreži se neće automatski promijeniti na osnovu dostupnosti"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Ne, hvala"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Da, promijeni"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Postavke ne mogu potvrditi vaš odgovor jer aplikacija zaklanja zahtjev za odobrenje."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Dozvoliti aplikaciji <xliff:g id="APP_0">%1$s</xliff:g> da prikazuje isječke aplikacije <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Može čitati informacije iz aplikacije <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -793,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Postavke prozora povećala"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Dodirnite da otvorite funkcije pristupačnosti. Prilagodite ili zamijenite dugme u Postavkama.\n\n"<annotation id="link">"Postavke"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Premjestite dugme do ivice da ga privremeno sakrijete"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Poništavanje"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} prečica je uklonjena}one{# prečica je uklonjena}few{# prečice su uklonjene}other{# prečica je uklonjenao}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Pomjeranje gore lijevo"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Pomjeranje gore desno"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Pomjeranje dolje lijevo"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Pomjeranje dolje desno"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Pomjeranje do ivice i sakrivanje"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Pomjeranje izvan ivice i prikaz"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Uklanjanje"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"aktiviranje/deaktiviranje"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Kontrole uređaja"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Odaberite aplikaciju da dodate kontrole"</string>
@@ -947,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Prijenos podataka na mobilnoj mreži"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Povezano"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Privremeno povezano"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Slaba veza"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Prijenos podataka se neće automatski povezati"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Niste povezani s mrežom"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Druge mreže nisu dostupne"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index b61ee2c..484de00 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"No es reconeix la cara"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Utilitza l\'empremta digital"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth connectat."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Es desconeix el percentatge de bateria."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"S\'ha connectat a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillantor"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversió de colors"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correcció de color"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Gestiona els usuaris"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Fet"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Tanca"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Connectat"</string>
@@ -304,6 +305,28 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Micròfon disponible"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Càmera disponible"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Càmera i micròfon disponibles"</string>
+ <!-- no translation found for sensor_privacy_mic_turned_on_dialog_title (6348853159838376513) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_turned_off_dialog_title (5760464281790732849) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_unblocked_dialog_content (4889961886199270224) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_blocked_no_exception_dialog_content (5864898470772965394) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_blocked_with_exception_dialog_content (810289713700437896) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_turned_on_dialog_title (8039095295100075952) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_turned_off_dialog_title (1936603903120742696) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_unblocked_dialog_content (7847190103011782278) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_blocked_dialog_content (3182428709314874616) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_htt_blocked_dialog_content (3333321592997666441) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_dialog_open_settings (1503088305279285048) -->
+ <skip />
<string name="media_seamless_other_device" msgid="4654849800789196737">"Un altre dispositiu"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Activa o desactiva Aplicacions recents"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"No t\'interromprà cap so ni cap vibració, tret dels de les alarmes, recordatoris, esdeveniments i trucades de les persones que especifiquis. Continuaràs sentint tot allò que decideixis reproduir, com ara música, vídeos i jocs."</string>
@@ -374,6 +397,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Quan estàs compartint, gravant o emetent, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> té accés a qualsevol cosa que es vegi a la pantalla o que es reprodueixi a l\'aplicació. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges o altra informació sensible."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Continua"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Comparteix o grava una aplicació"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Esborra-ho tot"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Gestiona"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historial"</string>
@@ -728,14 +761,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Vols desactivar les dades mòbils?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"No tindràs accés a dades ni a Internet mitjançant <xliff:g id="CARRIER">%s</xliff:g>. Internet només estarà disponible per Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"el teu operador de telefonia mòbil"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Vols tornar a <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Les dades mòbils no canviaran automàticament en funció de la disponibilitat"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"No, gràcies"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Sí, fes el canvi"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Com que hi ha una aplicació que oculta una sol·licitud de permís, no es pot verificar la teva resposta des de la configuració."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Vols permetre que <xliff:g id="APP_0">%1$s</xliff:g> mostri porcions de l\'aplicació <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Pot llegir informació de l\'aplicació <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +823,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Configuració de la finestra de la lupa"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Toca per obrir funcions d\'accessibilitat. Personalitza o substitueix el botó a Configuració.\n\n"<annotation id="link">"Mostra"</annotation>"."</string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mou el botó a l\'extrem per amagar-lo temporalment"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Desfés"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{S\'ha suprimit la drecera {label}}other{S\'han suprimit # dreceres}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mou a dalt a l\'esquerra"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mou a dalt a la dreta"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Mou a baix a l\'esquerra"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Mou a baix a la dreta"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Mou dins de les vores i amaga"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Mou fora de les vores i mostra"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Suprimeix"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"commuta"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Controls de dispositius"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Selecciona l\'aplicació per afegir controls"</string>
@@ -948,10 +974,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Dades mòbils"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Connectat"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Connexió temporal"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Connexió feble"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Les dades mòbils no es connectaran automàticament"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Sense connexió"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"No hi ha cap altra xarxa disponible"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index ee36468..71bea80 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Obličej nelze rozpoznat"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Použijte otisk prstu"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Rozhraní Bluetooth je připojeno."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Procento baterie není známé."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Připojeno k zařízení <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Jas"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Převrácení barev"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Korekce barev"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Správa uživatelů"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Hotovo"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Zavřít"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Připojeno"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Mikrofon je k dispozici"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Fotoaparát je k dispozici"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Mikrofon a fotoaparát jsou k dispozici"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Mikrofon je zapnutý"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Mikrofon je vypnutý"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Přístup k mikrofonu je aktivován pro všechny aplikace a služby."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Přístup k mikrofonu je deaktivován pro všechny aplikace a služby. Přístup k mikrofonu můžete udělit v Nastavení > Ochrana soukromí > Mikrofon."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Přístup k mikrofonu je deaktivován pro všechny aplikace a služby. Můžete to změnit v Nastavení > Ochrana soukromí > Mikrofon."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Fotoaparát je zapnutý"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Fotoaparát je vypnutý"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Přístup k fotoaparátu je aktivován pro všechny aplikace a služby."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Přístup k fotoaparátu je deaktivován pro všechny aplikace a služby."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Pokud chcete použít tlačítko mikrofonu, v nastavení udělte přístup k mikrofonu."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Otevřít nastavení."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Další zařízení"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Přepnout přehled"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Nebudou vás rušit zvuky ani vibrace s výjimkou budíků, upozornění, událostí a volajících, které zadáte. Nadále uslyšíte veškerý obsah, který si sami pustíte (např. hudba, videa nebo hry)."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Když sdílíte, nahráváte nebo odesíláte aplikaci, aplikace <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> má přístup k veškerému obsahu, který je v této aplikaci zobrazen nebo přehráván. Dejte proto pozor na hesla, platební údaje, zprávy nebo jiné citlivé informace."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Pokračovat"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Sdílení nebo nahrání aplikace"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Smazat vše"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Spravovat"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historie"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Vypnout mobilní data?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Prostřednictvím <xliff:g id="CARRIER">%s</xliff:g> nebudete moci používat data ani internet. Internet bude dostupný pouze přes Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"vašeho operátora"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Přepnout zpět na operátora <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Mobilní data se nebudou automaticky přepínat podle dostupnosti"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Ne, díky"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Ano, přepnout"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Žádost o oprávnění je blokována jinou aplikací. Nastavení proto vaši odpověď nemůže ověřit."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Povolit aplikaci <xliff:g id="APP_0">%1$s</xliff:g> zobrazovat ukázky z aplikace <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"– Může číst informace z aplikace <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Nastavení okna zvětšení"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Klepnutím otevřete funkce přístupnosti. Tlačítko lze upravit nebo nahradit v Nastavení.\n\n"<annotation id="link">"Nastavení"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Přesunutím tlačítka k okraji ho dočasně skryjete"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Vrátit zpět"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{Zkratka {label} byla odstraněna}few{Byly odstraněny # zkratky}many{Bylo odstraněno # zkratky}other{Bylo odstraněno # zkratek}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Přesunout vlevo nahoru"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Přesunout vpravo nahoru"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Přesunout vlevo dolů"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Přesunout vpravo dolů"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Přesunout k okraji a skrýt"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Přesunout okraj ven a zobrazit"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Odstranit"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"přepnout"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Ovládání zařízení"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Vyberte aplikaci, pro kterou chcete přidat ovládací prvky"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilní data"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Připojeno"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Dočasně připojeno"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Nekvalitní připojení"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobilní data se nebudou připojovat automaticky"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Žádné připojení"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Žádné další sítě nejsou k dispozici"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 220014e..0eb3d95 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -161,13 +161,15 @@
<string name="biometric_dialog_last_pattern_attempt_before_wipe_profile" msgid="6045224069529284686">"Hvis du angiver et forkert mønster i næste forsøg, slettes din arbejdsprofil og de tilhørende data."</string>
<string name="biometric_dialog_last_pin_attempt_before_wipe_profile" msgid="545567685899091757">"Hvis du angiver en forkert pinkode i næste forsøg, slettes din arbejdsprofil og de tilhørende data."</string>
<string name="biometric_dialog_last_password_attempt_before_wipe_profile" msgid="8538032972389729253">"Hvis du angiver en forkert adgangskode i næste forsøg, slettes din arbejdsprofil og de tilhørende data."</string>
- <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"Sæt fingeren på fingeraftrykslæseren"</string>
+ <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"Sæt fingeren på fingeraftrykssensoren"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="4465698996175640549">"Ikon for fingeraftryk"</string>
<string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Ansigtet kan ikke genkendes. Brug fingeraftryk i stedet."</string>
<!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Ansigt kan ikke genkendes"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Brug fingeraftryk i stedet"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth tilsluttet."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Batteriniveauet er ukendt."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Tilsluttet <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -303,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Mikrofon kan benyttes"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Kamera kan benyttes"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Mikrofon og kamera kan benyttes"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Mikrofonen er aktiveret"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Mikrofonen er deaktiveret"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Mikrofonen er aktiveret for alle apps og tjenester."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Mikrofonadgang er deaktiveret for alle apps og tjenester. Du kan aktivere mikrofonadgang under Indstillinger > Privatliv > Mikrofon."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Mikrofonadgang er deaktiveret for alle apps og tjenester. Du kan ændre dette under Indstillinger > Privatliv > Mikrofon."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Kameraet er aktiveret"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Kameraet er deaktiveret"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Kameraet er aktiveret for alle apps og tjenester."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Kameraadgang er deaktiveret for alle apps og tjenester."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Hvis du vil bruge mikrofonknappen, skal du aktivere mikrofonadgang under Indstillinger."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Åbn Indstillinger."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Anden enhed"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Slå Oversigt til/fra"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Du bliver ikke forstyrret af lyde eller vibrationer, undtagen fra alarmer, påmindelser, begivenheder og opkald fra udvalgte personer, du selv angiver. Du kan stadig høre alt, du vælger at afspille, f.eks. musik, videoer og spil."</string>
@@ -373,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Når du deler, optager eller caster en app, har <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> adgang til alt, der vises eller afspilles i den pågældende app. Vær derfor forsigtig med adgangskoder, betalingsoplysninger, beskeder og andre følsomme oplysninger."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Fortsæt"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Del eller optag en app"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Ryd alle"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Administrer"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historik"</string>
@@ -727,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Vil du deaktivere mobildata?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Du vil ikke have data- eller internetadgang via <xliff:g id="CARRIER">%s</xliff:g>. Der vil kun være adgang til internettet via Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"dit mobilselskab"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Vil du skifte tilbage til <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Mobildata skifter ikke automatisk på baggrund af tilgængelighed"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Nej tak"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Ja, skift"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Indstillinger kan ikke bekræfte dit svar, da en app dækker for en anmodning om tilladelse."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Vil du give <xliff:g id="APP_0">%1$s</xliff:g> tilladelse til at vise eksempler fra <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Den kan læse oplysninger fra <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -793,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Indstillinger for lupvindue"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tryk for at åbne hjælpefunktioner. Tilpas eller erstat denne knap i Indstillinger.\n\n"<annotation id="link">"Se indstillingerne"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Flyt knappen til kanten for at skjule den midlertidigt"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Fortryd"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} genvej blev fjernet}one{# genvej blev fjernet}other{# genveje blev fjernet}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Flyt op til venstre"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Flyt op til højre"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Flyt ned til venstre"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Flyt ned til højre"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Flyt ud til kanten, og skjul"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Flyt ud til kanten, og vis"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Fjern"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"slå til/fra"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Enhedsstyring"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Vælg en app for at tilføje styring"</string>
@@ -938,19 +954,17 @@
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Der er problemer med at aflæse dit batteriniveau"</string>
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tryk for at få flere oplysninger"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Ingen alarm er indstillet"</string>
- <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingeraftrykslæser"</string>
+ <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingeraftrykssensor"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"godkende"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"få adgang til enheden"</string>
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Brug fingeraftryk for at åbne"</string>
- <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Godkendelse er påkrævet. Sæt fingeren på fingeraftrykslæseren for at godkende."</string>
+ <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Godkendelse er påkrævet. Sæt fingeren på fingeraftrykssensoren for at godkende."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Igangværende telefonopkald"</string>
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobildata"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Forbundet"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Midlertidigt forbundet"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Dårlig forbindelse"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Ingen automatisk mobildataforbindelse"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Der er ingen forbindelse"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Der er ingen andre tilgængelige netværk"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 0b3af67..2829950 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Gesicht nicht erkannt"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Fingerabdruck verwenden"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Mit Bluetooth verbunden"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Akkustand unbekannt."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Mit <xliff:g id="BLUETOOTH">%s</xliff:g> verbunden"</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Helligkeit"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Farbumkehr"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Farbkorrektur"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Nutzer verwalten"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Fertig"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Schließen"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Verbunden"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Mikrofon verfügbar"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Kamera verfügbar"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Mikrofon und Kamera verfügbar"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Mikrofon aktiviert"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Mikrofon deaktiviert"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Mikrofon ist für alle Apps und Dienste aktiviert."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Mikrofonzugriff ist für alle Apps und Dienste deaktiviert. Du kannst ihn in den Einstellungen unter „Datenschutz“ > „Mikrofon“ aktivieren."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Mikrofonzugriff ist für alle Apps und Dienste deaktiviert. Du kannst dies in den Einstellungen unter „Datenschutz“ > „Mikrofon“ ändern."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Kamera eingeschaltet"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Kamera ausgeschaltet"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Kamera ist für alle Apps und Dienste aktiviert."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Kamerazugriff ist für alle Apps und Dienste deaktiviert."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Wenn du die Mikrofontaste verwenden möchtest, musst du den Mikrofonzugriff in den Einstellungen aktivieren."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Einstellungen öffnen"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Sonstiges Gerät"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Übersicht ein-/ausblenden"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Klingeltöne und die Vibration werden deaktiviert, außer für Weckrufe, Erinnerungen, Termine sowie Anrufe von zuvor von dir festgelegten Personen. Du hörst jedoch weiterhin Sound, wenn du dir Musik anhörst, Videos ansiehst oder Spiele spielst."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Beim Teilen, Aufnehmen oder Übertragen einer App hat <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> Zugriff auf alle Inhalte, die in dieser App sichtbar sind oder wiedergegeben werden. Sei daher mit Passwörtern, Zahlungsdetails, Nachrichten oder anderen vertraulichen Informationen vorsichtig."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Weiter"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"App teilen oder aufnehmen"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Alle löschen"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Verwalten"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Verlauf"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Mobile Daten deaktivieren?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Du kannst dann nicht mehr über <xliff:g id="CARRIER">%s</xliff:g> auf Daten und das Internet zugreifen. Das Internet ist nur noch über WLAN verfügbar."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"deinen Mobilfunkanbieter"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Zurück zu <xliff:g id="CARRIER">%s</xliff:g> wechseln?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Mobile Daten werden nicht je nach Verfügbarkeit automatisch gewechselt"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Nein danke"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Ja, wechseln"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Deine Eingabe wird von \"Einstellungen\" nicht erkannt, weil die Berechtigungsanfrage von einer App verdeckt wird."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> erlauben, Teile von <xliff:g id="APP_2">%2$s</xliff:g> anzuzeigen?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"– Darf Informationen aus <xliff:g id="APP">%1$s</xliff:g> lesen"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Einstellungen für das Vergrößerungsfenster"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tippe, um die Bedienungshilfen aufzurufen. Du kannst diese Schaltfläche in den Einstellungen anpassen oder ersetzen.\n\n"<annotation id="link">"Zu den Einstellungen"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Durch Ziehen an den Rand wird die Schaltfläche zeitweise ausgeblendet"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Rückgängig machen"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} Verknüpfung entfernt}other{# Verknüpfungen entfernt}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Nach oben links verschieben"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Nach rechts oben verschieben"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Nach unten links verschieben"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Nach unten rechts verschieben"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"An den Rand verschieben und verbergen"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Vom Rand verschieben und anzeigen"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Entfernen"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"Wechseln"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Gerätesteuerung"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"App zum Hinzufügen von Steuerelementen auswählen"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile Daten"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Verbunden"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Vorübergehend verbunden"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Schwache Verbindung"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Keine automatische Verbindung über mobile Daten"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Keine Verbindung"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Keine anderen Netzwerke verfügbar"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index d707e3e..51af1f3 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Αδύνατη η αναγν. προσώπου"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Χρησιμ. δακτυλ. αποτύπ."</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Το Bluetooth είναι συνδεδεμένο."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Άγνωστο ποσοστό μπαταρίας."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Συνδέθηκε στο <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -303,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Διαθέσιμο μικρόφωνο"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Διαθέσιμη κάμερα"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Διαθέσιμη κάμερα και μικρόφωνο"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Το μικρόφωνο ενεργοποιήθηκε"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Το μικρόφωνο απενεργοποιήθηκε"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Το μικρόφωνο ενεργοποιήθηκε για όλες τις εφαρμογές και τις υπηρεσίες."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Η πρόσβαση στο μικρόφωνο είναι απενεργοποιημένη για όλες τις εφαρμογές και τις υπηρεσίες. Μπορείτε να ενεργοποιήσετε την πρόσβαση στο μικρόφωνο από τις Ρυθμίσεις > Απόρρητο > Μικρόφωνο."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Η πρόσβαση στο μικρόφωνο είναι απενεργοποιημένη για όλες τις εφαρμογές και τις υπηρεσίες. Μπορείτε να αλλάξετε την επιλογή από τις Ρυθμίσεις > Απόρρητο > Μικρόφωνο."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Η κάμερα ενεργοποιήθηκε"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Η κάμερα απενεργοποιήθηκε"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Η κάμερα ενεργοποιήθηκε για όλες τις εφαρμογές και τις υπηρεσίες."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Η πρόσβαση στην κάμερα είναι απενεργοποιημένη για όλες τις εφαρμογές και τις υπηρεσίες."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Για να χρησιμοποιήσετε το κουμπί μικροφώνου, ενεργοποιήστε την πρόσβαση στο μικρόφωνο από τις Ρυθμίσεις."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Άνοιγμα ρυθμίσεων."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Άλλη συσκευή"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Εναλλαγή επισκόπησης"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Δεν θα ενοχλείστε από ήχους και δονήσεις, παρά μόνο από ξυπνητήρια, υπενθυμίσεις, συμβάντα και καλούντες που έχετε καθορίσει. Θα εξακολουθείτε να ακούτε όλο το περιεχόμενο που επιλέγετε να αναπαραγάγετε, συμπεριλαμβανομένης της μουσικής, των βίντεο και των παιχνιδιών."</string>
@@ -373,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Όταν κάνετε κοινοποίηση, εγγραφή ή μετάδοση μιας εφαρμογής, η εφαρμογή <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> έχει πρόσβαση σε οτιδήποτε είναι ορατό ή αναπαράγεται στη συγκεκριμένη εφαρμογή. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα ή άλλες ευαίσθητες πληροφορίες."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Συνέχεια"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Κοινοποίηση ή εγγραφή εφαρμογής"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Διαγραφή όλων"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Διαχείριση"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Ιστορικό"</string>
@@ -727,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Απενεργοποίηση δεδομένων κινητής τηλεφωνίας;"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Δεν θα έχετε πρόσβαση σε δεδομένα ή στο διαδίκτυο μέσω της εταιρείας κινητής τηλεφωνίας <xliff:g id="CARRIER">%s</xliff:g>. Θα έχετε πρόσβαση στο διαδίκτυο μόνο μέσω Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"η εταιρεία κινητής τηλεφωνίας"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Επιστροφή σε <xliff:g id="CARRIER">%s</xliff:g>;"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Δεν θα γίνεται αυτόματα εναλλαγή των δεδομένων κινητής τηλεφωνίας βάσει της διαθεσιμότητας"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Όχι, ευχαριστώ"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Ναι, εναλλαγή"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Επειδή μια εφαρμογή αποκρύπτει ένα αίτημα άδειας, δεν είναι δυνατή η επαλήθευση της απάντησής σας από τις Ρυθμίσεις."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Να επιτρέπεται στο <xliff:g id="APP_0">%1$s</xliff:g> να εμφανίζει τμήματα της εφαρμογής <xliff:g id="APP_2">%2$s</xliff:g>;"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Μπορεί να διαβάζει πληροφορίες από την εφαρμογή <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -793,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Ρυθμίσεις παραθύρου μεγεθυντικού φακού"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Πατήστε για άνοιγμα των λειτουργιών προσβασιμότητας. Προσαρμόστε ή αντικαταστήστε το κουμπί στις Ρυθμίσεις.\n\n"<annotation id="link">"Προβολή ρυθμίσεων"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Μετακινήστε το κουμπί στο άκρο για προσωρινή απόκρυψη"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Αναίρεση"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{Η συντόμευση {label} καταργήθηκε}other{Καταργήθηκαν # συντομεύσεις}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Μετακίνηση επάνω αριστερά"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Μετακίνηση επάνω δεξιά"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Μετακίνηση κάτω αριστερά"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Μετακίνηση κάτω δεξιά"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Μετακίν. στο άκρο και απόκρυψη"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Μετακ. εκτός άκρου και εμφάν."</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Κατάργηση"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"εναλλαγή"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Στοιχεία ελέγχου συσκευής"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Επιλογή εφαρμογής για προσθήκη στοιχείων ελέγχου"</string>
@@ -947,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Δεδομένα κινητής τηλεφωνίας"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Συνδέθηκε"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Προσωρινή σύνδεση"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Ασθενής σύνδεση"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Χωρίς αυτόματη σύνδεση δεδομένων κινητ. τηλεφωνίας"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Χωρίς σύνδεση"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Δεν υπάρχουν άλλα διαθέσιμα δίκτυα"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 3aaa5f5..c23bfe6 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Can’t recognise face"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Use fingerprint instead"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth connected."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Battery percentage unknown."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connected to <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -303,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Microphone available"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Camera available"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Microphone and camera available"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Microphone turned on"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Microphone turned off"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Microphone is enabled for all apps and services."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Microphone access is disabled for all apps and services. You can enable microphone access in Settings > Privacy > Microphone."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Microphone access is disabled for all apps and services. You can change this in Settings > Privacy > Microphone."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Camera turned on"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Camera turned off"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Camera is enabled for all apps and services."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Camera access is disabled for all apps and services."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"To use the microphone button, enable microphone access in Settings."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Open settings."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Other device"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"You won\'t be disturbed by sounds and vibrations, except from alarms, reminders, events and callers you specify. You\'ll still hear anything you choose to play including music, videos and games."</string>
@@ -373,6 +386,11 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"When you\'re sharing, recording or casting an app, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> has access to anything shown or played on that app. So, be careful with passwords, payment details, messages or other sensitive information."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Continue"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Share or record an app"</string>
+ <string name="media_projection_permission_dialog_system_service_title" msgid="6827129613741303726">"Allow this app to share or record?"</string>
+ <string name="media_projection_permission_dialog_system_service_warning_entire_screen" msgid="8801616203805837575">"When you\'re sharing, recording or casting, this app has access to anything visible on your screen or played on your device. So be careful with passwords, payment details, messages or other sensitive information."</string>
+ <string name="media_projection_permission_dialog_system_service_warning_single_app" msgid="543310680568419338">"When you\'re sharing, recording or casting an app, this app has access to anything shown or played on that app. So be careful with passwords, payment details, messages or other sensitive information."</string>
+ <string name="screen_capturing_disabled_by_policy_dialog_title" msgid="2113331792064527203">"Blocked by your IT admin"</string>
+ <string name="screen_capturing_disabled_by_policy_dialog_description" msgid="6015975736747696431">"Screen capturing is disabled by device policy"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Clear all"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Manage"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"History"</string>
@@ -727,14 +745,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Turn off mobile data?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"You won\'t have access to data or the Internet through <xliff:g id="CARRIER">%s</xliff:g>. Internet will only be available via Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"your operator"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Switch back to <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Mobile data won\'t automatically switch based on availability"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"No thanks"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Yes, switch"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Because an app is obscuring a permission request, Settings can’t verify your response."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Allow <xliff:g id="APP_0">%1$s</xliff:g> to show <xliff:g id="APP_2">%2$s</xliff:g> slices?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"– It can read information from <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -793,18 +807,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Magnifier window settings"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tap to open accessibility features. Customise or replace this button in Settings.\n\n"<annotation id="link">"View settings"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Move button to the edge to hide it temporarily"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Undo"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} shortcut removed}other{# shortcuts removed}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Move top left"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Move top right"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Move bottom left"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Move bottom right"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Move to edge and hide"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Move out edge and show"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Remove"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"toggle"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Choose app to add controls"</string>
@@ -947,10 +958,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Connected"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Temporarily connected"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Poor connection"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobile data won\'t auto‑connect"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"No connection"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"No other networks available"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index fb56d9f..7c1f8fd 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Can’t recognise face"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Use fingerprint instead"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth connected."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Battery percentage unknown."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connected to <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -303,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Microphone available"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Camera available"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Microphone and camera available"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Microphone turned on"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Microphone turned off"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Microphone is enabled for all apps and services."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Microphone access is disabled for all apps and services. You can enable microphone access in Settings > Privacy > Microphone."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Microphone access is disabled for all apps and services. You can change this in Settings > Privacy > Microphone."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Camera turned on"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Camera turned off"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Camera is enabled for all apps and services."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Camera access is disabled for all apps and services."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"To use the microphone button, enable microphone access in Settings."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Open settings."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Other device"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"You won\'t be disturbed by sounds and vibrations, except from alarms, reminders, events and callers you specify. You\'ll still hear anything you choose to play including music, videos and games."</string>
@@ -373,6 +386,11 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"When you\'re sharing, recording or casting an app, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> has access to anything shown or played on that app. So, be careful with passwords, payment details, messages or other sensitive information."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Continue"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Share or record an app"</string>
+ <string name="media_projection_permission_dialog_system_service_title" msgid="6827129613741303726">"Allow this app to share or record?"</string>
+ <string name="media_projection_permission_dialog_system_service_warning_entire_screen" msgid="8801616203805837575">"When you\'re sharing, recording or casting, this app has access to anything visible on your screen or played on your device. So be careful with passwords, payment details, messages or other sensitive information."</string>
+ <string name="media_projection_permission_dialog_system_service_warning_single_app" msgid="543310680568419338">"When you\'re sharing, recording or casting an app, this app has access to anything shown or played on that app. So be careful with passwords, payment details, messages or other sensitive information."</string>
+ <string name="screen_capturing_disabled_by_policy_dialog_title" msgid="2113331792064527203">"Blocked by your IT admin"</string>
+ <string name="screen_capturing_disabled_by_policy_dialog_description" msgid="6015975736747696431">"Screen capturing is disabled by device policy"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Clear all"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Manage"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"History"</string>
@@ -727,14 +745,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Turn off mobile data?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"You won\'t have access to data or the Internet through <xliff:g id="CARRIER">%s</xliff:g>. Internet will only be available via Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"your carrier"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Switch back to <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Mobile data won\'t automatically switch based on availability"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"No thanks"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Yes, switch"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Because an app is obscuring a permission request, Settings can’t verify your response."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Allow <xliff:g id="APP_0">%1$s</xliff:g> to show <xliff:g id="APP_2">%2$s</xliff:g> slices?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"– It can read information from <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -793,18 +807,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Magnifier window settings"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tap to open accessibility features. Customise or replace this button in Settings.\n\n"<annotation id="link">"View settings"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Move button to the edge to hide it temporarily"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Undo"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} shortcut removed}other{# shortcuts removed}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Move top left"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Move top right"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Move bottom left"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Move bottom right"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Move to edge and hide"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Move out edge and show"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Remove"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"toggle"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Choose app to add controls"</string>
@@ -947,10 +958,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Connected"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Temporarily connected"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Poor connection"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobile data won\'t auto‑connect"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"No connection"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"No other networks available"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 3aaa5f5..c23bfe6 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Can’t recognise face"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Use fingerprint instead"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth connected."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Battery percentage unknown."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connected to <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -303,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Microphone available"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Camera available"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Microphone and camera available"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Microphone turned on"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Microphone turned off"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Microphone is enabled for all apps and services."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Microphone access is disabled for all apps and services. You can enable microphone access in Settings > Privacy > Microphone."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Microphone access is disabled for all apps and services. You can change this in Settings > Privacy > Microphone."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Camera turned on"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Camera turned off"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Camera is enabled for all apps and services."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Camera access is disabled for all apps and services."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"To use the microphone button, enable microphone access in Settings."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Open settings."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Other device"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"You won\'t be disturbed by sounds and vibrations, except from alarms, reminders, events and callers you specify. You\'ll still hear anything you choose to play including music, videos and games."</string>
@@ -373,6 +386,11 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"When you\'re sharing, recording or casting an app, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> has access to anything shown or played on that app. So, be careful with passwords, payment details, messages or other sensitive information."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Continue"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Share or record an app"</string>
+ <string name="media_projection_permission_dialog_system_service_title" msgid="6827129613741303726">"Allow this app to share or record?"</string>
+ <string name="media_projection_permission_dialog_system_service_warning_entire_screen" msgid="8801616203805837575">"When you\'re sharing, recording or casting, this app has access to anything visible on your screen or played on your device. So be careful with passwords, payment details, messages or other sensitive information."</string>
+ <string name="media_projection_permission_dialog_system_service_warning_single_app" msgid="543310680568419338">"When you\'re sharing, recording or casting an app, this app has access to anything shown or played on that app. So be careful with passwords, payment details, messages or other sensitive information."</string>
+ <string name="screen_capturing_disabled_by_policy_dialog_title" msgid="2113331792064527203">"Blocked by your IT admin"</string>
+ <string name="screen_capturing_disabled_by_policy_dialog_description" msgid="6015975736747696431">"Screen capturing is disabled by device policy"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Clear all"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Manage"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"History"</string>
@@ -727,14 +745,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Turn off mobile data?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"You won\'t have access to data or the Internet through <xliff:g id="CARRIER">%s</xliff:g>. Internet will only be available via Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"your operator"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Switch back to <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Mobile data won\'t automatically switch based on availability"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"No thanks"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Yes, switch"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Because an app is obscuring a permission request, Settings can’t verify your response."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Allow <xliff:g id="APP_0">%1$s</xliff:g> to show <xliff:g id="APP_2">%2$s</xliff:g> slices?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"– It can read information from <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -793,18 +807,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Magnifier window settings"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tap to open accessibility features. Customise or replace this button in Settings.\n\n"<annotation id="link">"View settings"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Move button to the edge to hide it temporarily"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Undo"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} shortcut removed}other{# shortcuts removed}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Move top left"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Move top right"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Move bottom left"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Move bottom right"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Move to edge and hide"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Move out edge and show"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Remove"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"toggle"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Choose app to add controls"</string>
@@ -947,10 +958,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Connected"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Temporarily connected"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Poor connection"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobile data won\'t auto‑connect"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"No connection"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"No other networks available"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 3aaa5f5..c23bfe6 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Can’t recognise face"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Use fingerprint instead"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth connected."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Battery percentage unknown."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connected to <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -303,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Microphone available"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Camera available"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Microphone and camera available"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Microphone turned on"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Microphone turned off"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Microphone is enabled for all apps and services."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Microphone access is disabled for all apps and services. You can enable microphone access in Settings > Privacy > Microphone."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Microphone access is disabled for all apps and services. You can change this in Settings > Privacy > Microphone."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Camera turned on"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Camera turned off"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Camera is enabled for all apps and services."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Camera access is disabled for all apps and services."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"To use the microphone button, enable microphone access in Settings."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Open settings."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Other device"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"You won\'t be disturbed by sounds and vibrations, except from alarms, reminders, events and callers you specify. You\'ll still hear anything you choose to play including music, videos and games."</string>
@@ -373,6 +386,11 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"When you\'re sharing, recording or casting an app, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> has access to anything shown or played on that app. So, be careful with passwords, payment details, messages or other sensitive information."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Continue"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Share or record an app"</string>
+ <string name="media_projection_permission_dialog_system_service_title" msgid="6827129613741303726">"Allow this app to share or record?"</string>
+ <string name="media_projection_permission_dialog_system_service_warning_entire_screen" msgid="8801616203805837575">"When you\'re sharing, recording or casting, this app has access to anything visible on your screen or played on your device. So be careful with passwords, payment details, messages or other sensitive information."</string>
+ <string name="media_projection_permission_dialog_system_service_warning_single_app" msgid="543310680568419338">"When you\'re sharing, recording or casting an app, this app has access to anything shown or played on that app. So be careful with passwords, payment details, messages or other sensitive information."</string>
+ <string name="screen_capturing_disabled_by_policy_dialog_title" msgid="2113331792064527203">"Blocked by your IT admin"</string>
+ <string name="screen_capturing_disabled_by_policy_dialog_description" msgid="6015975736747696431">"Screen capturing is disabled by device policy"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Clear all"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Manage"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"History"</string>
@@ -727,14 +745,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Turn off mobile data?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"You won\'t have access to data or the Internet through <xliff:g id="CARRIER">%s</xliff:g>. Internet will only be available via Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"your operator"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Switch back to <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Mobile data won\'t automatically switch based on availability"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"No thanks"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Yes, switch"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Because an app is obscuring a permission request, Settings can’t verify your response."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Allow <xliff:g id="APP_0">%1$s</xliff:g> to show <xliff:g id="APP_2">%2$s</xliff:g> slices?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"– It can read information from <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -793,18 +807,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Magnifier window settings"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tap to open accessibility features. Customise or replace this button in Settings.\n\n"<annotation id="link">"View settings"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Move button to the edge to hide it temporarily"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Undo"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} shortcut removed}other{# shortcuts removed}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Move top left"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Move top right"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Move bottom left"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Move bottom right"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Move to edge and hide"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Move out edge and show"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Remove"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"toggle"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Choose app to add controls"</string>
@@ -947,10 +958,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Connected"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Temporarily connected"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Poor connection"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobile data won\'t auto‑connect"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"No connection"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"No other networks available"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index ee56838..06b9f45 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Can’t recognize face"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Use fingerprint instead"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth connected."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Battery percentage unknown."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connected to <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -303,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Microphone available"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Camera available"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Microphone and camera available"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Microphone turned on"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Microphone turned off"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Microphone is enabled for all apps and services."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Microphone access is disabled for all apps and services. You can enable microphone access in Settings > Privacy > Microphone."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Microphone access is disabled for all apps and services. You can change this in Settings > Privacy > Microphone."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Camera turned on"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Camera turned off"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Camera is enabled for all apps and services."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Camera access is disabled for all apps and services."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"To use the microphone button, enable microphone access in Settings."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Open settings."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Other device"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"You won\'t be disturbed by sounds and vibrations, except from alarms, reminders, events, and callers you specify. You\'ll still hear anything you choose to play including music, videos, and games."</string>
@@ -373,6 +386,11 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"When you\'re sharing, recording, or casting an app, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> has access to anything shown or played on that app. So be careful with passwords, payment details, messages, or other sensitive information."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Continue"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Share or record an app"</string>
+ <string name="media_projection_permission_dialog_system_service_title" msgid="6827129613741303726">"Allow this app to share or record?"</string>
+ <string name="media_projection_permission_dialog_system_service_warning_entire_screen" msgid="8801616203805837575">"When you\'re sharing, recording, or casting, this app has access to anything visible on your screen or played on your device. So be careful with passwords, payment details, messages, or other sensitive information."</string>
+ <string name="media_projection_permission_dialog_system_service_warning_single_app" msgid="543310680568419338">"When you\'re sharing, recording, or casting an app, this app has access to anything shown or played on that app. So be careful with passwords, payment details, messages, or other sensitive information."</string>
+ <string name="screen_capturing_disabled_by_policy_dialog_title" msgid="2113331792064527203">"Blocked by your IT admin"</string>
+ <string name="screen_capturing_disabled_by_policy_dialog_description" msgid="6015975736747696431">"Screen capturing is disabled by device policy"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Clear all"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Manage"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"History"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 6160caf..d6f9a5e 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"No se reconoce el rostro"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Usa la huella dactilar"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth conectado"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Se desconoce el porcentaje de la batería."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectado a <xliff:g id="BLUETOOTH">%s</xliff:g>"</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillo"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Invertir colores"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Corregir colores"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Administrar usuarios"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Listo"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Cerrar"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Conectado"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Micrófono disponible"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Cámara disponible"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Micrófono y cámara disponibles"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Se activó el micrófono"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Se desactivó el micrófono"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Se habilitó el micrófono para todos los servicios y las apps."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"El acceso al micrófono está inhabilitado para todos los servicios y las apps. Puedes habilitar su acceso en Configuración > Privacidad > Micrófono."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"El acceso al micrófono está inhabilitado para todos los servicios y las apps. Puedes habilitarlo en Configuración > Privacidad > Micrófono."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Se activó la cámara"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Se desactivó la cámara"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Se habilitó la cámara para todos los servicios y las apps."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"El acceso a la cámara está inhabilitado para todos los servicios y las apps."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Para habilitar el botón de micrófono, habilita su acceso en Configuración."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Abrir Configuración"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Otro dispositivo"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Ocultar o mostrar Recientes"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"No te molestarán los sonidos ni las vibraciones, excepto las alarmas, los recordatorios, los eventos y las llamadas de los emisores que especifiques. Podrás escuchar el contenido que reproduzcas, como música, videos y juegos."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Cuando compartas, grabes o transmitas una app, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> podrá acceder a todo el contenido que se muestre o reproduzca en ella. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes y otra información sensible."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Continuar"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Compartir o grabar una app"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Borrar todo"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Administrar"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historial"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"¿Deseas desactivar los datos móviles?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"No tendrás acceso a datos móviles ni a Internet a través de <xliff:g id="CARRIER">%s</xliff:g>. Solo podrás conectarte a Internet mediante Wi‑Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"tu proveedor"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"¿Volver a cambiar a <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Los datos móviles no cambiarán automáticamente en función de la disponibilidad"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"No, gracias"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Sí, cambiar"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Como una app está bloqueando una solicitud de permiso, Configuración no puede verificar tu respuesta."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"¿Permitir que <xliff:g id="APP_0">%1$s</xliff:g> muestre fragmentos de <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Puede leer información sobre <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Configuración de la ventana de ampliación"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Presiona para abrir las funciones de accesibilidad. Personaliza o cambia botón en Config.\n\n"<annotation id="link">"Ver config"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mueve el botón hacia el borde para ocultarlo temporalmente"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Deshacer"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{Se quitó el acceso directo {label}}many{Se quitaron # de accesos directos}other{Se quitaron # accesos directos}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mover arriba a la izquierda"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mover arriba a la derecha"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Mover abajo a la izquierda"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Mover abajo a la derecha"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Mover fuera de borde y ocultar"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Mover fuera de borde y mostrar"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Quitar"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"activar o desactivar"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Controles de dispositivos"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Elige la app para agregar los controles"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Datos móviles"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Conexión establecida"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Conectado temporalmente"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Conexión deficiente"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"No se conectarán automáticamente los datos móviles"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Sin conexión"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"No hay otras redes disponibles"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index ed9e7d9..9480e4c 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"No se reconoce la cara"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Usa la huella digital"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth conectado"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Porcentaje de batería desconocido."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectado a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillo"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Invertir colores"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Corrección de color"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Gestionar usuarios"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Hecho"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Cerrar"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Conectado"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Micrófono disponible"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Cámara disponible"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Micrófono y cámara disponible"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Micrófono activado"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Micrófono desactivado"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"El micrófono está habilitado para todas las aplicaciones y servicios."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"El acceso al micrófono está inhabilitado para todas las aplicaciones y servicios. Puedes habilitar el acceso al micrófono en Ajustes > Privacidad > Micrófono."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"El acceso al micrófono está inhabilitado para todas las aplicaciones y servicios. Puedes cambiarlo en Ajustes > Privacidad > Micrófono."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Cámara activada"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Cámara desactivada"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"La cámara está habilitada para todas las aplicaciones y servicios."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"El acceso a la cámara está inhabilitado para todas las aplicaciones y servicios."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Para usar el botón del micrófono, habilita el acceso al micrófono en Ajustes."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Abrir ajustes"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Otro dispositivo"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Mostrar u ocultar aplicaciones recientes"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"No te molestarán los sonidos ni las vibraciones, excepto las alarmas, los recordatorios, los eventos y las llamadas que especifiques. Seguirás escuchando el contenido que quieras reproducir, como música, vídeos y juegos."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Cuando compartas, grabes o envíes una aplicación, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> podrá acceder a todo lo que muestre o reproduzca la aplicación. Debes tener cuidado con contraseñas, detalles de pagos, mensajes o cualquier otra información sensible."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Continuar"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Compartir o grabar una aplicación"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Borrar todo"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Gestionar"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historial"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"¿Desactivar datos móviles?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"No tendrás acceso a datos móviles ni a Internet a través de <xliff:g id="CARRIER">%s</xliff:g>. Solo podrás conectarte a Internet mediante Wi‑Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"tu operador"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"¿Cambiar de nuevo a <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Los datos móviles no cambiarán automáticamente en función de la disponibilidad"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"No, gracias"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Sí, cambiar"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Una aplicación impide ver una solicitud de permiso, por lo que Ajustes no puede verificar tu respuesta."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"¿Permitir que <xliff:g id="APP_0">%1$s</xliff:g> muestre fragmentos de <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Puede leer información de <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Configuración de la ventana de la lupa"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Toca para abrir funciones de accesibilidad. Personaliza o sustituye este botón en Ajustes.\n\n"<annotation id="link">"Ver ajustes"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mueve el botón hacia el borde para ocultarlo temporalmente"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Deshacer"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} acceso directo eliminado}many{# accesos directos eliminados}other{# accesos directos eliminados}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mover arriba a la izquierda"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mover arriba a la derecha"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Mover abajo a la izquierda"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Mover abajo a la derecha"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Mover al borde y ocultar"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Mover al borde y mostrar"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Quitar"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"activar/desactivar"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Control de dispositivos"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Elige una aplicación para añadir controles"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Datos móviles"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Conectado"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Conectada temporalmente"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Conexión inestable"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Los datos móviles no se conectarán automáticamente"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Sin conexión"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"No hay otras redes disponibles"</string>
diff --git a/packages/SystemUI/res/values-es/tiles_states_strings.xml b/packages/SystemUI/res/values-es/tiles_states_strings.xml
index d7a8133..fe4cbed 100644
--- a/packages/SystemUI/res/values-es/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-es/tiles_states_strings.xml
@@ -78,8 +78,8 @@
</string-array>
<string-array name="tile_states_location">
<item msgid="3316542218706374405">"No disponible"</item>
- <item msgid="4813655083852587017">"Desactivado"</item>
- <item msgid="6744077414775180687">"Activado"</item>
+ <item msgid="4813655083852587017">"Desactivada"</item>
+ <item msgid="6744077414775180687">"Activada"</item>
</string-array>
<string-array name="tile_states_hotspot">
<item msgid="3145597331197351214">"No disponible"</item>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 417c2c2..c5ac0c7 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Nägu ei õnnestu tuvastada"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Kasutage sõrmejälge"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth on ühendatud."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Aku laetuse protsent on teadmata."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Ühendatud: <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Heledus"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Värvide ümberpööramine"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Värviparandus"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Kasutajate haldamine"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Valmis"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Sule"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Ühendatud"</string>
@@ -304,6 +305,28 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Mikrofon on saadaval"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Kaamera on saadaval"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Mikrofon ja kaamera on saadaval"</string>
+ <!-- no translation found for sensor_privacy_mic_turned_on_dialog_title (6348853159838376513) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_turned_off_dialog_title (5760464281790732849) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_unblocked_dialog_content (4889961886199270224) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_blocked_no_exception_dialog_content (5864898470772965394) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_blocked_with_exception_dialog_content (810289713700437896) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_turned_on_dialog_title (8039095295100075952) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_turned_off_dialog_title (1936603903120742696) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_unblocked_dialog_content (7847190103011782278) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_blocked_dialog_content (3182428709314874616) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_htt_blocked_dialog_content (3333321592997666441) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_dialog_open_settings (1503088305279285048) -->
+ <skip />
<string name="media_seamless_other_device" msgid="4654849800789196737">"Muu seade"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Lehe Ülevaade sisse- ja väljalülitamine"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Helid ja värinad ei sega teid. Kuulete siiski enda määratud äratusi, meeldetuletusi, sündmusi ja helistajaid. Samuti kuulete kõike, mille esitamise ise valite, sh muusika, videod ja mängud."</string>
@@ -374,6 +397,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Kui jagate, salvestate või kannate rakendust üle, on rakendusel <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> juurdepääs kõigele, mida selles rakenduses kuvatakse või esitatakse. Seega olge paroolide, makseteabe, sõnumite ja muu tundliku teabega ettevaatlik."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Jätka"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Rakenduse jagamine või salvestamine"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Tühjenda kõik"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Haldamine"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Ajalugu"</string>
@@ -728,14 +761,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Kas lülitada mobiilne andmeside välja?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Pärast seda pole teil operaatori <xliff:g id="CARRIER">%s</xliff:g> kaudu juurdepääsu andmesidele ega internetile. Internet on saadaval ainult WiFi kaudu."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"teie operaator"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Kas vahetada tagasi operaatorile <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Mobiilandmeside operaatorit ei vahetata saadavuse alusel automaatselt"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Tänan, ei"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Jah, vaheta"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Seaded ei saa teie vastust kinnitada, sest rakendus varjab loataotlust."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Kas lubada rakendusel <xliff:g id="APP_0">%1$s</xliff:g> näidata rakenduse <xliff:g id="APP_2">%2$s</xliff:g> lõike?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- See saab lugeda teavet rakendusest <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +823,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Luubi akna seaded"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Puudutage juurdepääsufunktsioonide avamiseks. Kohandage nuppu või asendage see seadetes.\n\n"<annotation id="link">"Kuva seaded"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Teisaldage nupp serva, et see ajutiselt peita"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Võta tagasi"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} otsetee on eemaldatud}other{# otseteed on eemaldatud}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Teisalda üles vasakule"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Teisalda üles paremale"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Teisalda alla vasakule"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Teisalda alla paremale"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Teisalda serva ja kuva"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Teisalda servast eemale ja kuva"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Eemalda"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"lülita"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Seadmete juhikud"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Valige juhtelementide lisamiseks rakendus"</string>
@@ -948,10 +974,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiilne andmeside"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Ühendatud"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Ajutiselt ühendatud"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Kehv ühendus"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobiilset andmesideühendust ei looda automaatselt"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Ühendus puudub"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Ühtegi muud võrku pole saadaval"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index ee1ba23..2461886 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Ezin da ezagutu aurpegia"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Erabili hatz-marka"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetootha konektatuta."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Bateriaren ehunekoa ezezaguna da."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> gailura konektatuta."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Distira"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Kolore-alderantzikatzea"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Koloreen zuzenketa"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Kudeatu erabiltzaileak"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Eginda"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Itxi"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Konektatuta"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Mikrofonoa erabilgarri dago"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Kamera erabilgarri dago"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Mikrofonoa eta kamera erabilgarri daude"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Aktibatu da mikrofonoa"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Desaktibatu da mikrofonoa"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Aplikazio eta zerbitzu guztiek mikrofonoa erabil dezakete."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Mikrofonoa erabiltzeko baimena desgaituta dago aplikazio eta zerbitzu guztietarako. Mikrofonoa erabiltzeko baimena gaitzeko, joan Ezarpenak > Pribatutasuna > Mikrofonoa atalera."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Mikrofonoa erabiltzeko baimena desgaituta dago aplikazio eta zerbitzu guztietarako. Baimen hori aldatzeko, joan Ezarpenak > Pribatutasuna > Mikrofonoa atalera."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Piztu da kamera"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Itzali da kamera"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Aplikazio eta zerbitzu guztiek kamera erabil dezakete."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Kamera erabiltzeko baimena desgaituta dago aplikazio eta zerbitzu guztietarako."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Mikrofonoaren botoia erabiltzeko, gaitu mikrofonoa erabiltzeko baimena ezarpenetan."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Ireki ezarpenak."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Beste gailu bat"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Aldatu ikuspegi orokorra"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Gailuak ez du egingo ez soinurik ez dardararik, baina alarmak, gertaera eta abisuen tonuak, eta aukeratzen dituzun deitzaileen dei-tonuak joko ditu. Bestalde, zuk erreproduzitutako guztia entzungo duzu, besteak beste, musika, bideoak eta jokoak."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Aplikazio bat partekatzen, grabatzen edo igortzen ari zarenean, aplikazio horretan ikusgai dagoen edo bertan erreproduzitzen ari den guztirako sarbidea du <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aplikazioak. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin edo bestelako kontuzko informazioarekin."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Egin aurrera"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Partekatu edo grabatu aplikazioak"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Garbitu guztiak"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Kudeatu"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historia"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Datu-konexioa desaktibatu nahi duzu?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"<xliff:g id="CARRIER">%s</xliff:g> erabilita ezingo dituzu erabili datuak edo Internet. Wifi-sare baten bidez soilik konektatu ahal izango zara Internetera."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"Zure operadorea"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"<xliff:g id="CARRIER">%s</xliff:g> operadorera aldatu nahi duzu berriro?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Datu-konexioa ez da automatikoki aldatuko erabilgarritasunaren arabera"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Ez, eskerrik asko"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Bai, aldatu nahi dut"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Aplikazio bat baimen-eskaera oztopatzen ari denez, ezarpenek ezin dute egiaztatu erantzuna."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_2">%2$s</xliff:g> aplikazioaren zatiak erakusteko baimena eman nahi diozu <xliff:g id="APP_0">%1$s</xliff:g> aplikazioari?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- <xliff:g id="APP">%1$s</xliff:g> aplikazioaren informazioa irakur dezake."</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Luparen leihoaren ezarpenak"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Erabilerraztasun-eginbideak irekitzeko, sakatu hau. Ezarpenetan pertsonalizatu edo ordez dezakezu botoia.\n\n"<annotation id="link">"Ikusi ezarpenak"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Eraman botoia ertzera aldi baterako ezkutatzeko"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Desegin"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} lasterbide kendu da}other{# lasterbide kendu dira}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Eraman goialdera, ezkerretara"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Eraman goialdera, eskuinetara"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Eraman behealdera, ezkerretara"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Eraman behealdera, eskuinetara"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Eraman ertzera eta ezkutatu"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Atera ertzetik eta erakutsi"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Kendu"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"aldatu"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Gailuak kontrolatzeko widgetak"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Aukeratu aplikazio bat kontrolatzeko aukerak gehitzeko"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Datu-konexioa"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> (<xliff:g id="NETWORKMODE">%2$s</xliff:g>)"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Konektatuta"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Aldi baterako konektatuta"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Konexio ahula"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Ez da automatikoki aktibatuko datu-konexioa"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Konexiorik gabe"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Ez dago beste sare erabilgarririk"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 62d73ae..458b480 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"چهره شناسایی نشد"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"از اثر انگشت استفاده کنید"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"بلوتوث متصل است."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"درصد شارژ باتری مشخص نیست."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"به <xliff:g id="BLUETOOTH">%s</xliff:g> متصل شد."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"روشنایی"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"وارونگی رنگ"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"تصحیح رنگ"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"مدیریت کاربران"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"تمام"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"بستن"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"متصل"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"میکروفون دردسترس است"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"دوربین دردسترس است"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"میکروفون و دوربین دردسترساند"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"میکروفون روشن شد"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"میکروفون خاموش شد"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"میکروفون برای همه برنامهها و سرویسها فعال است."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"دسترسی به میکروفون برای همه برنامهها و سرویسها غیرفعال است. میتوانید دسترسی به میکروفون را در «تنظیمات > حریم خصوصی > میکروفون» فعال کنید."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"دسترسی به میکروفون برای همه برنامهها و سرویسها غیرفعال است. میتوانید آن را در «تنظیمات > حریم خصوصی > میکروفون» تغییر دهید."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"دوربین روشن شد"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"دوربین خاموش شد"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"دوربین برای همه برنامهها و سرویسها فعال است."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"دسترسی به دوربین برای همه برنامهها و سرویسها غیرفعال است."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"برای استفاده از دکمه میکروفون، دسترسی به میکروفون را در «تنظیمات» فعال کنید."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"باز کردن تنظیمات."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"دستگاه دیگر"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"تغییر وضعیت نمای کلی"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"بهجز هشدارها، یادآوریها، رویدادها و تماسگیرندگانی که خودتان مشخص میکنید، هیچ صدا و لرزشی نخواهید داشت. همچنان صدای مواردی را که پخش میکنید میشنوید (ازجمله صدای موسیقی، ویدیو و بازی)."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"وقتی درحال همرسانی، ضبط، یا پخش محتوای برنامهای هستید، <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> به همه محتوایی که در آن برنامه نمایان است یا پخش میشود دسترسی دارد. بنابراین مراقب گذرواژهها، جزئیات پرداخت، پیامها، یا دیگر اطلاعات حساس باشید."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"ادامه"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"همرسانی یا ضبط برنامه"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"پاک کردن همه موارد"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"مدیریت"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"سابقه"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"داده تلفن همراه خاموش شود؟"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"نمیتوانید ازطریق <xliff:g id="CARRIER">%s</xliff:g> به داده یا اینترنت دسترسی داشته باشید. اینترنت فقط ازطریق Wi-Fi در دسترس خواهد بود."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"شرکت مخابراتی شما"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"میخواهید به <xliff:g id="CARRIER">%s</xliff:g> برگردید؟"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"وضعیت داده تلفن همراه بهطور خودکار براساس دردسترس بودن تغییر نخواهد کرد"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"نه متشکرم"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"بله، عوض شود"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"چون برنامهای درحال ایجاد تداخل در درخواست مجوز است، «تنظیمات» نمیتواند پاسخ شما را تأیید کند."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"به <xliff:g id="APP_0">%1$s</xliff:g> اجازه داده شود تکههای <xliff:g id="APP_2">%2$s</xliff:g> را نشان دهد؟"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- میتواند اطلاعات <xliff:g id="APP">%1$s</xliff:g> را بخواند"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"تنظیمات پنجره ذرهبین"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"برای باز کردن ویژگیهای دسترسپذیری ضربه بزنید. در تنظیمات این دکمه را سفارشی یا جایگزین کنید\n\n"<annotation id="link">"تنظیمات"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"برای پنهان کردن موقتی دکمه، آن را به لبه ببرید"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"واگرد"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} میانبر برداشته شد}one{# میانبر برداشته شد}other{# میانبر برداشته شد}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"انتقال به بالا سمت راست"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"انتقال به بالا سمت چپ"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"انتقال به پایین سمت راست"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"انتقال به پایین سمت چپ"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"انتقال به لبه و پنهان کردن"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"انتقال به خارج از لبه و نمایش"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"برداشتن"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"روشن/ خاموش کردن"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"کنترلهای دستگاه"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"انتخاب برنامه برای افزودن کنترلها"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"داده تلفن همراه"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"متصل است"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"موقتاً متصل است"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"اتصال ضعیف"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"داده تلفن همراه بهطور خودکار متصل نخواهد شد"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"اتصال برقرار نیست"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"شبکه دیگری وجود ندارد"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index bfb450f..1da012a 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Kasvoja ei voi tunnistaa"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Käytä sormenjälkeä"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth yhdistetty."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Akun varaustaso ei tiedossa."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Yhteys: <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Kirkkaus"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Käänteiset värit"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Värinkorjaus"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Ylläpidä käyttäjiä"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Valmis"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Sulje"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Yhdistetty"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Mikrofoni käytettävissä"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Kamera käytettävissä"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Mikrofoni ja kamera käytettävissä"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Mikrofoni laitettu päälle"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Mikrofoni laitettu pois päältä"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Mikrofonin käyttö on sallittu kaikille sovelluksille ja palveluille."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Pääsy mikrofoniin on estetty kaikilta sovelluksilta ja palveluilta. Jos haluat sallia pääsyn mikrofoniin, valitse Asetukset > Yksityisyys > Mikrofoni."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Pääsy mikrofoniin on estetty kaikilta sovelluksilta ja palveluilta. Jos haluat muuttaa tätä, valitse Asetukset > Yksityisyys > Mikrofoni."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Kamera laitettu päälle"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Kamera laitettu pois päältä"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Kameran käyttö on sallittu kaikille sovelluksille ja palveluille."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Pääsy kameraan on estetty kaikilta sovelluksilta ja palveluilta."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Jos haluat käyttää mikrofonipainiketta, salli pääsy mikrofoniin asetuksista."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Avaa asetukset."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Muu laite"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Näytä/piilota viimeisimmät"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Äänet ja värinät eivät häiritse sinua, paitsi jos ne ovat hälytyksiä, muistutuksia, tapahtumia tai määrittämiäsi soittajia. Kuulet edelleen kaiken valitsemasi sisällön, kuten musiikin, videot ja pelit."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Kun jaat, tallennat tai striimaat sovellusta, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> saa pääsyn kaikkeen sovelluksessa näkyvään tai toistettuun sisältöön. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä tai muita arkaluontoisia tietoja."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Jatka"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Jaa sovellus tai tallenna sen sisältöä"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Tyhjennä kaikki"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Muuta asetuksia"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historia"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Laitetaanko mobiilidata pois päältä?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"<xliff:g id="CARRIER">%s</xliff:g> ei enää tarjoa pääsyä dataan eikä internetyhteyttä, joka on saatavilla vain Wi-Fin kautta."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"operaattorisi"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Palauta käyttöön <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Mobiilidata ei vaihdu automaattisesti saatavuuden perusteella"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Ei kiitos"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Kyllä, vaihda"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Sovellus peittää käyttöoikeuspyynnön, joten Asetukset ei voi vahvistaa valintaasi."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Saako <xliff:g id="APP_0">%1$s</xliff:g> näyttää osia sovelluksesta <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"– Se voi lukea tietoja sovelluksesta <xliff:g id="APP">%1$s</xliff:g>."</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Ikkunan suurennuksen asetukset"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Avaa esteettömyysominaisuudet napauttamalla. Yksilöi tai vaihda painike asetuksista.\n\n"<annotation id="link">"Avaa asetukset"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Piilota painike tilapäisesti siirtämällä se reunaan"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Kumoa"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} pikanäppäin poistettu}other{# pikanäppäintä poistettu}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Siirrä vasempaan yläreunaan"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Siirrä oikeaan yläreunaan"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Siirrä vasempaan alareunaan"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Siirrä oikeaan alareunaan"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Siirrä reunaan ja piilota"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Siirrä pois reunasta ja näytä"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Poista"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"vaihda"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Laitehallinta"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Valitse sovellus lisätäksesi säätimiä"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiilidata"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Yhdistetty"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Väliaikaisesti yhdistetty"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Heikko yhteys"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobiilidata ei yhdisty automaattisesti"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Ei yhteyttä"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Ei muita verkkoja käytettävissä"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index b2d925c..cc8bdb8 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Visage non reconnu"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Utiliser l\'empreinte digitale"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth connecté"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Pourcentage de la pile inconnu."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connecté à : <xliff:g id="BLUETOOTH">%s</xliff:g>"</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Luminosité"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversion des couleurs"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correction des couleurs"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Gérer les utilisateurs"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Terminé"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Fermer"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Connecté"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Microphone disponible"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Appareil photo disponible"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Microphone et appareil photo disponibles"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Microphone activé"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Microphone désactivé"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Le microphone est activé pour toutes les applications et tous les services."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"L\'accès au microphone est désactivé pour toutes les applications et tous les services. Vous pouvez l\'activer dans Paramètres > Confidentialité > Microphone."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"L\'accès au microphone est désactivé pour toutes les applications et tous les services. Vous pouvez modifier cette option dans Paramètres > Confidentialité > Microphone."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Appareil photo activé"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Appareil photo désactivé"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"L\'appareil photo est activé pour toutes les applications et tous les services."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"L\'accès à l\'appareil photo est désactivé pour toutes les applications et tous les services."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Pour utiliser le bouton du microphone, activez l\'accès au microphone dans les paramètres."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Ouvrir les paramètres."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Autre appareil"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Basculer l\'aperçu"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Vous ne serez pas dérangé par les sons et les vibrations, sauf pour les alarmes, les rappels, les événements et les appelants que vous sélectionnez. Vous entendrez tout ce que vous choisissez d\'écouter, y compris la musique, les vidéos et les jeux."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Lorsque vous partagez, enregistrez ou diffusez une application, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> a accès à tout ce qui est affiché ou lu sur cette application. Par conséquent, soyez prudent avec les mots de passe, les détails du paiement, les messages ou toute autre information confidentielle."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Continuer"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Partager ou enregistrer une application"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Tout effacer"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Gérer"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historique"</string>
@@ -608,8 +630,8 @@
<string name="accessibility_status_bar_headset" msgid="2699275863720926104">"Écouteurs connectés"</string>
<string name="data_saver" msgid="3484013368530820763">"Économiseur de données"</string>
<string name="accessibility_data_saver_on" msgid="5394743820189757731">"La fonction Économiseur de données est activée"</string>
- <string name="switch_bar_on" msgid="1770868129120096114">"Activé"</string>
- <string name="switch_bar_off" msgid="5669805115416379556">"Désactivé"</string>
+ <string name="switch_bar_on" msgid="1770868129120096114">"Activée"</string>
+ <string name="switch_bar_off" msgid="5669805115416379556">"Désactivée"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Non disponible"</string>
<string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"En savoir plus"</string>
<string name="nav_bar" msgid="4642708685386136807">"Barre de navigation"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Désactiver les données cellulaires?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Vous n\'aurez pas accès aux données ni à Internet avec <xliff:g id="CARRIER">%s</xliff:g>. Vous ne pourrez accéder à Internet que par Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"votre fournisseur de services"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Rebasculer vers <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Il n\'y aura pas de basculement automatique entre les données mobiles selon la disponibilité"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Non merci"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Oui, basculer"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Une application obscurcit une demande d\'autorisation, alors Paramètres ne peut pas vérifier votre réponse."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Autoriser <xliff:g id="APP_0">%1$s</xliff:g> à afficher <xliff:g id="APP_2">%2$s</xliff:g> tranches?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Il peut lire de l\'information de <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Paramètres de la fenêtre de loupe"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Touchez pour ouvrir fonction. d\'access. Personnalisez ou remplacez bouton dans Param.\n\n"<annotation id="link">"Afficher param."</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Déplacez le bouton vers le bord pour le masquer temporairement"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Annuler"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} raccourci retiré}one{# raccourci retiré}many{# de raccourcis retirés}other{# raccourcis retirés}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Déplacer dans coin sup. gauche"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Déplacer dans coin sup. droit"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Déplacer dans coin inf. gauche"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Déplacer dans coin inf. droit"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Éloigner du bord et masquer"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Éloigner du bord et afficher"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Retirer"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"basculer"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Commandes des appareils"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Sélectionnez l\'application pour laquelle ajouter des commandes"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Données cellulaires"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Connexion active"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Connectée temporairement"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Connexion faible"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Aucune connexion auto. des données cellulaires"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Aucune connexion"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Aucun autre réseau n\'est accessible"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 12a88c6..1508b9c 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Visage non reconnu"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Utilisez empreinte digit."</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth connecté"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Pourcentage de la batterie inconnu."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connecté à : <xliff:g id="BLUETOOTH">%s</xliff:g>"</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Luminosité"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversion des couleurs"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correction des couleurs"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Gérer les utilisateurs"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"OK"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Fermer"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Connecté"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Micro accessible"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Caméra accessible"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Micro et caméra accessibles"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Micro activé"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Micro désactivé"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Le micro est activé pour tous les services et applis."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"L\'accès au micro est désactivé pour tous les services et applis. Vous pouvez activer l\'accès au micro dans Paramètres > Confidentialité > Micro."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"L\'accès au micro est désactivé pour tous les services et applis. Vous pouvez modifier cette option dans Paramètres > Confidentialité > Micro."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Appareil photo activé"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Appareil photo désactivé"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"L\'appareil photo est activé pour tous les services et applis."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"L\'accès à l\'appareil photo est désactivé pour tous les services et applis."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Pour utiliser le bouton du micro, activez l\'accès au micro dans les paramètres."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Ouvrir les paramètres."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Autre appareil"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Activer/Désactiver l\'aperçu"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Vous ne serez dérangé par aucun son ni aucune vibration, hormis ceux des alarmes, des rappels, des événements et des appels des contacts de votre choix. Le son continuera de fonctionner notamment pour la musique, les vidéos et les jeux."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Lorsque vous partagez, enregistrez ou castez une appli, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> a accès à tout ce qui est visible sur votre écran ou lu sur votre appareil. Faites donc attention à vos mots de passe, détails de mode de paiement, messages ou autres informations sensibles."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Continuer"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Partager ou enregistrer une appli"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Tout effacer"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Gérer"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historique"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Désactiver les données mobiles ?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Vous n\'aurez pas accès aux données mobiles ni à Internet via <xliff:g id="CARRIER">%s</xliff:g>. Vous ne pourrez accéder à Internet que par Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"votre opérateur"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Rebasculer sur <xliff:g id="CARRIER">%s</xliff:g> ?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Il n\'y aura pas de basculement automatique entre les données mobile selon la disponibilité"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Non, merci"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Oui, revenir"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"L\'application Paramètres ne peut pas valider votre réponse, car une application masque la demande d\'autorisation."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Autoriser <xliff:g id="APP_0">%1$s</xliff:g> à afficher des éléments de <xliff:g id="APP_2">%2$s</xliff:g> ?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Accès aux informations de <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Paramètres de la fenêtre d\'agrandissement"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Appuyez pour ouvrir fonctionnalités d\'accessibilité. Personnalisez ou remplacez bouton dans paramètres.\n\n"<annotation id="link">"Voir paramètres"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Déplacer le bouton vers le bord pour le masquer temporairement"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Annuler"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} raccourci supprimé}one{# raccourci supprimé}many{# raccourcis supprimés}other{# raccourcis supprimés}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Déplacer en haut à gauche"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Déplacer en haut à droite"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Déplacer en bas à gauche"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Déplacer en bas à droite"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Rapprocher du bord et masquer"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Éloigner du bord et afficher"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Supprimer"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"activer/désactiver"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Commandes des appareils"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Sélectionnez l\'appli pour laquelle ajouter des commandes"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Données mobiles"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Connecté"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Connectée temporairement"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Connexion médiocre"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Pas de connexion automatique des données mobiles"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Aucune connexion"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Aucun autre réseau disponible"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index d18d596..3807a87 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Non se recoñeceu a cara"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Usa a impresión dixital"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth conectado"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Descoñécese a porcentaxe da batería."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectado a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillo"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversión da cor"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Corrección da cor"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Administrar usuarios"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Feito"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Pechar"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Conectado"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"O micrófono está dispoñible"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"A cámara está dispoñible"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"O micrófono e a cámara están dispoñibles"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Activouse o micrófono"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Desactivouse o micrófono"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"O micrófono está activado para todas as aplicacións e servizos."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"O acceso ao micrófono está desactivado para todas as aplicacións e servizos. Podes activar o acceso ao micrófono en Configuración > Privacidade > Micrófono."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"O acceso ao micrófono está desactivado para todas as aplicacións e servizos. Podes cambialo en Configuración > Privacidade > Micrófono."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Activouse a cámara"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Desactivouse a cámara"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"A cámara está activada para todas as aplicacións e servizos."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"O acceso á cámara está desactivado para todas as aplicacións e servizos."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Para usar o botón do micrófono, activa o acceso ao micrófono en Configuración."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Abrir configuración."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Outro dispositivo"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Activar/desactivar Visión xeral"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Non te molestará ningún son nin vibración, agás os procedentes de alarmas, recordatorios, eventos e os emisores de chamada especificados. Seguirás escoitando todo aquilo que decidas reproducir, mesmo a música, os vídeos e os xogos."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Cando compartes, gravas ou emites unha aplicación, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ten acceso a todo o que se vexa ou se reproduza nela. Polo tanto, debes ter coidado cos contrasinais, os detalles de pago, as mensaxes ou outra información confidencial."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Continuar"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Compartir ou gravar unha aplicación"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Eliminar todas"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Xestionar"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historial"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 034c161..d992d6a 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"ચહેરો ઓળખાતો નથી"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"તો ફિંગરપ્રિન્ટ વાપરો"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"બ્લૂટૂથ કનેક્ટ થયું."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"બૅટરીની ટકાવારી અજાણ છે."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> થી કનેક્ટ થયાં."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"તેજ"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"વિપરીત રંગમાં બદલવું"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"રંગ સુધારણા"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"વપરાશકર્તાઓને મેનેજ કરો"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"થઈ ગયું"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"બંધ કરો"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"કનેક્ટ થયેલું"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"માઇક્રોફોનનો ઍક્સેસ ઉપલબ્ધ છે"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"કૅમેરાનો ઍક્સેસ ઉપલબ્ધ છે"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"માઇક્રોફોન અને કૅમેરાનો ઍક્સેસ ઉપલબ્ધ છે"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"માઇક્રોફોન ચાલુ કર્યું"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"માઇક્રોફોન બંધ કર્યું"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"બધી ઍપ અને સેવાઓ માટે માઇક્રોફોન ચાલુ કર્યું."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"બધી ઍપ અને સેવાઓ માટે માઇક્રોફોનનો ઍક્સેસ બંધ કર્યો છે. તમે સેટિંગ > પ્રાઇવસી > માઇક્રોફોનમાં જઈને માઇક્રોફોનનો ઍક્સેસ ચાલુ કરી શકો છો."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"બધી ઍપ અને સેવાઓ માટે માઇક્રોફોનનો ઍક્સેસ બંધ કર્યો છે. તમે સેટિંગ > પ્રાઇવસી > માઇક્રોફોનમાં જઈને આને બદલી શકો છો."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"કૅમેરા ચાલુ કર્યો"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"કૅમેરા બંધ કર્યો"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"બધી ઍપ અને સેવાઓ માટે કૅમેરા ચાલુ કર્યો."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"બધી ઍપ અને સેવાઓ માટે કૅમેરાનો ઍક્સેસ બંધ કર્યો છે."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"માઇક્રોફોન બટનનો ઉપયોગ કરવા માટે, સેટિંગમાં માઇક્રોફોનનો ઍક્સેસ ચાલુ કરો."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"સેટિંગ ખોલો."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"અન્ય ડિવાઇસ"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ઝલકને ટૉગલ કરો"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"અલાર્મ, રિમાઇન્ડર, ઇવેન્ટ અને તમે ઉલ્લેખ કરો તે કૉલર સિવાય તમને ધ્વનિ કે વાઇબ્રેશન દ્વારા ખલેલ પહોંચાડવામાં આવશે નહીં. સંગીત, વીડિઓ અને રમતો સહિત તમે જે કંઈપણ ચલાવવાનું પસંદ કરશો તે સંભળાતું રહેશે."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"જ્યારે તમે કોઈ ઍપ શેર, રેકોર્ડ અથવા કાસ્ટ કરી રહ્યાં હો, ત્યારે તે ઍપ પર બતાવવામાં કે ચલાવવામાં આવતી હોય તેવી કોઈપણ વસ્તુનો ઍક્સેસ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ધરાવે છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ અથવા અન્ય સંવેદનશીલ માહિતીની બાબતે સાવચેત રહેશો."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"ચાલુ રાખો"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"કોઈ ઍપ શેર કરો અથવા રેકોર્ડ કરો"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"બધુ સાફ કરો"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"મેનેજ કરો"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ઇતિહાસ"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"મોબાઇલ ડેટા બંધ કરીએ?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"તમને <xliff:g id="CARRIER">%s</xliff:g> મારફતે ડેટા અથવા ઇન્ટરનેટનો ઍક્સેસ મળશે નહીં. ઇન્ટરનેટ માત્ર વાઇ-ફાઇ દ્વારા ઉપલબ્ધ થશે."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"તમારા કૅરિઅર"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"<xliff:g id="CARRIER">%s</xliff:g> પર પાછા સ્વિચ કરીએ?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"મોબાઇલ ડેટાને ઉપલબ્ધતાના આધારે ઑટોમૅટિક રીતે સ્વિચ કરવામાં આવશે નહીં"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"ના, આભાર"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"હા, સ્વિચ કરો"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"કોઈ ઍપ પરવાનગી વિનંતીને અસ્પષ્ટ કરતી હોવાને કારણે, સેટિંગ તમારા પ્રતિસાદને ચકાસી શકતું નથી."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g>ને <xliff:g id="APP_2">%2$s</xliff:g> સ્લાઇસ બતાવવાની મંજૂરી આપીએ?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- મારાથી <xliff:g id="APP">%1$s</xliff:g>ની માહિતી વાંચી શકાતી નથી"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"મેગ્નિફાયર વિન્ડોના સેટિંગ"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ઍક્સેસિબિલિટી સુવિધાઓ ખોલવા માટે ટૅપ કરો. સેટિંગમાં આ બટનને કસ્ટમાઇઝ કરો અથવા બદલો.\n\n"<annotation id="link">"સેટિંગ જુઓ"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"તેને હંગામી રૂપે ખસેડવા માટે બટનને કિનારી પર ખસેડો"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"છેલ્લો ફેરફાર રદ કરો"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} શૉર્ટકટ કાઢી નાખ્યો}one{# શૉર્ટકટ કાઢી નાખ્યો}other{# શૉર્ટકટ કાઢી નાખ્યો}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ઉપર ડાબે ખસેડો"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ઉપર જમણે ખસેડો"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"નીચે ડાબે ખસેડો"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"નીચે જમણે ખસેડો"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"કિનારી પર ખસેડો અને છુપાવો"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"કિનારીથી ખસેડો અને બતાવો"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"કાઢી નાખો"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ટૉગલ કરો"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ડિવાઇસનાં નિયંત્રણો"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"નિયંત્રણો ઉમેરવા માટે ઍપ પસંદ કરો"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"મોબાઇલ ડેટા"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"કનેક્ટ કરેલું"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"હંગામી રીતે કનેક્ટ કર્યું"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"નબળું કનેક્શન"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"મોબાઇલ ડેટા ઑટોમૅટિક રીતે કનેક્ટ થશે નહીં"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"કોઈ કનેક્શન નથી"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"બીજાં કોઈ નેટવર્ક ઉપલબ્ધ નથી"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index dc94a8d..e8458dd 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"चेहरे की पहचान नहीं हुई"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"फ़िंगरप्रिंट इस्तेमाल करें"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"ब्लूटूथ कनेक्ट किया गया."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"इस बारे में जानकारी नहीं है कि अभी बैटरी कितने प्रतिशत चार्ज है."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> से कनेक्ट किया गया."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"स्क्रीन की रोशनी"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"रंग बदलने की सुविधा"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"रंग में सुधार करने की सुविधा"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"उपयोगकर्ताओं को मैनेज करें"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"हो गया"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"रद्द करें"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"कनेक्ट है"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"माइक्रोफ़ोन का ऐक्सेस उपलब्ध है"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"कैमरे का ऐक्सेस उपलब्ध है"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"माइक्रोफ़ोन और कैमरे का ऐक्सेस उपलब्ध है"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"माइक्रोफ़ोन का ऐक्सेस चालू किया गया"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"माइक्रोफ़ोन का ऐक्सेस बंद किया गया"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"माइक्रोफ़ोन का ऐक्सेस, सभी ऐप्लिकेशन और सेवाओं के लिए चालू किया गया."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"माइक्रोफ़ोन का ऐक्सेस, सभी ऐप्लिकेशन और सेवाओं के लिए बंद किया गया. इसे चालू करने के लिए, सेटिंग > निजता > माइक्रोफ़ोन पर जाएं."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"माइक्रोफ़ोन का ऐक्सेस, सभी ऐप्लिकेशन और सेवाओं के लिए बंद किया गया. इसे चालू करने के लिए, सेटिंग > निजता > माइक्रोफ़ोन पर जाएं."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"कैमरे का ऐक्सेस चालू किया गया"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"कैमरे का ऐक्सेस बंद किया गया"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"कैमरे का ऐक्सेस, सभी ऐप्लिकेशन और सेवाओं के लिए चालू किया गया."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"सभी ऐप्लिकेशन और सेवाओं के लिए कैमरे का ऐक्सेस बंद किया गया."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"माइक्रोफ़ोन बटन का इस्तेमाल करने के लिए, सेटिंग में जाकर माइक्रोफ़ोन का ऐक्सेस चालू करें."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"सेटिंग खोलें."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"अन्य डिवाइस"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"खास जानकारी टॉगल करें"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"आपको अलार्म, रिमाइंडर, इवेंट और चुनिंदा कॉल करने वालों के अलावा किसी और तरह से (आवाज़ करके और थरथरा कर ) परेशान नहीं किया जाएगा. आप फिर भी संगीत, वीडियो और गेम सहित अपना चुना हुआ सब कुछ सुन सकते हैं."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"शेयर, रिकॉर्ड या कास्ट करते समय, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> के पास उस ऐप्लिकेशन पर दिख रही हर चीज़ या उस पर चल रहे हर मीडिया का ऐक्सेस होता है. इसलिए, शेयर, रिकॉर्ड या कास्ट करते समय, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज या किसी और संवेदनशील जानकारी को लेकर खास सावधानी बरतें."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"जारी रखें"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"ऐप्लिकेशन शेयर करें या उसकी रिकॉर्डिंग करें"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"सभी को हटाएं"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"मैनेज करें"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"इतिहास"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"मोबाइल डेटा बंद करना चाहते हैं?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"आप <xliff:g id="CARRIER">%s</xliff:g> से डेटा या इंटरनेट का इस्तेमाल नहीं कर पाएंगे. इंटरनेट सिर्फ़ वाई-फ़ाई से चलेगा."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"आपको मोबाइल और इंटरनेट सेवा देने वाली कंपनी"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"क्या आपको मोबाइल डेटा, <xliff:g id="CARRIER">%s</xliff:g> पर वापस से स्विच करना है?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"उपलब्ध होने पर, मोबाइल डेटा अपने-आप स्विच नहीं होगा"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"स्विच न करें"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"स्विच करें"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"ऐप की वजह से मंज़ूरी के अनुरोध को समझने में दिक्कत हो रही है, इसलिए सेटिंग से आपके जवाब की पुष्टि नहीं हो पा रही है."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> को <xliff:g id="APP_2">%2$s</xliff:g> के हिस्से (स्लाइस) दिखाने की मंज़ूरी दें?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- यह <xliff:g id="APP">%1$s</xliff:g> से सूचना पढ़ सकता है"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"ज़ूम करने की सुविधा वाली विंडो से जुड़ी सेटिंग"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"सुलभता सुविधाएं खोलने के लिए टैप करें. सेटिंग में, इस बटन को बदलें या अपने हिसाब से सेट करें.\n\n"<annotation id="link">"सेटिंग देखें"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"बटन को कुछ समय छिपाने के लिए, उसे किनारे पर ले जाएं"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"पहले जैसा करें"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} शॉर्टकट हटाया गया}one{# शॉर्टकट हटाया गया}other{# शॉर्टकट हटाए गए}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"सबसे ऊपर बाईं ओर ले जाएं"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"सबसे ऊपर दाईं ओर ले जाएं"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"सबसे नीचे बाईं ओर ले जाएं"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"सबसे नीचे दाईं ओर ले जाएं"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"एज पर ले जाएं और छिपाएं"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"एज से निकालें और दिखाएं"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"हटाएं"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"टॉगल करें"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"डिवाइस कंट्रोल"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"कंट्रोल जोड़ने के लिए ऐप्लिकेशन चुनें"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"मोबाइल डेटा"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"कनेक्ट हो गया"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"इंटरनेट कनेक्शन कुछ समय के लिए है"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"इंटरनेट कनेक्शन खराब है"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"मोबाइल डेटा अपने-आप कनेक्ट नहीं होगा"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"इंटरनेट कनेक्शन नहीं है"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"कोई दूसरा नेटवर्क उपलब्ध नहीं है"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index be5253f..d8fd225 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Lice nije prepoznato"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Upotrijebite otisak prsta"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth povezan."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Postotak baterije nije poznat."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Spojen na <xliff:g id="BLUETOOTH">%s</xliff:g> ."</string>
@@ -303,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Mikrofon je dostupan"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Kamera je dostupna"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Mikrofon i kamera su dostupni"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Mikrofon je uključen"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Mikrofon je isključen"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Mikrofon je omogućen za sve aplikacije i usluge."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Pristup mikrofonu onemogućen je za sve aplikacije i usluge. Pristup mikrofonu možete omogućiti u odjeljku Postavke > Privatnost > Mikrofon."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Pristup mikrofonu onemogućen je za sve aplikacije i usluge. Tu postavku možete promijeniti u odjeljku Postavke > Privatnost > Mikrofon."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Kamera je uključena"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Kamera je isključena"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Kamera je omogućena za sve aplikacije i usluge."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Pristup kameri onemogućen je za sve aplikacije i usluge."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Da biste koristili gumb mikrofona, omogućite pristup mikrofonu u postavkama."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Otvori postavke"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Ostali uređaji"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Uključivanje/isključivanje pregleda"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Neće vas ometati zvukovi i vibracije, osim alarma, podsjetnika, događaja i pozivatelja koje navedete. I dalje ćete čuti sve što želite reproducirati, uključujući glazbu, videozapise i igre."</string>
@@ -373,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Kad dijelite, snimate ili emitirate aplikaciju, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ima pristup svemu što se prikazuje ili reproducira u toj aplikaciji. Stoga pazite na zaporke, podatke o plaćanju, poruke i druge osjetljive podatke."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Nastavi"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Dijeljenje ili snimanje pomoću aplikacije"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Izbriši sve"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Upravljajte"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Povijest"</string>
@@ -727,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Isključiti mobilne podatke?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Nećete imati pristup mobilnim podacima ili internetu putem operatera <xliff:g id="CARRIER">%s</xliff:g>. Internet će biti dostupan samo putem Wi-Fija."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"vaš mobilni operater"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Vratiti se na mobilnog operatera <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Mobilni podaci neće se automatski prebaciti na temelju dostupnosti"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Ne, hvala"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Da, prebaci"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Budući da aplikacija prekriva zahtjev za dopuštenje, Postavke ne mogu potvrditi vaš odgovor."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Želite li dopustiti aplikaciji <xliff:g id="APP_0">%1$s</xliff:g> da prikazuje isječke aplikacije <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"– može čitati informacije aplikacije <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -793,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Postavke prozora povećala"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Dodirnite za otvaranje značajki pristupačnosti. Prilagodite ili zamijenite taj gumb u postavkama.\n\n"<annotation id="link">"Pregledajte postavke"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Pomaknite gumb do ruba da biste ga privremeno sakrili"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Poništi"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{Uklonjen je prečac {label}}one{# prečac je uklonjen}few{# prečaca su uklonjena}other{# prečaca je uklonjeno}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Premjesti u gornji lijevi kut"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Premjesti u gornji desni kut"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Premjesti u donji lijevi kut"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Premjesti u donji desni kut"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Premjesti na rub i sakrij"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Ukloni s ruba i prikaži"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Ukloni"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"promijeni"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Kontrole uređaja"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Odabir aplikacije za dodavanje kontrola"</string>
@@ -947,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilni podaci"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Povezano"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Privremeno povezano"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Slaba veza"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobilna veza neće se automatski uspostaviti"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Niste povezani"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nije dostupna nijedna druga mreža"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index cf07438..c1a8dd9 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Az arc nem ismerhető fel"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Használjon ujjlenyomatot"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth csatlakoztatva."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Az akkumulátor töltöttségi szintje ismeretlen."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Csatlakoztatva a következőhöz: <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Fényerő"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Színek invertálása"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Színjavítás"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Felhasználók kezelése"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Kész"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Bezárás"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Csatlakoztatva"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"A mikrofon használható"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"A kamera használható"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"A mikrofon és a kamera használható"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Mikrofon bekapcsolva"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Mikrofon kikapcsolva"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"A mikrofon az összes alkalmazás és szolgáltatás számára engedélyezve van."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"A mikrofonhoz való hozzáférés az összes alkalmazás és szolgáltatás számára le van tiltva. A mikrofonhoz való hozzáférést a következő menüpontban engedélyezheti: Beállítások > Adatvédelem > Mikrofon."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"A mikrofonhoz való hozzáférés az összes alkalmazás és szolgáltatás számára le van tiltva. Ezt a következő menüpontban módosíthatja: Beállítások > Adatvédelem > Mikrofon."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Kamera bekapcsolva"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Kamera kikapcsolva"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"A kamera az összes alkalmazás és szolgáltatás számára engedélyezve van."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"A kamerához való hozzáférés az összes alkalmazás és szolgáltatás számára le van tiltva."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Ha használni szeretné a Mikrofon gombot, engedélyezze a mikrofonhoz való hozzáférést a Beállításokban."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Beállítások megnyitása."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Más eszköz"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Áttekintés be- és kikapcsolása"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Az Ön által meghatározott ébresztéseken, emlékeztetőkön, eseményeken és hívókon kívül nem fogja Önt más hang vagy rezgés megzavarni. Továbbra is lesz hangjuk azoknak a tartalmaknak, amelyeket Ön elindít, például zenék, videók és játékok."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Amikor Ön megoszt, rögzít vagy átküld egy alkalmazást, a(z) <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> az adott appban látható vagy lejátszott minden tartalomhoz hozzáfér. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel és más bizalmas információkkal."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Folytatás"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Alkalmazás megosztása és rögzítése"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Az összes törlése"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Kezelés"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Előzmények"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Kikapcsolja a mobiladatokat?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Nem lesz adat-, illetve internet-hozzáférése a <xliff:g id="CARRIER">%s</xliff:g> szolgáltatón keresztül. Az internethez csak Wi-Fi-n keresztül csatlakozhat."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"saját mobilszolgáltató"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Átvált a következőre: <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Nem kerül sor a mobiladat-forgalom automatikus átváltására a rendelkezésre állás alapján"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Most nem"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Igen, átváltok"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Mivel az egyik alkalmazás eltakarja az engedélykérést, a Beállítások alkalmazás nem tudja ellenőrizni az Ön válaszát."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Engedélyezi a(z) <xliff:g id="APP_0">%1$s</xliff:g> alkalmazásnak, hogy részleteket mutasson a(z) <xliff:g id="APP_2">%2$s</xliff:g> alkalmazásból?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"– Információkat olvashat a(z) <xliff:g id="APP">%1$s</xliff:g> alkalmazásból"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Nagyítóablak beállításai"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Koppintson a kisegítő lehetőségek megnyitásához. A gombot a Beállításokban módosíthatja.\n\n"<annotation id="link">"Beállítások"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"A gombot a szélre áthelyezve ideiglenesen elrejtheti"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Visszavonás"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} gyorsparancs eltávolítva}other{# gyorsparancs eltávolítva}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Áthelyezés fel és balra"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Áthelyezés fel és jobbra"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Áthelyezés le és balra"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Áthelyezés le és jobbra"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Áthelyezés a szélen kívül és elrejtés"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Áthelyezés a szélen kívül és mutatás"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Eltávolítás"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"váltás"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Eszközvezérlők"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Válasszon alkalmazást a vezérlők hozzáadásához"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiladat"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="NETWORKMODE">%2$s</xliff:g>/<xliff:g id="STATE">%1$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Csatlakozva"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Ideiglenesen csatlakoztatva"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Gyenge kapcsolat"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Nincs automatikus mobiladat-kapcsolat"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Nincs kapcsolat"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nincs több rendelkezésre álló hálózat"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index bfd051b..691fe64 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Դեմքը չի ճանաչվել"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Օգտագործեք մատնահետք"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth-ը միացված է:"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Մարտկոցի լիցքի մակարդակն անհայտ է։"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Միացված է <xliff:g id="BLUETOOTH">%s</xliff:g>-ին:"</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Պայծառություն"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Գունաշրջում"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Գունաշտկում"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Կառավարել օգտատերերին"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Պատրաստ է"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Փակել"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Միացված է"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Խոսափողը հասանելի է"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Տեսախցիկը հասանելի է"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Խոսափողն ու տեսացիկը հասանելի են"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Խոսափողը միացավ"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Խոսափողն անջատվեց"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Խոսափողը բոլոր հավելվածների և ծառայությունների համար միացված է։"</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Խոսափողն օգտագործելու թույլտվությունը բոլոր հավելվածների և ծառայությունների համար անջատված է։ Խոսափողի օգտագործումը թույլատրելու համար անցեք Կարգավորումներ > Գաղտնիություն > Խոսափող։"</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Խոսափողն օգտագործելու թույլտվությունը բոլոր հավելվածների և ծառայությունների համար անջատված է։ Սա փոխելու համար անցեք Կարգավորումներ > Գաղտնիություն > Խոսափող։"</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Տեսախցիկը միացված է"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Տեսախցիկն անջատված է"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Տեսախցիկը բոլոր հավելվածների և ծառայությունների համար միացված է։"</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Տեսախցիկն օգտագործելու թույլտվությունը բոլոր հավելվածների և ծառայությունների համար անջատված է։"</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Խոսափողի կոճակն օգտագործելու համար կարգավորումներում թույլատրեք խոսափողի օգտագործումը։"</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Բացել կարգավորումները։"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Այլ սարք"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Միացնել/անջատել համատեսքը"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Ձայները և թրթռոցները չեն անհանգստացնի ձեզ, բացի ձեր կողմից նշված զարթուցիչները, հիշեցումները, միջոցառումների ծանուցումները և զանգերը։ Դուք կլսեք ձեր ընտրածի նվագարկումը, այդ թվում՝ երաժշտություն, տեսանյութեր և խաղեր:"</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Երբ դուք ցուցադրում, տեսագրում կամ հեռարձակում եք որևէ հավելվածի էկրանը, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> հավելվածին հասանելի է դառնում այն ամենը, ինչ ցուցադրվում է կամ նվագարկվում այդ հավելվածում։ Հիշեք այդ մասին, երբ պատրաստվում եք դիտել կամ մուտքագրել գաղտնաբառեր, վճարային տվյալներ, հաղորդագրություններ և այլ կոնֆիդենցիալ տեղեկություններ։"</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Շարունակել"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Հավելվածի էկրանի ցուցադրում կամ տեսագրում"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Մաքրել բոլորը"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Կառավարել"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Պատմություն"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Անջատե՞լ բջջային ինտերնետը"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"<xliff:g id="CARRIER">%s</xliff:g> օպերատորի բջջային ինտերնետը հասանելի չի լինի: Համացանցից կկարողանաք օգտվել միայն Wi-Fi-ի միջոցով:"</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"Ձեր"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Անցնե՞լ <xliff:g id="CARRIER">%s</xliff:g> ցանցին"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Սարքն ավտոմատ չի անցնի բջջային ինտերնետին"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Ոչ, շնորհակալություն"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Այո"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Քանի որ ներածումն արգելափակված է ինչ-որ հավելվածի կողմից, Կարգավորումները չեն կարող հաստատել ձեր պատասխանը:"</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Թույլատրե՞լ <xliff:g id="APP_0">%1$s</xliff:g> հավելվածին ցուցադրել հատվածներ <xliff:g id="APP_2">%2$s</xliff:g> հավելվածից"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Կարող է կարդալ տեղեկություններ <xliff:g id="APP">%1$s</xliff:g> հավելվածից"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Խոշորացույցի պատուհանի կարգավորումներ"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Հատուկ գործառույթները բացելու համար հպեք։ Անհատականացրեք այս կոճակը կարգավորումներում։\n\n"<annotation id="link">"Կարգավորումներ"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Կոճակը ժամանակավորապես թաքցնելու համար այն տեղափոխեք էկրանի եզր"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Հետարկել"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} դյուրանցում հեռացվեց}one{# դյուրանցում հեռացվեց}other{# դյուրանցում հեռացվեց}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Տեղափոխել վերև՝ ձախ"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Տեղափոխել վերև՝ աջ"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Տեղափոխել ներքև՝ ձախ"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Տեղափոխել ներքև՝ աջ"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Տեղափոխել եզրից դուրս և թաքցնել"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Տեղափոխել եզրից դուրս և ցուցադրել"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Հեռացնել"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"միացնել/անջատել"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Սարքերի կառավարման տարրեր"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Ընտրեք հավելված` կառավարման տարրեր ավելացնելու համար"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Բջջային ինտերնետ"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Միացած է"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Ժամանակավոր կապ"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Թույլ կապ"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Բջջային ինտերնետն ավտոմատ չի միանա"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Կապ չկա"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Այլ հասանելի ցանցեր չկան"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 87496cf..236ad22 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Tidak mengenali wajah"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Gunakan sidik jari"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth terhubung."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Persentase baterai tidak diketahui."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Terhubung ke <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Kecerahan"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversi warna"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Koreksi warna"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Kelola pengguna"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Selesai"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Tutup"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Terhubung"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Mikrofon tersedia"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Kamera tersedia"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Mikrofon dan kamera tersedia"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Mikrofon diaktifkan"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Mikrofon dinonaktifkan"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Mikrofon diaktifkan untuk semua aplikasi dan layanan."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Akses mikrofon dinonaktifkan untuk semua aplikasi dan layanan. Anda dapat mengaktifkan akses mikrofon di Setelan > Privasi > Mikrofon."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Akses mikrofon dinonaktifkan untuk semua aplikasi dan layanan. Anda dapat mengubahnya di Setelan > Privasi > Mikrofon."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Kamera diaktifkan"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Kamera dinonaktifkan"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Kamera diaktifkan untuk semua aplikasi dan layanan."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Akses kamera dinonaktifkan untuk semua aplikasi dan layanan."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Untuk menggunakan tombol mikrofon, aktifkan akses mikrofon di Setelan."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Buka setelan."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Perangkat lainnya"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Aktifkan Ringkasan"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Anda tidak akan terganggu oleh suara dan getaran, kecuali dari alarm, pengingat, acara, dan penelepon yang Anda tentukan. Anda akan tetap mendengar apa pun yang telah dipilih untuk diputar, termasuk musik, video, dan game."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Jika Anda membagikan, merekam, atau mentransmisikan suatu aplikasi, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> akan memiliki akses ke semua hal yang ditampilkan atau yang diputar di aplikasi tersebut. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, atau informasi sensitif lainnya."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Lanjutkan"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Bagikan atau rekam aplikasi"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Hapus semua"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Kelola"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Histori"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Nonaktifkan data seluler?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Anda tidak akan dapat mengakses data atau internet melalui <xliff:g id="CARRIER">%s</xliff:g>. Internet hanya akan tersedia melalui Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"Operator Seluler Anda"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Beralih kembali ke <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Data seluler tidak akan dialihkan secara otomatis berdasarkan ketersediaan"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Lain kali"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Ya, alihkan"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Karena sebuah aplikasi menghalangi permintaan izin, Setelan tidak dapat memverifikasi respons Anda."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Izinkan <xliff:g id="APP_0">%1$s</xliff:g> menampilkan potongan <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Dapat membaca informasi dari <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Setelan jendela kaca pembesar"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Ketuk untuk membuka fitur aksesibilitas. Sesuaikan atau ganti tombol ini di Setelan.\n\n"<annotation id="link">"Lihat setelan"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Pindahkan tombol ke tepi agar tersembunyi untuk sementara"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Urungkan"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{Pintasan {label} dihapus}other{# pintasan dihapus}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Pindahkan ke kiri atas"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Pindahkan ke kanan atas"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Pindahkan ke kiri bawah"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Pindahkan ke kanan bawah"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Pindahkan ke tepi dan sembunyikan"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Pindahkan dari tepi dan tampilkan"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Hapus"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"alihkan"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Kontrol perangkat"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Pilih aplikasi untuk menambahkan kontrol"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Data seluler"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Terhubung"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Terhubung sementara"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Koneksi buruk"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Data seluler tidak akan terhubung otomatis"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Tidak ada koneksi"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Jaringan lain tidak tersedia"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 3d7e2ea..537af36 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Andlit þekkist ekki"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Nota fingrafar í staðinn"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth tengt."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Staða rafhlöðu óþekkt."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Tengt við <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Birtustig"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Umsnúningur lita"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Litaleiðrétting"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Stjórna notendum"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Lokið"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Loka"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Tengt"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Hljóðnemi tiltækur"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Myndavél tiltæk"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Hljóðnemi og myndavél tiltæk"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Kveikt á hljóðnemanum"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Slökkt á hljóðnemanum"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Kveikt er á hljóðnema fyrir öll forrit og þjónustur."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Slökkt er á aðgangi að hljóðnema fyrir öll forrit og þjónustur. Þú getur veitt aðgang að hljóðnema í „Stillingar > Persónuvernd > Hljóðnemi“."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Slökkt er á aðgangi að hljóðnema fyrir öll forrit og þjónustur. Þú getur breytt þessu í „Stillingar > Persónuvernd > Hljóðnemi“."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Kveikt á myndavél"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Slökkt á myndavél"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Kveikt er á myndavél fyrir öll forrit og þjónustur."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Slökkt er á aðgangi að myndavél fyrir öll forrit og þjónustur."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Veittu aðgang að hljóðnema í stillingunum til að nota hljóðnemahnappinn."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Opna stillingar."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Annað tæki"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Kveikja/slökkva á yfirliti"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Þú verður ekki fyrir truflunum frá hljóðmerkjum og titringi, fyrir utan vekjara, áminningar, viðburði og símtöl frá þeim sem þú leyfir fyrirfram. Þú heyrir áfram í öllu sem þú velur að spila, svo sem tónlist, myndskeiðum og leikjum."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Þegar þú deilir, tekur upp eða sendir út forrit hefur <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aðgang að öllu sem sést eða spilast í viðkomandi forriti. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð eða aðrar viðkvæmar upplýsingar."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Áfram"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Deila eða taka upp forrit"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Hreinsa allt"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Stjórna"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Ferill"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Viltu slökkva á farsímagögnum?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Þú munt ekki hafa aðgang að gögnum eða internetinu í gegnum <xliff:g id="CARRIER">%s</xliff:g>. Aðeins verður hægt að tengjast internetinu með Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"símafyrirtækið þitt"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Skipta aftur yfir í <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Ekki verður skipt sjálfkrafa á milli farsímagagna byggt á tiltækileika"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Nei takk"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Já, skipta"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Stillingar geta ekki staðfest svarið þitt vegna þess að forrit er að fela heimildarbeiðni."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Viltu leyfa <xliff:g id="APP_0">%1$s</xliff:g> að sýna sneiðar úr <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Það getur lesið upplýsingar úr <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Stillingar stækkunarglugga"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Ýttu til að opna aðgengiseiginleika. Sérsníddu eða skiptu hnappinum út í stillingum.\n\n"<annotation id="link">"Skoða stillingar"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Færðu hnappinn að brúninni til að fela hann tímabundið"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Afturkalla"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} flýtileið fjarlægð}one{# flýtileið fjarlægð}other{# flýtileiðir fjarlægðar}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Færa efst til vinstri"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Færa efst til hægri"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Færa neðst til vinstri"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Færa neðst til hægri"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Færa að jaðri og fela"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Færa að jaðri og birta"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Fjarlægja"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"kveikja/slökkva"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Tækjastjórnun"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Veldu forrit til að bæta við stýringum"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Farsímagögn"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Tengt"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Tímabundin tenging"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Léleg tenging"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Farsímagögn tengjast ekki sjálfkrafa"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Engin tenging"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Engin önnur net í boði"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 1115680..255885e 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Volto non riconosciuto"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Usa l\'impronta"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth collegato."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Percentuale della batteria sconosciuta."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connesso a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Luminosità"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversione dei colori"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correzione del colore"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Gestisci utenti"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Fine"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Chiudi"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Connesso"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Microfono disponibile"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Fotocamera disponibile"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Fotocamera e microfono disponibili"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Microfono attivo"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Microfono non attivo"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Il microfono è attivo per tutti i servizi e le app."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"L\'accesso al microfono è disattivato per tutti i servizi e le app. Puoi attivarlo in Impostazioni > Privacy > Microfono."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"L\'accesso al microfono è disattivato per tutti i servizi e le app. Puoi modificare questa preferenza in Impostazioni > Privacy > Microfono."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Fotocamera attiva"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Fotocamera non attiva"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"La fotocamera è attiva per tutti i servizi e le app."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"L\'accesso alla fotocamera è disattivato per tutti i servizi e le app."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Per usare il pulsante del microfono devi attivare l\'accesso al microfono nelle Impostazioni."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Apri le impostazioni"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Altro dispositivo"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Attiva/disattiva la panoramica"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Non verrai disturbato da suoni e vibrazioni, ad eccezione di sveglie, promemoria, eventi, chiamate da contatti da te specificati ed elementi che hai scelto di continuare a riprodurre, inclusi video, musica e giochi."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Quando condividi, registri o trasmetti un\'app, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ha accesso a qualsiasi elemento visualizzato o riprodotto sull\'app. Presta quindi attenzione a password, dati di pagamento, messaggi o altre informazioni sensibili."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Continua"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Condividi o registra un\'app"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Cancella tutto"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Gestisci"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Cronologia"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Disattivare i dati mobili?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Non avrai accesso ai dati o a Internet tramite <xliff:g id="CARRIER">%s</xliff:g>. Internet sarà disponibile soltanto tramite Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"il tuo operatore"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Vuoi passare nuovamente all\'operatore <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"I dati mobili non passeranno automaticamente all\'operatore in base alla disponibilità"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"No, grazie"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Sì, confermo"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Un\'app sta oscurando una richiesta di autorizzazione, pertanto Impostazioni non può verificare la tua risposta."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Vuoi consentire all\'app <xliff:g id="APP_0">%1$s</xliff:g> di mostrare porzioni dell\'app <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Può leggere informazioni dell\'app <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Impostazioni della finestra di ingrandimento"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tocca per aprire funzioni di accessibilità. Personalizza o sostituisci il pulsante in Impostazioni.\n\n"<annotation id="link">"Vedi impostazioni"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Sposta il pulsante fino al bordo per nasconderlo temporaneamente"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Elimina"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} scorciatoia rimossa}many{# scorciatoie rimosse}other{# scorciatoie rimosse}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Sposta in alto a sinistra"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Sposta in alto a destra"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Sposta in basso a sinistra"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Sposta in basso a destra"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Sposta fino a bordo e nascondi"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Sposta fuori da bordo e mostra"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Rimuovi"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"attiva/disattiva"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Controllo dispositivi"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Scegli un\'app per aggiungere controlli"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Dati mobili"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Connessione attiva"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Connessa temporaneamente"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Connessione debole"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Nessuna connessione dati mobili automatica"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Nessuna connessione"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nessun\'altra rete disponibile"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 1fe4e3e..8611c272 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"לא ניתן לזהות את הפנים"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"שימוש בטביעת אצבע במקום זאת"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth מחובר."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"אחוז טעינת הסוללה לא ידוע."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"התבצע חיבור אל <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -303,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"המיקרופון זמין"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"המצלמה זמינה"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"המיקרופון והמצלמה זמינים"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"המיקרופון פועל"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"המיקרופון כבוי"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"הגישה למיקרופון הופעלה לכל האפליקציות והשירותים."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"הגישה למיקרופון הושבתה לכל האפליקציות והשירותים. אפשר להפעיל את הגישה למיקרופון ב\'הגדרות\' > \'פרטיות\' > \'מיקרופון\'."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"הגישה למיקרופון הושבתה לכל האפליקציות והשירותים. אפשר לשנות את הגישה ב\'הגדרות\' > \'פרטיות\' > \'מיקרופון\'."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"המצלמה פועלת"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"המצלמה כבויה"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"הגישה למצלמה הופעלה לכל האפליקציות והשירותים."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"הגישה למצלמה הושבתה לכל האפליקציות והשירותים."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"כדי להשתמש בלחצן המיקרופון יש להפעיל את הגישה למיקרופון ב\'הגדרות\'."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"פתיחת ההגדרות."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"מכשיר אחר"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"החלפת מצב של מסכים אחרונים"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"כדי לא להפריע לך, המכשיר לא ירטוט ולא ישמיע שום צליל, חוץ מהתראות, תזכורות, אירועים ושיחות ממתקשרים מסוימים לבחירתך. המצב הזה לא ישפיע על צלילים שהם חלק מתוכן שבחרת להפעיל, כמו מוזיקה, סרטונים ומשחקים."</string>
@@ -373,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"בזמן שיתוף, הקלטה או העברה (cast) של אפליקציה, תהיה ל-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> גישה לכל מה שגלוי באפליקציה או מופעל מהאפליקציה. כדאי להיזהר עם סיסמאות, פרטי תשלום, הודעות או מידע רגיש אחר."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"המשך"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"שיתוף או הקלטה של אפליקציה"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"ניקוי הכול"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"ניהול"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"היסטוריה"</string>
@@ -727,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"לכבות את חבילת הגלישה?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"לא תהיה לך גישה לנתונים או לאינטרנט באמצעות <xliff:g id="CARRIER">%s</xliff:g>. אינטרנט יהיה זמין רק באמצעות Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"הספק שלך"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"לחזור אל <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"לא תתבצע החלפה אוטומטית של חבילת הגלישה על סמך זמינות"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"לא, תודה"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"כן, אני רוצה להחליף"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"יש אפליקציה שמסתירה את בקשת ההרשאה, ולכן אין אפשרות לאמת את התשובה בהגדרות."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"האם לאפשר ל-<xliff:g id="APP_0">%1$s</xliff:g> להציג חלקים מ-<xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- תהיה לה אפשרות לקרוא מידע מאפליקציית <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -793,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"ההגדרות של חלון ההגדלה"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"מקישים כדי לפתוח את תכונות הנגישות. אפשר להחליף את הלחצן או להתאים אותו אישית בהגדרות.\n\n"<annotation id="link">"הצגת ההגדרות"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"כדי להסתיר זמנית את הלחצן, יש להזיז אותו לקצה"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"ביטול"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{קיצור הדרך אל {label} הוסר}two{# קיצורי דרך הוסרו}many{# קיצורי דרך הוסרו}other{# קיצורי דרך הוסרו}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"העברה לפינה השמאלית העליונה"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"העברה לפינה הימנית העליונה"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"העברה לפינה השמאלית התחתונה"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"העברה לפינה הימנית התחתונה"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"העברה לשוליים והסתרה"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"העברה מהשוליים והצגה"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"הסרה"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"החלפת מצב"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"פקדי מכשירים"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"יש לבחור אפליקציה כדי להוסיף פקדים"</string>
@@ -947,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"חבילת גלישה"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"מחובר"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"מחובר באופן זמני"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"חיבור באיכות ירודה"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"החיבור לנתונים סלולריים לא מתבצע באופן אוטומטי"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"אין חיבור"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"אין רשתות זמינות אחרות"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index b00ceb4..b0d5437 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"顔を認識できません"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"指紋認証をお使いください"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetoothに接続済み。"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"バッテリー残量は不明です。"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>に接続しました。"</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"画面の明るさ"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"色反転"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"色補正"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"ユーザーを管理"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"完了"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"閉じる"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"接続済み"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"マイクを利用できます"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"カメラを利用できます"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"マイクとカメラを利用できます"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"マイクを ON にしました"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"マイクを OFF にしました"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"マイクはすべてのアプリとサービスで有効になっています。"</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"マイクへのアクセスは、すべてのアプリとサービスで無効になっています。[設定] > [プライバシー] > [マイク] で有効にできます。"</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"マイクへのアクセスは、すべてのアプリとサービスで無効になっています。この設定は、[設定] > [プライバシー] > [マイク] で変更できます。"</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"カメラを ON にしました"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"カメラを OFF にしました"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"カメラはすべてのアプリとサービスで有効になっています。"</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"カメラへのアクセスは、すべてのアプリとサービスで無効になっています。"</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"マイクボタンを使用するには、[設定] でマイクへのアクセスを有効にしてください。"</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"設定を開く"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"その他のデバイス"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"概要を切り替え"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"アラーム、リマインダー、予定、指定した人からの着信以外の音やバイブレーションに煩わされることはありません。音楽、動画、ゲームなど再生対象として選択したコンテンツは引き続き再生されます。"</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"アプリの共有、録画、キャスト中は、そのアプリで表示されている内容や再生している内容に <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> がアクセスできるため、パスワード、お支払いの詳細、メッセージなどの機密情報にご注意ください。"</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"続行"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"アプリの共有、録画"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"すべて消去"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"管理"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"履歴"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"モバイルデータを OFF にしますか?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"<xliff:g id="CARRIER">%s</xliff:g>でデータやインターネットにアクセスできなくなります。インターネットには Wi-Fi からのみ接続できます。"</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"携帯通信会社"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"<xliff:g id="CARRIER">%s</xliff:g> に戻しますか?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"利用可能な場合でも、モバイルデータを利用するよう自動的に切り替わることはありません"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"キャンセル"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"切り替える"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"アプリが許可リクエストを隠しているため、設定側でユーザーの応答を確認できません。"</string>
<string name="slice_permission_title" msgid="3262615140094151017">"「<xliff:g id="APP_2">%2$s</xliff:g>」のスライスの表示を「<xliff:g id="APP_0">%1$s</xliff:g>」に許可しますか?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- 「<xliff:g id="APP">%1$s</xliff:g>」からの情報の読み取り"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"拡大鏡ウィンドウの設定"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"タップしてユーザー補助機能を開きます。ボタンのカスタマイズや入れ替えを [設定] で行えます。\n\n"<annotation id="link">"設定を表示"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ボタンを一時的に非表示にするには、端に移動させてください"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"元に戻す"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} 個のショートカットを削除}other{# 個のショートカットを削除}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"左上に移動"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"右上に移動"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"左下に移動"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"右下に移動"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"端に移動して非表示"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"端から移動して表示"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"削除"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"切り替え"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"デバイス コントロール"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"コントロールを追加するアプリの選択"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"モバイルデータ"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"接続済み"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"一時的に接続されています"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"接続が不安定です"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"モバイルデータには自動接続しません"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"接続なし"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"利用できるネットワークはありません"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index aab9a32..f7725ae 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"სახის ამოცნობა შეუძლებ."</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"გამოიყენეთ თითის ანაბეჭდი"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth დაკავშირებულია."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ბატარეის პროცენტული მაჩვენებელი უცნობია."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"დაკავშირებულია <xliff:g id="BLUETOOTH">%s</xliff:g>-თან."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"განათება"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"ფერთა ინვერსია"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ფერთა კორექცია"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"მომხმარებლების მართვა"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"დასრულდა"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"დახურვა"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"დაკავშირებულია"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"მიკროფონი ხელმისაწვდომია"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"კამერა ხელმისაწვდომია"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"მიკროფონი და კამერა ხელმისაწვდომია"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"მიკროფონი ჩართულია"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"მიკროფონი გამორთულია"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"მიკროფონი ჩართულია ყველა აპისა და სერვისისთვის."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"მიკროფონზე წვდომა გათიშულია ყველა აპისა და სერვისისთვის. მიკროფონზე წვდომის ჩართვა შეგიძლიათ აქედან: პარამეტრები > კონფიდენციალურობა > მიკროფონი."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"მიკროფონზე წვდომა გათიშულია ყველა აპისა და სერვისისთვის. ამის შეცვლა შეგიძლიათ აქედან: პარამეტრები > კონფიდენციალურობა > მიკროფონი."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"კამერა ჩაირთო"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"კამერა გამოირთო"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"კამერა ჩართულია ყველა აპისა და სერვისისთვის."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"კამერაზე წვდომა გათიშულია ყველა აპისა და სერვისისთვის."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"მიკროფონის ღილაკის გამოსაყენებლად, ჩართეთ მიკროფონზე წვდომა პარამეტრებიდან."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"პარამეტრების გახსნა."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"სხვა მოწყობილობა"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"მიმოხილვის გადართვა"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"თქვენ მიერ მითითებული მაღვიძარების, შეხსენებების, მოვლენებისა და ზარების გარდა, არავითარი ხმა და ვიბრაცია არ შეგაწუხებთ. თქვენ მაინც შეძლებთ სასურველი კონტენტის, მაგალითად, მუსიკის, ვიდეოებისა და თამაშების აუდიოს მოსმენა."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"აპის გაზიარებისას, ჩაწერისას ან ტრანსლირებისას <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> აქვს წვდომა აქვს ყველაფერზე, რაც ჩანს აპში ან ითამაშეთ. ამიტომ იყავით ფრთხილად პაროლებთან, გადახდის დეტალებთან, შეტყობინებებთან ან სხვა მგრძნობიარე ინფორმაციასთან."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"გაგრძელება"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"გააზიარეთ ან ჩაწერეთ აპი"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"ყველას გასუფთავება"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"მართვა"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ისტორია"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"გსურთ მობილური ინტერნეტის გამორთვა?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"თქვენ არ გექნებათ მობილურ ინტერნეტზე ან ზოგადად ინტერნეტზე წვდომა <xliff:g id="CARRIER">%s</xliff:g>-ის მეშვეობით. ინტერნეტი მხოლოდ Wi-Fi-კავშირის მეშვეობით იქნება ხელმისაწვდომი."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"თქვენი ოპერატორი"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"გსურთ ისევ <xliff:g id="CARRIER">%s</xliff:g>-ზე გადართვა?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"მობილური მონაცემების ხელმისაწვდომობის მიხედვით ავტომატური გადართვა არ მოხდება"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"არა, გმადლობთ"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"დიახ, გადაირთოს"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"ვინაიდან აპი ფარავს ნებართვის მოთხოვნას, პარამეტრების მიერ თქვენი პასუხი ვერ დასტურდება."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"ანიჭებთ ნებართვას <xliff:g id="APP_0">%1$s</xliff:g>-ს, აჩვენოს <xliff:g id="APP_2">%2$s</xliff:g>-ის ფრაგმენტები?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- მას შეუძლია ინფორმაციის <xliff:g id="APP">%1$s</xliff:g>-დან წაკითხვა"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"გადიდების ფანჯრის პარამეტრები"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"შეეხეთ მარტივი წვდომის ფუნქციების გასახსნელად. მოარგეთ ან შეცვალეთ ეს ღილაკი პარამეტრებში.\n\n"<annotation id="link">"პარამეტრების ნახვა"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"გადაიტანეთ ღილაკი კიდეში, რათა დროებით დამალოთ ის"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"მოქმედების გაუქმება"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} მალსახმობი ამოშლილია}other{# მალსახმობი ამოშლილია}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ზევით და მარცხნივ გადატანა"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ზევით და მარჯვნივ გადატანა"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"ქვევით და მარცხნივ გადატანა"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"ქვემოთ და მარჯვნივ გადატანა"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"კიდეში გადატანა და დამალვა"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"კიდეში გადატანა და გამოჩენა"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"წაშლა"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"გადართვა"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"მოწყობილ. მართვის საშუალებები"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"აირჩიეთ აპი მართვის საშუალებების დასამატებლად"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"მობილური ინტერნეტი"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"დაკავშირებული"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"დროებით დაკავშირებული"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"სუსტი კავშირი"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"მობილურ ინტერნეტს ავტომატურად არ დაუკავშირდება"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"კავშირი არ არის"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"სხვა ქსელები მიუწვდომელია"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 19bfdf5..ac77719 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Бет танылмады."</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Орнына саусақ ізін пайдаланыңыз."</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth қосылған."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Батарея зарядының мөлшері белгісіз."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> қосылған."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Жарықтығы"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Түс инверсиясы"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Түсті түзету"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Пайдаланушыларды басқару"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Дайын"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Жабу"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Қосылды"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Микрофон қолжетімді"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Камера қолжетімді"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Микрофон мен камера қолжетімді"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Микрофон қосулы"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Микрофон өшірулі"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Микрофон барлық қолданба мен қызмет үшін қосулы."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Микрофон пайдалану рұқсаты барлық қолданба мен қызмет үшін өшірулі. Микрофон пайдалану рұқсатын \"Параметрлер> Құпиялылық > Микрофон\" тармағынан қоса аласыз."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Микрофон пайдалану рұқсаты барлық қолданба мен қызмет үшін өшірулі. Мұны \"Параметрлер > Құпиялылық > Микрофон\" тармағынан өзгерте аласыз."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Камера қосулы"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Камера өшірулі"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Камера барлық қолданба мен қызмет үшін қосулы."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Камера пайдалану рұқсаты барлық қолданба мен қызмет үшін өшірулі."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Микрофон түймесін пайдалану үшін параметрлерден микрофон пайдалану рұқсатын қосыңыз."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Параметрлерді ашу."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Басқа құрылғы"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Шолуды қосу/өшіру"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Оятқыш, еске салғыш, іс-шара мен өзіңіз көрсеткен контактілердің қоңырауларынан басқа дыбыстар мен дірілдер мазаламайтын болады. Музыка, бейне және ойын сияқты медиафайлдарды қоссаңыз, оларды естисіз."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Қолданба экранын бөлісу, жазу не трансляциялау кезінде <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> қолданбасы онда көрінетін не ойнатылатын барлық нәрсені пайдалана алады. Сондықтан құпия сөздерді, төлем туралы мәліметті, хабарларды немесе басқа құпия ақпаратты енгізу кезінде сақ болыңыз."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Жалғастыру"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Қолданба экранын бөлісу не жазу"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Барлығын тазалау"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Басқару"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Тарих"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Мобильдік интернет өшірілсін бе?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"<xliff:g id="CARRIER">%s</xliff:g> операторы арқылы деректерге немесе интернетке кіре алмайсыз. Интернетке тек Wi-Fi арқылы кіресіз."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"операторыңыз"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"<xliff:g id="CARRIER">%s</xliff:g> операторына қайта ауысу керек пе?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Мобильдік интернет операторды қолдану мүмкіндігіне қарай автоматты түрде ауыспайды."</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Жоқ, рақмет"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Иә, ауыстырылсын"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Басқа қолданба рұқсат сұрауын жасырып тұрғандықтан, параметрлер жауабыңызды растай алмайды."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> қолданбасына <xliff:g id="APP_2">%2$s</xliff:g> қолданбасының үзінділерін көрсетуге рұқсат берілсін бе?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Бұл <xliff:g id="APP">%1$s</xliff:g> қолданбасындағы ақпаратты оқи алады"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Ұлғайтқыш терезесінің параметрлері"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Арнайы мүмкіндікті ашу үшін түртіңіз. Түймені параметрден реттеңіз не ауыстырыңыз.\n\n"<annotation id="link">"Параметрді көру"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Түймені уақытша жасыру үшін оны шетке қарай жылжытыңыз."</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Қайтару"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} таңбаша өшірілді.}other{# таңбаша өшірілді.}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Жоғарғы сол жаққа жылжыту"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Жоғарғы оң жаққа жылжыту"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Төменгі сол жаққа жылжыту"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Төменгі оң жаққа жылжыту"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Шетке жылжыту және жасыру"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Шетке жылжыту және көрсету"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Өшіру"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ауыстыру"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Құрылғыны басқару элементтері"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Басқару элементтері қосылатын қолданбаны таңдаңыз"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобильдік интернет"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Жалғанды"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Уақытша байланыс орнатылды."</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Байланыс нашар."</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Мобильдік интернет автоматты түрде қосылмайды."</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Байланыс жоқ"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Басқа қолжетімді желі жоқ"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 186244b..6504990 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"មិនអាចសម្គាល់មុខបានទេ"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"ប្រើស្នាមម្រាមដៃជំនួសវិញ"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"បានតភ្ជាប់ប៊្លូធូស។"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"មិនដឹងអំពីភាគរយថ្មទេ។"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"បានភ្ជាប់ទៅ <xliff:g id="BLUETOOTH">%s</xliff:g> ។"</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ពន្លឺ"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"ការបញ្ច្រាសពណ៌"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ការកែតម្រូវពណ៌"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"គ្រប់គ្រងអ្នកប្រើប្រាស់"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"រួចរាល់"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"បិទ"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"បានភ្ជាប់"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"អាចប្រើមីក្រូហ្វូនបាន"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"អាចប្រើកាមេរ៉ាបាន"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"អាចប្រើកាមេរ៉ា និងមីក្រូហ្វូនបាន"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"បានបើកមីក្រូហ្វូន"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"បានបិទមីក្រូហ្វូន"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"មីក្រូហ្វូនត្រូវបានបើកសម្រាប់កម្មវិធី និងសេវាកម្មទាំងអស់។"</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"សិទ្ធិចូលប្រើប្រាស់មីក្រូហ្វូនត្រូវបានបិទសម្រាប់កម្មវិធី និងសេវាកម្មទាំងអស់។ អ្នកអាចបើកសិទ្ធិចូលប្រើប្រាស់មីក្រូហ្វូននៅក្នុងការកំណត់ > ឯកជនភាព > មីក្រូហ្វូន។"</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"សិទ្ធិចូលប្រើប្រាស់មីក្រូហ្វូនត្រូវបានបិទសម្រាប់កម្មវិធី និងសេវាកម្មទាំងអស់។ អ្នកអាចផ្លាស់ប្ដូរលក្ខណៈនេះនៅក្នុងការកំណត់ > ឯកជនភាព > មីក្រូហ្វូន។"</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"បានបើកកាមេរ៉ា"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"បានបិទកាមេរ៉ា"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"កាមេរ៉ាត្រូវបានបើកសម្រាប់កម្មវិធី និងសេវាកម្មទាំងអស់។"</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"សិទ្ធិចូលប្រើប្រាស់កាមេរ៉ាត្រូវបានបិទសម្រាប់កម្មវិធី និងសេវាកម្មទាំងអស់។"</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"ដើម្បីប្រើប្រាស់ប៊ូតុងមីក្រូហ្វូន សូមបើកសិទ្ធិចូលប្រើប្រាស់មីក្រូហ្វូននៅក្នុងការកំណត់។"</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"បើកការកំណត់។"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"ឧបករណ៍ផ្សេងទៀត"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"បិទ/បើកទិដ្ឋភាពរួម"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"សំឡេង និងរំញ័រនឹងមិនរំខានដល់អ្នកឡើយ លើកលែងតែម៉ោងរោទ៍ ការរំលឹក ព្រឹត្តិការណ៍ និងអ្នកហៅទូរសព្ទដែលអ្នកបញ្ជាក់ប៉ុណ្ណោះ។ អ្នកនឹងនៅតែឮសំឡេងសកម្មភាពគ្រប់យ៉ាងដែលអ្នកលេងដដែល រួមទាំងតន្រ្តី វីដេអូ និងហ្គេម។"</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"នៅពេលអ្នកកំពុងចែករំលែក ថត ឬបញ្ជូនកម្មវិធី <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> មានសិទ្ធិចូលប្រើប្រាស់អ្វីៗដែលបង្ហាញ ឬលេងនៅលើកម្មវិធីនោះ។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ ឬព័ត៌មានរសើបផ្សេងទៀត។"</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"បន្ត"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"ចែករំលែក ឬថតកម្មវិធី"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"សម្អាតទាំងអស់"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"គ្រប់គ្រង"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ប្រវត្តិ"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"បិទទិន្នន័យទូរសព្ទចល័ត?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"អ្នកនឹងមិនមានសិទ្ធិចូលប្រើទិន្នន័យ ឬអ៊ីនធឺណិតតាមរយៈ <xliff:g id="CARRIER">%s</xliff:g> បានឡើយ។ អ៊ីនធឺណិតនឹងអាចប្រើបានតាមរយៈ Wi-Fi តែប៉ុណ្ណោះ។"</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"ក្រុមហ៊ុនសេវាទូរសព្ទរបស់អ្នក"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"ប្ដូរទៅ <xliff:g id="CARRIER">%s</xliff:g> វិញឬ?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"ទិន្នន័យទូរសព្ទចល័តនឹងមិនប្ដូរដោយស្វ័យប្រវត្តិដោយផ្អែកតាមភាពអាចប្រើបាននោះទេ"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"ទេ អរគុណ"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"បាទ/ចាស ប្ដូរ"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"ការកំណត់មិនអាចផ្ទៀងផ្ទាត់ការឆ្លើយតបរបស់អ្នកបានទេ ដោយសារកម្មវិធីកំពុងបាំងសំណើសុំការអនុញ្ញាត។"</string>
<string name="slice_permission_title" msgid="3262615140094151017">"អនុញ្ញាតឱ្យ <xliff:g id="APP_0">%1$s</xliff:g> បង្ហាញស្ថិតិប្រើប្រាស់របស់ <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- វាអាចអានព័ត៌មានពី <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"ការកំណត់វិនដូកម្មវិធីពង្រីក"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ចុចដើម្បីបើកមុខងារភាពងាយស្រួល។ ប្ដូរ ឬប្ដូរប៊ូតុងនេះតាមបំណងនៅក្នុងការកំណត់។\n\n"<annotation id="link">"មើលការកំណត់"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ផ្លាស់ទីប៊ូតុងទៅគែម ដើម្បីលាក់វាជាបណ្ដោះអាសន្ន"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"ត្រឡប់វិញ"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{បានដកផ្លូវកាត់ {label} ចេញ}other{បានដកផ្លូវកាត់ # ចេញ}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ផ្លាស់ទីទៅខាងលើផ្នែកខាងឆ្វេង"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ផ្លាស់ទីទៅខាងលើផ្នែកខាងស្ដាំ"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"ផ្លាស់ទីទៅខាងក្រោមផ្នែកខាងឆ្វេង"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"ផ្លាស់ទីទៅខាងក្រោមផ្នែកខាងស្ដាំ"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"ផ្លាស់ទីទៅផ្នែកខាងចុង រួចលាក់"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"ផ្លាស់ទីចេញពីផ្នែកខាងចុង រួចបង្ហាញ"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"ដកចេញ"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"បិទ/បើក"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ផ្ទាំងគ្រប់គ្រងឧបករណ៍"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"ជ្រើសរើសកម្មវិធីដែលត្រូវបញ្ចូលផ្ទាំងគ្រប់គ្រង"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"ទិន្នន័យទូរសព្ទចល័ត"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"បានភ្ជាប់"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"បានភ្ជាប់ជាបណ្ដោះអាសន្ន"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"ការតភ្ជាប់ខ្សោយ"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"ទិន្នន័យទូរសព្ទចល័តនឹងមិនភ្ជាប់ដោយស្វ័យប្រវត្តិទេ"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"មិនមានការតភ្ជាប់ទេ"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"មិនមានបណ្ដាញផ្សេងទៀតដែលអាចប្រើបានទេ"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 3341f8d..e1124bb 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"ಮುಖ ಗುರುತಿಸಲಾಗುತ್ತಿಲ್ಲ"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"ಬದಲಿಗೆ ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಬಳಸಿ"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"ಬ್ಲೂಟೂತ್ ಸಂಪರ್ಕಗೊಂಡಿದೆ."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ಬ್ಯಾಟರಿ ಶೇಕಡಾವಾರು ತಿಳಿದಿಲ್ಲ."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> ಗೆ ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದೆ."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ಪ್ರಕಾಶಮಾನ"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"ಕಲರ್ ಇನ್ವರ್ಶನ್"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ಬಣ್ಣದ ತಿದ್ದುಪಡಿ"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"ಬಳಕೆದಾರರನ್ನು ನಿರ್ವಹಿಸಿ"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"ಮುಗಿದಿದೆ"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"ಮುಚ್ಚಿರಿ"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"ಮೈಕ್ರೊಫೋನ್ ಲಭ್ಯವಿದೆ"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"ಕ್ಯಾಮರಾ ಲಭ್ಯವಿದೆ"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"ಮೈಕ್ರೊಫೋನ್ ಮತ್ತು ಕ್ಯಾಮರಾ ಲಭ್ಯವಿದೆ"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"ಮೈಕ್ರೊಫೋನ್ ಅನ್ನು ಆನ್ ಮಾಡಲಾಗಿದೆ"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"ಮೈಕ್ರೊಫೋನ್ ಅನ್ನು ಆಫ್ ಮಾಡಲಾಗಿದೆ"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"ಎಲ್ಲಾ ಆ್ಯಪ್ಗಳು ಹಾಗೂ ಸೇವೆಗಳಿಗಾಗಿ ಮೈಕ್ರೊಫೋನ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"ಎಲ್ಲಾ ಆ್ಯಪ್ಗಳು ಹಾಗೂ ಸೇವೆಗಳಿಗಾಗಿ ಮೈಕ್ರೊಫೋನ್ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ. ಸೆಟ್ಟಿಂಗ್ಗಳು > ಗೌಪ್ಯತೆ > ಮೈಕ್ರೊಫೋನ್ ಎಂಬಲ್ಲಿಗೆ ಹೋಗುವ ಮೂಲಕ ನೀವು ಮೈಕ್ರೊಫೋನ್ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಬಹುದು."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"ಎಲ್ಲಾ ಆ್ಯಪ್ಗಳು ಹಾಗೂ ಸೇವೆಗಳಿಗಾಗಿ ಮೈಕ್ರೊಫೋನ್ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ. ಸೆಟ್ಟಿಂಗ್ಗಳು > ಗೌಪ್ಯತೆ > ಮೈಕ್ರೊಫೋನ್ ಎಂಬಲ್ಲಿಗೆ ಹೋಗುವ ಮೂಲಕ ನೀವು ಇದನ್ನು ಬದಲಾಯಿಸಬಹುದು."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"ಕ್ಯಾಮರಾವನ್ನು ಆನ್ ಮಾಡಲಾಗಿದೆ"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"ಕ್ಯಾಮರಾವನ್ನು ಆಫ್ ಮಾಡಲಾಗಿದೆ"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"ಎಲ್ಲಾ ಆ್ಯಪ್ಗಳು ಹಾಗೂ ಸೇವೆಗಳಿಗಾಗಿ ಕ್ಯಾಮರಾವನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"ಎಲ್ಲಾ ಆ್ಯಪ್ಗಳು ಹಾಗೂ ಸೇವೆಗಳಿಗಾಗಿ ಕ್ಯಾಮರಾ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"ಮೈಕ್ರೊಫೋನ್ ಬಟನ್ ಅನ್ನು ಬಳಸಲು, ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಮೈಕ್ರೊಫೋನ್ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ತೆರೆಯಿರಿ."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"ಅನ್ಯ ಸಾಧನ"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ಟಾಗಲ್ ನ ಅವಲೋಕನ"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"ಅಲಾರಾಂಗಳು, ಜ್ಞಾಪನೆಗಳು, ಈವೆಂಟ್ಗಳು ಹಾಗೂ ನೀವು ಸೂಚಿಸಿರುವ ಕರೆದಾರರನ್ನು ಹೊರತುಪಡಿಸಿ ಬೇರಾವುದೇ ಸದ್ದುಗಳು ಅಥವಾ ವೈಬ್ರೇಶನ್ಗಳು ನಿಮಗೆ ತೊಂದರೆ ನೀಡುವುದಿಲ್ಲ. ಹಾಗಿದ್ದರೂ, ನೀವು ಪ್ಲೇ ಮಾಡುವ ಸಂಗೀತ, ವೀಡಿಯೊಗಳು ಮತ್ತು ಆಟಗಳ ಆಡಿಯೊವನ್ನು ನೀವು ಕೇಳಿಸಿಕೊಳ್ಳುತ್ತೀರಿ."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"ನೀವು ಆ್ಯಪ್ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳುತ್ತಿರುವಾಗ, ರೆಕಾರ್ಡ್ ಮಾಡುತ್ತಿರುವಾಗ ಅಥವಾ ಬಿತ್ತರಿಸುತ್ತಿರುವಾಗ, ಆ ಆ್ಯಪ್ನಲ್ಲಿ ತೋರಿಸಲಾಗುವ ಅಥವಾ ಪ್ಲೇ ಆಗುವ ಯಾವುದೇ ವಿಷಯಕ್ಕೆ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಹೊಂದಿರುತ್ತದೆ. ಹಾಗಾಗಿ, ಪಾಸ್ವರ್ಡ್ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು ಅಥವಾ ಇತರ ಸೂಕ್ಷ್ಮ ಮಾಹಿತಿಯ ಕುರಿತು ಜಾಗರೂಕರಾಗಿರಿ."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"ಮುಂದುವರಿಸಿ"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"ಆ್ಯಪ್ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳಿ ಅಥವಾ ರೆಕಾರ್ಡ್ ಮಾಡಿ"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"ಎಲ್ಲವನ್ನೂ ತೆರವುಗೊಳಿಸಿ"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"ನಿರ್ವಹಿಸಿ"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ಇತಿಹಾಸ"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"ಮೊಬೈಲ್ ಡೇಟಾ ಆಫ್ ಮಾಡಬೇಕೆ?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"ನೀವು <xliff:g id="CARRIER">%s</xliff:g> ಮೂಲಕ ಡೇಟಾ ಅಥವಾ ಇಂಟರ್ನೆಟ್ಗೆ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿರುವುದಿಲ್ಲ. ಇಂಟರ್ನೆಟ್, ವೈ-ಫೈ ಮೂಲಕ ಮಾತ್ರ ಲಭ್ಯವಿರುತ್ತದೆ."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"ನಿಮ್ಮ ವಾಹಕ"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"<xliff:g id="CARRIER">%s</xliff:g> ಗೆ ಬದಲಿಸುವುದೇ?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"ಲಭ್ಯತೆಯ ಆಧಾರದ ಮೇಲೆ ಮೊಬೈಲ್ ಡೇಟಾ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಬದಲಾಗುವುದಿಲ್ಲ"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"ಬೇಡ, ಧನ್ಯವಾದಗಳು"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"ಹೌದು, ಬದಲಿಸಿ"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"ಅನುಮತಿ ವಿನಂತಿಯನ್ನು ಅಪ್ಲಿಕೇಶನ್ ಮರೆಮಾಚುತ್ತಿರುವ ಕಾರಣ, ಸೆಟ್ಟಿಂಗ್ಗಳಿಗೆ ನಿಮ್ಮ ಪ್ರತಿಕ್ರಿಯೆಯನ್ನು ಪರಿಶೀಲಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_2">%2$s</xliff:g> ಸ್ಲೈಸ್ಗಳನ್ನು ತೋರಿಸಲು <xliff:g id="APP_0">%1$s</xliff:g> ಅನ್ನು ಅನುಮತಿಸುವುದೇ?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- ಇದು <xliff:g id="APP">%1$s</xliff:g> ನಿಂದ ಮಾಹಿತಿಯನ್ನು ಓದಬಹುದು"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"ಮ್ಯಾಗ್ನಿಫೈರ್ ವಿಂಡೋ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ಪ್ರವೇಶಿಸುವಿಕೆ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ತೆರೆಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ. ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಈ ಬಟನ್ ಅನ್ನು ಕಸ್ಟಮೈಸ್ ಮಾಡಿ ಅಥವಾ ಬದಲಾಯಿಸಿ.\n\n"<annotation id="link">"ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ಅದನ್ನು ತಾತ್ಕಾಲಿಕವಾಗಿ ಮರೆಮಾಡಲು ಅಂಚಿಗೆ ಬಟನ್ ಸರಿಸಿ"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"ರದ್ದುಗೊಳಿಸಿ"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} ಶಾರ್ಟ್ಕಟ್ ಅನ್ನು ತೆಗೆದುಹಾಕಲಾಗಿದೆ}one{# ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ತೆಗೆದುಹಾಕಲಾಗಿದೆ}other{# ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ತೆಗೆದುಹಾಕಲಾಗಿದೆ}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ಎಡ ಮೇಲ್ಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ಬಲ ಮೇಲ್ಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"ಸ್ಕ್ರೀನ್ನ ಎಡ ಕೆಳಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"ಕೆಳಗಿನ ಬಲಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"ಅಂಚಿಗೆ ಸರಿಸಿ ಮತ್ತು ಮರೆಮಾಡಿ"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"ಅಂಚನ್ನು ಸರಿಸಿ ಮತ್ತು ತೋರಿಸಿ"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"ತೆಗೆದುಹಾಕಿ"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ಟಾಗಲ್ ಮಾಡಿ"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ಸಾಧನ ನಿಯಂತ್ರಣಗಳು"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"ನಿಯಂತ್ರಣಗಳನ್ನು ಸೇರಿಸಲು ಆ್ಯಪ್ ಅನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"ಮೊಬೈಲ್ ಡೇಟಾ"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"ತಾತ್ಕಾಲಿಕವಾಗಿ ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"ಕಳಪೆ ಸಂಪರ್ಕ"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"ಮೊಬೈಲ್ ಡೇಟಾ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಕನೆಕ್ಟ್ ಆಗುವುದಿಲ್ಲ"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"ಯಾವುದೇ ಕನೆಕ್ಷನ್ ಇಲ್ಲ"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"ಇತರ ಯಾವುದೇ ನೆಟ್ವರ್ಕ್ಗಳು ಲಭ್ಯವಿಲ್ಲ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 783365c..e5e4657 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"얼굴을 인식할 수 없습니다."</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"대신 지문을 사용하세요."</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"블루투스가 연결되었습니다."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"배터리 잔량을 알 수 없습니다."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>에 연결되었습니다."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"밝기"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"색상 반전"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"색상 보정"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"사용자 관리"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"완료"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"닫기"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"연결됨"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"마이크 사용 가능"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"카메라 사용 가능"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"마이크 및 카메라 사용 가능"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"마이크 사용 설정됨"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"마이크 사용 중지됨"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"모든 앱 및 서비스의 마이크가 사용 설정됩니다."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"모든 앱 및 서비스의 마이크 액세스가 사용 중지됩니다. 설정 > 개인 정보 보호 > 마이크에서 마이크 액세스를 사용 설정할 수 있습니다."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"모든 앱 및 서비스의 마이크 액세스가 사용 중지됩니다. 설정 > 개인 정보 보호 > 마이크에서 변경할 수 있습니다."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"카메라 사용 설정됨"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"카메라 사용 중지됨"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"모든 앱 및 서비스의 카메라가 사용 설정됩니다."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"모든 앱 및 서비스의 카메라 액세스가 사용 중지됩니다."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"마이크 버튼을 사용하려면 설정에서 마이크 액세스를 사용 설정하세요."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"설정 열기"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"기타 기기"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"최근 사용 버튼 전환"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"알람, 알림, 일정 및 지정한 발신자로부터 받은 전화를 제외한 소리와 진동을 끕니다. 음악, 동영상, 게임 등 재생하도록 선택한 소리는 정상적으로 들립니다."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"앱을 공유하거나 녹화하거나 전송할 때는 <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>에서 해당 앱에 표시되거나 재생되는 모든 항목에 액세스할 수 있으므로 비밀번호, 결제 세부정보, 메시지 등 민감한 정보가 노출되지 않도록 주의하세요."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"계속"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"앱 공유 또는 녹화"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"모두 지우기"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"관리"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"기록"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"모바일 데이터를 사용 중지하시겠습니까?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"<xliff:g id="CARRIER">%s</xliff:g>을(를) 통해 데이터 또는 인터넷에 액세스할 수 없게 됩니다. 인터넷은 Wi-Fi를 통해서만 사용할 수 있습니다."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"이동통신사"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"다시 <xliff:g id="CARRIER">%s</xliff:g>(으)로 전환할까요?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"모바일 데이터가 가용성에 따라 자동으로 전환하지 않습니다."</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"나중에"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"예, 전환합니다"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"앱이 권한 요청을 가리고 있기 때문에 설정에서 내 응답을 확인할 수 없습니다."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g>에서 <xliff:g id="APP_2">%2$s</xliff:g>의 슬라이스를 표시하도록 허용하시겠습니까?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- <xliff:g id="APP">%1$s</xliff:g>의 정보를 읽을 수 있음"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"돋보기 창 설정"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"접근성 기능을 열려면 탭하세요. 설정에서 이 버튼을 맞춤설정하거나 교체할 수 있습니다.\n\n"<annotation id="link">"설정 보기"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"버튼을 가장자리로 옮겨서 일시적으로 숨기세요."</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"실행취소"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{바로가기 {label}개 삭제됨}other{바로가기 #개 삭제됨}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"왼쪽 상단으로 이동"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"오른쪽 상단으로 이동"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"왼쪽 하단으로 이동"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"오른쪽 하단으로 이동"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"가장자리로 옮겨서 숨기기"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"가장자리 바깥으로 옮겨서 표시"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"삭제"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"전환"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"기기 컨트롤"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"컨트롤을 추가할 앱을 선택하세요"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"모바일 데이터"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"연결됨"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"일시적으로 연결됨"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"연결 상태 나쁨"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"모바일 데이터가 자동으로 연결되지 않음"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"연결되지 않음"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"사용 가능한 다른 네트워크가 없음"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index b9da9f0..bef48dc 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Жүз таанылбай жатат"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Манжа изин колдонуңуз"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth байланышта"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Батарея кубатынын деңгээли белгисиз."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> менен туташкан."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Жарыктыгы"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Түстөрдү инверсиялоо"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Түстөрдү тууралоо"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Колдонуучуларды тескөө"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Бүттү"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Жабуу"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Туташкан"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Микрофон жеткиликтүү"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Камера жеткиликтүү"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Микрофон жана камера жеткиликтүү"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Микрофон күйгүзүлдү"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Микрофон өчүрүлдү"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Микрофон бардык колдонмолор жана кызматтар үчүн иштетилди."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Микрофон бардык колдонмолор жана кызматтар үчүн өчүрүлдү. Микрофонду колдонууну иштетүү үчүн > Купуялык > Микрофон бөлүмүнө өтүңүз."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Микрофон бардык колдонмолор жана кызматтар үчүн өчүрүлдү. Муну Параметрлер > Купуялык > Микрофон бөлүмүнөн өзгөртө аласыз."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Камера күйгүзүлдү"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Камера өчүрүлдү"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Камера бардык колдонмолор жана кызматтар үчүн иштетилди."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Камера бардык колдонмолор жана кызматтар үчүн өчүрүлдү."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Микрофон баскычын колдонуу үчүн Параметрлерден микрофонду колдонууну иштетиңиз."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Параметрлерди ачуу."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Башка түзмөк"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Назар режимин өчүрүү/күйгүзүү"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Ойготкучтардан, эскертүүлөрдөн, жылнаамадагы иш-чараларды эстеткичтерден жана белгиленген байланыштардын чалууларынан тышкары башка үндөр жана дирилдөөлөр тынчыңызды албайт. Бирок ойнотулуп жаткан музыканы, видеолорду жана оюндарды мурдагыдай эле уга бересиз."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Бөлүшүп, жаздырып же тышкы экранда бөлүшкөндө <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ал колдонмодо көрүнүп жана ойнотулуп жаткан нерселерге мүмкүнчүлүк алат. Андыктан сырсөздөрдү, төлөм маалыматын, билдирүүлөрдү жана башка купуя маалыматты көрсөтүп албаңыз."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Улантуу"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Колдонмону бөлүшүү же жаздыруу"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Баарын тазалап салуу"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Башкаруу"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Таржымал"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Мобилдик Интернетти өчүрөсүзбү?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"<xliff:g id="CARRIER">%s</xliff:g> байланыш оператору аркылуу Интернетке кире албай каласыз. Интернетке Wi-Fi аркылуу гана кирүүгө болот."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"байланыш операторуңуз"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Кайра <xliff:g id="CARRIER">%s</xliff:g> байланыш операторуна которуласызбы?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Жеткиликтүү болгондо мобилдик Интернет автоматтык түрдө которулбайт"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Жок, рахмат"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Ооба, которулуу"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Уруксат берүү сурамыңыз көрүнбөй калгандыктан, Жөндөөлөр жообуңузду ырастай албай жатат."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> колдонмосуна <xliff:g id="APP_2">%2$s</xliff:g> үлгүлөрүн көрсөтүүгө уруксат берилсинби?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- <xliff:g id="APP">%1$s</xliff:g> колдонмосунун маалыматын окуйт"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Чоңойткуч терезесинин параметрлери"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Атайын мүмкүнчүлүктөрдү ачуу үчүн басыңыз. Бул баскычты Жөндөөлөрдөн өзгөртүңүз.\n\n"<annotation id="link">"Жөндөөлөрдү көрүү"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Баскычты убактылуу жашыра туруу үчүн экрандын четине жылдырыңыз"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Кайтаруу"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} ыкчам баскыч өчүрүлдү}other{# ыкчам баскыч өчүрүлдү}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Жогорку сол жакка жылдыруу"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Жогорку оң жакка жылдырыңыз"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Төмөнкү сол жакка жылдыруу"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Төмөнкү оң жакка жылдырыңыз"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Ичине жылдырып, көрсөтүңүз"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Сыртка жылдырып, көрсөтүңүз"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Өчүрүү"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"өчүрүү/күйгүзүү"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Түзмөктү башкаруу элементтери"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Башкаруу элементтери кошула турган колдонмону тандоо"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобилдик трафик"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Туташты"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Убактылуу туташып турат"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Байланыш начар"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Мобилдик трафик автоматтык түрдө туташтырылбайт"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Байланыш жок"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Башка тармактар жеткиликсиз"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index d7dbb86..0d0d9de 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"ບໍ່ສາມາດຈຳແນກໃບໜ້າໄດ້"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"ກະລຸນາໃຊ້ລາຍນິ້ວມືແທນ"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"ເຊື່ອມຕໍ່ Bluetooth ແລ້ວ."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ບໍ່ຮູ້ເປີເຊັນແບັດເຕີຣີ."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"ເຊື່ອມຕໍ່ຫາ <xliff:g id="BLUETOOTH">%s</xliff:g> ແລ້ວ."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ຄວາມແຈ້ງ"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"ການປີ້ນສີ"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ການແກ້ໄຂສີ"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"ຈັດການຜູ້ໃຊ້"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"ແລ້ວໆ"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"ປິດ"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"ເຊື່ອມຕໍ່ແລ້ວ"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"ສາມາດໃຊ້ໄມໂຄຣໂຟນໄດ້"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"ສາມາດໃຊ້ກ້ອງຖ່າຍຮູບໄດ້"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"ສາມາດໃຊ້ໄມໂຄຣໂຟນ ແລະ ກ້ອງຖ່າຍຮູບໄດ້"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"ເປີດໄມໂຄຣໂຟນແລ້ວ"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"ປິດໄມໂຄຣໂຟນແລ້ວ"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"ເປີດການນຳໃຊ້ໄມໂຄຣໂຟນສຳລັບແອັບ ແລະ ບໍລິການທັງໝົດແລ້ວ."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"ສິດເຂົ້າເຖິງໄມໂຄຣໂຟນຖືກປິດການນຳໃຊ້ສຳລັບແອັບ ແລະ ບໍລິການທັງໝົດແລ້ວ. ທ່ານສາມາດເປີດການນຳໃຊ້ສິດເຂົ້າເຖິງໄມໂຄຣໂຟນໄດ້ໃນການຕັ້ງຄ່າ > ຄວາມເປັນສ່ວນຕົວ > ໄມໂຄຣໂຟນ."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"ສິດເຂົ້າເຖິງໄມໂຄຣໂຟນຖືກປິດການນຳໃຊ້ສຳລັບແອັບ ແລະ ບໍລິການທັງໝົດແລ້ວ. ທ່ານສາມາດປ່ຽນສິ່ງນີ້ໄດ້ໃນການຕັ້ງຄ່າ > ຄວາມເປັນສ່ວນຕົວ > ໄມໂຄຣໂຟນ."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"ເປີດກ້ອງຖ່າຍຮູບແລ້ວ"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"ປິດກ້ອງຖ່າຍຮູບແລ້ວ"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"ເປີດການນຳໃຊ້ກ້ອງຖ່າຍຮູບສຳລັບແອັບ ແລະ ບໍລິການທັງໝົດແລ້ວ."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"ສິດເຂົ້າເຖິງກ້ອງຖ່າຍຮູບຖືກປິດການນຳໃຊ້ສຳລັບແອັບ ແລະ ບໍລິການທັງໝົດແລ້ວ."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"ເພື່ອໃຊ້ປຸ່ມໄມໂຄຣໂຟນ, ໃຫ້ເປີດການນຳໃຊ້ສິດເຂົ້າເຖິງໄມໂຄຣໂຟນໃນການຕັ້ງຄ່າກ່ອນ."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"ເປີດການຕັ້ງຄ່າ."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"ອຸປະກອນອື່ນໆ"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ສະຫຼັບພາບຮວມ"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"ທ່ານຈະບໍ່ໄດ້ຮັບການລົບກວນຈາກສຽງ ແລະ ການສັ່ນເຕືອນ, ຍົກເວັ້ນໃນເວລາໂມງປຸກດັງ, ມີການແຈ້ງເຕືອນ ຫຼື ມີສາຍໂທເຂົ້າຈາກຜູ້ໂທທີ່ທ່ານລະບຸໄວ້. ທ່ານອາດຍັງຄົງໄດ້ຍິນຫາກທ່ານເລືອກຫຼິ້ນເພງ, ວິດີໂອ ແລະ ເກມ."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"ໃນຕອນທີ່ທ່ານກຳລັງແບ່ງປັນ, ບັນທຶກ ຫຼື ສົ່ງສັນຍານແອັບ, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ມີສິດເຂົ້າເຖິງສິ່ງທີ່ສະແດງ ຫຼື ຫຼິ້ນຢູ່ໃນແອັບນັ້ນ. ດັ່ງນັ້ນໃຫ້ລະມັດລະວັງກ່ຽວກັບລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ ຫຼື ຂໍ້ມູນທີ່ລະອຽດອ່ອນອື່ນໆ."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"ສືບຕໍ່"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"ແບ່ງປັນ ຫຼື ບັນທຶກແອັບ"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"ລຶບລ້າງທັງໝົດ"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"ຈັດການ"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ປະຫວັດ"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"ປິດອິນເຕີເນັດມືຖືໄວ້ບໍ?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"ທ່ານຈະບໍ່ມີສິດເຂົ້າເຖິງຂໍ້ມູນ ຫຼື ອິນເຕີເນັດຜ່ານ <xliff:g id="CARRIER">%s</xliff:g>. ອິນເຕີເນັດຈະສາມາດໃຊ້ໄດ້ຜ່ານ Wi-Fi ເທົ່ານັ້ນ."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"ຜູ້ໃຫ້ບໍລິການຂອງທ່ານ"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"ສະຫຼັບກັບໄປໃຊ້ <xliff:g id="CARRIER">%s</xliff:g> ບໍ?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"ອິນເຕີເນັດມືຖືຈະບໍ່ປ່ຽນຕາມຄວາມພ້ອມໃຫ້ບໍລິການໂດຍອັດຕະໂນມັດ"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"ບໍ່, ຂອບໃຈ"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"ແມ່ນແລ້ວ, ສະຫຼັບ"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"ເນື່ອງຈາກມີແອັບໃດໜຶ່ງກຳລັງຂັດຂວາງການຂໍອະນຸຍາດ, ການຕັ້ງຄ່າຈຶ່ງບໍ່ສາມາດຢັ້ງຢືນການຕອບຮັບຂອງທ່ານໄດ້."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"ອະນຸຍາດ <xliff:g id="APP_0">%1$s</xliff:g> ໃຫ້ສະແດງ <xliff:g id="APP_2">%2$s</xliff:g> ສະໄລ້ບໍ?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- ມັນສາມາດອ່ານຂໍ້ມູນຈາກ <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"ການຕັ້ງຄ່າໜ້າຈໍຂະຫຍາຍ"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ແຕະເພື່ອເປີດຄຸນສົມບັດການຊ່ວຍເຂົ້າເຖິງ. ປັບແຕ່ງ ຫຼື ປ່ຽນປຸ່ມນີ້ໃນການຕັ້ງຄ່າ.\n\n"<annotation id="link">"ເບິ່ງການຕັ້ງຄ່າ"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ຍ້າຍປຸ່ມໄປໃສ່ຂອບເພື່ອເຊື່ອງມັນຊົ່ວຄາວ"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"ຍົກເລີກ"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{ລຶບທາງລັດ {label} ອອກແລ້ວ}other{ລຶບທາງລັດ # ອອກແລ້ວ}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ຍ້າຍຊ້າຍເທິງ"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ຍ້າຍຂວາເທິງ"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"ຍ້າຍຊ້າຍລຸ່ມ"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"ຍ້າຍຂວາລຸ່ມ"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"ຍ້າຍອອກຂອບ ແລະ ເຊື່ອງ"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"ຍ້າຍອອກຂອບ ແລະ ສະແດງ"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"ລຶບອອກ"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ສະຫຼັບ"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ການຄວບຄຸມອຸປະກອນ"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"ເລືອກແອັບເພື່ອເພີ່ມການຄວບຄຸມ"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"ອິນເຕີເນັດມືຖື"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"ເຊື່ອມຕໍ່ແລ້ວ"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"ເຊື່ອມຕໍ່ແລ້ວຊົ່ວຄາວ"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"ສັນຍານເຊື່ອມຕໍ່ຊ້າ"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"ຈະບໍ່ເຊື່ອມຕໍ່ອິນເຕີເນັດມືຖືອັດຕະໂນມັດ"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"ບໍ່ມີການເຊື່ອມຕໍ່"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"ບໍ່ມີເຄືອຂ່າຍອື່ນທີ່ສາມາດໃຊ້ໄດ້"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index e631ce2..5c9969f 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Veidas neatpažintas"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Naudoti piršto antspaudą"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"„Bluetooth“ prijungtas."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Akumuliatoriaus energija procentais nežinoma."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Prisijungta prie „<xliff:g id="BLUETOOTH">%s</xliff:g>“."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Šviesumas"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Spalvų inversija"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Spalvų taisymas"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Tvarkyti naudotojus"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Atlikta"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Uždaryti"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Prijungtas"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Mikrofonas pasiekiamas"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Vaizdo kamera pasiekiama"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Mikrofonas ir vaizdo kamera pasiekiami"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Mikrofonas įjungtas"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Mikrofonas išjungtas"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Mikrofonas įgalintas veikiant visoms programoms ir paslaugoms."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Visų programų ir paslaugų prieiga prie mikrofono išjungta. Prieigą prie mikrofono galite įjungti nuėję į „Nustatymai“ > „Privatumas“ > „Mikrofonas“."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Visų programų ir paslaugų prieiga prie mikrofono išjungta. Tai galite pakeisti nuėję į „Nustatymai“ > „Privatumas“ > „Mikrofonas“."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Fotoaparatas įjungtas"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Fotoaparatas išjungtas"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Fotoaparatas įgalintas veikiant visoms programoms ir paslaugoms."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Visų programų ir paslaugų prieiga prie fotoaparato išjungta."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Norėdami naudotis mikrofono mygtuku, nustatymuose įjunkite prieigą prie mikrofono."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Atidaryti nustatymus."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Kitas įrenginys"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Perjungti apžvalgą"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Jūsų netrikdys garsai ir vibravimas, išskyrus nurodytų signalų, priminimų, įvykių ir skambintojų garsus. Vis tiek girdėsite viską, ką pasirinksite leisti, įskaitant muziką, vaizdo įrašus ir žaidimus."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Kai bendrinate, įrašote ar perduodate turinį, „<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>“ gali pasiekti viską, kas rodoma ar leidžiama programoje. Todėl būkite atsargūs su slaptažodžiais, išsamia mokėjimo metodo informacija, pranešimais ar kita neskelbtina informacija."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Tęsti"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Programos bendrinimas ar įrašymas"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Viską išvalyti"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Tvarkyti"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Istorija"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Išjungti mobiliojo ryšio duomenis?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Naudodamiesi „<xliff:g id="CARRIER">%s</xliff:g>“ paslaugomis neturėsite galimybės pasiekti duomenų arba interneto. Internetą galėsite naudoti tik prisijungę prie „Wi-Fi“."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"savo operatoriaus"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Perjungti atgal į „<xliff:g id="CARRIER">%s</xliff:g>“?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Mobiliojo ryšio duomenys nebus automatiškai perjungti atsižvelgiant į pasiekiamumą"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Ne, ačiū"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Taip, perjungti"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Kadangi programa užstoja leidimo užklausą, nustatymuose negalima patvirtinti jūsų atsakymo."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Leisti „<xliff:g id="APP_0">%1$s</xliff:g>“ rodyti „<xliff:g id="APP_2">%2$s</xliff:g>“ fragmentus?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Gali nuskaityti informaciją iš „<xliff:g id="APP">%1$s</xliff:g>“"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Didinimo lango nustatymai"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Palietę atidarykite pritaikymo neįgaliesiems funkcijas. Tinkinkite arba pakeiskite šį mygtuką nustatymuose.\n\n"<annotation id="link">"Žr. nustatymus"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Perkelkite mygtuką prie krašto, kad laikinai jį paslėptumėte"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Anuliuoti"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{Pašalintas spartusis klavišas „{label}“}one{Pašalintas # spartusis klavišas}few{Pašalinti # spartieji klavišai}many{Pašalinta # sparčiojo klavišo}other{Pašalinta # sparčiųjų klavišų}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Perkelti į viršų kairėje"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Perkelti į viršų dešinėje"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Perkelti į apačią kairėje"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Perkelti į apačią dešinėje"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Perkelti į kraštą ir slėpti"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Perkelti iš krašto ir rodyti"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Pašalinti"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"perjungti"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Įrenginio valdikliai"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Pasirinkite programą, kad pridėtumėte valdiklių"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiliojo ryšio duomenys"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Prisijungta"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Laikinai prijungta"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Prastas ryšys"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Naud. mob. r. duomenis nebus autom. prisijungiama"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Nėra ryšio"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nėra kitų pasiekiamų tinklų"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 0a08654..415ba8f9 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Nevar atpazīt seju"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Lietot pirksta nospiedumu"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth savienojums ir izveidots."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Akumulatora uzlādes līmenis procentos nav zināms."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Ir izveidots savienojum ar <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Spilgtums"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Krāsu inversija"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Krāsu korekcija"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Pārvaldīt lietotājus"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Gatavs"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Aizvērt"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Pievienota"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Mikrofons ir pieejams."</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Kamera ir pieejama."</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Mikrofons un kamera ir pieejami."</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Mikrofons tika ieslēgts"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Mikrofons tika izslēgts"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Piekļuve mikrofonam ir iespējota visām lietotnēm un pakalpojumiem."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Piekļuve mikrofonam ir atspējota visām lietotnēm un pakalpojumiem. Varat iespējot piekļuvi mikrofonam sadaļā Iestatījumi > Konfidencialitāte > Mikrofons."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Piekļuve mikrofonam ir atspējota visām lietotnēm un pakalpojumiem. Varat to mainīt sadaļā Iestatījumi > Konfidencialitāte > Mikrofons."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Kamera tika ieslēgta"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Kamera tika izslēgta"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Piekļuve kamerai ir iespējota visām lietotnēm un pakalpojumiem."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Piekļuve kamerai ir atspējota visām lietotnēm un pakalpojumiem."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Lai varētu izmantot mikrofona pogu, iespējojiet piekļuvi mikrofonam sadaļā Iestatījumi."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Atvērt iestatījumus"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Cita ierīce"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Pārskata pārslēgšana"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Jūs netraucēs skaņas un vibrācija, izņemot signālus, atgādinājumus, pasākumus un zvanītājus, ko būsiet norādījis. Jūs joprojām dzirdēsiet atskaņošanai izvēlētos vienumus, tostarp mūziku, videoklipus un spēles."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Lietotnes kopīgošanas, ierakstīšanas vai apraides laikā <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> var piekļūt visam, kas tiek rādīts vai atskaņots attiecīgajā lietotnē. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem un citu sensitīvu informāciju."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Turpināt"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Lietotnes kopīgošana vai ierakstīšana"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Dzēst visu"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Pārvaldīt"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Vēsture"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Vai izslēgt mobilos datus?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Izmantojot mobilo sakaru operatora <xliff:g id="CARRIER">%s</xliff:g> pakalpojumus, nevarēsiet piekļūt datiem vai internetam. Internetam varēsiet piekļūt, tikai izmantojot Wi-Fi savienojumu."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"jūsu mobilo sakaru operators"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Vai pārslēgties atpakaļ uz operatoru <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Mobilie dati netiks automātiski pārslēgti, pamatojoties uz pieejamību."</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Nē, paldies"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Jā, pārslēgties"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Lietotne Iestatījumi nevar verificēt jūsu atbildi, jo cita lietotne aizsedz atļaujas pieprasījumu."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Vai atļaut lietotnei <xliff:g id="APP_0">%1$s</xliff:g> rādīt lietotnes <xliff:g id="APP_2">%2$s</xliff:g> sadaļas?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Var lasīt informāciju no lietotnes <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Lupas loga iestatījumi"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Atveriet pieejamības funkcijas. Pielāgojiet vai aizstājiet šo pogu iestatījumos.\n\n"<annotation id="link">"Skatīt iestatījumus"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Lai īslaicīgi paslēptu pogu, pārvietojiet to uz malu"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Atsaukt"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{Noņemta saīsne “{label}”}zero{Noņemtas # saīsnes}one{Noņemta # saīsne}other{Noņemtas # saīsnes}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Pārvietot augšpusē pa kreisi"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Pārvietot augšpusē pa labi"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Pārvietot apakšpusē pa kreisi"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Pārvietot apakšpusē pa labi"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Pārvietot uz malu un paslēpt"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Pārvietot no malas un parādīt"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Noņemt"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"pārslēgt"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Ierīču vadīklas"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Izvēlieties lietotni, lai pievienotu vadīklas"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilie dati"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Ir izveidots savienojums"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Īslaicīgi izveidots savienojums"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Vājš savienojums"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobilo datu savienojums netiks veidots automātiski"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Nav savienojuma"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nav pieejams neviens cits tīkls"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index e714055..0ff5545 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Не се препознава ликот"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Користи отпечаток"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth е поврзан."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Процентот на батеријата е непознат."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Поврзано со <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Осветленост"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Инверзија на боите"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Корекција на боите"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Управувајте со корисниците"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Готово"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Затвори"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Поврзано"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Микрофонот е достапен"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Камерата е достапна"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Микрофонот и камерата се достапни"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Микрофонот е вклучен"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Микрофонот е исклучен"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Микрофонот е овозможен за сите апликации и услуги."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Пристапот до микрофонот е оневозможен за сите апликации и услуги. Може да овозможите пристап до микрофонот во „Поставки > Приватност > Микрофон“."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Пристапот до микрофонот е оневозможен за сите апликации и услуги. Ова може да го промените во „Поставки > Приватност > Микрофон“."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Камерата е вклучена"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Камерата е исклучена"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Камерата е овозможена за сите апликации и услуги."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Пристапот до камерата е оневозможен за сите апликации и услуги."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"За да го користите копчето за микрофон, овозможете пристап до микрофонот во „Поставки“."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Отворете ги поставките."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Друг уред"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Вклучи/исклучи преглед"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Нема да ве вознемируваат звуци и вибрации, освен од аларми, потсетници, настани и повикувачи што ќе ги наведете. Сѐ уште ќе слушате сѐ што ќе изберете да пуштите, како музика, видеа и игри."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Кога споделувате, снимате или емитувате апликација, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> има пристап до сѐ што се прикажува или пушта на таа апликација. Затоа, бидете внимателни со лозинки, детали за плаќање, пораки или други чувствителни податоци."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Продолжи"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Споделете или снимете апликација"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Избриши сѐ"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Управувајте"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Историја"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Да се исклучи мобилниот интернет?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Нема да имате пристап до податоците или интернетот преку <xliff:g id="CARRIER">%s</xliff:g>. Интернетот ќе биде достапен само преку Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"вашиот оператор"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Да се префрли на <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Мобилниот интернет нема автоматски да се префрли според достапноста"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Не, фала"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Да, префрли се"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Бидејќи апликацијата го прикрива барањето за дозвола, „Поставките“ не може да го потврдат вашиот одговор."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Да се дозволи <xliff:g id="APP_0">%1$s</xliff:g> да прикажува делови од <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Може да чита информации од <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Поставки за прозорец за лупа"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Допрете за функциите за пристапност. Приспособете или заменете го копчево во „Поставки“.\n\n"<annotation id="link">"Прикажи поставки"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Преместете го копчето до работ за да го сокриете привремено"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Врати"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{Отстранета е {label} кратенка}one{Отстранети се # кратенка}other{Отстранети се # кратенки}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Премести горе лево"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Премести горе десно"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Премести долу лево"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Премести долу десно"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Премести до работ и сокриј"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Премести над работ и прикажи"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Отстрани"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"вклучување/исклучување"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Контроли за уредите"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Изберете апликација за да додадете контроли"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобилен интернет"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Поврзано"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Привремено поврзано"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Слаба интернет-врска"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Мобилниот интернет не може да се поврзе автоматски"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Нема интернет-врска"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Нема други достапни мрежи"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 666e601..e8fdef35 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"മുഖം തിരിച്ചറിയാനാകുന്നില്ല"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"പകരം ഫിംഗർപ്രിന്റ് ഉപയോഗിക്കൂ"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"ബ്ലൂടൂത്ത് കണക്റ്റുചെയ്തു."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ബാറ്ററി ശതമാനം അജ്ഞാതമാണ്."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> എന്നതിലേക്ക് കണക്റ്റുചെയ്തു."</string>
@@ -303,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"മൈക്രോഫോൺ ലഭ്യമാണ്"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"ക്യാമറ ലഭ്യമാണ്"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"മൈക്രോഫോണും ക്യാമറയും ലഭ്യമാണ്"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"മൈക്രോഫോൺ ഓണാക്കി"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"മൈക്രോഫോൺ ഓഫാക്കി"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"എല്ലാ ആപ്പുകൾക്കും സേവനങ്ങൾക്കും മൈക്രോഫോൺ പ്രവർത്തനക്ഷമമാക്കിയിരിക്കുന്നു."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"എല്ലാ ആപ്പുകൾക്കും സേവനങ്ങൾക്കും മൈക്രോഫോൺ ആക്സസ് പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു. ക്രമീകരണം > സ്വകാര്യത > മൈക്രോഫോൺ എന്നതിൽ നിങ്ങൾക്ക് മൈക്രോഫോൺ ആക്സസ് പ്രവർത്തനക്ഷമമാക്കാം."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"എല്ലാ ആപ്പുകൾക്കും സേവനങ്ങൾക്കും മൈക്രോഫോൺ ആക്സസ് പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു. ക്രമീകരണം > സ്വകാര്യത > മൈക്രോഫോൺ എന്നതിൽ ഇത് നിങ്ങൾക്ക് മാറ്റാൻ കഴിയും."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"ക്യാമറ ഓണാക്കി"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"ക്യാമറ ഓഫാക്കി"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"എല്ലാ ആപ്പുകൾക്കും സേവനങ്ങൾക്കും ക്യാമറ പ്രവർത്തനക്ഷമമാക്കിയിരിക്കുന്നു."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"എല്ലാ ആപ്പുകൾക്കും സേവനങ്ങൾക്കും ക്യാമറാ ആക്സസ് പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"മൈക്രോഫോൺ ബട്ടൺ ഉപയോഗിക്കുന്നതിന്, ക്രമീകരണത്തിൽ മൈക്രോഫോൺ ആക്സസ് പ്രവർത്തനക്ഷമമാക്കുക."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"ക്രമീകരണം തുറക്കുക."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"മറ്റ് ഉപകരണം"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"അവലോകനം മാറ്റുക"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"നിങ്ങൾ സജ്ജീകരിച്ച അലാറങ്ങൾ, റിമൈൻഡറുകൾ, ഇവന്റുകൾ, കോളർമാർ എന്നിവയിൽ നിന്നുള്ള ശബ്ദങ്ങളും വൈബ്രേഷനുകളുമൊഴികെ മറ്റൊന്നും നിങ്ങളെ ശല്യപ്പെടുത്തുകയില്ല. സംഗീതം, വീഡിയോകൾ, ഗെയിമുകൾ എന്നിവയുൾപ്പെടെ പ്ലേ ചെയ്യുന്നതെന്തും നിങ്ങൾക്ക് തുടർന്നും കേൾക്കാൻ കഴിയും."</string>
@@ -373,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"ഒരു ആപ്പ് പങ്കിടുമ്പോൾ, റെക്കോർഡ് ചെയ്യുമ്പോൾ അല്ലെങ്കിൽ കാസ്റ്റ് ചെയ്യുമ്പോൾ, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> എന്നതിന് ആപ്പിൽ കാണിക്കുന്ന അല്ലെങ്കിൽ പ്ലേ ചെയ്യുന്ന എല്ലാത്തിലേക്കും ആക്സസ് ഉണ്ട്. അതിനാൽ, പാസ്വേഡുകൾ, പേയ്മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ അല്ലെങ്കിൽ സൂക്ഷ്മമായി കൈകാര്യം ചെയ്യേണ്ട മറ്റു വിവരങ്ങൾ എന്നിവ നൽകുമ്പോൾ സൂക്ഷിക്കുക."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"തുടരുക"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"ഒരു ആപ്പ് പങ്കിടുക അല്ലെങ്കിൽ റെക്കോർഡ് ചെയ്യുക"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"എല്ലാം മായ്ക്കുക"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"മാനേജ് ചെയ്യുക"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ചരിത്രം"</string>
@@ -727,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"മൊബൈൽ ഡാറ്റ ഓഫാക്കണോ?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"നിങ്ങൾക്ക് ഡാറ്റയിലേക്ക് ആക്സസോ അല്ലെങ്കിൽ <xliff:g id="CARRIER">%s</xliff:g> മുഖേനയുള്ള ഇന്റർനെറ്റോ ഉണ്ടാകില്ല. വൈഫൈ മുഖേന മാത്രമായിരിക്കും ഇന്റർനെറ്റ് ലഭ്യത."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"നിങ്ങളുടെ കാരിയർ"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"<xliff:g id="CARRIER">%s</xliff:g> എന്നതിലേക്ക് വീണ്ടും മാറണോ?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"ലഭ്യതയുടെ അടിസ്ഥാനത്തിൽ, മൊബൈൽ ഡാറ്റ സ്വയമേവ മാറില്ല"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"വേണ്ട"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"ഉവ്വ്, മാറുക"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"അനുമതി അഭ്യർത്ഥനയെ ഒരു ആപ്പ് മറയ്ക്കുന്നതിനാൽ, ക്രമീകരണത്തിന് നിങ്ങളുടെ പ്രതികരണം പരിശോധിച്ചുറപ്പിക്കാനാകില്ല."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_2">%2$s</xliff:g> സ്ലൈസുകൾ കാണിക്കാൻ <xliff:g id="APP_0">%1$s</xliff:g>-നെ അനുവദിക്കണോ?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- ഇതിന് <xliff:g id="APP">%1$s</xliff:g>-ൽ നിന്ന് വിവരങ്ങൾ വായിക്കാനാകും"</string>
@@ -793,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"മാഗ്നിഫയർ വിൻഡോ ക്രമീകരണം"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ഉപയോഗസഹായി ഫീച്ചർ തുറക്കാൻ ടാപ്പ് ചെയ്യൂ. ക്രമീകരണത്തിൽ ഈ ബട്ടൺ ഇഷ്ടാനുസൃതമാക്കാം, മാറ്റാം.\n\n"<annotation id="link">"ക്രമീകരണം കാണൂ"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"തൽക്കാലം മറയ്ക്കുന്നതിന് ബട്ടൺ അരുകിലേക്ക് നീക്കുക"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"പഴയപടിയാക്കുക"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} കുറുക്കുവഴി നീക്കം ചെയ്തു}other{# കുറുക്കുവഴികൾ നീക്കം ചെയ്തു}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"മുകളിൽ ഇടതുഭാഗത്തേക്ക് നീക്കുക"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"മുകളിൽ വലതുഭാഗത്തേക്ക് നീക്കുക"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"ചുവടെ ഇടതുഭാഗത്തേക്ക് നീക്കുക"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"ചുവടെ വലതുഭാഗത്തേക്ക് നീക്കുക"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"എഡ്ജിലേക്ക് നീക്കി മറയ്ക്കുക"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"എഡ്ജിൽ നിന്ന് നീക്കി കാണിക്കൂ"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"നീക്കം ചെയ്യുക"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"മാറ്റുക"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ഉപകരണ നിയന്ത്രണങ്ങൾ"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"നിയന്ത്രണങ്ങൾ ചേർക്കാൻ ആപ്പ് തിരഞ്ഞെടുക്കുക"</string>
@@ -947,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"മൊബൈൽ ഡാറ്റ"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"കണക്റ്റ് ചെയ്തു"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"താൽക്കാലികമായി കണക്റ്റ് ചെയ്തിരിക്കുന്നു"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"ദുർബലമായ കണക്ഷൻ"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"മൊബൈൽ ഡാറ്റ സ്വയം കണക്റ്റ് ചെയ്യില്ല"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"കണക്ഷനില്ല"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"മറ്റ് നെറ്റ്വർക്കുകളൊന്നും ലഭ്യമല്ല"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 769cdda..6be28dd 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Царайг танихгүй байна"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Оронд нь хурууны хээ ашиглах"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth холбогдсон."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Батарейн хувь тодорхойгүй байна."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>-тай холбогдсон."</string>
@@ -303,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Микрофон боломжтой байна"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Камер боломжтой байна"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Микрофон болон камер боломжтой байна"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Микрофоныг асаасан"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Микрофоныг унтраасан"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Микрофоныг бүх програм, үйлчилгээнд идэвхжүүлсэн."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Бүх програм, үйлчилгээнд микрофоны хандалтыг идэвхгүй болгосон. Та микрофоны хандалтыг тохиргоо идэвхжүүлж байна > Нууцлал > Микрофон идэвхжүүлж болно."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Бүх програм, үйлчилгээнд микрофоны хандалтыг идэвхгүй болгосон. Та үүнийг Тохиргоо > Нууцлал > Микрофон идэвхжүүлж болно."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Камерыг асаасан"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Камерыг унтраасан"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Камерыг бүх програм, үйлчилгээнд идэвхжүүлсэн."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Бүх апп болон үйлчилгээнд камерын хандалтыг идэвхгүй болгосон."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Микрофоны товчлуурыг ашиглахын тулд \"Тохиргоо\" хэсэгт микрофоны хандалтыг идэвхжүүлнэ үү."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Тохиргоог нээнэ үү."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Бусад төхөөрөмж"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Тоймыг асаах/унтраах"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Танд сэрүүлэг, сануулга, арга хэмжээ, таны сонгосон дуудлага илгээгчээс бусад дуу, чичиргээ саад болохгүй. Та хөгжим, видео, тоглоом зэрэг тоглуулахыг хүссэн бүх зүйлээ сонсох боломжтой хэвээр байна."</string>
@@ -373,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Таныг хуваалцаж, бичиж эсвэл дамжуулж байх үед <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> нь тухайн апп дээр харуулсан эсвэл тоглуулсан аливаа зүйлд хандах эрхтэй. Тиймээс нууц үг, төлбөрийн дэлгэрэнгүй, мессеж эсвэл бусад эмзэг мэдээлэлд болгоомжтой хандаарай."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Үргэлжлүүлэх"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Хуваалцах эсвэл бичих апп"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Бүгдийг арилгах"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Удирдах"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Түүх"</string>
@@ -727,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Мобайл датаг унтраах уу?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Та <xliff:g id="CARRIER">%s</xliff:g>-р дата эсвэл интернэтэд хандах боломжгүй болно. Интернэтэд зөвхөн Wi-Fi-р холбогдох боломжтой болно."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"таны оператор компани"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"<xliff:g id="CARRIER">%s</xliff:g> руу буцаан сэлгэх үү?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Мобайл дата нь боломжтой эсэхэд тулгуурлан автоматаар сэлгэхгүй"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Үгүй, баярлалаа"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Тийм, сэлгэе"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Апп нь зөвшөөрлийн хүсэлтийг танихгүй байгаа тул Тохиргооноос таны хариултыг баталгаажуулах боломжгүй байна."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g>-д <xliff:g id="APP_2">%2$s</xliff:g>-н хэсгүүдийг (slices) харуулахыг зөвшөөрөх үү?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Энэ нь <xliff:g id="APP">%1$s</xliff:g>-с мэдээлэл унших боломжтой"</string>
@@ -793,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Томруулагчийн цонхны тохиргоо"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Хандалтын онцлогуудыг нээхийн тулд товшино уу. Энэ товчлуурыг Тохиргоо хэсэгт өөрчилж эсвэл солиорой.\n\n"<annotation id="link">"Тохиргоог харах"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Үүнийг түр нуухын тулд товчлуурыг зах руу зөөнө үү"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Болих"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} товчлолыг хассан}other{# товчлолыг хассан}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Зүүн дээш зөөх"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Баруун дээш зөөх"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Зүүн доош зөөх"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Баруун доош зөөх"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Ирмэг рүү зөөж, нуух"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Ирмэгээс гаргаж, харуулах"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Хасах"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"асаах/унтраах"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Төхөөрөмжийн хяналт"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Хяналтууд нэмэхийн тулд аппыг сонгоно уу"</string>
@@ -947,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобайл дата"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Холбогдсон"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Түр зуур холбогдсон"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Холболт сул байна"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Мобайл дата автоматаар холбогдохгүй"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Холболт алга"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Өөр боломжтой сүлжээ байхгүй байна"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index d45b1e5..7c1dcc3 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"चेहरा ओळखू शकत नाही"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"त्याऐवजी फिंगरप्रिंट वापरा"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"ब्लूटूथ कनेक्ट केले."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"बॅटरीच्या चार्जिंगची टक्केवारी माहित नाही."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> शी कनेक्ट केले."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"चमक"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"कलर इन्व्हर्जन"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"रंग सुधारणा"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"वापरकर्ते व्यवस्थापित करा"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"पूर्ण झाले"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"बंद करा"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"कनेक्ट केलेले"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"मायक्रोफोन उपलब्ध आहे"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"कॅमेरा उपलब्ध आहे"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"मायक्रोफोन आणि कॅमेरा या गोष्टी उपलब्ध आहेत"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"मायक्रोफोन सुरू केला"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"मायक्रोफोन बंद केला"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"सर्व ॲप्स आणि सेवांसाठी मायक्रोफोन सुरू केला आहे."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"सर्व ॲप्स आणि सेवांसाठी मायक्रोफोन अॅक्सेस बंद केला आहे. तुम्ही मायक्रोफोन अॅक्सेस सेटिंग्ज > गोपनीयता > मायक्रोफोन मधून सुरू करू शकता."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"सर्व ॲप्स आणि सेवांसाठी मायक्रोफोन अॅक्सेस बंद केला आहे. तुम्ही हे सेटिंग्ज > गोपनीयता > मायक्रोफोन मधून बदलू शकता ."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"कॅमेरा सुरू केला आहे"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"कॅमेरा बंद केला आहे"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"सर्व ॲप्स आणि सेवांसाठी कॅमेरा सुरू केला आहे."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"सर्व ॲप्स आणि सेवांसाठी कॅमेरा अॅक्सेस बंद केला आहे."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"मायक्रोफोन बटण वापरण्यासाठी, सेटिंग्जमधून मायक्रोफोन अॅक्सेस सुरू करा."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"सेटिंग्ज उघडा."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"इतर डिव्हाइस"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"अवलोकन टॉगल करा."</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"अलार्म, रिमाइंडर, इव्हेंट आणि तुम्ही निश्चित केलेल्या कॉलर व्यतिरिक्त तुम्हाला कोणत्याही आवाज आणि कंपनांचा व्यत्त्यय आणला जाणार नाही. तरीही तुम्ही प्ले करायचे ठरवलेले कोणतेही संगीत, व्हिडिओ आणि गेमचे आवाज ऐकू शकतात."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"तुम्ही अॅप शेअर, रेकॉर्ड किंवा कास्ट करत असताना, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ला त्या अॅपवर दाखवलेल्या किंवा प्ले केलेल्या कोणत्याही गोष्टीचा अॅक्सेस असतो. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज किंवा इतर संवेदनशील माहिती काळजीपूर्वक वापरा."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"पुढे सुरू ठेवा"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"अॅप शेअर किंवा रेकॉर्ड करा"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"सर्व साफ करा"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"व्यवस्थापित करा"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"इतिहास"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"मोबाइल डेटा बंद करायचा?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"तुम्हाला <xliff:g id="CARRIER">%s</xliff:g> मधून डेटा किंवा इंटरनेटचा अॅक्सेस नसेल. इंटरनेट फक्त वाय-फाय मार्फत उपलब्ध असेल."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"तुमचा वाहक"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"<xliff:g id="CARRIER">%s</xliff:g> वर परत स्विच करायचे आहे का?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"उपलब्धतेच्या आधारावर मोबाइल डेटा आपोआप स्विच होणार नाही"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"नाही, नको"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"होय, स्विच करा"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"अॅप परवानगी विनंती अस्पष्ट करत असल्याने, सेटिंग्ज तुमचा प्रतिसाद पडताळू शकत नाहीत."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> ला <xliff:g id="APP_2">%2$s</xliff:g> चे तुकडे दाखवण्याची अनुमती द्यायची का?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- ते <xliff:g id="APP">%1$s</xliff:g> ची माहिती वाचू शकते"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"मॅग्निफायर विंडो सेटिंग्ज"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"अॅक्सेसिबिलिटी वैशिष्ट्ये उघडण्यासाठी, टॅप करा. सेटिंग्जमध्ये हे बटण कस्टमाइझ करा किंवा बदला.\n\n"<annotation id="link">"सेटिंग्ज पहा"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"बटण तात्पुरते लपवण्यासाठी ते कोपर्यामध्ये हलवा"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"पहिल्यासारखे करा"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} शॉर्टकट काढून टाकला आहे}other{# शॉर्टकट काढून टाकले आहेत}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"वर डावीकडे हलवा"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"वर उजवीकडे हलवा"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"तळाशी डावीकडे हलवा"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"तळाशी उजवीकडे हलवा"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"एजवर हलवा आणि लपवा"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"एजवर हलवा आणि दाखवा"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"काढून टाका"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"टॉगल करा"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"डिव्हाइस नियंत्रणे"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"नियंत्रणे जोडण्यासाठी ॲप निवडा"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"मोबाइल डेटा"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"कनेक्ट केले आहे"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"तात्पुरते कनेक्ट केलेले"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"खराब कनेक्शन"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"मोबाइल डेटा ऑटो-कनेक्ट होणार नाही"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"कोणतेही कनेक्शन नाही"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"इतर कोणतेही नेटवर्क उपलब्ध नाहीत"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 9e43396..08132f4 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Tak dapat mengecam wajah"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Gunakan cap jari"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth disambungkan."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Peratusan kuasa bateri tidak diketahui."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Disambungkan kepada <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Kecerahan"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Penyongsangan warna"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Pembetulan warna"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Urus pengguna"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Selesai"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Tutup"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Disambungkan"</string>
@@ -304,6 +305,28 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Mikrofon tersedia"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Kamera tersedia"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Mikrofon dan kamera tersedia"</string>
+ <!-- no translation found for sensor_privacy_mic_turned_on_dialog_title (6348853159838376513) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_turned_off_dialog_title (5760464281790732849) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_unblocked_dialog_content (4889961886199270224) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_blocked_no_exception_dialog_content (5864898470772965394) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_blocked_with_exception_dialog_content (810289713700437896) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_turned_on_dialog_title (8039095295100075952) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_turned_off_dialog_title (1936603903120742696) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_unblocked_dialog_content (7847190103011782278) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_blocked_dialog_content (3182428709314874616) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_htt_blocked_dialog_content (3333321592997666441) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_dialog_open_settings (1503088305279285048) -->
+ <skip />
<string name="media_seamless_other_device" msgid="4654849800789196737">"Peranti lain"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Togol Ikhtisar"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Anda tidak akan diganggu oleh bunyi dan getaran, kecuali daripada penggera, peringatan, acara dan pemanggil yang anda tetapkan. Anda masih mendengar item lain yang anda pilih untuk dimainkan termasuk muzik, video dan permainan."</string>
@@ -374,6 +397,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Apabila anda berkongsi, merakam atau menghantar apl, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> mempunyai akses kepada apa-apa yang dipaparkan atau dimainkan pada apl tersebut. Jadi berhati-hati dengan kata laluan, butiran pembayaran, mesej atau maklumat sensitif lain."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Teruskan"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Kongsi atau rakam apl"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Kosongkan semua"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Urus"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Sejarah"</string>
@@ -728,14 +761,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Matikan data mudah alih?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Anda tidak akan mempunyai akses kepada data atau Internet melalui <xliff:g id="CARRIER">%s</xliff:g>. Internet hanya tersedia melaui Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"pembawa anda"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Tukar kembali kepada <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Data mudah alih tidak akan ditukar secara automatik berdasarkan ketersediaan"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Tidak perlu"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Ya, tukar"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Oleh sebab apl melindungi permintaan kebenaran, Tetapan tidak dapat mengesahkan jawapan anda."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Benarkan <xliff:g id="APP_0">%1$s</xliff:g> menunjukkan <xliff:g id="APP_2">%2$s</xliff:g> hirisan?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Hos hirisan boleh membaca maklumat daripada <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +823,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Tetapan tetingkap penggadang"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Ketik untuk membuka ciri kebolehaksesan. Sesuaikan/gantikan butang ini dalam Tetapan.\n\n"<annotation id="link">"Lihat tetapan"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Gerakkan butang ke tepi untuk disembunyikan buat sementara waktu"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Buat asal"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} pintasan dialih keluar}other{# pintasan dialih keluar}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Alihkan ke atas sebelah kiri"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Alihkan ke atas sebelah kanan"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Alihkan ke bawah sebelah kiri"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Alihkan ke bawah sebelah kanan"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Alihkan ke tepi dan sorokkan"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Alihkan ke tepi dan tunjukkan"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Alih keluar"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"togol"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Kawalan peranti"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Pilih apl untuk menambahkan kawalan"</string>
@@ -948,10 +974,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Data mudah alih"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Disambungkan"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Disambungkan buat sementara waktu"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Sambungan lemah"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Data mudah alih tidak akan autosambung"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Tiada sambungan"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Tiada rangkaian lain yang tersedia"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 8f404b8..51dbb5d 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"မျက်နှာကို မမှတ်မိပါ"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"လက်ဗွေကို အစားထိုးသုံးပါ"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"ဘလူးတုသ်ချိတ်ဆက်ထားမှု"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ဘက်ထရီရာခိုင်နှုန်းကို မသိပါ။"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>သို့ ချိတ်ဆက်ထား"</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"အလင်းတောက်ပမှု"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"အရောင်ပြောင်းပြန်ပြုလုပ်ရန်"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"အရောင် အမှန်ပြင်ခြင်း"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"အသုံးပြုသူများ စီမံရန်"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"ပြီးပါပြီ"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"ပိတ်ရန်"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"ချိတ်ဆက်ထား"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"မိုက်ခရိုဖုန်း သုံးနိုင်သည်"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"ကင်မရာ သုံးနိုင်သည်"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"မိုက်ခရိုဖုန်းနှင့် ကင်မရာ သုံးနိုင်သည်"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"မိုက်ခရိုဖုန်းကို ဖွင့်ထားသည်"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"မိုက်ခရိုဖုန်း ပိတ်ထားသည်"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"အက်ပ်နှင့် ဝန်ဆောင်မှုအားလုံးအတွက် မိုက်ခရိုဖုန်းကို ဖွင့်ထားသည်။"</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"အက်ပ်နှင့် ဝန်ဆောင်မှုအားလုံးအတွက် မိုက်ခရိုဖုန်းသုံးခွင့်ကို ပိတ်ထားသည်။ မိုက်ခရိုဖုန်းသုံးခွင့်ကို ဆက်တင်များ > ကိုယ်ရေးအချက်အလက်လုံခြုံမှု > မိုက်ခရိုဖုန်း တွင်ဖွင့်နိုင်သည်။"</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"အက်ပ်နှင့် ဝန်ဆောင်မှုအားလုံးအတွက် မိုက်ခရိုဖုန်းသုံးခွင့်ကို ပိတ်ထားသည်။ ၎င်းကို ဆက်တင်များ > ကိုယ်ရေးအချက်အလက်လုံခြုံမှု > မိုက်ခရိုဖုန်း တွင်ပြောင်းနိုင်သည်။"</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"ကင်မရာ ဖွင့်ထားသည်"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"ကင်မရာ ပိတ်ထားသည်"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"အက်ပ်နှင့် ဝန်ဆောင်မှုအားလုံးအတွက် ကင်မရာကို ဖွင့်ထားသည်။"</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"အက်ပ်နှင့် ဝန်ဆောင်မှုအားလုံးအတွက် ကင်မရာသုံးခွင့်ကို ပိတ်ထားသည်။"</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"မိုက်ခရိုဖုန်းခလုတ် အသုံးပြုရန် ဆက်တင်များတွင် မိုက်ခရိုဖုန်းသုံးခွင့်ကို ဖွင့်ပါ။"</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"ဆက်တင်များဖွင့်ရန်။"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"အခြားစက်ပစ္စည်း"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ဖွင့်၊ ပိတ် အနှစ်ချုပ်"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"နှိုးစက်သံ၊ သတိပေးချက်အသံများ၊ ပွဲစဉ်သတိပေးသံများနှင့် သင်ခွင့်ပြုထားသူများထံမှ ဖုန်းခေါ်မှုများမှလွဲ၍ အခြားအသံများနှင့် တုန်ခါမှုများက သင့်ကို အနှောင့်အယှက်ပြုမည် မဟုတ်ပါ။ သို့သော်လည်း သီချင်း၊ ဗီဒီယိုနှင့် ဂိမ်းများအပါအဝင် သင်ကရွေးချယ်ဖွင့်ထားသည့် အရာတိုင်း၏ အသံကိုမူ ကြားနေရဆဲဖြစ်ပါလိမ့်မည်။"</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"အက်ပ်ဖြင့် မျှဝေ၊ ရိုက်ကူး (သို့) ကာစ်လုပ်သည့်အခါ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> သည် ၎င်းအက်ပ်တွင် ပြထားသည့် (သို့) ဖွင့်ထားသည့် အရာအားလုံးကို တွေ့နိုင်သည်။ ထို့ကြောင့် စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ် (သို့) အခြားအရေးကြီးအချက်အလက်များနှင့်ပတ်သက်၍ ဂရုစိုက်ပါ။"</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"ရှေ့ဆက်ရန်"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"အက်ပ် မျှဝေခြင်း (သို့) ရိုက်ကူးခြင်း"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"အားလုံးရှင်းရန်"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"စီမံရန်"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"မှတ်တမ်း"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"မိုဘိုင်းဒေတာ ပိတ်မလား။"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"<xliff:g id="CARRIER">%s</xliff:g> မှတစ်ဆင့် ဒေတာ သို့မဟုတ် အင်တာနက်ကို မသုံးနိုင်ပါ။ Wi-Fi ဖြင့်သာ အင်တာနက် သုံးနိုင်သည်။"</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"သင်၏ ဝန်ဆောင်မှုပေးသူ"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"<xliff:g id="CARRIER">%s</xliff:g> သို့ ပြန်ပြောင်းမလား။"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"ရနိုင်မှုပေါ် အခြေခံပြီး မိုဘိုင်းဒေတာကို အလိုအလျောက် မပြောင်းပါ"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"မလိုပါ"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"ပြောင်းရန်"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"အပလီကေးရှင်းတစ်ခုက ခွင့်ပြုချက်တောင်းခံမှုကို ပိတ်ထားသောကြောင့် ဆက်တင်များသည် သင်၏ လုပ်ဆောင်ကို တုံ့ပြန်နိုင်ခြင်းမရှိပါ။"</string>
<string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> အား <xliff:g id="APP_2">%2$s</xliff:g> ၏အချပ်များ ပြသခွင့်ပြုပါသလား။"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- ၎င်းသည် <xliff:g id="APP">%1$s</xliff:g> မှ အချက်အလက်ကို ဖတ်နိုင်သည်"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"မှန်ဘီလူးဝင်းဒိုး ဆက်တင်များ"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"အများသုံးစွဲနိုင်မှုဆိုင်ရာ ဝန်ဆောင်မှုများ ဖွင့်ရန် တို့ပါ။ ဆက်တင်များတွင် ဤခလုတ်ကို စိတ်ကြိုက်ပြင်ပါ (သို့) လဲပါ။\n\n"<annotation id="link">"ဆက်တင်များ ကြည့်ရန်"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ခလုတ်ကို ယာယီဝှက်ရန် အစွန်းသို့ရွှေ့ပါ"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"နောက်ပြန်ရန်"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{ဖြတ်လမ်းလင့်ခ် {label} ခု ဖယ်ရှားပြီးပြီ}other{ဖြတ်လမ်းလင့်ခ် # ခု ဖယ်ရှားပြီးပြီ}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ဘယ်ဘက်ထိပ်သို့ ရွှေ့ရန်"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ညာဘက်ထိပ်သို့ ရွှေ့ရန်"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"ဘယ်ဘက်အောက်ခြေသို့ ရွှေ့ရန်"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"ညာဘက်အောက်ခြေသို့ ရွှေ့ရန်"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"အစွန်းသို့ရွှေ့ပြီး ဝှက်ရန်"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"အစွန်းမှရွှေ့ပြီး ပြရန်"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"ဖယ်ရှားရန်"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ပြောင်းရန်"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"စက်ထိန်းစနစ်"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"ထိန်းချုပ်မှုများထည့်ရန် အက်ပ်ရွေးခြင်း"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"မိုဘိုင်းဒေတာ"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"ချိတ်ဆက်ထားသည်"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"ယာယီချိတ်ဆက်ထားသည်"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"ချိတ်ဆက်မှုအားနည်းသည်"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"မိုဘိုင်းဒေတာ အော်တိုမချိတ်ပါ"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"ချိတ်ဆက်မှုမရှိပါ"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"အခြားကွန်ရက်များ မရှိပါ"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 94b8e90..eba9731 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Ansiktet gjenkjennes ikke"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Bruk fingeravtrykk"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth er tilkoblet."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Batteriprosenten er ukjent."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Koblet til <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Lysstyrke"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Fargeinvertering"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Fargekorrigering"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Administrer brukere"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Ferdig"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Lukk"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Tilkoblet"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Mikrofon er tilgjengelig"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Kamera er tilgjengelig"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Mikrofon og kamera er tilgjengelig"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Mikrofonen er slått på"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Mikrofonen er slått av"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Mikrofonen er slått på for alle apper og tjenester."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Mikrofontilgang er slått av for alle apper og tjenester. Du kan gi mikrofontilgang i Innstillinger > Personvern > Mikrofon."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Mikrofontilgang er slått av for alle apper og tjenester. Du kan endre dette i Innstillinger > Personvern > Mikrofon."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Kameraet er slått på"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Kameraet er slått av"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Kameraet er slått på for alle apper og tjenester."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Kameratilgang er slått av for alle apper og tjenester."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"For å bruke mikrofonknappen, gi mikrofontilgang i innstillingene."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Åpne innstillingene."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Annen enhet"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Slå oversikten av eller på"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Du blir ikke forstyrret av lyder og vibrasjoner, med unntak av alarmer, påminnelser, aktiviteter og oppringere du angir. Du kan fremdeles høre alt du velger å spille av, for eksempel musikk, videoer og spill."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Når du deler, tar opp eller caster en app, har <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tilgang til alt som vises eller spilles av i den aktuelle appen. Derfor bør du være forsiktig med passord, betalingsopplysninger, meldinger og annen sensitiv informasjon."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Fortsett"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Del eller ta opp en app"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Fjern alt"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Administrer"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Logg"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Vil du slå av mobildata?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Du får ikke tilgang til data eller internett via <xliff:g id="CARRIER">%s</xliff:g>. Internett er bare tilgjengelig via Wifi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"operatøren din"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Vil du bytte tilbake til <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Det byttes ikke mobildataoperatør automatisk basert på tilgjengelighet"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Nei takk"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Ja, bytt"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Fordi en app skjuler tillatelsesforespørselen, kan ikke Innstillinger bekrefte svaret ditt."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Vil du tillate at <xliff:g id="APP_0">%1$s</xliff:g> viser <xliff:g id="APP_2">%2$s</xliff:g>-utsnitt?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"– Den kan lese informasjon fra <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Innstillinger for forstørringsvindu"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Trykk for å åpne tilgj.funksjoner. Tilpass eller bytt knappen i Innstillinger.\n\n"<annotation id="link">"Se innstillingene"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Flytt knappen til kanten for å skjule den midlertidig"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Angre"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} snarvei er fjernet}other{# snarveier er fjernet}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Flytt til øverst til venstre"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Flytt til øverst til høyre"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Flytt til nederst til venstre"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Flytt til nederst til høyre"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Flytt til kanten og skjul"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Flytt ut kanten og vis"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Fjern"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"slå av/på"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Enhetsstyring"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Velg en app for å legge til kontroller"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobildata"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Tilkoblet"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Koblet til midlertidig"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Dårlig forbindelse"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobildata kobler ikke til automatisk"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Ingen tilkobling"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Ingen andre nettverk er tilgjengelige"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 35937a9..23ff09b 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"अनुहार पहिचान गर्न सकिएन"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"बरु फिंगरप्रिन्ट प्रयोग गर्नुहोस्"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"ब्लुटुथ जडान भयो।"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ब्याट्रीमा कति प्रतिशत चार्ज छ भन्ने कुराको जानाकरी छैन।"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> मा जडित।"</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"उज्यालपन"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"कलर इन्भर्सन"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"कलर करेक्सन"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"प्रयोगकर्ताहरू व्यवस्थित गर्नुहोस्"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"भयो"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"बन्द गर्नुहोस्"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"जोडिएको"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"माइक्रोफोन उपलब्ध छ"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"क्यामेरा उपलब्ध छ"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"माइक्रोफोन र क्यामेरा उपलब्ध छन्"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"माइक्रोफोन अन गरियो"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"माइक्रोफोन अफ गरियो"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"सबै एप तथा सेवाहरूलाई माइक्रोफोन प्रयोग गर्ने अनुमति दिइएको छ।"</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"कुनै पनि एप तथा सेवालाई माइक्रोफोन प्रयोग गर्ने अनुमति दिइएको छैन। तपाईं \"सेटिङ > गोपनीयता > माइक्रोफोन\" मा गई माइक्रोफोन प्रयोग गर्ने अनुमति दिन सक्नुहुन्छ।"</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"कुनै पनि एप तथा सेवालाई माइक्रोफोन प्रयोग गर्ने अनुमति दिइएको छैन। तपाईं \"सेटिङ > गोपनीयता > माइक्रोफोन\" मा गई यो कुरा परिवर्तन गर्न सक्नुहुन्छ।"</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"क्यामेरा अन गरियो"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"क्यामेरा अफ गरियो"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"सबै एप तथा सेवाहरूलाई क्यामेरा प्रयोग गर्ने अनुमति दिइएको छ।"</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"कुनै पनि एप तथा सेवालाई क्यामेरा प्रयोग गर्ने अनुमति दिइएको छैन।"</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"तपाईं माइक्रोफोन बटन प्रयोग गर्न चाहनुहुन्छ भने सेटिङमा गई माइक्रोफोन प्रयोग गर्ने अनुमति दिनुहोस्।"</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"सेटिङ खोल्नुहोस्।"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"अर्को डिभाइड"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"परिदृश्य टगल गर्नुहोस्"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"तपाईंलाई अलार्म, रिमाइन्डर, कार्यक्रम र तपाईंले निर्दिष्ट गर्नुभएका कलरहरू बाहेकका ध्वनि र कम्पनहरूले बाधा पुऱ्याउने छैनन्। तपाईंले अझै सङ्गीत, भिडियो र खेलहरू लगायत आफूले प्ले गर्न छनौट गरेका जुनसुकै कुरा सुन्न सक्नुहुनेछ।"</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"तपाईंले सेयर गर्दा, रेकर्ड गर्दा वा कास्ट गर्दा<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ले तपाईंको स्क्रिनमा देखिने वा डिभाइसमा प्ले गरिएका सबै कुरा खिच्न सक्छ। त्यसैले पासवर्ड, भुक्तानीको विवरण, म्यासेज वा अन्य संवेदनशील जानकारी सुरक्षित राख्नुहोला।"</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"जारी राख्नुहोस्"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"सेयर वा रेकर्ड गर्नका लागि एप चयन गर्नुहोस्"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"सबै हटाउनुहोस्"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"व्यवस्थित गर्नुहोस्"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"इतिहास"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"मोबाइल डेटा निष्क्रिय पार्ने हो?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"तपाईं <xliff:g id="CARRIER">%s</xliff:g> मार्फत डेटा वा इन्टरनेट प्रयोग गर्न सक्नुहुने छैन। Wi-Fi मार्फत मात्र इन्टरनेट उपलब्ध हुने छ।"</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"तपाईंको सेवा प्रदायक"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"फेरि <xliff:g id="CARRIER">%s</xliff:g> को मोबाइल डेटा अन गर्ने हो?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"मोबाइल डेटा उपलब्धताका आधारमा स्वतः बदलिँदैन"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"पर्दैन, धन्यवाद"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"हुन्छ, बदल्नुहोस्"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"कुनै एपको कारणले अनुमतिसम्बन्धी अनुरोध बुझ्न गाह्रो भइरहेकोले सेटिङहरूले तपाईंको प्रतिक्रिया प्रमाणित गर्न सक्दैनन्।"</string>
<string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> लाई <xliff:g id="APP_2">%2$s</xliff:g> का स्लाइसहरू देखाउन अनुमति दिने हो?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- यसले <xliff:g id="APP">%1$s</xliff:g> बाट जानकारी पढ्न सक्छ"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"म्याग्निफायर विन्डोसम्बन्धी सेटिङ"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"सर्वसुलभता कायम गर्ने सुविधा खोल्न ट्याप गर्नुहोस्। सेटिङमा गई यो बटन कस्टमाइज गर्नुहोस् वा बदल्नुहोस्।\n\n"<annotation id="link">"सेटिङ हेर्नुहोस्"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"यो बटन केही बेर नदेखिने पार्न किनारातिर सार्नुहोस्"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"अन्डू गर्नुहोस्"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} वटा सर्टकट हटाइयो}other{# वटा सर्टकट हटाइयो}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"सिरानको बायाँतिर सार्नुहोस्"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"सिरानको दायाँतिर सार्नुहोस्"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"पुछारको बायाँतिर सार्नुहोस्"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"पुछारको दायाँतिर सार्नुहोस्"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"किनारामा सार्नुहोस् र नदेखिने पार्नु…"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"किनाराबाट सार्नुहोस् र देखिने पार्नु…"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"हटाउनुहोस्"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"टगल गर्नुहोस्"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"डिभाइस नियन्त्रण गर्ने विजेटहरू"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"कन्ट्रोल थप्नु पर्ने एप छान्नुहोस्"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"मोबाइल डेटा"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"इन्टरनेटमा कनेक्ट गरिएको छ"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"केही समयका लागि मोबाइल डेटामा कनेक्ट गरिएको छ"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"इन्टरनेट राम्री चलेको छैन"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"मोबाइल डेटा स्वतः कनेक्ट हुँदैन"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"इन्टरनेट छैन"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"अन्य नेटवर्क उपलब्ध छैनन्"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 4de0331..a36f811 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Gezicht niet herkend"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Vingerafdruk gebruiken"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth-verbinding ingesteld."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Batterijpercentage onbekend."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Verbonden met <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Helderheid"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Kleurinversie"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Kleurcorrectie"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Gebruikers beheren"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Klaar"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Sluiten"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Verbonden"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Microfoon beschikbaar"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Camera beschikbaar"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Microfoon en camera beschikbaar"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Microfoon staat aan"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Microfoon staat uit"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Microfoon staat aan voor alle apps en services."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Microfoontoegang staat uit voor alle apps en services. Je kunt microfoontoegang aanzetten via Instellingen > Privacy > Microfoon."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Microfoontoegang staat uit voor alle apps en services. Je kunt dit wijzigen via Instellingen > Privacy > Microfoon."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Camera staat aan"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Camera staat uit"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Camera staat aan voor alle apps en services."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Cameratoegang staat uit voor alle apps en services."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Als je de microfoonknop wilt gebruiken, zet je microfoontoegang aan via Instellingen."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Instellingen openen."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Ander apparaat"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Overzicht aan- of uitzetten"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Je wordt niet gestoord door geluiden en trillingen, behalve bij wekkers, herinneringen, afspraken en specifieke bellers die je selecteert. Je kunt nog steeds alles horen wat je wilt afspelen, waaronder muziek, video\'s en games."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Als je deelt, opneemt of cast, heeft <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> toegang tot alles dat wordt getoond of afgespeeld in die app. Wees daarom voorzichtig met wachtwoorden, betalingsgegevens, berichten en andere gevoelige informatie."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Doorgaan"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"App delen of opnemen"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Alles wissen"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Beheren"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Geschiedenis"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Mobiele data uitzetten?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Je hebt dan geen toegang meer tot data of internet via <xliff:g id="CARRIER">%s</xliff:g>. Internet is alleen nog beschikbaar via wifi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"je provider"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Terugschakelen naar <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Mobiele data worden niet automatisch overgezet op basis van beschikbaarheid"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Nee, bedankt"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Ja, overschakelen"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Aangezien een app een rechtenverzoek afdekt, kan Instellingen je reactie niet verifiëren."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> toestaan om segmenten van <xliff:g id="APP_2">%2$s</xliff:g> te tonen?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Deze kan informatie lezen van <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Instellingen voor vergrotingsvenster"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tik voor toegankelijkheidsfuncties. Wijzig of vervang deze knop via Instellingen.\n\n"<annotation id="link">"Naar Instellingen"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Knop naar de rand verplaatsen om deze tijdelijk te verbergen"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Ongedaan maken"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{Snelkoppeling voor {label} verwijderd}other{# snelkoppelingen verwijderd}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Naar linksboven verplaatsen"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Naar rechtsboven verplaatsen"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Naar linksonder verplaatsen"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Naar rechtsonder verplaatsen"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Naar rand verplaatsen en verbergen"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Over rand verplaatsen en tonen"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Verwijderen"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"schakelen"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Apparaatbediening"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Kies de app waaraan je bedieningselementen wilt toevoegen"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiele data"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Verbonden"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Tijdelijk verbonden"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Matige verbinding"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobiele data maakt niet automatisch verbinding"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Geen verbinding"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Geen andere netwerken beschikbaar"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 16941b8..fe57b75 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"ଫେସ ଚିହ୍ନଟ ହୋଇପାରିବ ନାହିଁ"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"ଟିପଚିହ୍ନ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"ବ୍ଲୁଟୂଥ୍ ସଂଯୋଗ କରାଯାଇଛି।"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ବ୍ୟାଟେରୀ ଶତକଡ଼ା ଅଜଣା ଅଟେ।"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> ସହ ସଂଯୁକ୍ତ"</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ଉଜ୍ଜ୍ୱଳତା"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"ରଙ୍ଗ ଇନଭାର୍ସନ"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ରଙ୍ଗ ସଂଶୋଧନ"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"ୟୁଜରମାନଙ୍କୁ ପରିଚାଳନା କରନ୍ତୁ"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"ହୋଇଗଲା"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"ସଂଯୁକ୍ତ"</string>
@@ -304,6 +305,28 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"ମାଇକ୍ରୋଫୋନ ଉପଲବ୍ଧ"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"କ୍ୟାମେରା ଉପଲବ୍ଧ"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"ମାଇକ୍ରୋଫୋନ ଏବଂ କ୍ୟାମେରା ଉପଲବ୍ଧ"</string>
+ <!-- no translation found for sensor_privacy_mic_turned_on_dialog_title (6348853159838376513) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_turned_off_dialog_title (5760464281790732849) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_unblocked_dialog_content (4889961886199270224) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_blocked_no_exception_dialog_content (5864898470772965394) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_blocked_with_exception_dialog_content (810289713700437896) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_turned_on_dialog_title (8039095295100075952) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_turned_off_dialog_title (1936603903120742696) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_unblocked_dialog_content (7847190103011782278) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_blocked_dialog_content (3182428709314874616) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_htt_blocked_dialog_content (3333321592997666441) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_dialog_open_settings (1503088305279285048) -->
+ <skip />
<string name="media_seamless_other_device" msgid="4654849800789196737">"ଅନ୍ୟ ଡିଭାଇସ୍"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ସଂକ୍ଷିପ୍ତ ବିବରଣୀକୁ ଟୋଗଲ୍ କରନ୍ତୁ"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"ଆଲାର୍ମ, ରିମାଇଣ୍ଡର୍, ଇଭେଣ୍ଟ ଏବଂ ଆପଣ ନିର୍ଦ୍ଦିଷ୍ଟ କରିଥିବା କଲର୍ଙ୍କ ବ୍ୟତୀତ ଆପଣଙ୍କ ଧ୍ୟାନ ଅନ୍ୟ କୌଣସି ଧ୍ୱନୀ ଏବଂ ଭାଇବ୍ରେଶନ୍ରେ ଆକର୍ଷଣ କରାଯିବନାହିଁ। ମ୍ୟୁଜିକ୍, ଭିଡିଓ ଏବଂ ଗେମ୍ ସମେତ ନିଜେ ଚଲାଇବାକୁ ବାଛିଥିବା ଅନ୍ୟ ସବୁକିଛି ଆପଣ ଶୁଣିପାରିବେ।"</string>
@@ -374,6 +397,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"ଆପଣ ସେୟାର, ରେକର୍ଡ କିମ୍ବା କାଷ୍ଟ କରିବା ସମୟରେ, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ଆପରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ପ୍ଲେ ହେଉଥିବା ସବୁକିଛିକୁ ସେହି ଆପର ଆକ୍ସେସ ଅଛି। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ କିମ୍ବା ଅନ୍ୟ ସମ୍ବେଦନଶୀଳ ସୂଚନା ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"ଜାରି ରଖନ୍ତୁ"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"ଏକ ଆପକୁ ସେୟାର କିମ୍ବା ରେକର୍ଡ କରନ୍ତୁ"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"ସମସ୍ତ ଖାଲି କରନ୍ତୁ"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"ପରିଚାଳନା କରନ୍ତୁ"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ଇତିହାସ"</string>
@@ -728,14 +761,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"ମୋବାଇଲ୍ ଡାଟା ବନ୍ଦ କରିବେ?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"ଡାଟା କିମ୍ବା ଇଣ୍ଟରନେଟ୍କୁ <xliff:g id="CARRIER">%s</xliff:g> ଦ୍ଵାରା ଆପଣଙ୍କର ଆକ୍ସେସ୍ ରହିବ ନାହିଁ। ଇଣ୍ଟରନେଟ୍ କେବଳ ୱାଇ-ଫାଇ ମାଧ୍ୟମରେ ଉପଲବ୍ଧ ହେବ।"</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"ଆପଣଙ୍କ କେରିଅର୍"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"<xliff:g id="CARRIER">%s</xliff:g>କୁ ପୁଣି ସ୍ୱିଚ କରିବେ?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"ଉପଲବ୍ଧତା ଆଧାରରେ ମୋବାଇଲ ଡାଟା ସ୍ୱଚାଳିତ ଭାବେ ସ୍ୱିଚ ହେବ ନାହିଁ"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"ନା, ଧନ୍ୟବାଦ"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"ହଁ, ସ୍ୱିଚ କରନ୍ତୁ"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"ଗୋଟିଏ ଆପ୍ ଏକ ଅନୁମତି ଅନୁରୋଧକୁ ଦେଖିବାରେ ବାଧା ଦେଉଥିବାରୁ, ସେଟିଙ୍ଗ ଆପଣଙ୍କ ଉତ୍ତରକୁ ଯାଞ୍ଚ କରିପାରିବ ନାହିଁ।"</string>
<string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_2">%2$s</xliff:g> ସ୍ଲାଇସ୍କୁ ଦେଖାଇବା ପାଇଁ <xliff:g id="APP_0">%1$s</xliff:g>କୁ ଅନୁମତି ଦେବେ?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- ଏହା <xliff:g id="APP">%1$s</xliff:g>ରୁ ସୂଚନାକୁ ପଢ଼ିପାରିବ"</string>
@@ -794,18 +823,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"ମ୍ୟାଗ୍ନିଫାୟର ୱିଣ୍ଡୋର ସେଟିଂସ"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ଆକ୍ସେସିବିଲିଟୀ ଫିଚର ଖୋଲିବାକୁ ଟାପ କରନ୍ତୁ। ସେଟିଂସରେ ଏହି ବଟନକୁ କଷ୍ଟମାଇଜ କର କିମ୍ବା ବଦଳାଅ।\n\n"<annotation id="link">"ସେଟିଂସ ଦେଖନ୍ତୁ"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ବଟନକୁ ଅସ୍ଥାୟୀ ଭାବେ ଲୁଚାଇବା ପାଇଁ ଏହାକୁ ଗୋଟିଏ ଧାରକୁ ମୁଭ୍ କରନ୍ତୁ"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"ପୂର୍ବବତ୍ କରନ୍ତୁ"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label}ଟି ସର୍ଟକଟକୁ କାଢ଼ି ଦିଆଯାଇଛି}other{#ଟି ସର୍ଟକଟକୁ କାଢ଼ି ଦିଆଯାଇଛି}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ଶୀର୍ଷ ବାମକୁ ମୁଭ୍ କରନ୍ତୁ"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ଶୀର୍ଷ ଡାହାଣକୁ ମୁଭ୍ କରନ୍ତୁ"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"ନିମ୍ନ ବାମକୁ ମୁଭ୍ କରନ୍ତୁ"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"ନିମ୍ନ ଡାହାଣକୁ ମୁଭ୍ କରନ୍ତୁ"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"ଧାରକୁ ମୁଭ୍ କରି ଲୁଚାନ୍ତୁ"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"ଧାର ବାହାରକୁ ମୁଭ୍ କରି ଦେଖାନ୍ତୁ"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"କାଢ଼ି ଦିଅନ୍ତୁ"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ଟୋଗଲ୍ କରନ୍ତୁ"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ଡିଭାଇସ୍ ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକୁ ଯୋଗ କରିବାକୁ ଆପ୍ ବାଛନ୍ତୁ"</string>
@@ -948,10 +974,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"ମୋବାଇଲ ଡାଟା"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"ସଂଯୋଗ କରାଯାଇଛି"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"ଅସ୍ଥାୟୀ ରୂପେ କନେକ୍ଟ କରାଯାଇଛି"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"ଦୁର୍ବଳ କନେକ୍ସନ"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"ମୋବାଇଲ ଡାଟା ସ୍ୱତଃ-ସଂଯୋଗ ହେବ ନାହିଁ"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"ସଂଯୋଗ ନାହିଁ"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"ଅନ୍ୟ କୌଣସି ନେଟୱାର୍କ ଉପଲବ୍ଧ ନାହିଁ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index fce69ec..c119e8c 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"ਚਿਹਰੇ ਦੀ ਪਛਾਣ ਨਹੀਂ ਹੋਈ"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"ਇਸਦੀ ਬਜਾਏ ਫਿੰਗਰਪ੍ਰਿੰਟ ਵਰਤੋ"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth ਕਨੈਕਟ ਕੀਤੀ।"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ਬੈਟਰੀ ਪ੍ਰਤੀਸ਼ਤ ਅਗਿਆਤ ਹੈ।"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ।"</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ਚਮਕ"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"ਰੰਗ ਪਲਟਨਾ"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ਰੰਗ ਸੁਧਾਈ"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"ਵਰਤੋਂਕਾਰਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"ਹੋ ਗਿਆ"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"ਬੰਦ ਕਰੋ"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"ਕਨੈਕਟ ਕੀਤਾ"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਉਪਲਬਧ ਹੈ"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"ਕੈਮਰਾ ਉਪਲਬਧ ਹੈ"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਅਤੇ ਕੈਮਰਾ ਉਪਲਬਧ ਹੈ"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਚਾਲੂ ਕੀਤਾ ਗਿਆ"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਬੰਦ ਕੀਤਾ ਗਿਆ"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਸੇਵਾਵਾਂ ਲਈ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਚਾਲੂ ਹੈ।"</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਸੇਵਾਵਾਂ ਲਈ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਪਹੁੰਚ ਬੰਦ ਹੈ। ਤੁਸੀਂ ਸੈਟਿੰਗਾਂ > ਪਰਦੇਦਾਰੀ > ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਵਿੱਚ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਪਹੁੰਚ ਨੂੰ ਚਾਲੂ ਕਰ ਸਕਦੇ ਹੋ।"</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਸੇਵਾਵਾਂ ਲਈ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਪਹੁੰਚ ਬੰਦ ਹੈ। ਤੁਸੀਂ ਸੈਟਿੰਗਾਂ > ਪਰਦੇਦਾਰੀ > ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਵਿੱਚ ਇਸਨੂੰ ਬਦਲ ਸਕਦੇ ਹੋ।"</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"ਕੈਮਰਾ ਚਾਲੂ ਕੀਤਾ ਗਿਆ"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"ਕੈਮਰਾ ਬੰਦ ਕੀਤਾ ਗਿਆ"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਸੇਵਾਵਾਂ ਲਈ ਕੈਮਰਾ ਚਾਲੂ ਹੈ।"</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਸੇਵਾਵਾਂ ਲਈ ਕੈਮਰਾ ਪਹੁੰਚ ਬੰਦ ਹੈ।"</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਬਟਨ ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ, ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਪਹੁੰਚ ਚਾਲੂ ਕਰੋ।"</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ।"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"ਹੋਰ ਡੀਵਾਈਸ"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ਰੂਪ-ਰੇਖਾ ਨੂੰ ਟੌਗਲ ਕਰੋ"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"ਧੁਨੀਆਂ ਅਤੇ ਥਰਥਰਾਹਟਾਂ ਤੁਹਾਨੂੰ ਪਰੇਸ਼ਾਨ ਨਹੀਂ ਕਰਨਗੀਆਂ, ਸਿਵਾਏ ਅਲਾਰਮਾਂ, ਯਾਦ-ਦਹਾਨੀਆਂ, ਵਰਤਾਰਿਆਂ, ਅਤੇ ਤੁਹਾਡੇ ਵੱਲੋਂ ਨਿਰਧਾਰਤ ਕੀਤੇ ਕਾਲਰਾਂ ਦੀ ਸੂਰਤ ਵਿੱਚ। ਤੁਸੀਂ ਅਜੇ ਵੀ ਸੰਗੀਤ, ਵੀਡੀਓ ਅਤੇ ਗੇਮਾਂ ਸਮੇਤ ਆਪਣੀ ਚੋਣ ਅਨੁਸਾਰ ਕੁਝ ਵੀ ਸੁਣ ਸਕਦੇ ਹੋ।"</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਸਾਂਝਾ ਕਰਨ, ਰਿਕਾਰਡ ਕਰਨ, ਜਾਂ ਕਾਸਟ ਕਰਨ \'ਤੇ, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ਕੋਲ ਉਸ ਐਪ \'ਤੇ ਦਿਖਾਈ ਗਈ ਜਾਂ ਚਲਾਈ ਗਈ ਹਰੇਕ ਚੀਜ਼ ਤੱਕ ਪਹੁੰਚ ਹੁੰਦੀ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ ਜਾਂ ਹੋਰ ਸੰਵੇਦਨਸ਼ੀਲ ਜਾਣਕਾਰੀ ਸੰਬੰਧੀ ਸਾਵਧਾਨ ਰਹੋ।"</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"ਜਾਰੀ ਰੱਖੋ"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"ਐਪ ਨੂੰ ਸਾਂਝਾ ਕਰੋ ਜਾਂ ਰਿਕਾਰਡ ਕਰੋ"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"ਸਭ ਕਲੀਅਰ ਕਰੋ"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ਇਤਿਹਾਸ"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"ਕੀ ਮੋਬਾਈਲ ਡਾਟਾ ਬੰਦ ਕਰਨਾ ਹੈ?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"ਤੁਸੀਂ <xliff:g id="CARRIER">%s</xliff:g> ਰਾਹੀਂ ਡਾਟੇ ਜਾਂ ਇੰਟਰਨੈੱਟ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਕਰ ਸਕੋਗੇ। ਇੰਟਰਨੈੱਟ ਸਿਰਫ਼ ਵਾਈ-ਫਾਈ ਰਾਹੀਂ ਉਪਲਬਧ ਹੋਵੇਗਾ।"</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"ਤੁਹਾਡਾ ਕੈਰੀਅਰ"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"ਕੀ ਵਾਪਸ <xliff:g id="CARRIER">%s</xliff:g> \'ਤੇ ਸਵਿੱਚ ਕਰਨਾ ਹੈ?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"ਮੋਬਾਈਲ ਡਾਟਾ ਉਪਲਬਧਤਾ ਦੇ ਆਧਾਰ \'ਤੇ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਸਵਿੱਚ ਨਹੀਂ ਹੋਵੇਗਾ"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"ਨਹੀਂ ਧੰਨਵਾਦ"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"ਹਾਂ, ਸਵਿੱਚ ਕਰੋ"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"ਕਿਸੇ ਐਪ ਵੱਲੋਂ ਇਜਾਜ਼ਤ ਬੇਨਤੀ ਨੂੰ ਢਕੇ ਜਾਣ ਕਾਰਨ ਸੈਟਿੰਗਾਂ ਤੁਹਾਡੇ ਜਵਾਬ ਦੀ ਪੁਸ਼ਟੀ ਨਹੀਂ ਕਰ ਸਕਦੀਆਂ।"</string>
<string name="slice_permission_title" msgid="3262615140094151017">"ਕੀ <xliff:g id="APP_0">%1$s</xliff:g> ਨੂੰ <xliff:g id="APP_2">%2$s</xliff:g> ਦੇ ਹਿੱਸੇ ਦਿਖਾਉਣ ਦੇਣੇ ਹਨ?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- ਇਹ <xliff:g id="APP">%1$s</xliff:g> ਵਿੱਚੋਂ ਜਾਣਕਾਰੀ ਪੜ੍ਹ ਸਕਦਾ ਹੈ"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"ਵੱਡਦਰਸ਼ੀ ਵਿੰਡੋ ਸੈਟਿੰਗਾਂ"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ਪਹੁੰਚਯੋਗਤਾ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਖੋਲ੍ਹਣ ਲਈ ਟੈਪ ਕਰੋ। ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਇਹ ਬਟਨ ਵਿਉਂਤਬੱਧ ਕਰੋ ਜਾਂ ਬਦਲੋ।\n\n"<annotation id="link">"ਸੈਟਿੰਗਾਂ ਦੇਖੋ"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ਬਟਨ ਨੂੰ ਅਸਥਾਈ ਤੌਰ \'ਤੇ ਲੁਕਾਉਣ ਲਈ ਕਿਨਾਰੇ \'ਤੇ ਲਿਜਾਓ"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"ਅਣਕੀਤਾ ਕਰੋ"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਹਟਾਇਆ ਗਿਆ}one{# ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਹਟਾਇਆ ਗਿਆ}other{# ਸ਼ਾਰਟਕੱਟਾਂ ਨੂੰ ਹਟਾਇਆ ਗਿਆ}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ਉੱਪਰ ਵੱਲ ਖੱਬੇ ਲਿਜਾਓ"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ਉੱਪਰ ਵੱਲ ਸੱਜੇ ਲਿਜਾਓ"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"ਹੇਠਾਂ ਵੱਲ ਖੱਬੇ ਲਿਜਾਓ"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"ਹੇਠਾਂ ਵੱਲ ਸੱਜੇ ਲਿਜਾਓ"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"ਕਿਨਾਰੇ ਵਿੱਚ ਲਿਜਾ ਕੇ ਲੁਕਾਓ"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"ਕਿਨਾਰੇ ਤੋਂ ਬਾਹਰ ਕੱਢ ਕੇ ਦਿਖਾਓ"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"ਹਟਾਓ"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ਟੌਗਲ ਕਰੋ"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ਡੀਵਾਈਸ ਕੰਟਰੋਲ"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"ਕੰਟਰੋਲ ਸ਼ਾਮਲ ਕਰਨ ਲਈ ਐਪ ਚੁਣੋ"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"ਮੋਬਾਈਲ ਡਾਟਾ"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"ਕਨੈਕਟ ਹੈ"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"ਕੁਝ ਸਮੇਂ ਲਈ ਕਨੈਕਟ ਹੈ"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"ਖਰਾਬ ਕਨੈਕਸ਼ਨ"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"ਮੋਬਾਈਲ ਡਾਟਾ ਸਵੈ-ਕਨੈਕਟ ਨਹੀਂ ਹੋਵੇਗਾ"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"ਕੋਈ ਕਨੈਕਸ਼ਨ ਨਹੀਂ"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"ਕੋਈ ਹੋਰ ਨੈੱਟਵਰਕ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 8787f36..cb496fe 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Nie można rozpoznać twarzy"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Użyj odcisku palca"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth połączony."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Poziom naładowania baterii jest nieznany."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Połączono z <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Jasność"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Odwrócenie kolorów"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Korekcja kolorów"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Zarządzaj użytkownikami"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Gotowe"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Zamknij"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Połączono"</string>
@@ -304,6 +305,28 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Mikrofon jest dostępny"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Aparat jest dostępny"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Mikrofon i aparat są dostępne"</string>
+ <!-- no translation found for sensor_privacy_mic_turned_on_dialog_title (6348853159838376513) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_turned_off_dialog_title (5760464281790732849) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_unblocked_dialog_content (4889961886199270224) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_blocked_no_exception_dialog_content (5864898470772965394) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_blocked_with_exception_dialog_content (810289713700437896) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_turned_on_dialog_title (8039095295100075952) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_turned_off_dialog_title (1936603903120742696) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_unblocked_dialog_content (7847190103011782278) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_blocked_dialog_content (3182428709314874616) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_htt_blocked_dialog_content (3333321592997666441) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_dialog_open_settings (1503088305279285048) -->
+ <skip />
<string name="media_seamless_other_device" msgid="4654849800789196737">"Inne urządzenie"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Przełącz Przegląd"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Nie będą Cię niepokoić żadne dźwięki ani wibracje z wyjątkiem alarmów, przypomnień, wydarzeń i połączeń od wybranych osób. Będziesz słyszeć wszystkie odtwarzane treści, takie jak muzyka, filmy czy gry."</string>
@@ -374,6 +397,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Podczas udostępniania, nagrywania lub przesyłania treści aplikacja <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ma dostęp do wszystkiego, co jest w niej wyświetlane lub odtwarzane. Zachowaj ostrożność w przypadku haseł, danych do płatności, wiadomości i innych informacji poufnych."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Dalej"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Udostępnianie i nagrywanie za pomocą aplikacji"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Usuń wszystkie"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Zarządzaj"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historia"</string>
@@ -728,14 +761,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Wyłączyć mobilną transmisję danych?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Nie będziesz mieć dostępu do transmisji danych ani internetu w <xliff:g id="CARRIER">%s</xliff:g>. Internet będzie dostępny tylko przez Wi‑Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"Twój operator"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Wrócić do operatora <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Mobilna transmisja danych nie będzie automatycznie przełączana na podstawie dostępności"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Nie, dziękuję"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Tak, wróć"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Aplikacja Ustawienia nie może zweryfikować Twojej odpowiedzi, ponieważ inna aplikacja zasłania prośbę o udzielenie uprawnień."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Zezwolić aplikacji <xliff:g id="APP_0">%1$s</xliff:g> na pokazywanie wycinków z aplikacji <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Może odczytywać informacje z aplikacji <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +823,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Ustawienia okna powiększania"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Kliknij, aby otworzyć ułatwienia dostępu. Dostosuj lub zmień ten przycisk w Ustawieniach.\n\n"<annotation id="link">"Wyświetl ustawienia"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Przesuń przycisk do krawędzi, aby ukryć go tymczasowo"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Cofnij"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} skrót został usunięty}few{# skróty zostały usunięte}many{# skrótów zostało usuniętych}other{# skrótu został usunięte}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Przenieś w lewy górny róg"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Przenieś w prawy górny róg"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Przenieś w lewy dolny róg"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Przenieś w prawy dolny róg"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Przenieś do krawędzi i ukryj"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Przenieś poza krawędź i pokaż"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Usuń"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"przełącz"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Sterowanie urządzeniami"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Wybierz aplikację, do której chcesz dodać elementy sterujące"</string>
@@ -948,10 +974,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilna transmisja danych"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Połączono"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Tymczasowe połączenie"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Słabe połączenie"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobilna transmisja danych nie połączy się automatycznie"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Brak połączenia"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Brak innych dostępnych sieci"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index e6f0580..cbc8fc7a 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Rosto não reconhecido"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Use a impressão digital"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth conectado."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Porcentagem da bateria desconhecida."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectado a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brilho"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversão de cores"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correção de cor"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Gerenciar usuários"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Concluído"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Fechar"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Conectado"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Microfone disponível"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Câmera disponível"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Microfone e câmera disponíveis"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Microfone ativado"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Microfone desativado"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"O microfone está ativado para todos os apps e serviços."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"O acesso ao microfone está desativado para todos os apps e serviços. Ative em \"Configurações > Privacidade > Microfone\"."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"O acesso ao microfone está desativado para todos os apps e serviços. Você pode mudar isso em \"Configurações > Privacidade > Microfone\"."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Câmera ativada"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Câmera desativada"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"A câmera está ativada para todos os apps e serviços."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"O acesso à câmera está desativado para todos os apps e serviços."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Ative o acesso ao microfone nas Configurações para usar o botão dele."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Abrir configurações."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Outro dispositivo"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Alternar Visão geral"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Você não será perturbado por sons e vibrações, exceto alarmes, lembretes, eventos e chamadas de pessoas especificadas. No entanto, você ouvirá tudo o que decidir reproduzir, como músicas, vídeos e jogos."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Quando você compartilha, grava ou transmite um app, o <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tem acesso a todas as informações visíveis na tela ou reproduzidas no dispositivo. Tenha cuidado com senhas, detalhes de pagamento, mensagens ou outras informações sensíveis."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Continuar"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Compartilhar ou gravar um app"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Limpar tudo"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Gerenciar"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Histórico"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Desativar os dados móveis?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Você não terá acesso a dados ou à Internet pela operadora <xliff:g id="CARRIER">%s</xliff:g>. A Internet só estará disponível via Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"sua operadora"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Voltar para a operadora <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"A conexão de dados móveis não vai ser alternada automaticamente de acordo com a disponibilidade"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Agora não"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Sim, voltar"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Como um app está ocultando uma solicitação de permissão, as configurações não podem verificar sua resposta."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Permitir que <xliff:g id="APP_0">%1$s</xliff:g> mostre partes do app <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Pode ler informações do app <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Configurações da janela de lupa"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Toque para abrir os recursos de acessibilidade. Personalize ou substitua o botão nas Configurações.\n\n"<annotation id="link">"Ver configurações"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mova o botão para a borda para ocultá-lo temporariamente"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Desfazer"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} atalho removido}one{# atalho removido}many{# de atalhos removidos}other{# atalhos removidos}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mover para o canto superior esquerdo"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mover para o canto superior direito"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Mover para o canto inferior esquerdo"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Mover para o canto inferior direito"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Mover para a borda e ocultar"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Mover para fora da borda e exibir"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Remover"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"alternar"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Controles do dispositivo"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Escolha um app para adicionar controles"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Dados móveis"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Conectado"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Temporariamente conectado"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Conexão fraca"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Sem conexão automática com dados móveis"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Sem conexão"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nenhuma outra rede disponível"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 6c07ce0..d37ac01 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -143,7 +143,7 @@
<string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Rosto reconhecido. Prima para continuar."</string>
<string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Rosto reconhecido. Prima ícone de desbloqueio para continuar"</string>
<string name="biometric_dialog_authenticated" msgid="7337147327545272484">"Autenticado"</string>
- <string name="biometric_dialog_use_pin" msgid="8385294115283000709">"Utilizar PIN"</string>
+ <string name="biometric_dialog_use_pin" msgid="8385294115283000709">"Usar PIN"</string>
<string name="biometric_dialog_use_pattern" msgid="2315593393167211194">"Utilizar padrão"</string>
<string name="biometric_dialog_use_password" msgid="3445033859393474779">"Utilizar palavra-passe"</string>
<string name="biometric_dialog_wrong_pin" msgid="1878539073972762803">"PIN incorreto."</string>
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Imposs. reconhecer rosto"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Usar impressão digital"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth ligado."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Percentagem da bateria desconhecida."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Ligado a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -303,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Microfone disponível"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Câmara disponível"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Microfone e câmara disponíveis"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Microfone ativado"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Microfone desativado"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"O microfone está ativado para todas as apps e serviços."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"O acesso ao microfone está desativado para todas as apps e serviços. Pode ativar o acesso ao microfone em Definições > Privacidade > Microfone."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"O acesso ao microfone está desativado para todas as apps e serviços. Pode alterar esta opção em Definições > Privacidade > Microfone."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Câmara ativada"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Câmara desativada"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"A câmara está ativada para todas as apps e serviços."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"O acesso à câmara está desativado para todas as apps e serviços."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Para usar o botão do microfone, ative o acesso do microfone nas Definições."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Abrir definições."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Outro dispositivo"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Ativar/desativar Vista geral"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Não é incomodado por sons e vibrações, exceto de alarmes, lembretes, eventos e autores de chamadas que especificar. Continua a ouvir tudo o que optar por reproduzir, incluindo música, vídeos e jogos."</string>
@@ -373,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Quando está a partilhar, gravar ou transmitir uma app, a app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tem acesso a tudo o que é apresentado ou reproduzido nessa app. Por isso, tenha cuidado com palavras-passe, detalhes de pagamento, mensagens ou outras informações confidenciais."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Continuar"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Partilhe ou grave uma app"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Limpar tudo"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Gerir"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Histórico"</string>
@@ -727,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Desativar os dados móveis?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Não terá acesso a dados ou à Internet através do operador <xliff:g id="CARRIER">%s</xliff:g>. A Internet estará disponível apenas por Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"o seu operador"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Mudar de novo para <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Os dados móveis não vão mudar automaticamente com base na disponibilidade"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Não"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Sim, mudar"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Uma vez que uma app está a ocultar um pedido de autorização, as Definições não conseguem validar a sua resposta."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Permitir que a app <xliff:g id="APP_0">%1$s</xliff:g> mostre partes da app <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Pode ler informações da app <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -793,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Definições da janela da lupa"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Toque para abrir funcionalidades de acessibilidade. Personal. ou substitua botão em Defin.\n\n"<annotation id="link">"Ver defin."</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mova o botão para a extremidade para o ocultar temporariamente"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Anular"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} atalho removido}many{# atalhos removidos}other{# atalhos removidos}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mover p/ parte sup. esquerda"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mover parte superior direita"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Mover p/ parte infer. esquerda"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Mover parte inferior direita"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Mover p/ extremidade e ocultar"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Retirar extremidade e mostrar"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Remover"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ativar/desativar"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Controlos de dispositivos"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Escolha uma app para adicionar controlos"</string>
@@ -947,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Dados móveis"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Ligado"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Ligado temporariamente"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Ligação fraca"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Sem ligação automática com dados móveis"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Sem ligação"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nenhuma outra rede disponível"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index e6f0580..cbc8fc7a 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Rosto não reconhecido"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Use a impressão digital"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth conectado."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Porcentagem da bateria desconhecida."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectado a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brilho"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversão de cores"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correção de cor"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Gerenciar usuários"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Concluído"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Fechar"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Conectado"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Microfone disponível"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Câmera disponível"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Microfone e câmera disponíveis"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Microfone ativado"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Microfone desativado"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"O microfone está ativado para todos os apps e serviços."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"O acesso ao microfone está desativado para todos os apps e serviços. Ative em \"Configurações > Privacidade > Microfone\"."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"O acesso ao microfone está desativado para todos os apps e serviços. Você pode mudar isso em \"Configurações > Privacidade > Microfone\"."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Câmera ativada"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Câmera desativada"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"A câmera está ativada para todos os apps e serviços."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"O acesso à câmera está desativado para todos os apps e serviços."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Ative o acesso ao microfone nas Configurações para usar o botão dele."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Abrir configurações."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Outro dispositivo"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Alternar Visão geral"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Você não será perturbado por sons e vibrações, exceto alarmes, lembretes, eventos e chamadas de pessoas especificadas. No entanto, você ouvirá tudo o que decidir reproduzir, como músicas, vídeos e jogos."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Quando você compartilha, grava ou transmite um app, o <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tem acesso a todas as informações visíveis na tela ou reproduzidas no dispositivo. Tenha cuidado com senhas, detalhes de pagamento, mensagens ou outras informações sensíveis."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Continuar"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Compartilhar ou gravar um app"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Limpar tudo"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Gerenciar"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Histórico"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Desativar os dados móveis?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Você não terá acesso a dados ou à Internet pela operadora <xliff:g id="CARRIER">%s</xliff:g>. A Internet só estará disponível via Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"sua operadora"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Voltar para a operadora <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"A conexão de dados móveis não vai ser alternada automaticamente de acordo com a disponibilidade"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Agora não"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Sim, voltar"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Como um app está ocultando uma solicitação de permissão, as configurações não podem verificar sua resposta."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Permitir que <xliff:g id="APP_0">%1$s</xliff:g> mostre partes do app <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Pode ler informações do app <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Configurações da janela de lupa"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Toque para abrir os recursos de acessibilidade. Personalize ou substitua o botão nas Configurações.\n\n"<annotation id="link">"Ver configurações"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mova o botão para a borda para ocultá-lo temporariamente"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Desfazer"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} atalho removido}one{# atalho removido}many{# de atalhos removidos}other{# atalhos removidos}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mover para o canto superior esquerdo"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mover para o canto superior direito"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Mover para o canto inferior esquerdo"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Mover para o canto inferior direito"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Mover para a borda e ocultar"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Mover para fora da borda e exibir"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Remover"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"alternar"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Controles do dispositivo"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Escolha um app para adicionar controles"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Dados móveis"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Conectado"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Temporariamente conectado"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Conexão fraca"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Sem conexão automática com dados móveis"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Sem conexão"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nenhuma outra rede disponível"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 080edfd..5667884 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Chip nerecunoscut"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Folosește amprenta"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Conectat prin Bluetooth."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Procentajul bateriei este necunoscut."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectat la <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Luminozitate"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversarea culorilor"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Corecția culorii"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Gestionează utilizatorii"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Terminat"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Închide"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Conectat"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Microfon disponibil"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Cameră foto disponibilă"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Microfon și cameră disponibile"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Microfonul a fost activat"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Microfonul a fost dezactivat"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Microfonul este activat pentru toate aplicațiile și serviciile."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Accesul la microfon este dezactivat pentru toate aplicațiile și serviciile. Activează accesul la microfon în Setări > Confidențialitate > Microfon."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Accesul la microfon este dezactivat pentru toate aplicațiile și serviciile. Modifică opțiunea în Setări > Confidențialitate > Microfon."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Camera este activată"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Camera este dezactivată"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Camera este activată pentru toate aplicațiile și serviciile."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Accesul la cameră este dezactivat pentru toate aplicațiile și serviciile."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Pentru a folosi butonul microfonului, activează accesul la microfon în Setări."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Deschide setările."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Alt dispozitiv"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Comută secțiunea Recente"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Se vor anunța prin sunete și vibrații numai alarmele, mementourile, evenimentele și apelanții specificați de tine. Totuși, vei auzi tot ce alegi să redai, inclusiv muzică, videoclipuri și jocuri."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Când permiți accesul, înregistrezi sau proiectezi o aplicație, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> are acces la orice se afișează pe ecran sau se redă în aplicație. Ai grijă cu parolele, detaliile de plată, mesajele sau alte informații sensibile."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Continuă"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Permite accesul la o aplicație sau înregistreaz-o"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Șterge toate notificările"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Gestionează"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Istoric"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Dezactivezi datele mobile?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Nu vei avea acces la date sau la internet prin intermediul <xliff:g id="CARRIER">%s</xliff:g>. Internetul va fi disponibil numai prin Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"operatorul tău"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Revii la <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Comutarea la datele mobile nu se va face automat în funcție de disponibilitate"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Nu, mulțumesc"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Da, fac trecerea"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Deoarece o aplicație acoperă o solicitare de permisiune, Setările nu îți pot verifica răspunsul."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Permiți ca <xliff:g id="APP_0">%1$s</xliff:g> să afișeze porțiuni din <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Poate citi informații din <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Setările ferestrei de mărire"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Atinge ca să deschizi funcțiile de accesibilitate. Personalizează sau înlocuiește butonul în setări.\n\n"<annotation id="link">"Vezi setările"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mută butonul spre margine pentru a-l ascunde temporar"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Anulează"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} comandă rapidă eliminată}few{# comenzi rapide eliminate}other{# de comenzi rapide eliminate}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mută în stânga sus"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mută în dreapta sus"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Mută în stânga jos"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Mută în dreapta jos"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Mută la margine și ascunde"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Mută de la margine și afișează"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Elimină"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"Activează / dezactivează"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Comenzile dispozitivelor"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Alege aplicația pentru a adăuga comenzi"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Date mobile"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Conectat"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Conectat temporar"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Conexiune slabă"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Nu se conectează automat la date mobile"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Nicio conexiune"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nu sunt disponibile alte rețele"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index b42701e..3b820ec 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Лицо не распознано."</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Используйте отпечаток."</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth-соединение установлено."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Уровень заряда батареи в процентах неизвестен."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>: подключено."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Яркость"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Инверсия цветов"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Коррекция цвета"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Управление пользователями"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Готово"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Закрыть"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Подключено"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Микрофон готов."</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Камера готова."</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Микрофон и камера готовы."</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Микрофон включен"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Микрофон отключен"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Микрофон включен для всех приложений и сервисов."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Доступ к микрофону отключен для всех приложений и сервисов. Вы можете разрешить доступ к микрофону, перейдя в Настройки > Конфиденциальность > Микрофон."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Доступ к микрофону отключен для всех приложений и сервисов. Вы можете изменить этот параметр, перейдя в Настройки > Конфиденциальность > Микрофон."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Камера включена"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Камера отключена"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Камера включена для всех приложений и сервисов."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Доступ к камере отключен для всех приложений и сервисов."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Чтобы использовать кнопку микрофона, разрешите доступ к микрофону в настройках."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Открыть настройки"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Другое устройство"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Переключить режим обзора"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Вас не будут отвлекать звуки и вибрация, за исключением сигналов будильника, напоминаний, уведомлений о мероприятиях и звонков от помеченных контактов. Вы по-прежнему будете слышать включенную вами музыку, видео, игры и т. д."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Когда вы демонстрируете, транслируете экран или записываете видео с него, приложение \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" получает доступ ко всему, что видно и воспроизводится на экране устройства. Помните об этом, если соберетесь вводить или просматривать пароли, платежные данные, сообщения и другую конфиденциальную информацию."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Далее"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Демонстрация экрана или запись видео с него"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Очистить все"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Настроить"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"История"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Отключить мобильный Интернет?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Вы не сможете передавать данные или выходить в Интернет через оператора \"<xliff:g id="CARRIER">%s</xliff:g>\". Интернет будет доступен только по сети Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"ваш оператор"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Переключиться на сеть \"<xliff:g id="CARRIER">%s</xliff:g>\"?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Мобильный интернет не будет переключаться автоматически."</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Нет"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Да"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Невозможно принять ваше согласие, поскольку запрос скрыт другим приложением."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Разрешить приложению \"<xliff:g id="APP_0">%1$s</xliff:g>\" показывать фрагменты приложения \"<xliff:g id="APP_2">%2$s</xliff:g>\"?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"– Ему станут доступны данные из приложения \"<xliff:g id="APP">%1$s</xliff:g>\"."</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Настройка окна лупы"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Нажмите, чтобы открыть спец. возможности. Настройте или замените эту кнопку в настройках.\n\n"<annotation id="link">"Настройки"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Чтобы временно скрыть кнопку, переместите ее к краю экрана"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Отменить"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} сочетание клавиш удалено}one{# сочетание клавиш удалено}few{# сочетания клавиш удалено}many{# сочетаний клавиш удалено}other{# сочетания клавиш удалено}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Перенести в левый верхний угол"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Перенести в правый верхний угол"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Перенести в левый нижний угол"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Перенести в правый нижний угол"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Перенести к краю и скрыть"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Вернуть из-за края и показать"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Убрать"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"включить или отключить"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Управление устройствами"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Чтобы добавить виджеты управления, выберите приложение"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобильный интернет"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Подключено"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Временное подключение"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Слабый сигнал"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Без автоподключения к мобильному интернету"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Нет подключения к интернету"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Нет других доступных сетей"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index bd272c7..5696980 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"මුහුණ හඳුනා ගත නොහැක"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"ඒ වෙනුවට ඇඟිලි සලකුණ භාවිත කරන්න"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"බ්ලූටූත් සම්බන්ධිතයි."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"බැටරි ප්රතිශතය නොදනී."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> වෙත සම්බන්ධ කරන ලදි."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"දීප්තිමත් බව"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"වර්ණ අපවර්තනය"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"වර්ණ නිවැරදි කිරීම"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"පරිශීලකයන් කළමනාකරණය කරන්න"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"නිමයි"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"වසන්න"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"සම්බන්ධිත"</string>
@@ -304,6 +305,28 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"මයික්රෆෝනය ලබා ගත හැකිය"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"කැමරාව ලබා ගත හැකිය"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"මයික්රෆෝනය සහ කැමරාව ලබා ගත හැකිය"</string>
+ <!-- no translation found for sensor_privacy_mic_turned_on_dialog_title (6348853159838376513) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_turned_off_dialog_title (5760464281790732849) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_unblocked_dialog_content (4889961886199270224) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_blocked_no_exception_dialog_content (5864898470772965394) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_blocked_with_exception_dialog_content (810289713700437896) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_turned_on_dialog_title (8039095295100075952) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_turned_off_dialog_title (1936603903120742696) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_unblocked_dialog_content (7847190103011782278) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_blocked_dialog_content (3182428709314874616) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_htt_blocked_dialog_content (3333321592997666441) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_dialog_open_settings (1503088305279285048) -->
+ <skip />
<string name="media_seamless_other_device" msgid="4654849800789196737">"වෙනත් උපාංගය"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"දළ විශ්ලේෂණය ටොගල කරන්න"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"එලාම සිහිකැඳවීම්, සිදුවීම්, සහ ඔබ සඳහන් කළ අමතන්නන් හැර, ශබ්ද සහ කම්පනවලින් ඔබට බාධා නොවනු ඇත. සංගීතය, වීඩියෝ, සහ ක්රීඩා ඇතුළු ඔබ වාදනය කිරීමට තෝරන ලද සියලු දේ ඔබට තවම ඇසෙනු ඇත."</string>
@@ -374,6 +397,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"ඔබ යෙදුමක් බෙදා ගන්නා විට, පටිගත කරන විට හෝ විකාශය කරන විට, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> හට එම යෙදුමේ පෙන්වන හෝ වාදනය කරන ඕනෑම දෙයකට ප්රවේශය ඇත. එබැවින් මුරපද, ගෙවීම් විස්තර, පණිවිඩ හෝ වෙනත් සංවේදී තොරතුරු සමග ප්රවේශම් වන්න."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"ඉදිරියට යන්න"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"යෙදුමක් බෙදා ගන්න හෝ පටිගත කරන්න"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"සියල්ල හිස් කරන්න"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"කළමනාකරණය කරන්න"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ඉතිහාසය"</string>
@@ -728,14 +761,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"ජංගම දත්ත ක්රියාවිරහිත කරන්නද?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"ඔබට <xliff:g id="CARRIER">%s</xliff:g> හරහා දත්ත හෝ අන්තර්ජාලයට පිවිසීමේ හැකියාවක් නැත. අන්තර්ජාලය Wi-Fi හරහා පමණක් ලබා ගත හැකිය."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"ඔබගේ වාහකය"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"<xliff:g id="CARRIER">%s</xliff:g> වෙත ආපසු මාරු කරන්නද?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"ජංගම දත්ත ලබා ගත හැකි වීමට අනුව ස්වයංක්රීයව මාරු නොවෙයි"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"එපා ස්තුතියි"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"ඔව්, මාරු කරන්න"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"යෙදුමක් අවසර ඉල්ලීමක් කරන නිසා, සැකසීම්වලට ඔබගේ ප්රතිචාරය සත්යාපනය කළ නොහැකිය."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> හට කොටස් <xliff:g id="APP_2">%2$s</xliff:g>ක් පෙන්වීමට ඉඩ දෙන්නද?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- එයට <xliff:g id="APP">%1$s</xliff:g> වෙතින් තොරතුරු කියවිය හැකිය"</string>
@@ -794,18 +823,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"විශාලන කවුළු සැකසීම්"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ප්රවේශ්යතා විශේෂාංග විවෘත කිරීමට තට්ටු කරන්න. සැකසීම් තුළ මෙම බොත්තම අභිරුචිකරණය හෝ ප්රතිස්ථාපනය කරන්න.\n\n"<annotation id="link">"සැකසීම් බලන්න"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"එය තාවකාලිකව සැඟවීමට බොත්තම දාරයට ගෙන යන්න"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"අස් කරන්න"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} කෙටිමඟ ඉවත් කළා}one{කෙටිමං # ක් ඉවත් කළා}other{කෙටිමං # ක් ඉවත් කළා}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ඉහළ වමට ගෙන යන්න"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ඉහළ දකුණට ගෙන යන්න"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"පහළ වමට ගෙන යන්න"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"පහළ දකුණට ගෙන යන්න"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"මායිමට ගෙන යන්න සහ සඟවන්න"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"මායිමෙන් පිටට ගන්න සහ පෙන්වන්න"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"ඉවත් කරන්න"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ටොගල් කරන්න"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"උපාංග පාලන"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"පාලන එක් කිරීමට යෙදුම තෝරා ගන්න"</string>
@@ -948,10 +974,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"ජංගම දත්ත"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"සම්බන්ධයි"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"තාවකාලිකව සම්බන්ධ කළා"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"දුර්වල සම්බන්ධතාව"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"ජංගම දත්ත ස්වංක්රියව සම්බන්ධ නොවනු ඇත"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"සම්බන්ධතාවයක් නැත"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"ලබා ගත හැකි වෙනත් ජාල නැත"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index a0dd3c3..6aaafc1 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Tvár sa nedá rozpoznať"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Používať radšej odtlačok"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth pripojené."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Percento batérie nie je známe."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Pripojené k zariadeniu <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Jas"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inverzia farieb"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Úprava farieb"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Spravovať používateľov"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Hotovo"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Zavrieť"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Pripojené"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Mikrofón je k dispozícii"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Kamera je k dispozícii"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Mikrofón a kamera sú k dispozícii"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Mikrofón je zapnutý"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Mikrofón je vypnutý"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Mikrofón je povolený pre všetky aplikácie a služby."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Prístup k mikrofónu bol zakázaný pre všetky aplikácie a služby. Môžete ho povoliť v sekcii Nastavenia > Ochrana súkromia > Mikrofón."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Prístup k mikrofónu bol zakázaný pre všetky aplikácie a služby. Môžete to zmeniť v sekcii Nastavenia > Ochrana súkromia > Mikrofón."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Kamera je zapnutá"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Kamera je vypnutá"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Kamera je povolená pre všetky aplikácie a služby."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Prístup ku kamere je zakázaný pre všetky aplikácie a služby."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Ak chcete použiť tlačidlo mikrofónu, povoľte prístup k mikrofónu v Nastaveniach."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Otvoriť nastavenia"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Iné zariadenie"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Prepnúť prehľad"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Nebudú vás vyrušovať zvuky ani vibrácie, iba budíky, pripomenutia, udalosti a volajúci, ktorých určíte. Budete naďalej počuť všetko, čo sa rozhodnete prehrať, ako napríklad hudbu, videá a hry."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Počas zdieľania, nahrávania alebo prenosu bude mať aplikácia <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> prístup k všetkému obsahu, ktorý sa v nej bude zobrazovať alebo prehrávať. Preto venujte zvýšenú pozornosť heslám, platobným údajom, správam a ďalším citlivým údajom."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Pokračovať"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Aplikácia na zdieľanie alebo nahrávanie"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Vymazať všetko"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Spravovať"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"História"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Chcete vypnúť mobilné dáta?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Nebudete mať prístup k dátam ani internetu prostredníctvom operátora <xliff:g id="CARRIER">%s</xliff:g>. Internet bude k dispozícii iba cez Wi‑Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"váš operátor"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Chcete prepnúť späť na operátora <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Mobilné dáta sa nebudú automaticky prepínať na základe dostupnosti"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Nie, vďaka"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Áno, prepnúť"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Nastavenia nemôžu overiť vašu odpoveď, pretože určitá aplikácia blokuje žiadosť o povolenie."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Povoliť aplikácii <xliff:g id="APP_0">%1$s</xliff:g> zobrazovať rezy z aplikácie <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"– Môže čítať informácie z aplikácie <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Nastavenia okna lupy"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Funkcie dostupnosti otvoríte klepnutím. Tlačidlo prispôsobte alebo nahraďte v Nastav.\n\n"<annotation id="link">"Zobraz. nast."</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Ak chcete tlačidlo dočasne skryť, presuňte ho k okraju"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Späť"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{Bola odstránená skratka {label}}few{Boli odstránené # skratky}many{# shortcuts removed}other{Bolo odstránených # skratiek}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Presunúť doľava nahor"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Presunúť doprava nahor"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Presunúť doľava nadol"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Presunúť doprava nadol"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Presunúť k okraju a skryť"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Presunúť z okraja a zobraziť"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Odstrániť"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"prepínač"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Ovládanie zariadení"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Vyberte aplikáciu, ktorej ovládače si chcete pridať"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilné dáta"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Pripojené"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Dočasne pripojené"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Slabé pripojenie"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Automatické pripojenie cez mobilné dáta nefunguje"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Bez pripojenia"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nie sú k dispozícii žiadne ďalšie siete"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 44108c3..36eead3 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Obraz ni bil prepoznan."</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Uporabite prstni odtis."</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Povezava Bluetooth vzpostavljena."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Neznan odstotek napolnjenosti baterije."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Povezava vzpostavljena z: <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Svetlost"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inverzija barv"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Popravljanje barv"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Upravljanje uporabnikov"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Končano"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Zapri"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Povezava je vzpostavljena"</string>
@@ -304,6 +305,28 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Mikrofon je na voljo"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Kamera je na voljo"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Mikrofon in kamera sta na voljo"</string>
+ <!-- no translation found for sensor_privacy_mic_turned_on_dialog_title (6348853159838376513) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_turned_off_dialog_title (5760464281790732849) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_unblocked_dialog_content (4889961886199270224) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_blocked_no_exception_dialog_content (5864898470772965394) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_blocked_with_exception_dialog_content (810289713700437896) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_turned_on_dialog_title (8039095295100075952) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_turned_off_dialog_title (1936603903120742696) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_unblocked_dialog_content (7847190103011782278) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_blocked_dialog_content (3182428709314874616) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_htt_blocked_dialog_content (3333321592997666441) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_dialog_open_settings (1503088305279285048) -->
+ <skip />
<string name="media_seamless_other_device" msgid="4654849800789196737">"Druga naprava"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Vklop/izklop pregleda"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Ne bodo vas motili zvoki ali vibriranje, razen v primeru alarmov, opomnikov, dogodkov in klicateljev, ki jih določite. Še vedno pa boste slišali vse, kar se boste odločili predvajati, vključno z glasbo, videoposnetki in igrami."</string>
@@ -374,6 +397,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Pri deljenju, snemanju ali predvajanju aplikacije ima aplikacija <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> dostop do vsega, kar je prikazano ali predvajano v tej aplikaciji, zato bodite previdni z gesli, podatki za plačilo, sporočili ali drugimi občutljivimi podatki."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Naprej"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Deljenje ali snemanje aplikacije"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Izbriši vse"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Upravljaj"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Zgodovina"</string>
@@ -728,14 +761,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Želite izklopiti prenos podatkov v mobilnih omrežjih?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Prek operaterja »<xliff:g id="CARRIER">%s</xliff:g>« ne boste imeli dostopa do podatkovne povezave ali interneta. Internet bo na voljo samo prek povezave Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"svojega operaterja"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Želite preklopiti nazaj na ponudnika <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Prenos podatkov v mobilnem omrežju ne preklopi samodejno glede na razpoložljivost."</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Ne, hvala"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Da, preklopi"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Ker aplikacija zakriva zahtevo za dovoljenje, z nastavitvami ni mogoče preveriti vašega odziva."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Želite dovoliti, da aplikacija <xliff:g id="APP_0">%1$s</xliff:g> prikaže izreze aplikacije <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"– lahko bere podatke v aplikaciji <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +823,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Nastavitve okna povečevalnika"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Dotaknite se za funkcije za ljudi s posebnimi potrebami. Ta gumb lahko prilagodite ali zamenjate v nastavitvah.\n\n"<annotation id="link">"Ogled nastavitev"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Če želite gumb začasno skriti, ga premaknite ob rob."</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Razveljavi"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{Odstranjena bližnjica za fun. {label}}one{Odstranjena # bližnjica}two{Odstranjeni # bližnjici}few{Odstranjene # bližnjice}other{Odstranjenih # bližnjic}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Premakni zgoraj levo"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Premakni zgoraj desno"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Premakni spodaj levo"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Premakni spodaj desno"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Premakni na rob in skrij"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Premakni z roba in pokaži"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Odstrani"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"preklop"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Kontrolniki naprave"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Izberite aplikacijo za dodajanje kontrolnikov"</string>
@@ -948,10 +974,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Prenos podatkov v mobilnem omrežju"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Povezano"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Začasno vzpostavljena povezava"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Slaba povezava"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobilna podatkovna povezava ne bo samodejna."</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Ni povezave"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nobeno drugo omrežje ni na voljo"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 7630261..e73a322 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Fytyra nuk mund të njihet"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Përdor më mirë gjurmën e gishtit"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Pajisja është lidhur me \"bluetooth\"."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Përqindja e baterisë e panjohur."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Lidhur me <xliff:g id="BLUETOOTH">%s</xliff:g>"</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ndriçimi"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Anasjellja e ngjyrës"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Korrigjimi i ngjyrës"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Menaxho përdoruesit"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"U krye"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Mbyll"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"I lidhur"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Mikrofoni ofrohet"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Kamera ofrohet"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Mikrofoni dhe kamera ofrohen"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Mikrofoni u aktivizua"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Mikrofoni u çaktivizua"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Mikrofoni është aktivizuar për të gjitha aplikacionet dhe shërbimet."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Qasja te mikrofoni është çaktivizuar për të gjitha aplikacionet dhe shërbimet. Mund ta aktivizosh qasjen te mikrofoni te \"Cilësimet > Privatësia > Mikrofoni\"."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Qasja te mikrofoni është çaktivizuar për të gjitha aplikacionet dhe shërbimet. Këtë mund ta ndryshosh te \"Cilësimet > Privatësia > Mikrofoni\"."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Kamera u aktivizua"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Kamera u çaktivizua"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Kamera është aktivizuar për të gjitha aplikacionet dhe shërbimet."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Qasja te kamera është çaktivizuar për të gjitha aplikacionet dhe shërbimet."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Për të përdorur butonin e mikrofonit, aktivizo qasjen te mikrofoni te \"Cilësimet\"."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Hap cilësimet."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Pajisje tjetër"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Kalo te përmbledhja"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Nuk do të shqetësohesh nga tingujt dhe dridhjet, përveç alarmeve, alarmeve rikujtuese, ngjarjeve dhe telefonuesve që specifikon. Do të vazhdosh të dëgjosh çdo gjë që zgjedh të luash duke përfshirë muzikën, videot dhe lojërat."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Gjatë shpërndarjes, regjistrimit ose transmetimit të një aplikacioni, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ka qasje te çdo gjë e dukshme në ekranin tënd ose që po luhet në atë aplikacion. Prandaj, ki kujdes me fjalëkalimet, detajet e pagesës, mesazhet ose informacione të tjera të ndjeshme."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Vazhdo"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Shpërndaj ose regjistro një aplikacion"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Pastroji të gjitha"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Menaxho"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historiku"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Të çaktivizohen të dhënat celulare?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Nuk do të kesh qasje te të dhënat ose interneti nëpërmjet <xliff:g id="CARRIER">%s</xliff:g>. Interneti do të ofrohet vetëm nëpërmjet Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"operatori yt celular"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Të kalohet përsëri te <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Të dhënat celulare nuk do të ndërrohen automatikisht në bazë të disponueshmërisë"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Jo, faleminderit"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Po, ndërro"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Duke qenë se një aplikacion po bllokon një kërkesë për leje, \"Cilësimet\" nuk mund të verifikojnë përgjigjen tënde."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Të lejohet <xliff:g id="APP_0">%1$s</xliff:g> që të shfaqë pjesë të <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Mund të lexojë informacion nga <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Cilësimet e dritares së zmadhimit"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Trokit dhe hap veçoritë e qasshmërisë. Modifiko ose ndërro butonin te \"Cilësimet\".\n\n"<annotation id="link">"Shih cilësimet"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Zhvendose butonin në skaj për ta fshehur përkohësisht"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Zhbëj"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{Shkurtorja {label} u hoq}other{# shkurtore u hoqën}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Zhvendos lart majtas"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Zhvendos lart djathtas"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Zhvendos poshtë majtas"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Zhvendos poshtë djathtas"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Zhvendose te skaji dhe fshihe"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Zhvendose jashtë skajit dhe shfaqe"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Hiq"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"aktivizo/çaktivizo"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Kontrollet e pajisjes"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Zgjidh aplikacionin për të shtuar kontrollet"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Të dhënat celulare"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Lidhur"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Lidhur përkohësisht"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Lidhje e dobët"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Të dhënat celulare nuk do të lidhen automatikisht"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Nuk ka lidhje"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nuk ofrohet asnjë rrjet tjetër"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 2a7b88f..6d0c201 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Лице није препознато"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Користите отисак прста"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth је прикључен."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Проценат напуњености батерије није познат."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Повезани сте са <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Осветљеност"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Инверзија боја"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Корекција боја"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Управљаjте корисницима"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Готово"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Затвори"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Повезан"</string>
@@ -304,6 +305,28 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Микрофон је доступан"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Камера је доступна"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Микрофон и камера су доступни"</string>
+ <!-- no translation found for sensor_privacy_mic_turned_on_dialog_title (6348853159838376513) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_turned_off_dialog_title (5760464281790732849) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_unblocked_dialog_content (4889961886199270224) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_blocked_no_exception_dialog_content (5864898470772965394) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_blocked_with_exception_dialog_content (810289713700437896) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_turned_on_dialog_title (8039095295100075952) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_turned_off_dialog_title (1936603903120742696) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_unblocked_dialog_content (7847190103011782278) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_blocked_dialog_content (3182428709314874616) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_htt_blocked_dialog_content (3333321592997666441) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_dialog_open_settings (1503088305279285048) -->
+ <skip />
<string name="media_seamless_other_device" msgid="4654849800789196737">"Други уређај"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Укључи/искључи преглед"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Неће вас узнемиравати звукови и вибрације осим за аларме, подсетнике, догађаје и позиваоце које наведете. И даље ћете чути све што одаберете да пустите, укључујући музику, видео снимке и игре."</string>
@@ -374,6 +397,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Када делите, снимате или пребацујете апликацију, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> има приступ комплетном садржају који је видљив или се пушта у тој апликацији. Будите пажљиви са лозинкама, информацијама о плаћању, порукама или другим осетљивим информацијама."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Настави"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Делите или снимите апликацију"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Обриши све"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Управљајте"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Историја"</string>
@@ -728,14 +761,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Желите да искључите мобилне податке?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Нећете имати приступ подацима или интернету преко мобилног оператера <xliff:g id="CARRIER">%s</xliff:g>. Интернет ће бити доступан само преко WiFi везе."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"мобилни оператер"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Желите да се вратите на мобилног оператера <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Мобилни подаци се неће аутоматски променити на основу доступности"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Не, хвала"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Да, пређи"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Подешавања не могу да верификују ваш одговор јер апликација скрива захтев за дозволу."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Желите ли да дозволите апликацији <xliff:g id="APP_0">%1$s</xliff:g> да приказује исечке из апликације <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"– Може да чита податке из апликације <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +823,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Подешавања прозора за увећање"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Додирните за функције приступачности. Прилагодите или замените ово дугме у Подешавањима.\n\n"<annotation id="link">"Подешавања"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Померите дугме до ивице да бисте га привремено сакрили"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Опозовите"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} пречица је уклоњена}one{# пречица је уклоњена}few{# пречице су уклоњене}other{# пречица је уклоњено}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Премести горе лево"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Премести горе десно"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Премести доле лево"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Премести доле десно"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Премести до ивице и сакриј"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Премести изван ивице и прикажи"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Уклоните"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"укључите/искључите"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Контроле уређаја"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Одаберите апликацију за додавање контрола"</string>
@@ -948,10 +974,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобилни подаци"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Повезано"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Привремено повезано"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Веза је лоша"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Није успело аутом. повезивање преко моб. података"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Веза није успостављена"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Није доступна ниједна друга мрежа"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 48654d5..2eac10f 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Ansiktet kändes inte igen"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Använd fingeravtryck"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth ansluten."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Okänd batterinivå."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Ansluten till <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ljusstyrka"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Färginvertering"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Färgkorrigering"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Hantera användare"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Klart"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Stäng"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Ansluten"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Mikrofonen kan användas"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Kameran kan användas"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Mikrofonen och kameran kan användas"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Mikrofonen sattes på"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Mikrofonen stängdes av"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Mikrofonen är aktiverad för alla appar och tjänster."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Mikrofonåtkomst är inaktiverad för alla appar och tjänster. Du kan aktivera mikrofonåtkomst i Inställningar > Integritet > Mikrofon."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Mikrofonåtkomst är inaktiverad för alla appar och tjänster. Du kan ändra detta i Inställningar > Integritet > Mikrofon."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Kameran sattes på"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Kameran stängdes av"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Kameran är aktiverad för alla appar och tjänster."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Kameraåtkomst är inaktiverad för alla appar och tjänster."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Aktivera mikrofonåtkomst i inställningarna om du vill använda mikrofonknappen."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Öppna inställningarna."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Annan enhet"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Aktivera och inaktivera översikten"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Du blir inte störd av ljud och vibrationer, förutom från alarm, påminnelser, händelser och specifika samtal. Ljudet är fortfarande på för sådant du väljer att spela upp, till exempel musik, videor och spel."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"När du delar, spelar in eller castar en app har <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> åtkomst till allt som visas eller spelas upp i appen. Så var försiktig med lösenord, betalningsuppgifter, meddelanden och andra känsliga uppgifter."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Fortsätt"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Dela eller spela in en app"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Rensa alla"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Hantera"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historik"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Vill du inaktivera mobildata?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Du kan inte skicka data eller använda internet via <xliff:g id="CARRIER">%s</xliff:g>. Internetanslutning blir bara möjlig via wifi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"din operatör"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Vill du byta tillbaka till <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Mobildatakällan byts inte automatiskt efter tillgänglighet"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Nej tack"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Ja, byt"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Svaret kan inte verifieras av Inställningar eftersom en app skymmer en begäran om behörighet."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Tillåter du att bitar av <xliff:g id="APP_2">%2$s</xliff:g> visas i <xliff:g id="APP_0">%1$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"– Kan läsa information från <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Inställningar för förstoringsfönster"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tryck för att öppna tillgänglighetsfunktioner. Anpassa/ersätt knappen i Inställningar.\n\n"<annotation id="link">"Inställningar"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Flytta knappen till kanten för att dölja den tillfälligt"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Ångra"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} genväg har tagits bort}other{# genvägar har tagits bort}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Flytta högst upp till vänster"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Flytta högst upp till höger"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Flytta längst ned till vänster"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Flytta längst ned till höger"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Flytta till kanten och dölj"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Flytta från kanten och visa"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Ta bort"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"aktivera och inaktivera"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Enhetsstyrning"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Välj en app om du vill lägga till snabbkontroller"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobildata"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Ansluten"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Tillfälligt ansluten"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Dålig anslutning"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Du ansluts inte till mobildata automatiskt"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Ingen anslutning"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Inga andra nätverk är tillgängliga"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 6bf2bc2..47fbdc43 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Imeshindwa kutambua uso"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Badala yake, tumia alama ya kidole"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth imeunganishwa."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Asilimia ya betri haijulikani."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Imeunganishwa kwenye <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ung\'avu"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Ugeuzaji rangi"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Usahihishaji wa rangirangi"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Dhibiti watumiaji"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Nimemaliza"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Funga"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Imeunganishwa"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Maikrofoni inapatikana"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Kamera inapatikana"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Maikrofoni na kamera zinapatikana"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Umewasha maikrofoni"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Umezima maikrofoni"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Umewasha maikrofoni kwenye programu na huduma zote."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Umezima ufikiaji wa maikrofoni kwenye programu na huduma zote. Unaweza kuruhusu ufikiaji wa maikrofoni kwenye Mipangilio > Faragha > Maikrofoni."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Umezima ufikiaji wa maikrofoni kwenye programu na huduma zote. Unaweza kubadilisha hali hii kwenye Mipangilio > Faragha > Maikrofoni."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Umewasha kamera"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Umezima kamera"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Umewasha kamera kwenye programu na huduma zote."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Umezima ufikiaji wa kamera kwenye programu na huduma zote."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Ili utumie kitufe cha maikrofoni, ruhusu ufikiaji wa maikrofoni kwenye Mipangilio."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Fungua mipangilio."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Kifaa kingine"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Washa Muhtasari"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Hutasumbuliwa na sauti na mitetemo, isipokuwa kengele, vikumbusho, matukio na simu zinazopigwa na watu uliobainisha. Bado utasikia chochote utakachochagua kucheza, ikiwa ni pamoja na muziki, video na michezo."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Unapotuma, kurekodi au kushiriki programu, programu ya <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> inaweza kufikia kitu chochote kitakachoonekana au kuchezwa kwenye programu hiyo. Hivyo kuwa mwangalifu na manenosiri, maelezo ya malipo, ujumbe au maelezo mengine nyeti."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Endelea"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Shiriki au rekodi programu"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Futa zote"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Dhibiti"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historia"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Ungependa kuzima data ya mtandao wa simu?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Hutaweza kufikia data au intaneti kupitia <xliff:g id="CARRIER">%s</xliff:g>. Intaneti itapatikana kupitia Wi-Fi pekee."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"mtoa huduma wako"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Ungependa kubadilisha ili utumie data ya mtandao wa <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Data ya mtandao wa simu haitabadilika kiotomatiki kulingana na upatikanaji"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Hapana"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Ndiyo, badili"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Kwa sababu programu nyingine inazuia ombi la ruhusa, hatuwezi kuthibitisha jibu lako katika Mipangilio."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Ungependa kuruhusu <xliff:g id="APP_0">%1$s</xliff:g> ionyeshe vipengee <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Inaweza kusoma maelezo kutoka <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Mipangilio ya dirisha la kikuzaji"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Gusa ili ufungue vipengele vya ufikivu. Weka mapendeleo au ubadilishe kitufe katika Mipangilio.\n\n"<annotation id="link">"Angalia mipangilio"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Sogeza kitufe kwenye ukingo ili ukifiche kwa muda"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Tendua"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{Njia {label} ya mkato imeondolewa}other{Njia # za mkato zimeondolewa}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Sogeza juu kushoto"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Sogeza juu kulia"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Sogeza chini kushoto"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Sogeza chini kulia"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Sogeza kwenye ukingo kisha ufiche"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Sogeza nje ya ukingo kisha uonyeshe"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Ondoa"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"geuza"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Vidhibiti vya vifaa"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Chagua programu ili uweke vidhibiti"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Data ya mtandao wa simu"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Imeunganishwa"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Imeunganishwa kwa muda"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Muunganisho duni"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Data ya mtandao wa simu haitaunganishwa kiotomatiki"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Hakuna muunganisho"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Hakuna mitandao mingine inayopatikana"</string>
diff --git a/packages/SystemUI/res/values-sw600dp-port/dimens.xml b/packages/SystemUI/res/values-sw600dp-port/dimens.xml
index d9df337..707bc9e 100644
--- a/packages/SystemUI/res/values-sw600dp-port/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-port/dimens.xml
@@ -17,7 +17,6 @@
<resources>
<dimen name="notification_panel_margin_horizontal">48dp</dimen>
<dimen name="status_view_margin_horizontal">62dp</dimen>
- <dimen name="bouncer_user_switcher_y_trans">20dp</dimen>
<!-- qs_tiles_page_horizontal_margin should be margin / 2, otherwise full space between two
pages is margin * 2, and that makes tiles page not appear immediately after user swipes to
diff --git a/packages/SystemUI/res/values-sw720dp-h1000dp/dimens.xml b/packages/SystemUI/res/values-sw720dp-h1000dp/dimens.xml
index 97ead01..b98165f 100644
--- a/packages/SystemUI/res/values-sw720dp-h1000dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-h1000dp/dimens.xml
@@ -21,4 +21,6 @@
<!-- Space between status view and notification shelf -->
<dimen name="keyguard_status_view_bottom_margin">70dp</dimen>
<dimen name="keyguard_clock_top_margin">80dp</dimen>
+ <dimen name="bouncer_user_switcher_view_mode_user_switcher_bottom_margin">186dp</dimen>
+ <dimen name="bouncer_user_switcher_view_mode_view_flipper_bottom_margin">110dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw720dp-port/dimens.xml b/packages/SystemUI/res/values-sw720dp-port/dimens.xml
index 17f82b5..8b41a44 100644
--- a/packages/SystemUI/res/values-sw720dp-port/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-port/dimens.xml
@@ -21,7 +21,6 @@
for different hardware and product builds. -->
<resources>
<dimen name="status_view_margin_horizontal">124dp</dimen>
- <dimen name="bouncer_user_switcher_y_trans">200dp</dimen>
<dimen name="large_screen_shade_header_left_padding">24dp</dimen>
<dimen name="qqs_layout_padding_bottom">40dp</dimen>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index b291004..f836e95 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"முகத்தை கண்டறிய இயலவில்லை"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"கைரேகையை உபயோகிக்கவும்"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"புளூடூத் இணைக்கப்பட்டது."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"பேட்டரி சதவீதம் தெரியவில்லை."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>க்கு இணைக்கப்பட்டது."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ஒளிர்வு"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"கலர் இன்வெர்ஷன்"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"கலர் கரெக்ஷன்"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"பயனர்களை நிர்வகியுங்கள்"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"முடிந்தது"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"மூடுக"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"இணைக்கப்பட்டது"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"மைக்ரோஃபோன் அணுகல் உள்ளது"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"கேமரா அணுகல் உள்ளது"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"மைக்ரோஃபோன் & கேமரா அணுகல் உள்ளது"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"மைக்ரோஃபோன் அணுகல் இயக்கப்பட்டது"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"மைக்ரோஃபோன் அணுகல் முடக்கப்பட்டது"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"அனைத்து ஆப்ஸுக்கும் சேவைகளுக்கும் மைக்ரோஃபோன் அணுகல் இயக்கப்பட்டது."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"அனைத்து ஆப்ஸுக்கும் சேவைகளுக்கும் மைக்ரோஃபோன் அணுகல் முடக்கப்பட்டது. அமைப்புகள் > தனியுரிமை > மைக்ரோஃபோன் என்பதற்குச் சென்று மைக்ரோஃபோன் அணுகலை இயக்கிக்கொள்ளலாம்."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"அனைத்து ஆப்ஸுக்கும் சேவைகளுக்கும் மைக்ரோஃபோன் அணுகல் முடக்கப்பட்டது. அமைப்புகள் > தனியுரிமை > மைக்ரோஃபோன் என்பதற்குச் சென்று இதை மாற்றிக்கொள்ளலாம்."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"கேமரா அணுகல் இயக்கப்பட்டது"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"கேமரா அணுகல் முடக்கப்பட்டது"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"அனைத்து ஆப்ஸுக்கும் சேவைகளுக்கும் கேமரா அணுகல் இயக்கப்பட்டது."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"அனைத்து ஆப்ஸுக்கும் சேவைகளுக்கும் கேமரா அணுகல் முடக்கப்பட்டது."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"மைக்ரோஃபோன் பட்டனைப் பயன்படுத்த, மைக்ரோஃபோன் அணுகலை அமைப்புகளில் இயக்கவும்."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"அமைப்புகளைத் திற."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"பிற சாதனம்"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"மேலோட்டப் பார்வையை நிலைமாற்று"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"அலாரங்கள், நினைவூட்டல்கள், நிகழ்வுகள் மற்றும் குறிப்பிட்ட அழைப்பாளர்களைத் தவிர்த்து, பிற ஒலிகள் மற்றும் அதிர்வுகளின் தொந்தரவு இருக்காது. எனினும், நீங்கள் எதையேனும் (இசை, வீடியோக்கள், கேம்ஸ் போன்றவை) ஒலிக்கும்படி தேர்ந்தெடுத்திருந்தால், அவை வழக்கம் போல் ஒலிக்கும்."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"ஓர் ஆப்ஸை நீங்கள் பகிரும்போதோ ரெக்கார்டு செய்யும்போதோ அலைபரப்பும்போதோ அந்த ஆப்ஸில் காட்டப்படும் அல்லது பிளே செய்யப்படும் அனைத்தையும் <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ஆப்ஸால் அணுக முடியும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், பிற பாதுகாக்கப்பட வேண்டிய தகவல்கள் ஆகியவை குறித்து கவனத்துடன் இருங்கள்."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"தொடர்க"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"ஆப்ஸைப் பகிர்தல் அல்லது ரெக்கார்டு செய்தல்"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"எல்லாவற்றையும் அழி"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"நிர்வகி"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"இதுவரை வந்த அறிவிப்புகள்"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"மொபைல் டேட்டாவை ஆஃப் செய்யவா?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"<xliff:g id="CARRIER">%s</xliff:g> மூலம் டேட்டா அல்லது இணையத்தை உங்களால் பயன்படுத்த முடியாது. வைஃபை வழியாக மட்டுமே இணையத்தைப் பயன்படுத்த முடியும்."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"உங்கள் மொபைல் நிறுவனம்"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"<xliff:g id="CARRIER">%s</xliff:g>க்கு மறுபடியும் மாற்றவா?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"கிடைக்கும் நிலையின் அடிப்படையில் மொபைல் டேட்டா தானாகவே மாறாது"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"வேண்டாம்"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"மாற்று"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"அனுமதிக் கோரிக்கையை ஆப்ஸ் மறைப்பதால், அமைப்புகளால் உங்கள் பதிலைச் சரிபார்க்க முடியாது."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> ஆப்ஸை, <xliff:g id="APP_2">%2$s</xliff:g> ஆப்ஸின் விழிப்பூட்டல்களைக் காண்பிக்க அனுமதிக்கவா?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- இது, <xliff:g id="APP">%1$s</xliff:g> பயன்பாட்டிலிருந்து தகவலைப் படிக்கும்"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"சாளரத்தைப் பெரிதாக்கும் கருவிக்கான அமைப்புகள்"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"அணுகல்தன்மை அம்சத்தை திறக்க தட்டவும். அமைப்பில் பட்டனை பிரத்தியேகமாக்கலாம்/மாற்றலாம்.\n\n"<annotation id="link">"அமைப்பில் காண்க"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"பட்டனைத் தற்காலிகமாக மறைக்க ஓரத்திற்கு நகர்த்தும்"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"செயல்தவிர்"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} ஷார்ட்கட் அகற்றப்பட்டது}other{# ஷார்ட்கட்கள் அகற்றப்பட்டன}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"மேலே இடதுபுறத்திற்கு நகர்த்து"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"மேலே வலதுபுறத்திற்கு நகர்த்து"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"கீழே இடதுபுறத்திற்கு நகர்த்து"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"கீழே வலதுபுறத்திற்கு நகர்த்து"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"ஓரத்திற்கு நகர்த்தி மறை"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"ஓரத்திற்கு நகர்த்தி, காட்டு"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"அகற்று"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"நிலைமாற்று"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"சாதனக் கட்டுப்பாடுகள்"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"கட்டுப்பாடுகளைச் சேர்க்க வேண்டிய ஆப்ஸைத் தேர்ந்தெடுங்கள்"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"மொபைல் டேட்டா"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"இணைக்கப்பட்டது"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"தற்காலிகமாக இணைக்கப்பட்டுள்ளது"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"இணைப்பு மோசமாக உள்ளது"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"மொபைல் டேட்டாவுடன் தானாக இணைக்காது"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"இணைப்பு இல்லை"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"வேறு நெட்வொர்க்குகள் எதுவும் கிடைக்கவில்லை"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 4c34f45..002a4b2 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"ముఖం గుర్తించడం కుదరలేదు"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"బదులుగా వేలిముద్రను ఉపయోగించండి"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"బ్లూటూత్ కనెక్ట్ చేయబడింది."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"బ్యాటరీ శాతం తెలియదు."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>కి కనెక్ట్ చేయబడింది."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ప్రకాశం"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"కలర్ మార్పిడి"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"కలర్ కరెక్షన్"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"యూజర్లను మేనేజ్ చేయండి"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"పూర్తయింది"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"మూసివేయి"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"కనెక్ట్ చేయబడినది"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"మైక్రోఫోన్ అందుబాటులో ఉంది"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"కెమెరా అందుబాటులో ఉంది"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"మైక్రోఫోన్, అలాగే కెమెరా అందుబాటులో ఉంది"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"మైక్రోఫోన్ ఆన్ చేయబడింది"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"మైక్రోఫోన్ ఆఫ్ చేయబడింది"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"యాప్లు, అలాగే సర్వీస్లన్నింటికీ మైక్రోఫోన్ ఎనేబుల్ చేయబడింది."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"యాప్లు, అలాగే సర్వీస్లన్నింటికీ మైక్రోఫోన్ యాక్సెస్ డిజేబుల్ చేయబడింది. సెట్టింగ్లు > గోప్యత > మైక్రోఫోన్లో మీరు మైక్రోఫోన్ యాక్సెస్ను ఎనేబుల్ చేయవచ్చు."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"యాప్లు, అలాగే సర్వీస్లన్నింటికీ మైక్రోఫోన్ యాక్సెస్ డిజేబుల్ చేయబడింది. సెట్టింగ్లు > గోప్యత > మైక్రోఫోన్లో మీరు దీన్ని మార్చవచ్చు."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"కెమెరా ఆన్ చేయబడింది"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"కెమెరా ఆఫ్ చేయబడింది"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"యాప్లు, అలాగే సర్వీస్లన్నింటికీ కెమెరా ఎనేబుల్ చేయబడింది."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"యాప్లు, అలాగే సర్వీస్లన్నింటికీ కెమెరా యాక్సెస్ డిజేబుల్ చేయబడింది."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"మైక్రోఫోన్ బటన్ను ఉపయోగించడానికి, సెట్టింగ్లలో మైక్రోఫోన్ యాక్సెస్ను ఎనేబుల్ చేయండి."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"సెట్టింగ్లను తెరవండి."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"ఇతర పరికరం"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"స్థూలదృష్టిని టోగుల్ చేయి"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"మీరు పేర్కొనే అలారాలు, రిమైండర్లు, ఈవెంట్లు మరియు కాలర్ల నుండి మినహా మరే ఇతర ధ్వనులు మరియు వైబ్రేషన్లతో మీకు అంతరాయం కలగదు. మీరు ఇప్పటికీ సంగీతం, వీడియోలు మరియు గేమ్లతో సహా మీరు ప్లే చేయడానికి ఎంచుకున్నవి ఏవైనా వింటారు."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"మీరు ఏదైనా యాప్ను షేర్ చేస్తున్నప్పుడు, రికార్డ్ చేస్తున్నప్పుడు, లేదా ప్రసారం చేస్తున్నప్పుడు, ఆ యాప్లో చూపబడిన దేనికైనా లేదా ప్లే అయిన దేనికైనా <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>కు యాక్సెస్ ఉంటుంది. కాబట్టి, పాస్వర్డ్లు, పేమెంట్ వివరాలు, మెసేజ్లు, లేదా ఏదైనా ఇతర సున్నితమైన సమాచారం పట్ల జాగ్రత్త వహించండి."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"కొనసాగించండి"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"యాప్ను షేర్ చేయండి లేదా రికార్డ్ చేయండి"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"అన్నీ క్లియర్ చేయండి"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"మేనేజ్ చేయండి"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"హిస్టరీ"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"మొబైల్ డేటాను ఆఫ్ చేయాలా?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"\"<xliff:g id="CARRIER">%s</xliff:g>\" ద్వారా మీకు డేటా లేదా ఇంటర్నెట్కు యాక్సెస్ ఉండదు. Wi-Fi ద్వారా మాత్రమే ఇంటర్నెట్ అందుబాటులో ఉంటుంది."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"మీ క్యారియర్"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"<xliff:g id="CARRIER">%s</xliff:g>కి తిరిగి మారాలా?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"మొబైల్ డేటా లభ్యత ఆధారంగా ఆటోమేటిక్గా స్విచ్ అవ్వదు"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"వద్దు, థ్యాంక్స్"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"అవును, మార్చండి"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"అనుమతి రిక్వెస్ట్కు ఒక యాప్ అడ్డు తగులుతున్నందున సెట్టింగ్లు మీ ప్రతిస్పందనను ధృవీకరించలేకపోయాయి."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_2">%2$s</xliff:g> స్లైస్లను చూపించడానికి <xliff:g id="APP_0">%1$s</xliff:g>ని అనుమతించండి?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- ఇది <xliff:g id="APP">%1$s</xliff:g> నుండి సమాచారాన్ని చదువుతుంది"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"మాగ్నిఫయర్ విండో సెట్టింగ్లు"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"యాక్సెసిబిలిటీ ఫీచర్లను తెరవడానికి ట్యాప్ చేయండి. సెట్టింగ్లలో ఈ బటన్ను అనుకూలంగా మార్చండి లేదా రీప్లేస్ చేయండి.\n\n"<annotation id="link">"వీక్షణ సెట్టింగ్లు"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"తాత్కాలికంగా దానిని దాచడానికి బటన్ను చివరకు తరలించండి"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"చర్య రద్దు చేయండి"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} షార్ట్కట్ తీసివేయబడింది}other{# షార్ట్కట్లు తీసివేయబడ్డాయి}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ఎగువ ఎడమ వైపునకు తరలించు"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ఎగువ కుడి వైపునకు తరలించు"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"దిగువ ఎడమ వైపునకు తరలించు"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"దిగువ కుడి వైపునకు తరలించు"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"అంచుకు తరలించి దాచండి"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"అంచుని తరలించి చూపించు"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"తీసివేయండి"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"టోగుల్ చేయి"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"డివైజ్ కంట్రోల్స్"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"కంట్రోల్స్ను యాడ్ చేయడానికి యాప్ను ఎంచుకోండి"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"మొబైల్ డేటా"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"కనెక్ట్ చేయబడింది"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"తాత్కాలికంగా కనెక్ట్ చేయబడింది"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"కనెక్షన్ బాగాలేదు"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"మొబైల్ డేటా ఆటోమెటిక్గా కనెక్ట్ అవ్వదు"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"కనెక్షన్ లేదు"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"ఇతర నెట్వర్క్లేవీ అందుబాటులో లేవు"</string>
diff --git a/packages/SystemUI/res/values-television/strings.xml b/packages/SystemUI/res/values-television/strings.xml
new file mode 100644
index 0000000..f30b73e
--- /dev/null
+++ b/packages/SystemUI/res/values-television/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2022, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Content for the log access confirmation dialog. [CHAR LIMIT=NONE]-->
+ <string name="log_access_confirmation_body">Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps you trust to access all device logs.
+ \n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device.\n\nLearn more at g.co/android/devicelogs.
+ </string>
+
+ <!-- Learn more URL for the log access confirmation dialog. [DO NOT TRANSLATE]-->
+ <string name="log_access_confirmation_learn_more" translatable="false"></string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-television/styles.xml b/packages/SystemUI/res/values-television/styles.xml
index 12020f9..c517845 100644
--- a/packages/SystemUI/res/values-television/styles.xml
+++ b/packages/SystemUI/res/values-television/styles.xml
@@ -63,4 +63,10 @@
<item name="android:paddingVertical">@dimen/bottom_sheet_button_padding_vertical</item>
<item name="android:stateListAnimator">@anim/tv_bottom_sheet_button_state_list_animator</item>
</style>
+
+ <!-- The style for log access consent button -->
+ <style name="LogAccessDialogTheme" parent="@android:style/Theme.DeviceDefault.Dialog.Alert">
+ <item name="permissionGrantButtonTopStyle">?android:buttonBarButtonStyle</item>
+ <item name="permissionGrantButtonBottomStyle">?android:buttonBarButtonStyle</item>
+ </style>
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 5de908c..fa8118e 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"ไม่รู้จักใบหน้า"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"ใช้ลายนิ้วมือแทน"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"เชื่อมต่อบลูทูธแล้ว"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ไม่ทราบเปอร์เซ็นต์แบตเตอรี่"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"เชื่อมต่อกับ <xliff:g id="BLUETOOTH">%s</xliff:g> แล้ว"</string>
@@ -303,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"ใช้งานไมโครโฟนได้"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"ใช้งานกล้องได้"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"ใช้งานไมโครโฟนและกล้องได้"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"เปิดไมโครโฟนแล้ว"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"ปิดไมโครโฟนแล้ว"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"เปิดไมโครโฟนสำหรับแอปและบริการทั้งหมดแล้ว"</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"ปิดการเข้าถึงไมโครโฟนสำหรับแอปและบริการทั้งหมดแล้ว คุณสามารถเปิดการเข้าถึงไมโครโฟนได้จากการตั้งค่า > ความเป็นส่วนตัว > ไมโครโฟน"</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"ปิดการเข้าถึงไมโครโฟนสำหรับแอปและบริการทั้งหมดแล้ว คุณเปลี่ยนการตั้งค่านี้ได้ในการตั้งค่า > ความเป็นส่วนตัว > ไมโครโฟน"</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"เปิดกล้องแล้ว"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"ปิดกล้องแล้ว"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"เปิดการเข้าถึงกล้องสำหรับแอปและบริการทั้งหมดแล้ว"</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"ปิดการเข้าถึงกล้องสำหรับแอปและบริการทั้งหมดแล้ว"</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"หากต้องการใช้ปุ่มไมโครโฟน ให้เปิดการเข้าถึงไมโครโฟนในการตั้งค่า"</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"เปิดการตั้งค่า"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"อุปกรณ์อื่น"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"สลับภาพรวม"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"คุณจะไม่ถูกรบกวนจากเสียงและการสั่น ยกเว้นเสียงนาฬิกาปลุก การช่วยเตือน กิจกรรม และผู้โทรที่ระบุไว้ คุณจะยังคงได้ยินสิ่งที่คุณเลือกเล่น เช่น เพลง วิดีโอ และเกม"</string>
@@ -373,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"เมื่อกำลังแชร์ บันทึก หรือแคสต์แอป \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" จะมีสิทธิ์เข้าถึงทุกสิ่งที่แสดงหรือเล่นอยู่ในแอปดังกล่าว ดังนั้นโปรดระวังเกี่ยวกับรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ หรือข้อมูลที่ละเอียดอ่อนอื่นๆ"</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"ต่อไป"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"แชร์หรือบันทึกแอป"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"ล้างทั้งหมด"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"จัดการ"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ประวัติ"</string>
@@ -727,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"ปิดอินเทอร์เน็ตมือถือไหม"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"คุณจะใช้เน็ตมือถือหรืออินเทอร์เน็ตผ่าน \"<xliff:g id="CARRIER">%s</xliff:g>\" ไม่ได้ แต่จะใช้ผ่าน Wi-Fi ได้เท่านั้น"</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"ผู้ให้บริการของคุณ"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"เปลี่ยนกลับเป็น <xliff:g id="CARRIER">%s</xliff:g> หรือไม่"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"อินเทอร์เน็ตมือถือไม่ได้เปลี่ยนตามความพร้อมบริการโดยอัตโนมัติ"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"ไม่เป็นไร"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"ใช่ เปลี่ยนเลย"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"เนื่องจากแอปหนึ่งได้บดบังคำขอสิทธิ์ ระบบจึงไม่สามารถยืนยันคำตอบของคุณสำหรับการตั้งค่าได้"</string>
<string name="slice_permission_title" msgid="3262615140094151017">"อนุญาตให้ <xliff:g id="APP_0">%1$s</xliff:g> แสดงส่วนต่างๆ ของ <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- อ่านข้อมูลจาก <xliff:g id="APP">%1$s</xliff:g> ได้"</string>
@@ -793,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"การตั้งค่าหน้าต่างแว่นขยาย"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"แตะเพื่อเปิดฟีเจอร์การช่วยเหลือพิเศษ ปรับแต่งหรือแทนที่ปุ่มนี้ในการตั้งค่า\n\n"<annotation id="link">"ดูการตั้งค่า"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ย้ายปุ่มไปที่ขอบเพื่อซ่อนชั่วคราว"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"เลิกทำ"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{นำทางลัด {label} รายการออกแล้ว}other{นำทางลัด # รายการออกแล้ว}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ย้ายไปด้านซ้ายบน"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ย้ายไปด้านขวาบน"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"ย้ายไปด้านซ้ายล่าง"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"ย้ายไปด้านขาวล่าง"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"ย้ายไปที่ขอบและซ่อน"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"ย้ายออกจากขอบและแสดง"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"นำออก"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"สลับ"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ระบบควบคุมอุปกรณ์"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"เลือกแอปเพื่อเพิ่มตัวควบคุม"</string>
@@ -947,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"อินเทอร์เน็ตมือถือ"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"เชื่อมต่อแล้ว"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"เชื่อมต่อแล้วชั่วคราว"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"การเชื่อมต่อไม่ดี"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"อินเทอร์เน็ตมือถือจะไม่เชื่อมต่ออัตโนมัติ"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"ไม่มีการเชื่อมต่อ"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"ไม่มีเครือข่ายอื่นๆ ที่พร้อมใช้งาน"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index f47b906..1451ab9 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Hindi makilala ang mukha"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Gumamit ng fingerprint"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Nakakonekta ang Bluetooth."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Hindi alam ang porsyento ng baterya."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Nakakonekta sa <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -303,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Available ang mikropono"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Available ang camera"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Available ang mikropono at camera"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Naka-on ang mikropono"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Naka-off ang mikropono"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Naka-enable para sa lahat ng app at serbisyo ang mikropono."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Naka-disable para sa lahat ng app at serbisyo ang access sa mikropono. Puwede mong i-enable ang access sa mikropono sa Mga Setting > Privacy > Mikropono."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Naka-disable para sa lahat ng app at serbisyo ang access sa mikropono. Puwede mo itong baguhin sa Mga Setting > Privacy > Mikropono."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Naka-on ang camera"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Naka-off ang camera"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Naka-enable para sa lahat ng app at serbisyo ang camera."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Naka-disable para sa lahat ng app at serbisyo ang access sa camera."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Para gamitin ang button ng mikropono, i-enable ang access sa mikropono sa Mga Setting."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Buksan ang mga setting."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Iba pang device"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"I-toggle ang Overview"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Hindi ka maiistorbo ng mga tunog at pag-vibrate, maliban mula sa mga alarm, paalala, event, at tumatawag na tutukuyin mo. Maririnig mo pa rin ang kahit na anong piliin mong i-play kabilang ang mga musika, video, at laro."</string>
@@ -373,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Kapag nagbabahagi, nagre-record, o nagka-cast ka ng app, may access ang <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> sa kahit anong ipinapakita o pine-play sa app na iyon. Kaya mag-ingat sa mga password, detalye ng pagbabayad, mensahe, o iba pang impormasyon."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Magpatuloy"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Ibahagi o i-record ang isang app"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"I-clear lahat"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Pamahalaan"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"History"</string>
@@ -727,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"I-off ang mobile data?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Hindi ka magkaka-access sa data o internet sa pamamagitan ng <xliff:g id="CARRIER">%s</xliff:g>. Available lang ang internet sa pamamagitan ng Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"ang iyong carrier"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Bumalik sa <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Hindi awtomatikong magbabago ang mobile data base sa availability"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Hindi, salamat na lang"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Oo, lumipat"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Hindi ma-verify ng Mga Setting ang iyong tugon dahil may app na tumatakip sa isang kahilingan sa pagpapahintulot."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Payagan ang <xliff:g id="APP_0">%1$s</xliff:g> na ipakita ang mga slice ng <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Nakakabasa ito ng impormasyon mula sa <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -793,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Mga setting ng window ng magnifier"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"I-tap, buksan mga feature ng accessibility. I-customize o palitan button sa Mga Setting.\n\n"<annotation id="link">"Tingnan ang mga setting"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Ilipat ang button sa gilid para pansamantala itong itago"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"I-undo"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} shortcut ang naalis}one{# shortcut ang naalis}other{# na shortcut ang naalis}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Ilipat sa kaliwa sa itaas"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Ilipat sa kanan sa itaas"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Ilipat sa kaliwa sa ibaba"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Ilipat sa kanan sa ibaba"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Ilipat sa sulok at itago"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Alisin sa sulok at ipakita"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Alisin"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"i-toggle"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Mga kontrol ng device"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Pumili ng app para magdagdag ng mga kontrol"</string>
@@ -947,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Nakakonekta"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Pansamantalang nakakonekta"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Mahina ang koneksyon"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Hindi awtomatikong kokonekta ang mobile data"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Walang koneksyon"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Walang available na iba pang network"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 17dfa48..098091a 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Yüz tanınamadı"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Bunun yerine parmak izi kullanın"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth bağlandı."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Pil yüzdesi bilinmiyor."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> ile bağlı."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Parlaklık"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Rengi ters çevirme"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Renk düzeltme"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Kullanıcıları yönet"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Bitti"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Kapat"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Bağlı"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Mikrofon kullanılabilir"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Kamera kullanılabilir"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Mikrofon ve kamera kullanılabilir"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Mikrofon açık"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Mikrofon kapalı"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Mikrofon tüm uygulama ve hizmetler için etkinleştirildi."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Mikrofon erişimi tüm uygulama ve hizmetler için devre dışı bırakıldı. Mikrofon erişimini Ayarlar > Gizlilik > Mikrofon\'da etkinleştirebilirsiniz."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Mikrofon erişimi tüm uygulama ve hizmetler için devre dışı bırakıldı. Bunu Ayarlar > Gizlilik > Mikrofon\'da değiştirebilirsiniz."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Kamera açıldı"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Kamera devre dışı bırakıldı"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Kamera tüm uygulama ve hizmetler için etkinleştirildi."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Kamera erişimi tüm uygulama ve hizmetler için devre dışı bırakıldı."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Mikrofon düğmesini kullanmak için Ayarlar\'da mikrofon erişimini etkinleştirin."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Ayarları aç."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Diğer cihaz"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Genel bakışı aç/kapat"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Alarmlar, hatırlatıcılar, etkinlikler ve sizin seçtiğiniz kişilerden gelen çağrılar dışında hiçbir ses ve titreşimle rahatsız edilmeyeceksiniz. O sırada çaldığınız müzik, seyrettiğiniz video ya da oynadığınız oyunların sesini duymaya devam edeceksiniz."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Bir uygulamayı paylaşma, kaydetme ve yayınlama özelliklerini kullandığınızda <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>, söz konusu uygulamada gösterilen veya oynatılan her şeye erişebilir. Dolayısıyla şifreler, ödeme ayrıntıları, mesajlar veya diğer hassas bilgiler konusunda dikkatli olun."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Devam"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Uygulamayı paylaşın veya kaydedin"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Tümünü temizle"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Yönet"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Geçmiş"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Mobil veri kapatılsın mı?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"<xliff:g id="CARRIER">%s</xliff:g> üzerinden veri veya internet erişiminiz olmayacak. İnternet yalnızca kablosuz bağlantı üzerinden kullanılabilecek."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"operatörünüz"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"<xliff:g id="CARRIER">%s</xliff:g> operatörüne geri dönülsün mü?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Uygunluk durumuna göre otomatik olarak mobil veriye geçilmez"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Hayır, teşekkürler"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Evet, geçilsin"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Bir uygulama bir izin isteğinin anlaşılmasını engellediğinden, Ayarlar, yanıtınızı doğrulayamıyor."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> uygulamasının, <xliff:g id="APP_2">%2$s</xliff:g> dilimlerini göstermesine izin verilsin mi?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- <xliff:g id="APP">%1$s</xliff:g> uygulamasından bilgileri okuyabilir"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Büyüteç penceresi ayarları"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Erişilebilirlik özelliklerini açmak için dokunun. Bu düğmeyi Ayarlar\'dan özelleştirin veya değiştirin.\n\n"<annotation id="link">"Ayarları göster"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Düğmeyi geçici olarak gizlemek için kenara taşıyın"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Geri al"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} kısayol kaldırıldı}other{# kısayol kaldırıldı}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Sol üste taşı"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Sağ üste taşı"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Sol alta taşı"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Sağ alta taşı"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Kenara taşıyıp gizle"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Kenarın dışına taşıyıp göster"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Kaldır"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"değiştir"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Cihaz denetimleri"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Denetim eklemek için uygulama seçin"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobil veri"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Bağlı"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Geçici olarak bağlandı"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Bağlantı zayıf"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobil veri otomatik olarak bağlanmıyor"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Bağlantı yok"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Kullanılabilir başka ağ yok"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index ff1b594..ef05be6 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Обличчя не розпізнано"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Скористайтеся відбитком"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth під’єднано."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Відсоток заряду акумулятора невідомий."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Підключено до <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Яскравість"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Інверсія кольорів"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Корекція кольору"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Керувати користувачами"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Готово"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Закрити"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Під’єднано"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Мікрофон доступний"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Камера доступна"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Мікрофон і камера доступні"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Мікрофон увімкнено"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Мікрофон вимкнено"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Мікрофон увімкнено для всіх додатків і сервісів."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Доступ до мікрофона вимкнено для всіх додатків і сервісів. Щоб надати доступ до мікрофона, виберіть \"Налаштування\" > \"Конфіденційність\" > \"Мікрофон\"."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Доступ до мікрофона вимкнено для всіх додатків і сервісів. Щоб змінити це, виберіть \"Налаштування\" > \"Конфіденційність\" > \"Мікрофон\"."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Камеру ввімкнено"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Камеру вимкнено"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Камеру ввімкнено для всіх додатків і сервісів."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Доступ до камери вимкнено для всіх додатків і сервісів."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Щоб використовувати кнопку мікрофона, надайте доступ до мікрофона в налаштуваннях."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Відкрити налаштування"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Інший пристрій"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Увімкнути або вимкнути огляд"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Ви отримуватиме звукові та вібросигнали лише для вибраних будильників, нагадувань, подій і абонентів. Однак ви чутимете все, що захочете відтворити, зокрема музику, відео й ігри."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Коли ви показуєте, записуєте або транслюєте додаток, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> отримує доступ до всього, що відображається або відтворюється в цьому додатку. Тому будьте уважні з паролями, повідомленнями, платіжною й іншою конфіденційною інформацією."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Продовжити"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Показувати або записувати додаток"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Очистити все"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Керувати"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Історія"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Вимкнути мобільний Інтернет?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Ви не матимете доступу до даних чи Інтернету через оператора <xliff:g id="CARRIER">%s</xliff:g>. Інтернет буде доступний лише через Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"ваш оператор"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Перейти на <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Пристрій не перемикатиметься на мобільний Інтернет автоматично"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Ні, дякую"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Так, перемикатися"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Не вдається підтвердити вашу відповідь у налаштуваннях, оскільки інший додаток заступає запит на дозвіл."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Дозволити додатку <xliff:g id="APP_0">%1$s</xliff:g> показувати фрагменти додатка <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Має доступ до інформації з додатка <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Налаштування розміру лупи"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Кнопка спеціальних можливостей. Змініть або замініть її в Налаштуваннях.\n\n"<annotation id="link">"Переглянути налаштування"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Щоб тимчасово сховати кнопку, перемістіть її на край екрана"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Відмінити"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{Функцію спеціальних можливостей \"{label}\" вилучено}one{# функцію спеціальних можливостей вилучено}few{# функції спеціальних можливостей вилучено}many{# функцій спеціальних можливостей вилучено}other{# функції спеціальних можливостей вилучено}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Перемістити ліворуч угору"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Перемістити праворуч угору"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Перемістити ліворуч униз"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Перемістити праворуч униз"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Перемістити до краю, приховати"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Перемістити від краю, показати"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Вилучити"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"перемкнути"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Керування пристроями"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Виберіть, для якого додатка налаштувати елементи керування"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобільний трафік"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Підключено"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Тимчасово з’єднано"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Погане з’єднання"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Мобільний Інтернет не підключатиметься автоматично"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Немає з\'єднання"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Інші мережі недоступні"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 10e0e68..f1992a7 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"چہرے کی پہچان نہیں ہو سکی"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"اس کے بجائے فنگر پرنٹ استعمال کریں"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"بلوٹوتھ مربوط ہے۔"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"بیٹری کی فیصد نامعلوم ہے۔"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> سے منسلک ہیں۔"</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"چمکیلا پن"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"رنگوں کی تقلیب"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"رنگ کی اصلاح"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"صارفین کا نظم کریں"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"ہو گیا"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"بند کریں"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"مربوط"</string>
@@ -304,6 +305,28 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"مائیکروفون دستیاب ہے"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"کیمرا دستیاب ہے"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"مائیکروفون اور کیمرا دستیاب ہیں"</string>
+ <!-- no translation found for sensor_privacy_mic_turned_on_dialog_title (6348853159838376513) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_turned_off_dialog_title (5760464281790732849) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_unblocked_dialog_content (4889961886199270224) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_blocked_no_exception_dialog_content (5864898470772965394) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_blocked_with_exception_dialog_content (810289713700437896) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_turned_on_dialog_title (8039095295100075952) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_turned_off_dialog_title (1936603903120742696) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_unblocked_dialog_content (7847190103011782278) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_blocked_dialog_content (3182428709314874616) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_htt_blocked_dialog_content (3333321592997666441) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_dialog_open_settings (1503088305279285048) -->
+ <skip />
<string name="media_seamless_other_device" msgid="4654849800789196737">"دوسرا آلہ"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"مجموعی جائزہ ٹوگل کریں"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"الارمز، یاددہانیوں، ایونٹس اور آپ کے متعین کردہ کالرز کے علاوہ، آپ آوازوں اور وائبریشنز سے ڈسٹرب نہیں ہوں گے۔ موسیقی، ویڈیوز اور گیمز سمیت آپ ابھی بھی ہر وہ چیز سنیں گے جسے چلانے کا آپ انتخاب کرتے ہیں۔"</string>
@@ -374,6 +397,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"جب آپ اشتراک، ریکارڈنگ یا کاسٹ کر رہے ہوتے ہیں تو <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> کو آپ کی اسکرین پر دکھائی گئی یا آپ کے آلے پر چلائی گئی ہر چیز تک رسائی حاصل ہوتی ہے۔ اس لیے پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، یا دیگر حساس معلومات کے سلسلے میں محتاط رہیں۔"</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"جاری رکھیں"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"ایپ کا اشتراک یا ریکارڈ کریں"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"سبھی کو صاف کریں"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"نظم کریں"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"سرگزشت"</string>
@@ -728,14 +761,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"موبائل ڈیٹا آف کریں؟"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"آپ کو <xliff:g id="CARRIER">%s</xliff:g> کے ذریعے ڈیٹا یا انٹرنیٹ تک رسائی حاصل نہیں ہوگی۔ انٹرنیٹ صرف Wi-Fi کے ذریعے دستیاب ہوگا۔"</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"آپ کا کریئر"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"<xliff:g id="CARRIER">%s</xliff:g> پر واپس سوئچ کریں؟"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"دستیابی کی بنیاد پر موبائل ڈیٹا خودکار طور پر تبدیل نہیں ہوگا"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"نہیں شکریہ"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"ہاں، سوئچ کریں"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"چونکہ ایک ایپ اجازت کی درخواست کو مبہم کر رہی ہے، لہذا ترتیبات آپ کے جواب کی توثیق نہیں کر سکتی ہیں۔"</string>
<string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> کو <xliff:g id="APP_2">%2$s</xliff:g> کے سلائسز دکھانے کی اجازت دیں؟"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- یہ <xliff:g id="APP">%1$s</xliff:g> کی معلومات پڑھ سکتا ہے"</string>
@@ -794,18 +823,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"میگنیفائر ونڈو کی ترتیبات"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ایکسیسبیلٹی خصوصیات کھولنے کے لیے تھپتھپائیں۔ ترتیبات میں اس بٹن کو حسب ضرورت بنائیں یا تبدیل کریں۔\n\n"<annotation id="link">"ترتیبات ملاحظہ کریں"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"عارضی طور پر بٹن کو چھپانے کے لئے اسے کنارے پر لے جائیں"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"کالعدم کریں"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} شارٹ کٹ ہٹا دیا گیا}other{# شارٹ کٹس ہٹا دیے گئے}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"اوپر بائیں جانب لے جائیں"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"اوپر دائیں جانب لے جائيں"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"نیچے بائیں جانب لے جائیں"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"نیچے دائیں جانب لے جائیں"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"EDGE پر لے جائیں اور چھپائیں"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"EDGE اور شو سے باہر منتقل کریں"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"ہٹائیں"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ٹوگل کریں"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"آلہ کے کنٹرولز"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"کنٹرولز شامل کرنے کے لیے ایپ منتخب کریں"</string>
@@ -948,10 +974,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"موبائل ڈیٹا"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="NETWORKMODE">%2$s</xliff:g> / <xliff:g id="STATE">%1$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"منسلک ہے"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"عارضی طور پر منسلک ہے"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"کمزور کنکشن"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"موبائل ڈیٹا خودکار طور پر منسلک نہیں ہوگا"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"کوئی کنکشن نہیں"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"کوئی دوسرا نیٹ ورک دستیاب نہیں ہے"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 184044d..96e031a 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Yuz aniqlanmadi"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Barmoq izi orqali urining"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth ulandi."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Batareya quvvati foizi nomaʼlum."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Ulangan: <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Yorqinlik"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Ranglarni akslantirish"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Ranglarni tuzatish"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Foydalanuvchilarni boshqarish"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Tayyor"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Yopish"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Ulangan"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Mikrofon mavjud"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Kamera mavjud"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Mikrofon va kamera mavjud"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Mikrofon yoqildi"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Mikrofon oʻchirildi"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Mikrofon barcha ilovalar va xizmatlar uchun yoqilgan."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Mikrofon ruxsati barcha ilovalar va xizmatlar uchun oʻchirilgan. Sozlamalar > Maxfiylik > Mikrofon orqali mikrofon ruxsatini yoqishingiz mumkin."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Mikrofon ruxsati barcha ilovalar va xizmatlar uchun oʻchirilgan. Buni Sozlamalar > Maxfiylik > Mikrofon menyusida oʻzgartirishingiz mumkin."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Kamera yoqildi"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Kamera oʻchirildi"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Kamera barcha ilovalar va xizmatlar uchun yoqilgan."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Kamera ruxsati barcha ilovalar va xizmatlar uchun oʻchirilgan."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Mikrofon tugmasidan foydalanish uchun Sozlamalar orqali mikrofon ruxsatini yoqing."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Sozlamalarni ochish."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Boshqa qurilma"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Umumiy nazar rejimini almashtirish"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Turli ovoz va tebranishlar endi sizni bezovta qilmaydi. Biroq, signallar, eslatmalar, tadbirlar haqidagi bildirishnomalar va siz tanlagan abonentlardan kelgan chaqiruvlar bundan mustasno. Lekin, ijro etiladigan barcha narsalar, jumladan, musiqa, video va o‘yinlar ovozi eshitiladi."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Ulashish, yozib olish va translatsiya qilish vaqtida <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ilovasi ekranda chiqadigan yoki qurilmada ijro qilinadigan kontentni koʻra oladi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar yoki boshqa maxfiy axborot chiqmasligi uchun ehtiyot boʻling."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Davom etish"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Ilovada ulashish yoki yozib olish"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Hammasini tozalash"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Boshqarish"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Tarix"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Mobil internet uzilsinmi?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"<xliff:g id="CARRIER">%s</xliff:g> orqali internetdan foydalana olmaysiz. Internet faqat Wi-Fi orqali ishlaydi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"aloqa operatoringiz"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"<xliff:g id="CARRIER">%s</xliff:g> xizmati qaytarilsinmi?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Mobil internet mavjudligi asosida avtomatik almashtirilmaydi"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Kerak emas"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Ha, almashtirilsin"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Ilova ruxsatnoma so‘roviga xalaqit qilayotgani tufayli, “Sozlamalar” ilovasi javobingizni tekshira olmaydi."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> ilovasiga <xliff:g id="APP_2">%2$s</xliff:g> ilovasidan fragmentlar ko‘rsatishga ruxsat berilsinmi?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"– <xliff:g id="APP">%1$s</xliff:g> ma’lumotlarini o‘qiy oladi"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Lupa oynasi sozlamalari"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Maxsus imkoniyatlarni ochish uchun bosing Sozlamalardan moslay yoki almashtira olasiz.\n\n"<annotation id="link">"Sozlamalar"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Vaqtinchalik berkitish uchun tugmani qirra tomon suring"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Bekor qilish"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{{label} ta yorliq olindi}other{# ta yorliq olindi}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Yuqori chapga surish"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Yuqori oʻngga surish"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Quyi chapga surish"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Quyi oʻngga surish"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Chetiga olib borish va yashirish"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Chetidan qaytarish va koʻrsatish"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Olib tashlash"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"oʻzgartirish"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Qurilmalarni boshqarish"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Boshqaruv elementlarini kiritish uchun ilovani tanlang"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobil internet"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Ulandi"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Vaqtincha ulangan"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Aloqa beqaror"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobil internetga avtomatik ulanmaydi"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Internetga ulanmagansiz"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Boshqa tarmoqlar mavjud emas"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 8ea3408..b253a91 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Không nhận ra khuôn mặt"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Hãy dùng vân tay"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Đã kết nối bluetooth."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Tỷ lệ phần trăm pin không xác định."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Đã kết nối với <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Độ sáng"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Đảo màu"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Chỉnh màu"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Quản lý người dùng"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Xong"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Đóng"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Đã kết nối"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Micrô đang bật"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Máy ảnh đang bật"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Micrô và máy ảnh đang bật"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"Đã bật micrô"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"Đã tắt micrô"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Mọi ứng dụng và dịch vụ được phép sử dụng micrô."</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Mọi ứng dụng và dịch vụ không có quyền truy cập vào micrô. Bạn có thể bật quyền truy cập vào micrô trong phần Cài đặt > Quyền riêng tư > Micrô."</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Mọi ứng dụng và dịch vụ không có quyền truy cập vào micrô. Bạn có thể thay đổi chế độ này trong phần Cài đặt > Quyền riêng tư > Micrô."</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Đã bật máy ảnh"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Đã tắt máy ảnh"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Mọi ứng dụng và dịch vụ được phép sử dụng máy ảnh."</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Mọi ứng dụng và dịch vụ không có quyền truy cập vào máy ảnh."</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Để dùng nút micrô, hãy bật quyền truy cập vào micrô trong phần Cài đặt."</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"Mở phần cài đặt."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Thiết bị khác"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Bật/tắt chế độ xem Tổng quan"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Bạn sẽ không bị làm phiền bởi âm thanh và tiếng rung, ngoại trừ báo thức, lời nhắc, sự kiện và người gọi mà bạn chỉ định. Bạn sẽ vẫn nghe thấy mọi thứ bạn chọn phát, bao gồm nhạc, video và trò chơi."</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Khi bạn chia sẻ, ghi hoặc truyền ứng dụng, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> sẽ có quyền truy cập vào mọi nội dung xuất hiện hoặc phát trên ứng dụng đó. Vì vậy, hãy thận trọng để không làm lộ mật khẩu, thông tin thanh toán, tin nhắn hoặc thông tin nhạy cảm khác."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Tiếp tục"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Chia sẻ hoặc ghi ứng dụng"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Xóa tất cả"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Quản lý"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Lịch sử"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Tắt dữ liệu di động?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Bạn sẽ không có quyền sử dụng dữ liệu hoặc truy cập Internet thông qua chế độ <xliff:g id="CARRIER">%s</xliff:g>. Bạn chỉ có thể truy cập Internet thông qua Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"nhà mạng của bạn"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Chuyển về <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Dữ liệu di động sẽ không tự động chuyển dựa trên tình trạng phủ sóng"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Không, cảm ơn"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Có, hãy chuyển"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Vì ứng dụng đang che khuất yêu cầu cấp quyền nên Cài đặt không thể xác minh câu trả lời của bạn."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Cho phép <xliff:g id="APP_0">%1$s</xliff:g> hiển thị các lát của <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Có thể đọc thông tin từ <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Chế độ cài đặt cửa sổ phóng to"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Nhấn để mở bộ tính năng hỗ trợ tiếp cận. Tuỳ chỉnh/thay thế nút này trong phần Cài đặt.\n\n"<annotation id="link">"Xem chế độ cài đặt"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Di chuyển nút sang cạnh để ẩn nút tạm thời"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Huỷ"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{Đã xoá {label} lối tắt}other{Đã xoá # lối tắt}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Chuyển lên trên cùng bên trái"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Chuyển lên trên cùng bên phải"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Chuyển tới dưới cùng bên trái"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Chuyển tới dưới cùng bên phải"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Chuyển đến cạnh và ẩn"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Chuyển ra xa cạnh và hiển thị"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Xoá"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"bật/tắt"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Điều khiển thiết bị"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Chọn ứng dụng để thêm các tùy chọn điều khiển"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Dữ liệu di động"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Đã kết nối"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Tạm thời có kết nối"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Kết nối kém"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Dữ liệu di động sẽ không tự động kết nối"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Không có kết nối mạng"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Không có mạng nào khác"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 1cead42..64357a0 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"人脸识别失败"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"改用指纹"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"蓝牙已连接。"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"电池电量百分比未知。"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"已连接到<xliff:g id="BLUETOOTH">%s</xliff:g>。"</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"亮度"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"颜色反转"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"色彩校正"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"管理用户"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"完成"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"关闭"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"已连接"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"麦克风可用"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"摄像头可用"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"麦克风和摄像头可用"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"麦克风已开启"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"麦克风已关闭"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"已允许所有应用和服务访问麦克风。"</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"已阻止所有应用和服务访问麦克风。您可依次前往“设置”>“隐私设置”>“麦克风”来启用麦克风访问权限。"</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"已阻止所有应用和服务访问麦克风。您可依次前往“设置”>“隐私设置”>“麦克风”来更改此权限设置。"</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"摄像头已开启"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"摄像头已关闭"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"已允许所有应用和服务访问摄像头。"</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"已阻止所有应用和服务访问摄像头。"</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"若要使用麦克风按钮,请在“设置”中启用麦克风访问权限。"</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"打开设置。"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"其他设备"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"切换概览"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"您将不会受到声音和振动的打扰(闹钟、提醒、活动和所指定来电者的相关提示音除外)。您依然可以听到您选择播放的任何内容(包括音乐、视频和游戏)的相关音效。"</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"在您进行分享、录制或投射时,<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> 可以访问通过此应用显示或播放的所有内容。因此,请注意保护密码、付款信息、消息或其他敏感信息。"</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"继续"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"分享或录制应用"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"全部清除"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"管理"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"历史记录"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"要关闭移动数据网络吗?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"您将无法通过<xliff:g id="CARRIER">%s</xliff:g>使用移动数据或互联网,只能通过 WLAN 连接到互联网。"</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"您的运营商"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"切换回 <xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"移动流量不会根据可用性自动切换"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"不用了"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"是,切换"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"由于某个应用遮挡了权限请求界面,因此“设置”应用无法验证您的回应。"</string>
<string name="slice_permission_title" msgid="3262615140094151017">"要允许“<xliff:g id="APP_0">%1$s</xliff:g>”显示“<xliff:g id="APP_2">%2$s</xliff:g>”图块吗?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- 可以读取“<xliff:g id="APP">%1$s</xliff:g>”中的信息"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"放大镜窗口设置"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"点按即可打开无障碍功能。您可在“设置”中自定义或更换此按钮。\n\n"<annotation id="link">"查看设置"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"将按钮移到边缘,即可暂时将其隐藏"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"撤消"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{已移除快捷方式 {label}}other{已移除 # 个快捷方式}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"移至左上角"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"移至右上角"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"移至左下角"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"移至右下角"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"移至边缘并隐藏"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"移至边缘以外并显示"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"移除"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"开启/关闭"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"设备控制器"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"选择要添加控制器的应用"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"移动数据网络"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"已连接"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"暂时已连接"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"连接状况不佳"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"系统将不会自动连接到移动数据网络"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"无网络连接"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"没有其他可用网络"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index cb90614..ec9112c 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"無法辨識面孔"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"請改用指紋"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"藍牙連線已建立。"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"電量百分比不明。"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"已連線至<xliff:g id="BLUETOOTH">%s</xliff:g>。"</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"亮度"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"色彩反轉"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"色彩校正"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"管理使用者"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"完成"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"關閉"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"已連線"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"可使用麥克風"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"可使用相機"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"可使用麥克風和相機"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"麥克風已開啟"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"麥克風已關閉"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"已為所有應用程式和服務啟用麥克風。"</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"已停用所有應用程式和服務的麥克風存取權。您可以在 [設定] > [私隱] > [麥克風] 啟用麥克風存取權。"</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"已停用所有應用程式和服務的麥克風存取權。您可以在 [設定] > [私隱] > [麥克風] 更改設定。"</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"相機已開啟"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"相機已關閉"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"已為所有應用程式和服務啟用相機。"</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"已停用所有應用程式和服務的相機存取權。"</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"如要使用麥克風按鈕,請在「設定」中啟用麥克風存取權。"</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"開啟設定。"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"其他裝置"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"切換概覽"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"您不會受到聲音和震動騷擾 (鬧鐘、提醒、活動和您指定的來電者鈴聲除外)。當您選擇播放音樂、影片和遊戲等,仍可以聽到該內容的聲音。"</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"進行分享、錄製或投放時,<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> 可存取顯示在螢幕畫面上或在裝置上播放的所有內容。因此請謹慎處理密碼、付款資料、訊息或其他敏感資料。"</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"繼續"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"分享或錄製應用程式"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"全部清除"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"管理"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"記錄"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"要關閉流動數據嗎?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"您無法透過「<xliff:g id="CARRIER">%s</xliff:g>」使用流動數據或互聯網。如要使用互聯網,您必須連接 Wi-Fi。"</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"您的流動網絡供應商"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"要切換回「<xliff:g id="CARRIER">%s</xliff:g>」嗎?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"流動數據不會根據可用性自動切換"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"不用了,謝謝"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"是,切換回 DDS 對話框"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"由於某個應用程式已阻擋權限要求畫面,因此「設定」應用程式無法驗證您的回應。"</string>
<string name="slice_permission_title" msgid="3262615140094151017">"要允許「<xliff:g id="APP_0">%1$s</xliff:g>」顯示「<xliff:g id="APP_2">%2$s</xliff:g>」的快訊嗎?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- 可以讀取「<xliff:g id="APP">%1$s</xliff:g>」中的資料"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"放大鏡視窗設定"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"㩒一下就可以開無障礙功能。喺「設定」度自訂或者取代呢個按鈕。\n\n"<annotation id="link">"查看設定"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"將按鈕移到邊緣即可暫時隱藏"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"復原"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{已移除 {label} 個捷徑}other{已移除 # 個捷徑}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"移去左上方"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"移去右上方"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"移到左下方"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"移去右下方"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"移到邊緣並隱藏"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"從邊緣移出並顯示"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"移除"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"切換"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"裝置控制"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"選擇要新增控制項的應用程式"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"流動數據"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"已連線"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"已暫時連線"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"連線速度欠佳"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"不會自動連線至流動數據"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"沒有連線"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"沒有可用的其他網絡"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index ccad340..0f82675 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"無法辨識臉孔"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"請改用指紋"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"藍牙連線已建立。"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"電池電量不明。"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"已連線至<xliff:g id="BLUETOOTH">%s</xliff:g>。"</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"亮度"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"色彩反轉"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"色彩校正"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"管理使用者"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"完成"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"關閉"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"已連線"</string>
@@ -304,6 +305,17 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"可使用麥克風"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"可使用相機"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"可使用麥克風和相機"</string>
+ <string name="sensor_privacy_mic_turned_on_dialog_title" msgid="6348853159838376513">"麥克風已開啟"</string>
+ <string name="sensor_privacy_mic_turned_off_dialog_title" msgid="5760464281790732849">"麥克風已關閉"</string>
+ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"所有應用程式和服務的麥克風存取權皆已啟用。"</string>
+ <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"所有應用程式和服務的麥克風存取權皆已停用。如要啟用麥克風存取權,請依序前往「設定」>「隱私權」>「麥克風」。"</string>
+ <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"所有應用程式和服務的麥克風存取權皆已停用。如要變更這項設定,請依序前往「設定」>「隱私權」>「麥克風」。"</string>
+ <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"相機已開啟"</string>
+ <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"相機已關閉"</string>
+ <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"所有應用程式和服務的相機存取權皆已啟用。"</string>
+ <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"所有應用程式和服務的相機存取權皆已停用。"</string>
+ <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"如要使用麥克風按鈕,請前往「設定」啟用麥克風存取權。"</string>
+ <string name="sensor_privacy_dialog_open_settings" msgid="1503088305279285048">"開啟設定。"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"其他裝置"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"切換總覽"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"裝置不會發出音效或震動造成干擾,但是會保留與鬧鐘、提醒、活動和指定來電者有關的設定。如果你選擇播放音樂、影片和遊戲等內容,還是可以聽見相關音訊。"</string>
@@ -374,6 +386,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"進行分享、錄製或投放應用程式時,<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> 可以存取在該應用程式中顯示或播放的所有內容。因此請謹慎處理密碼、付款資料、訊息或其他機密資訊。"</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"繼續"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"分享或錄製應用程式"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"全部清除"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"管理"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"記錄"</string>
@@ -728,14 +750,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"要關閉行動數據嗎?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"你將無法透過「<xliff:g id="CARRIER">%s</xliff:g>」使用行動數據或網際網路。你只能透過 Wi-Fi 使用網際網路。"</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"你的電信業者"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"要切換回「<xliff:g id="CARRIER">%s</xliff:g>」嗎?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"行動數據不會依據可用性自動切換"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"不用了,謝謝"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"是,切換回 DDS 對話方塊"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"由於某個應用程式覆蓋了權限要求畫面,因此「設定」應用程式無法驗證你的回應。"</string>
<string name="slice_permission_title" msgid="3262615140094151017">"要允許「<xliff:g id="APP_0">%1$s</xliff:g>」顯示「<xliff:g id="APP_2">%2$s</xliff:g>」的區塊嗎?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- 它可以讀取「<xliff:g id="APP">%1$s</xliff:g>」的資訊"</string>
@@ -794,18 +812,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"放大鏡視窗設定"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"輕觸即可開啟無障礙功能。你可以前往「設定」自訂或更換這個按鈕。\n\n"<annotation id="link">"查看設定"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"將按鈕移到邊緣處即可暫時隱藏"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"復原"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{已移除 {label} 個捷徑}other{已移除 # 個捷徑}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"移到左上方"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"移到右上方"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"移到左下方"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"移到右下方"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"移到邊緣並隱藏"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"從邊緣移出並顯示"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"移除"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"切換"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"裝置控制"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"選擇應用程式以新增控制項"</string>
@@ -948,10 +963,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"行動數據"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"已連線"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"已暫時建立連線"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"連線品質不佳"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"系統將不會自動使用行動數據連線"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"沒有網路連線"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"沒有可用的其他網路"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index b346494..061592a 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -168,6 +168,8 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Ayikwazi ukubona ubuso"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Kunalokho sebenzisa isigxivizo somunwe"</string>
+ <!-- no translation found for keyguard_face_unlock_unavailable (8145547300240405980) -->
+ <skip />
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth ixhunyiwe"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Iphesenti lebhethri alaziwa."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Xhuma ku-<xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -248,8 +250,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ukugqama"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Ukuguqulwa kombala"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Ukulungiswa kombala"</string>
- <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
- <skip />
+ <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Phatha abasebenzisi"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Kwenziwe"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Vala"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Ixhunyiwe"</string>
@@ -304,6 +305,28 @@
<string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Imakrofoni iyatholakala"</string>
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Ikhamera iyatholakala"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Imakrofoni nekhamera kuyatholakala"</string>
+ <!-- no translation found for sensor_privacy_mic_turned_on_dialog_title (6348853159838376513) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_turned_off_dialog_title (5760464281790732849) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_unblocked_dialog_content (4889961886199270224) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_blocked_no_exception_dialog_content (5864898470772965394) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_mic_blocked_with_exception_dialog_content (810289713700437896) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_turned_on_dialog_title (8039095295100075952) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_turned_off_dialog_title (1936603903120742696) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_unblocked_dialog_content (7847190103011782278) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_camera_blocked_dialog_content (3182428709314874616) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_htt_blocked_dialog_content (3333321592997666441) -->
+ <skip />
+ <!-- no translation found for sensor_privacy_dialog_open_settings (1503088305279285048) -->
+ <skip />
<string name="media_seamless_other_device" msgid="4654849800789196737">"Enye idivayisi"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Guqula ukubuka konke"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Ngeke uphazanyiswe imisindo nokudlidliza, ngaphandle kusukela kuma-alamu, izikhumbuzi, imicimbi, nabafonayo obacacisayo. Usazozwa noma yini okhetha ukuyidlala okufaka umculo, amavidiyo, namageyimu."</string>
@@ -374,6 +397,16 @@
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Uma wabelana, urekhoda, noma usakaza i-app, i-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> inokufinyelela kunoma yini eboniswayo noma edlalwayo kuleyo app. Ngakho-ke qaphela amagama ayimfihlo, imininingwane yokukhokha, imiyalezo, noma olunye ulwazi olubucayi."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Qhubeka"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Yabelana noma rekhoda i-app"</string>
+ <!-- no translation found for media_projection_permission_dialog_system_service_title (6827129613741303726) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_entire_screen (8801616203805837575) -->
+ <skip />
+ <!-- no translation found for media_projection_permission_dialog_system_service_warning_single_app (543310680568419338) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_title (2113331792064527203) -->
+ <skip />
+ <!-- no translation found for screen_capturing_disabled_by_policy_dialog_description (6015975736747696431) -->
+ <skip />
<string name="clear_all_notifications_text" msgid="348312370303046130">"Sula konke"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Phatha"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Umlando"</string>
@@ -728,14 +761,10 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Vala idatha yeselula?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Ngeke ube nokufinyelela kudatha noma ku-inthanethi nge-<xliff:g id="CARRIER">%s</xliff:g>. I-inthanethi izotholakala kuphela nge-Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"inkampani yakho yenethiwekhi"</string>
- <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
- <skip />
- <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
- <skip />
- <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
- <skip />
+ <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Shintshela emuva ku-<xliff:g id="CARRIER">%s</xliff:g>?"</string>
+ <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Idatha yeselula ngeke ishintshe ngokuzenzakalelayo ngokusekelwe ekutholakaleni"</string>
+ <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Cha ngiyabonga"</string>
+ <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Yebo, shintsha"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Ngoba uhlelo lokusebenza lusitha isicelo semvume, Izilungiselelo azikwazi ukuqinisekisa impendulo yakho."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Vumela i-<xliff:g id="APP_0">%1$s</xliff:g> ukuthi ibonise izingcezu ze-<xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Ingafunda ulwazi kusukela ku-<xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -794,18 +823,15 @@
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Amasethingi ewindi lesikhulisi"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Thepha ukuze uvule izakhi zokufinyelela. Enza ngendlela oyifisayo noma shintsha le nkinobho Kumasethingi.\n\n"<annotation id="link">"Buka amasethingi"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Hambisa inkinobho onqenqemeni ukuze uyifihle okwesikhashana"</string>
- <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
- <skip />
+ <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Hlehlisa"</string>
+ <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{Isinqamuleli se-{label} sisusiwe}one{Izinqamuleli ezingu-# zikhishiwe}other{Izinqamuleli ezingu-# zikhishiwe}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Hamba phezulu kwesokunxele"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Hamba phezulu ngakwesokudla"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Hamba phansi ngakwesokunxele"</string>
<string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Hamba phansi ngakwesokudla"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Hamba onqenqemeni ufihle"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Phuma onqenqemeni ubonise"</string>
- <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
- <skip />
+ <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Susa"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"guqula"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Izilawuli zezinsiza"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Khetha uhlelo lokusebenza ukwengeza izilawuli"</string>
@@ -948,10 +974,8 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Idatha yeselula"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Ixhunyiwe"</string>
- <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
- <skip />
- <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
- <skip />
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Ixhume okwesikhashana"</string>
+ <string name="mobile_data_poor_connection" msgid="819617772268371434">"Uxhumo olungeluhle"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Idatha yeselula ngeke ikwazi ukuxhuma ngokuzenzekelayo"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Alukho uxhumano"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Awekho amanye amanethiwekhi atholakalayo"</string>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index df0659d..44ba3f6 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -204,5 +204,10 @@
<attr name="passwordTextAppearance" format="reference" />
<attr name="errorTextAppearance" format="reference"/>
</declare-styleable>
+
+ <declare-styleable name="LogAccessPermissionGrantDialog">
+ <attr name="permissionGrantButtonTopStyle" format="reference"/>
+ <attr name="permissionGrantButtonBottomStyle" format="reference"/>
+ </declare-styleable>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index f02f29a..f9f2195 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -407,7 +407,7 @@
<dimen name="match_parent">-1px</dimen>
<!-- Height of status bar in split shade mode - visible only on large screens -->
- <dimen name="large_screen_shade_header_height">@*android:dimen/quick_qs_offset_height</dimen>
+ <dimen name="large_screen_shade_header_height">48dp</dimen>
<dimen name="large_screen_shade_header_min_height">@dimen/qs_header_row_min_height</dimen>
<dimen name="large_screen_shade_header_left_padding">@dimen/qs_horizontal_margin</dimen>
@@ -762,7 +762,7 @@
<dimen name="keyguard_lock_padding">20dp</dimen>
<dimen name="keyguard_indication_margin_bottom">32dp</dimen>
- <dimen name="lock_icon_margin_bottom">110dp</dimen>
+ <dimen name="lock_icon_margin_bottom">74dp</dimen>
<dimen name="ambient_indication_margin_bottom">71dp</dimen>
@@ -1496,10 +1496,12 @@
<!-- Dream overlay complications related dimensions -->
<dimen name="dream_overlay_complication_clock_time_text_size">86sp</dimen>
+ <dimen name="dream_overlay_complication_clock_time_padding">20dp</dimen>
<dimen name="dream_overlay_complication_clock_subtitle_text_size">24sp</dimen>
<dimen name="dream_overlay_complication_preview_text_size">36sp</dimen>
<dimen name="dream_overlay_complication_preview_icon_padding">28dp</dimen>
<dimen name="dream_overlay_complication_shadow_padding">2dp</dimen>
+ <dimen name="dream_overlay_complication_smartspace_padding">24dp</dimen>
<!-- The position of the end guide, which dream overlay complications can align their start with
if their end is aligned with the parent end. Represented as the percentage over from the
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 4fd25a9..2b6ab30 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -195,5 +195,7 @@
<item type="id" name="pm_lite"/>
<item type="id" name="settings_button_container"/>
+ <item type="id" name="log_access_dialog_allow_button" />
+ <item type="id" name="log_access_dialog_deny_button" />
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d2deb4f..6824d7f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -142,7 +142,7 @@
<string name="usb_debugging_secondary_user_title">USB debugging not allowed</string>
<!-- Message of notification shown when trying to enable USB debugging but a secondary user is the current foreground user. -->
- <string name="usb_debugging_secondary_user_message">The user currently signed in to this device can\'t turn on USB debugging. To use this feature, switch to the primary user.</string>
+ <string name="usb_debugging_secondary_user_message">The user currently signed in to this device can\'t turn on USB debugging. To use this feature, switch to an admin user.</string>
<!-- Title of confirmation dialog for wireless debugging [CHAR LIMIT=80] -->
<string name="hdmi_cec_set_menu_language_title">Do you want to change the system language to <xliff:g id="language" example="German">%1$s</xliff:g>?</string>
@@ -172,7 +172,7 @@
<string name="wifi_debugging_secondary_user_title">Wireless debugging not allowed</string>
<!-- Message of notification shown when trying to enable wireless debugging but a secondary user is the current foreground user. [CHAR LIMIT=NONE] -->
- <string name="wifi_debugging_secondary_user_message">The user currently signed in to this device can\u2019t turn on wireless debugging. To use this feature, switch to the primary user.</string>
+ <string name="wifi_debugging_secondary_user_message">The user currently signed in to this device can\u2019t turn on wireless debugging. To use this feature, switch to an admin user.</string>
<!-- Title of USB contaminant presence dialog [CHAR LIMIT=NONE] -->
<string name="usb_contaminant_title">USB port disabled</string>
@@ -235,6 +235,8 @@
<string name="screenshot_left_boundary_pct">Left boundary <xliff:g id="percent" example="50">%1$d</xliff:g> percent</string>
<!-- Content description for the right boundary of the screenshot being cropped, with the current position as a percentage. [CHAR LIMIT=NONE] -->
<string name="screenshot_right_boundary_pct">Right boundary <xliff:g id="percent" example="50">%1$d</xliff:g> percent</string>
+ <!-- Notification displayed when a screenshot is saved in a work profile. [CHAR LIMIT=NONE] -->
+ <string name="screenshot_work_profile_notification" translatable="false">Work screenshots are saved in the work <xliff:g id="app" example="Files">%1$s</xliff:g> app</string>
<!-- Notification title displayed for screen recording [CHAR LIMIT=50]-->
<string name="screenrecord_name">Screen Recorder</string>
@@ -403,6 +405,8 @@
<string name="keyguard_face_failed">Can\u2019t recognize face</string>
<!-- Message shown to suggest using fingerprint sensor to authenticate after another biometric failed. [CHAR LIMIT=25] -->
<string name="keyguard_suggest_fingerprint">Use fingerprint instead</string>
+ <!-- Message shown to inform the user that face unlock is not available. [CHAR LIMIT=65] -->
+ <string name="keyguard_face_unlock_unavailable">Face unlock unavailable.</string>
<!-- Content description of the bluetooth icon when connected for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_bluetooth_connected">Bluetooth connected.</string>
@@ -1045,6 +1049,21 @@
<!-- Title of the dialog that allows to select an app to share or record [CHAR LIMIT=NONE] -->
<string name="media_projection_permission_app_selector_title">Share or record an app</string>
+ <!-- Media projection permission dialog title when there is no app name (e.g. it could be a system service when casting). [CHAR LIMIT=100] -->
+ <string name="media_projection_permission_dialog_system_service_title">Allow this app to share or record?</string>
+
+ <!-- Media projection permission warning for capturing the whole screen when a system service requests it (e.g. when casting). [CHAR LIMIT=350] -->
+ <string name="media_projection_permission_dialog_system_service_warning_entire_screen">When you\'re sharing, recording, or casting, this app has access to anything visible on your screen or played on your device. So be careful with passwords, payment details, messages, or other sensitive information.</string>
+
+ <!-- Media projection permission warning for capturing a single app when a system service requests it (e.g. when casting). [CHAR LIMIT=350] -->
+ <string name="media_projection_permission_dialog_system_service_warning_single_app">When you\'re sharing, recording, or casting an app, this app has access to anything shown or played on that app. So be careful with passwords, payment details, messages, or other sensitive information.</string>
+
+ <!-- Title for the dialog that is shown when screen capturing is disabled by enterprise policy. [CHAR LIMIT=100] -->
+ <string name="screen_capturing_disabled_by_policy_dialog_title">Blocked by your IT admin</string>
+
+ <!-- Description for the dialog that is shown when screen capturing is disabled by enterprise policy. [CHAR LIMIT=350] -->
+ <string name="screen_capturing_disabled_by_policy_dialog_description">Screen capturing is disabled by device policy</string>
+
<!-- The text to clear all notifications. [CHAR LIMIT=60] -->
<string name="clear_all_notifications_text">Clear all</string>
@@ -1350,7 +1369,7 @@
<string name="wallet_lockscreen_settings_label">Lock screen settings</string>
<!-- QR Code Scanner label, title [CHAR LIMIT=32] -->
- <string name="qr_code_scanner_title">Scan QR code</string>
+ <string name="qr_code_scanner_title">QR code scanner</string>
<!-- Name of the work status bar icon. -->
<string name="status_bar_work">Work profile</string>
@@ -2753,4 +2772,19 @@
<!-- Time format for the Dream Time Complication for 24-hour time format [CHAR LIMIT=NONE] -->
<string name="dream_time_complication_24_hr_time_format">kk:mm</string>
+
+ <!-- Title for the log access confirmation dialog. [CHAR LIMIT=NONE] -->
+ <string name="log_access_confirmation_title">Allow <xliff:g id="log_access_app_name" example="Example App">%s</xliff:g> to access all device logs?</string>
+ <!-- Label for the allow button on the log access confirmation dialog. [CHAR LIMIT=40] -->
+ <string name="log_access_confirmation_allow">Allow one-time access</string>
+ <!-- Label for the deny button on the log access confirmation dialog. [CHAR LIMIT=20] -->
+ <string name="log_access_confirmation_deny">Don\u2019t allow</string>
+
+ <!-- Content for the log access confirmation dialog. [CHAR LIMIT=NONE]-->
+ <string name="log_access_confirmation_body">Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps you trust to access all device logs.
+ \n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device.
+ </string>
+
+ <!-- Learn more URL for the log access confirmation dialog. [DO NOT TRANSLATE]-->
+ <string name="log_access_confirmation_learn_more" translatable="false"><a href="https://support.google.com/android?p=system_logs#topic=7313011">Learn more</a></string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index e76887b..4e4bfe2 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -1274,4 +1274,45 @@
<item name="android:textColor">?androidprv:attr/textColorOnAccent</item>
<item name="android:textSize">@dimen/broadcast_dialog_btn_text_size</item>
</style>
+
+
+ <!-- The style for log access consent dialog -->
+ <style name="LogAccessDialogTheme" parent="@style/Theme.SystemUI.Dialog.Alert">
+ <item name="permissionGrantButtonTopStyle">@style/PermissionGrantButtonTop</item>
+ <item name="permissionGrantButtonBottomStyle">@style/PermissionGrantButtonBottom</item>
+ </style>
+
+ <style name="AllowLogAccess">
+ <item name="android:textSize">24sp</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ </style>
+
+ <style name="PrimaryAllowLogAccess">
+ <item name="android:textSize">14sp</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ </style>
+
+ <style name="PermissionGrantButtonTextAppearance">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:textColor">@android:color/system_neutral1_900</item>
+ </style>
+
+ <style name="PermissionGrantButtonTop"
+ parent="@android:style/Widget.DeviceDefault.Button.Borderless.Colored">
+ <item name="android:layout_width">332dp</item>
+ <item name="android:layout_height">56dp</item>
+ <item name="android:layout_marginTop">2dp</item>
+ <item name="android:layout_marginBottom">2dp</item>
+ <item name="android:background">@drawable/grant_permissions_buttons_top</item>
+ </style>
+
+ <style name="PermissionGrantButtonBottom"
+ parent="@android:style/Widget.DeviceDefault.Button.Borderless.Colored">
+ <item name="android:layout_width">332dp</item>
+ <item name="android:layout_height">56dp</item>
+ <item name="android:layout_marginTop">2dp</item>
+ <item name="android:layout_marginBottom">2dp</item>
+ <item name="android:background">@drawable/grant_permissions_buttons_bottom</item>
+ </style>
</resources>
diff --git a/packages/SystemUI/res/xml/large_screen_shade_header.xml b/packages/SystemUI/res/xml/large_screen_shade_header.xml
index cdbf8ab..06d425c 100644
--- a/packages/SystemUI/res/xml/large_screen_shade_header.xml
+++ b/packages/SystemUI/res/xml/large_screen_shade_header.xml
@@ -107,7 +107,7 @@
android:id="@+id/privacy_container">
<Layout
android:layout_width="wrap_content"
- android:layout_height="0dp"
+ android:layout_height="@dimen/large_screen_shade_header_min_height"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/date"
app:layout_constraintBottom_toBottomOf="@id/date"
diff --git a/packages/SystemUI/res/xml/qqs_header.xml b/packages/SystemUI/res/xml/qqs_header.xml
index 88b4f43..af4be1a 100644
--- a/packages/SystemUI/res/xml/qqs_header.xml
+++ b/packages/SystemUI/res/xml/qqs_header.xml
@@ -98,7 +98,7 @@
android:id="@+id/privacy_container">
<Layout
android:layout_width="wrap_content"
- android:layout_height="0dp"
+ android:layout_height="@dimen/large_screen_shade_header_min_height"
app:layout_constraintStart_toEndOf="@id/date"
app:layout_constraintEnd_toEndOf="@id/end_guide"
app:layout_constraintTop_toTopOf="parent"
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index ffaeaaa..485a0d3 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -52,7 +52,11 @@
"SystemUIUnfoldLib",
"androidx.dynamicanimation_dynamicanimation",
"androidx.concurrent_concurrent-futures",
- "gson",
+ "androidx.lifecycle_lifecycle-runtime-ktx",
+ "androidx.lifecycle_lifecycle-viewmodel-ktx",
+ "androidx.recyclerview_recyclerview",
+ "kotlinx_coroutines_android",
+ "kotlinx_coroutines",
"dagger2",
"jsr330",
],
@@ -64,6 +68,7 @@
},
min_sdk_version: "current",
plugins: ["dagger2-compiler"],
+ kotlincflags: ["-Xjvm-default=enable"],
}
java_library {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
index f7049cf..196f7f0 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
@@ -22,9 +22,19 @@
import android.os.Parcel
import android.os.Parcelable
+/**
+ * Base interface for flags that can change value on a running device.
+ * @property id unique id to help identify this flag. Must be unique. This will be removed soon.
+ * @property teamfood Set to true to include this flag as part of the teamfood flag. This will
+ * be removed soon.
+ * @property name Used for server-side flagging where appropriate. Also used for display. No spaces.
+ * @property namespace The server-side namespace that this flag lives under.
+ */
interface Flag<T> {
val id: Int
val teamfood: Boolean
+ val name: String
+ val namespace: String
}
interface ParcelableFlag<T> : Flag<T>, Parcelable {
@@ -38,13 +48,10 @@
}
interface DeviceConfigFlag<T> : Flag<T> {
- val name: String
- val namespace: String
val default: T
}
interface SysPropFlag<T> : Flag<T> {
- val name: String
val default: T
}
@@ -56,6 +63,8 @@
// Consider using the "parcelize" kotlin library.
abstract class BooleanFlag constructor(
override val id: Int,
+ override val name: String,
+ override val namespace: String,
override val default: Boolean = false,
override val teamfood: Boolean = false,
override val overridden: Boolean = false
@@ -71,6 +80,8 @@
private constructor(parcel: Parcel) : this(
id = parcel.readInt(),
+ name = parcel.readString(),
+ namespace = parcel.readString(),
default = parcel.readBoolean(),
teamfood = parcel.readBoolean(),
overridden = parcel.readBoolean()
@@ -78,6 +89,8 @@
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(id)
+ parcel.writeString(name)
+ parcel.writeString(namespace)
parcel.writeBoolean(default)
parcel.writeBoolean(teamfood)
parcel.writeBoolean(overridden)
@@ -89,30 +102,36 @@
*
* It can be changed or overridden in debug builds but not in release builds.
*/
-data class UnreleasedFlag @JvmOverloads constructor(
+data class UnreleasedFlag constructor(
override val id: Int,
+ override val name: String,
+ override val namespace: String,
override val teamfood: Boolean = false,
override val overridden: Boolean = false
-) : BooleanFlag(id, false, teamfood, overridden)
+) : BooleanFlag(id, name, namespace, false, teamfood, overridden)
/**
- * A Flag that is is true by default.
+ * A Flag that is true by default.
*
* It can be changed or overridden in any build, meaning it can be turned off if needed.
*/
-data class ReleasedFlag @JvmOverloads constructor(
+data class ReleasedFlag constructor(
override val id: Int,
+ override val name: String,
+ override val namespace: String,
override val teamfood: Boolean = false,
override val overridden: Boolean = false
-) : BooleanFlag(id, true, teamfood, overridden)
+) : BooleanFlag(id, name, namespace, true, teamfood, overridden)
/**
* A Flag that reads its default values from a resource overlay instead of code.
*
* Prefer [UnreleasedFlag] and [ReleasedFlag].
*/
-data class ResourceBooleanFlag @JvmOverloads constructor(
+data class ResourceBooleanFlag constructor(
override val id: Int,
+ override val name: String,
+ override val namespace: String,
@BoolRes override val resourceId: Int,
override val teamfood: Boolean = false
) : ResourceFlag<Boolean>
@@ -124,7 +143,7 @@
*
* Prefer [UnreleasedFlag] and [ReleasedFlag].
*/
-data class DeviceConfigBooleanFlag @JvmOverloads constructor(
+data class DeviceConfigBooleanFlag constructor(
override val id: Int,
override val name: String,
override val namespace: String,
@@ -139,17 +158,20 @@
*
* Prefer [UnreleasedFlag] and [ReleasedFlag].
*/
-data class SysPropBooleanFlag @JvmOverloads constructor(
+data class SysPropBooleanFlag constructor(
override val id: Int,
override val name: String,
- override val default: Boolean = false
+ override val namespace: String,
+ override val default: Boolean = false,
) : SysPropFlag<Boolean> {
// TODO(b/223379190): Teamfood not supported for sysprop flags yet.
override val teamfood: Boolean = false
}
-data class StringFlag @JvmOverloads constructor(
+data class StringFlag constructor(
override val id: Int,
+ override val name: String,
+ override val namespace: String,
override val default: String = "",
override val teamfood: Boolean = false,
override val overridden: Boolean = false
@@ -164,23 +186,31 @@
private constructor(parcel: Parcel) : this(
id = parcel.readInt(),
+ name = parcel.readString(),
+ namespace = parcel.readString(),
default = parcel.readString() ?: ""
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(id)
+ parcel.writeString(name)
+ parcel.writeString(namespace)
parcel.writeString(default)
}
}
-data class ResourceStringFlag @JvmOverloads constructor(
+data class ResourceStringFlag constructor(
override val id: Int,
+ override val name: String,
+ override val namespace: String,
@StringRes override val resourceId: Int,
override val teamfood: Boolean = false
) : ResourceFlag<String>
-data class IntFlag @JvmOverloads constructor(
+data class IntFlag constructor(
override val id: Int,
+ override val name: String,
+ override val namespace: String,
override val default: Int = 0,
override val teamfood: Boolean = false,
override val overridden: Boolean = false
@@ -196,25 +226,33 @@
private constructor(parcel: Parcel) : this(
id = parcel.readInt(),
+ name = parcel.readString(),
+ namespace = parcel.readString(),
default = parcel.readInt()
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(id)
+ parcel.writeString(name)
+ parcel.writeString(namespace)
parcel.writeInt(default)
}
}
-data class ResourceIntFlag @JvmOverloads constructor(
+data class ResourceIntFlag constructor(
override val id: Int,
+ override val name: String,
+ override val namespace: String,
@IntegerRes override val resourceId: Int,
override val teamfood: Boolean = false
) : ResourceFlag<Int>
-data class LongFlag @JvmOverloads constructor(
+data class LongFlag constructor(
override val id: Int,
override val default: Long = 0,
override val teamfood: Boolean = false,
+ override val name: String,
+ override val namespace: String,
override val overridden: Boolean = false
) : ParcelableFlag<Long> {
@@ -228,17 +266,23 @@
private constructor(parcel: Parcel) : this(
id = parcel.readInt(),
+ name = parcel.readString(),
+ namespace = parcel.readString(),
default = parcel.readLong()
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(id)
+ parcel.writeString(name)
+ parcel.writeString(namespace)
parcel.writeLong(default)
}
}
-data class FloatFlag @JvmOverloads constructor(
+data class FloatFlag constructor(
override val id: Int,
+ override val name: String,
+ override val namespace: String,
override val default: Float = 0f,
override val teamfood: Boolean = false,
override val overridden: Boolean = false
@@ -254,23 +298,31 @@
private constructor(parcel: Parcel) : this(
id = parcel.readInt(),
+ name = parcel.readString(),
+ namespace = parcel.readString(),
default = parcel.readFloat()
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(id)
+ parcel.writeString(name)
+ parcel.writeString(namespace)
parcel.writeFloat(default)
}
}
-data class ResourceFloatFlag @JvmOverloads constructor(
+data class ResourceFloatFlag constructor(
override val id: Int,
+ override val name: String,
+ override val namespace: String,
override val resourceId: Int,
- override val teamfood: Boolean = false
+ override val teamfood: Boolean = false,
) : ResourceFlag<Int>
-data class DoubleFlag @JvmOverloads constructor(
+data class DoubleFlag constructor(
override val id: Int,
+ override val name: String,
+ override val namespace: String,
override val default: Double = 0.0,
override val teamfood: Boolean = false,
override val overridden: Boolean = false
@@ -286,11 +338,15 @@
private constructor(parcel: Parcel) : this(
id = parcel.readInt(),
+ name = parcel.readString(),
+ namespace = parcel.readString(),
default = parcel.readDouble()
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(id)
+ parcel.writeString(name)
+ parcel.writeString(namespace)
parcel.writeDouble(default)
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagSerializer.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagSerializer.kt
index e9ea19d..eeb6031 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagSerializer.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagSerializer.kt
@@ -24,6 +24,7 @@
private const val FIELD_TYPE = "type"
private const val TYPE_BOOLEAN = "boolean"
private const val TYPE_STRING = "string"
+private const val TYPE_INT = "int"
private const val TAG = "FlagSerializer"
@@ -77,4 +78,10 @@
JSONObject::getString
)
+object IntFlagSerializer : FlagSerializer<Int>(
+ TYPE_INT,
+ JSONObject::put,
+ JSONObject::getInt
+)
+
class InvalidFlagStorageException : Exception("Data found but is invalid")
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index 48821e8..601cb66 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -28,7 +28,7 @@
import com.android.systemui.plugins.ClockProviderPlugin
import com.android.systemui.plugins.PluginListener
import com.android.systemui.shared.plugins.PluginManager
-import com.google.gson.Gson
+import org.json.JSONObject
private val TAG = ClockRegistry::class.simpleName
private const val DEBUG = true
@@ -47,7 +47,6 @@
fun onClockChanged()
}
- private val gson = Gson()
private val availableClocks = mutableMapOf<ClockId, ClockInfo>()
private val clockChangeListeners = mutableListOf<ClockChangeListener>()
private val settingObserver = object : ContentObserver(handler) {
@@ -70,7 +69,7 @@
context.contentResolver,
Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE
)
- gson.fromJson(json, ClockSetting::class.java)?.clockId ?: DEFAULT_CLOCK_ID
+ ClockSetting.deserialize(json)?.clockId ?: DEFAULT_CLOCK_ID
} catch (ex: Exception) {
Log.e(TAG, "Failed to parse clock setting", ex)
DEFAULT_CLOCK_ID
@@ -78,7 +77,7 @@
}
set(value) {
try {
- val json = gson.toJson(ClockSetting(value, System.currentTimeMillis()))
+ val json = ClockSetting.serialize(ClockSetting(value, System.currentTimeMillis()))
Settings.Secure.putString(
context.contentResolver,
Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE, json
@@ -198,8 +197,27 @@
)
@Keep
- private data class ClockSetting(
+ data class ClockSetting(
val clockId: ClockId,
val _applied_timestamp: Long?
- )
+ ) {
+ companion object {
+ private val KEY_CLOCK_ID = "clockId"
+ private val KEY_TIMESTAMP = "_applied_timestamp"
+
+ fun serialize(setting: ClockSetting): String {
+ return JSONObject()
+ .put(KEY_CLOCK_ID, setting.clockId)
+ .put(KEY_TIMESTAMP, setting._applied_timestamp)
+ .toString()
+ }
+
+ fun deserialize(jsonStr: String): ClockSetting {
+ val json = JSONObject(jsonStr)
+ return ClockSetting(
+ json.getString(KEY_CLOCK_ID),
+ if (!json.isNull(KEY_TIMESTAMP)) json.getLong(KEY_TIMESTAMP) else null)
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/keyguard/data/content/KeyguardQuickAffordanceProviderClient.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/keyguard/data/content/KeyguardQuickAffordanceProviderClient.kt
new file mode 100644
index 0000000..8612b3a
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/keyguard/data/content/KeyguardQuickAffordanceProviderClient.kt
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.shared.keyguard.data.content
+
+import android.annotation.SuppressLint
+import android.content.ContentValues
+import android.content.Context
+import android.database.ContentObserver
+import android.graphics.drawable.Drawable
+import android.net.Uri
+import android.os.UserHandle
+import androidx.annotation.DrawableRes
+import com.android.systemui.shared.keyguard.data.content.KeyguardQuickAffordanceProviderContract as Contract
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.withContext
+
+/** Collection of utility functions for using a content provider implementing the [Contract]. */
+object KeyguardQuickAffordanceProviderClient {
+
+ /**
+ * Selects an affordance with the given ID for a slot on the lock screen with the given ID.
+ *
+ * Note that the maximum number of selected affordances on this slot is automatically enforced.
+ * Selecting a slot that is already full (e.g. already has a number of selected affordances at
+ * its maximum capacity) will automatically remove the oldest selected affordance before adding
+ * the one passed in this call. Additionally, selecting an affordance that's already one of the
+ * selected affordances on the slot will move the selected affordance to the newest location in
+ * the slot.
+ */
+ suspend fun insertSelection(
+ context: Context,
+ slotId: String,
+ affordanceId: String,
+ dispatcher: CoroutineDispatcher = Dispatchers.IO,
+ ) {
+ withContext(dispatcher) {
+ context.contentResolver.insert(
+ Contract.SelectionTable.URI,
+ ContentValues().apply {
+ put(Contract.SelectionTable.Columns.SLOT_ID, slotId)
+ put(Contract.SelectionTable.Columns.AFFORDANCE_ID, affordanceId)
+ }
+ )
+ }
+ }
+
+ /** Returns all available slots supported by the device. */
+ suspend fun querySlots(
+ context: Context,
+ dispatcher: CoroutineDispatcher = Dispatchers.IO,
+ ): List<Slot> {
+ return withContext(dispatcher) {
+ context.contentResolver
+ .query(
+ Contract.SlotTable.URI,
+ null,
+ null,
+ null,
+ null,
+ )
+ ?.use { cursor ->
+ buildList {
+ val idColumnIndex = cursor.getColumnIndex(Contract.SlotTable.Columns.ID)
+ val capacityColumnIndex =
+ cursor.getColumnIndex(Contract.SlotTable.Columns.CAPACITY)
+ if (idColumnIndex == -1 || capacityColumnIndex == -1) {
+ return@buildList
+ }
+
+ while (cursor.moveToNext()) {
+ add(
+ Slot(
+ id = cursor.getString(idColumnIndex),
+ capacity = cursor.getInt(capacityColumnIndex),
+ )
+ )
+ }
+ }
+ }
+ }
+ ?: emptyList()
+ }
+
+ /**
+ * Returns [Flow] for observing the collection of slots.
+ *
+ * @see [querySlots]
+ */
+ fun observeSlots(
+ context: Context,
+ dispatcher: CoroutineDispatcher = Dispatchers.IO,
+ ): Flow<List<Slot>> {
+ return observeUri(
+ context,
+ Contract.SlotTable.URI,
+ )
+ .map { querySlots(context, dispatcher) }
+ }
+
+ /**
+ * Returns all available affordances supported by the device, regardless of current slot
+ * placement.
+ */
+ suspend fun queryAffordances(
+ context: Context,
+ dispatcher: CoroutineDispatcher = Dispatchers.IO,
+ ): List<Affordance> {
+ return withContext(dispatcher) {
+ context.contentResolver
+ .query(
+ Contract.AffordanceTable.URI,
+ null,
+ null,
+ null,
+ null,
+ )
+ ?.use { cursor ->
+ buildList {
+ val idColumnIndex =
+ cursor.getColumnIndex(Contract.AffordanceTable.Columns.ID)
+ val nameColumnIndex =
+ cursor.getColumnIndex(Contract.AffordanceTable.Columns.NAME)
+ val iconColumnIndex =
+ cursor.getColumnIndex(Contract.AffordanceTable.Columns.ICON)
+ if (idColumnIndex == -1 || nameColumnIndex == -1 || iconColumnIndex == -1) {
+ return@buildList
+ }
+
+ while (cursor.moveToNext()) {
+ add(
+ Affordance(
+ id = cursor.getString(idColumnIndex),
+ name = cursor.getString(nameColumnIndex),
+ iconResourceId = cursor.getInt(iconColumnIndex),
+ )
+ )
+ }
+ }
+ }
+ }
+ ?: emptyList()
+ }
+
+ /**
+ * Returns [Flow] for observing the collection of affordances.
+ *
+ * @see [queryAffordances]
+ */
+ fun observeAffordances(
+ context: Context,
+ dispatcher: CoroutineDispatcher = Dispatchers.IO,
+ ): Flow<List<Affordance>> {
+ return observeUri(
+ context,
+ Contract.AffordanceTable.URI,
+ )
+ .map { queryAffordances(context, dispatcher) }
+ }
+
+ /** Returns the current slot-affordance selections. */
+ suspend fun querySelections(
+ context: Context,
+ dispatcher: CoroutineDispatcher = Dispatchers.IO,
+ ): List<Selection> {
+ return withContext(dispatcher) {
+ context.contentResolver
+ .query(
+ Contract.SelectionTable.URI,
+ null,
+ null,
+ null,
+ null,
+ )
+ ?.use { cursor ->
+ buildList {
+ val slotIdColumnIndex =
+ cursor.getColumnIndex(Contract.SelectionTable.Columns.SLOT_ID)
+ val affordanceIdColumnIndex =
+ cursor.getColumnIndex(Contract.SelectionTable.Columns.AFFORDANCE_ID)
+ if (slotIdColumnIndex == -1 || affordanceIdColumnIndex == -1) {
+ return@buildList
+ }
+
+ while (cursor.moveToNext()) {
+ add(
+ Selection(
+ slotId = cursor.getString(slotIdColumnIndex),
+ affordanceId = cursor.getString(affordanceIdColumnIndex),
+ )
+ )
+ }
+ }
+ }
+ }
+ ?: emptyList()
+ }
+
+ /**
+ * Returns [Flow] for observing the collection of selections.
+ *
+ * @see [querySelections]
+ */
+ fun observeSelections(
+ context: Context,
+ dispatcher: CoroutineDispatcher = Dispatchers.IO,
+ ): Flow<List<Selection>> {
+ return observeUri(
+ context,
+ Contract.SelectionTable.URI,
+ )
+ .map { querySelections(context, dispatcher) }
+ }
+
+ /** Unselects an affordance with the given ID from the slot with the given ID. */
+ suspend fun deleteSelection(
+ context: Context,
+ slotId: String,
+ affordanceId: String,
+ dispatcher: CoroutineDispatcher = Dispatchers.IO,
+ ) {
+ withContext(dispatcher) {
+ context.contentResolver.delete(
+ Contract.SelectionTable.URI,
+ "${Contract.SelectionTable.Columns.SLOT_ID} = ? AND" +
+ " ${Contract.SelectionTable.Columns.AFFORDANCE_ID} = ?",
+ arrayOf(
+ slotId,
+ affordanceId,
+ ),
+ )
+ }
+ }
+
+ /** Unselects all affordances from the slot with the given ID. */
+ suspend fun deleteAllSelections(
+ context: Context,
+ slotId: String,
+ dispatcher: CoroutineDispatcher = Dispatchers.IO,
+ ) {
+ withContext(dispatcher) {
+ context.contentResolver.delete(
+ Contract.SelectionTable.URI,
+ "${Contract.SelectionTable.Columns.SLOT_ID}",
+ arrayOf(
+ slotId,
+ ),
+ )
+ }
+ }
+
+ private fun observeUri(
+ context: Context,
+ uri: Uri,
+ ): Flow<Unit> {
+ return callbackFlow {
+ val observer =
+ object : ContentObserver(null) {
+ override fun onChange(selfChange: Boolean) {
+ trySend(Unit)
+ }
+ }
+
+ context.contentResolver.registerContentObserver(
+ uri,
+ /* notifyForDescendants= */ true,
+ observer,
+ UserHandle.USER_CURRENT,
+ )
+
+ awaitClose { context.contentResolver.unregisterContentObserver(observer) }
+ }
+ .onStart { emit(Unit) }
+ }
+
+ @SuppressLint("UseCompatLoadingForDrawables")
+ suspend fun getAffordanceIcon(
+ context: Context,
+ @DrawableRes iconResourceId: Int,
+ dispatcher: CoroutineDispatcher = Dispatchers.IO,
+ ): Drawable {
+ return withContext(dispatcher) {
+ context.packageManager
+ .getResourcesForApplication(SYSTEM_UI_PACKAGE_NAME)
+ .getDrawable(iconResourceId)
+ }
+ }
+
+ data class Slot(
+ val id: String,
+ val capacity: Int,
+ )
+
+ data class Affordance(
+ val id: String,
+ val name: String,
+ val iconResourceId: Int,
+ )
+
+ data class Selection(
+ val slotId: String,
+ val affordanceId: String,
+ )
+
+ private const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui"
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/keyguard/data/content/KeyguardQuickAffordanceProviderContract.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/keyguard/data/content/KeyguardQuickAffordanceProviderContract.kt
new file mode 100644
index 0000000..c2658a9
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/keyguard/data/content/KeyguardQuickAffordanceProviderContract.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.shared.keyguard.data.content
+
+import android.content.ContentResolver
+import android.net.Uri
+
+/** Contract definitions for querying content about keyguard quick affordances. */
+object KeyguardQuickAffordanceProviderContract {
+
+ const val AUTHORITY = "com.android.systemui.keyguard.quickaffordance"
+ const val PERMISSION = "android.permission.ACCESS_KEYGUARD_QUICK_AFFORDANCES"
+
+ private val BASE_URI: Uri =
+ Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY).build()
+
+ /**
+ * Table for slots.
+ *
+ * Slots are positions where affordances can be placed on the lock screen. Affordances that are
+ * placed on slots are said to be "selected". The system supports the idea of multiple
+ * affordances per slot, though the implementation may limit the number of affordances on each
+ * slot.
+ *
+ * Supported operations:
+ * - Query - to know which slots are available, query the [SlotTable.URI] [Uri]. The result set
+ * will contain rows with the [SlotTable.Columns] columns.
+ */
+ object SlotTable {
+ const val TABLE_NAME = "slots"
+ val URI: Uri = BASE_URI.buildUpon().path(TABLE_NAME).build()
+
+ object Columns {
+ /** String. Unique ID for this slot. */
+ const val ID = "id"
+ /** Integer. The maximum number of affordances that can be placed in the slot. */
+ const val CAPACITY = "capacity"
+ }
+ }
+
+ /**
+ * Table for affordances.
+ *
+ * Affordances are actions/buttons that the user can execute. They are placed on slots on the
+ * lock screen.
+ *
+ * Supported operations:
+ * - Query - to know about all the affordances that are available on the device, regardless of
+ * which ones are currently selected, query the [AffordanceTable.URI] [Uri]. The result set will
+ * contain rows, each with the columns specified in [AffordanceTable.Columns].
+ */
+ object AffordanceTable {
+ const val TABLE_NAME = "affordances"
+ val URI: Uri = BASE_URI.buildUpon().path(TABLE_NAME).build()
+
+ object Columns {
+ /** String. Unique ID for this affordance. */
+ const val ID = "id"
+ /** String. User-visible name for this affordance. */
+ const val NAME = "name"
+ /**
+ * Integer. Resource ID for the drawable to load for this affordance. This is a resource
+ * ID from the system UI package.
+ */
+ const val ICON = "icon"
+ }
+ }
+
+ /**
+ * Table for selections.
+ *
+ * Selections are pairs of slot and affordance IDs.
+ *
+ * Supported operations:
+ * - Insert - to insert an affordance and place it in a slot, insert values for the columns into
+ * the [SelectionTable.URI] [Uri]. The maximum capacity rule is enforced by the system.
+ * Selecting a new affordance for a slot that is already full will automatically remove the
+ * oldest affordance from the slot.
+ * - Query - to know which affordances are set on which slots, query the [SelectionTable.URI]
+ * [Uri]. The result set will contain rows, each of which with the columns from
+ * [SelectionTable.Columns].
+ * - Delete - to unselect an affordance, removing it from a slot, delete from the
+ * [SelectionTable.URI] [Uri], passing in values for each column.
+ */
+ object SelectionTable {
+ const val TABLE_NAME = "selections"
+ val URI: Uri = BASE_URI.buildUpon().path(TABLE_NAME).build()
+
+ object Columns {
+ /** String. Unique ID for the slot. */
+ const val SLOT_ID = "slot_id"
+ /** String. Unique ID for the selected affordance. */
+ const val AFFORDANCE_ID = "affordance_id"
+ }
+ }
+}
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 bfbe88c..abefeba 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
@@ -60,11 +60,6 @@
void onSystemUiStateChanged(int stateFlags) = 16;
/**
- * Sent when the split screen is resized
- */
- void onSplitScreenSecondaryBoundsChanged(in Rect bounds, in Rect insets) = 17;
-
- /**
* Sent when suggested rotation button could be shown
*/
void onRotationProposal(int rotation, boolean isValid) = 18;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index 647dd47..0890465 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -20,7 +20,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
-import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
+import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE;
import android.app.ActivityManager;
import android.app.ActivityManager.TaskDescription;
@@ -255,7 +255,8 @@
// Also consider undefined activity type to include tasks in overview right after rebooting
// the device.
final boolean isDockable = taskInfo.supportsMultiWindow
- && ArrayUtils.contains(CONTROLLED_WINDOWING_MODES, taskInfo.getWindowingMode())
+ && ArrayUtils.contains(
+ CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE, taskInfo.getWindowingMode())
&& (taskInfo.getActivityType() == ACTIVITY_TYPE_UNDEFINED
|| ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, taskInfo.getActivityType()));
return new Task(taskKey,
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
index 40c8774..a790d89 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
@@ -61,26 +61,42 @@
* Updates the matrix based on the provided parameters
*/
public void updateThumbnailMatrix(Rect thumbnailBounds, ThumbnailData thumbnailData,
- int canvasWidth, int canvasHeight, int screenWidthPx, int taskbarSize, boolean isTablet,
+ int canvasWidth, int canvasHeight, int screenWidthPx, int screenHeightPx,
+ int taskbarSize, boolean isTablet,
int currentRotation, boolean isRtl) {
boolean isRotated = false;
boolean isOrientationDifferent;
- float fullscreenTaskWidth = screenWidthPx;
- if (mSplitBounds != null && !mSplitBounds.appsStackedVertically) {
- // For landscape, scale the width
- float taskPercent = mDesiredStagePosition == STAGE_POSITION_TOP_OR_LEFT
- ? mSplitBounds.leftTaskPercent
- : (1 - (mSplitBounds.leftTaskPercent + mSplitBounds.dividerWidthPercent));
- // Scale landscape width to that of actual screen
- fullscreenTaskWidth = screenWidthPx * taskPercent;
- }
int thumbnailRotation = thumbnailData.rotation;
int deltaRotate = getRotationDelta(currentRotation, thumbnailRotation);
RectF thumbnailClipHint = new RectF();
- float canvasScreenRatio = canvasWidth / fullscreenTaskWidth;
- float scaledTaskbarSize = taskbarSize * canvasScreenRatio;
- thumbnailClipHint.bottom = isTablet ? scaledTaskbarSize : 0;
+
+ float scaledTaskbarSize = 0;
+ if (mSplitBounds != null) {
+ float fullscreenTaskWidth;
+ float fullscreenTaskHeight;
+ float canvasScreenRatio;
+
+ float taskPercent;
+ if (!mSplitBounds.appsStackedVertically) {
+ // For landscape, scale the width
+ taskPercent = mDesiredStagePosition == STAGE_POSITION_TOP_OR_LEFT
+ ? mSplitBounds.leftTaskPercent
+ : (1 - (mSplitBounds.leftTaskPercent + mSplitBounds.dividerWidthPercent));
+ // Scale landscape width to that of actual screen
+ fullscreenTaskWidth = screenWidthPx * taskPercent;
+ canvasScreenRatio = canvasWidth / fullscreenTaskWidth;
+ } else {
+ taskPercent = mDesiredStagePosition != STAGE_POSITION_TOP_OR_LEFT
+ ? mSplitBounds.leftTaskPercent
+ : (1 - (mSplitBounds.leftTaskPercent + mSplitBounds.dividerWidthPercent));
+ // Scale landscape width to that of actual screen
+ fullscreenTaskHeight = screenHeightPx * taskPercent;
+ canvasScreenRatio = canvasHeight / fullscreenTaskHeight;
+ }
+ scaledTaskbarSize = taskbarSize * canvasScreenRatio;
+ thumbnailClipHint.bottom = isTablet ? scaledTaskbarSize : 0;
+ }
float scale = thumbnailData.scale;
final float thumbnailScale;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
index 8a25096..82d70116 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
@@ -53,6 +53,8 @@
InteractionJankMonitor.CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION;
public static final int CUJ_RECENTS_SCROLLING =
InteractionJankMonitor.CUJ_RECENTS_SCROLLING;
+ public static final int CUJ_APP_SWIPE_TO_RECENTS =
+ InteractionJankMonitor.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS;
@IntDef({
CUJ_APP_LAUNCH_FROM_RECENTS,
@@ -62,7 +64,8 @@
CUJ_QUICK_SWITCH,
CUJ_APP_LAUNCH_FROM_WIDGET,
CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION,
- CUJ_RECENTS_SCROLLING
+ CUJ_RECENTS_SCROLLING,
+ CUJ_APP_SWIPE_TO_RECENTS
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt
new file mode 100644
index 0000000..74519c2
--- /dev/null
+++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.flags
+
+import android.annotation.BoolRes
+
+object FlagsFactory {
+ private val flagMap = mutableMapOf<String, Flag<*>>()
+
+ val knownFlags: Map<String, Flag<*>>
+ get() = flagMap
+
+ fun unreleasedFlag(
+ id: Int,
+ name: String,
+ namespace: String = "systemui",
+ teamfood: Boolean = false
+ ): UnreleasedFlag {
+ val flag = UnreleasedFlag(id = id, name = name, namespace = namespace, teamfood = teamfood)
+ FlagsFactory.checkForDupesAndAdd(flag)
+ return flag
+ }
+
+ fun releasedFlag(
+ id: Int,
+ name: String,
+ namespace: String = "systemui",
+ teamfood: Boolean = false
+ ): ReleasedFlag {
+ val flag = ReleasedFlag(id = id, name = name, namespace = namespace, teamfood = teamfood)
+ FlagsFactory.checkForDupesAndAdd(flag)
+ return flag
+ }
+
+ fun resourceBooleanFlag(
+ id: Int,
+ @BoolRes resourceId: Int,
+ name: String,
+ namespace: String = "systemui",
+ teamfood: Boolean = false
+ ): ResourceBooleanFlag {
+ val flag =
+ ResourceBooleanFlag(
+ id = id,
+ name = name,
+ namespace = namespace,
+ resourceId = resourceId,
+ teamfood = teamfood
+ )
+ FlagsFactory.checkForDupesAndAdd(flag)
+ return flag
+ }
+
+ fun sysPropBooleanFlag(
+ id: Int,
+ name: String,
+ namespace: String = "systemui",
+ default: Boolean = false
+ ): SysPropBooleanFlag {
+ val flag =
+ SysPropBooleanFlag(id = id, name = name, namespace = "systemui", default = default)
+ FlagsFactory.checkForDupesAndAdd(flag)
+ return flag
+ }
+
+ private fun checkForDupesAndAdd(flag: Flag<*>) {
+ if (flagMap.containsKey(flag.name)) {
+ throw IllegalArgumentException("Name {flag.name} is already registered")
+ }
+ flagMap.forEach {
+ if (it.value.id == flag.id) {
+ throw IllegalArgumentException("Name {flag.id} is already registered")
+ }
+ }
+ flagMap[flag.name] = flag
+ }
+}
diff --git a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt
new file mode 100644
index 0000000..89c0786
--- /dev/null
+++ b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.flags
+
+import android.annotation.BoolRes
+
+object FlagsFactory {
+ private val flagMap = mutableMapOf<String, Flag<*>>()
+
+ val knownFlags: Map<String, Flag<*>>
+ get() = flagMap
+
+ fun unreleasedFlag(
+ id: Int,
+ name: String,
+ namespace: String = "systemui",
+ teamfood: Boolean = false
+ ): UnreleasedFlag {
+ // Unreleased flags are always false in this build.
+ val flag = UnreleasedFlag(id = id, name = "", namespace = "", teamfood = false)
+ return flag
+ }
+
+ fun releasedFlag(
+ id: Int,
+ name: String,
+ namespace: String = "systemui",
+ teamfood: Boolean = false
+ ): ReleasedFlag {
+ val flag = ReleasedFlag(id = id, name = name, namespace = namespace, teamfood = teamfood)
+ flagMap[name] = flag
+ return flag
+ }
+
+ fun resourceBooleanFlag(
+ id: Int,
+ @BoolRes resourceId: Int,
+ name: String,
+ namespace: String = "systemui",
+ teamfood: Boolean = false
+ ): ResourceBooleanFlag {
+ val flag =
+ ResourceBooleanFlag(
+ id = id,
+ name = name,
+ namespace = namespace,
+ resourceId = resourceId,
+ teamfood = teamfood
+ )
+ flagMap[name] = flag
+ return flag
+ }
+
+ fun sysPropBooleanFlag(
+ id: Int,
+ name: String,
+ namespace: String = "systemui",
+ default: Boolean = false
+ ): SysPropBooleanFlag {
+ val flag =
+ SysPropBooleanFlag(id = id, name = name, namespace = namespace, default = default)
+ flagMap[name] = flag
+ return flag
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
index db64f05..8fa7b11 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
@@ -68,7 +68,7 @@
private final KeyguardUpdateMonitorCallback mUpdateCallback =
new KeyguardUpdateMonitorCallback() {
@Override
- public void onTrustGrantedWithFlags(int flags, int userId) {
+ public void onTrustGrantedWithFlags(int flags, int userId, String message) {
if (userId != KeyguardUpdateMonitor.getCurrentUser()) return;
boolean bouncerVisible = mView.isVisibleToUser();
boolean temporaryAndRenewable =
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 73229c3..faaba63 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -21,6 +21,7 @@
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import android.util.Log;
import android.view.inputmethod.InputMethodManager;
@@ -152,7 +153,9 @@
}
public void startAppearAnimation() {
- mMessageAreaController.setMessage(getInitialMessageResId());
+ if (TextUtils.isEmpty(mMessageAreaController.getMessage())) {
+ mMessageAreaController.setMessage(getInitialMessageResId());
+ }
mView.startAppearAnimation();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
index 2bd3ca5..db986e0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
@@ -103,6 +103,11 @@
mView.setNextMessageColor(colorState);
}
+ /** Returns the message of the underlying TextView. */
+ public CharSequence getMessage() {
+ return mView.getText();
+ }
+
/**
* Reload colors from resources.
**/
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 2bb3a5f..5c4126e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -727,6 +727,11 @@
mViewMode.reloadColors();
}
+ /** Handles density or font scale changes. */
+ void onDensityOrFontScaleChanged() {
+ mViewMode.onDensityOrFontScaleChanged();
+ }
+
/**
* Enscapsulates the differences between bouncer modes for the container.
*/
@@ -752,6 +757,9 @@
/** Refresh colors */
default void reloadColors() {};
+ /** Handles density or font scale changes. */
+ default void onDensityOrFontScaleChanged() {}
+
/** On a successful auth, optionally handle how the view disappears */
default void startDisappearAnimation(SecurityMode securityMode) {};
@@ -899,14 +907,9 @@
mFalsingA11yDelegate = falsingA11yDelegate;
if (mUserSwitcherViewGroup == null) {
- LayoutInflater.from(v.getContext()).inflate(
- R.layout.keyguard_bouncer_user_switcher,
- mView,
- true);
- mUserSwitcherViewGroup = mView.findViewById(R.id.keyguard_bouncer_user_switcher);
+ inflateUserSwitcher();
}
updateSecurityViewLocation();
- mUserSwitcher = mView.findViewById(R.id.user_switcher_header);
setupUserSwitcher();
mUserSwitcherController.addUserSwitchCallback(mUserSwitchCallback);
}
@@ -937,6 +940,12 @@
}
@Override
+ public void onDensityOrFontScaleChanged() {
+ mView.removeView(mUserSwitcherViewGroup);
+ inflateUserSwitcher();
+ }
+
+ @Override
public void onDestroy() {
mUserSwitcherController.removeUserSwitchCallback(mUserSwitchCallback);
}
@@ -1097,11 +1106,19 @@
new KeyguardSecurityViewTransition());
}
int yTrans = mResources.getDimensionPixelSize(R.dimen.bouncer_user_switcher_y_trans);
+ int viewFlipperBottomMargin = mResources.getDimensionPixelSize(
+ R.dimen.bouncer_user_switcher_view_mode_view_flipper_bottom_margin);
+ int userSwitcherBottomMargin = mResources.getDimensionPixelSize(
+ R.dimen.bouncer_user_switcher_view_mode_user_switcher_bottom_margin);
if (mResources.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.connect(mUserSwitcherViewGroup.getId(), TOP, PARENT_ID, TOP, yTrans);
- constraintSet.connect(mViewFlipper.getId(), TOP, PARENT_ID, TOP);
- constraintSet.connect(mViewFlipper.getId(), BOTTOM, PARENT_ID, BOTTOM);
+ constraintSet.connect(mUserSwitcherViewGroup.getId(), BOTTOM, mViewFlipper.getId(),
+ TOP, userSwitcherBottomMargin);
+ constraintSet.connect(mViewFlipper.getId(), TOP, mUserSwitcherViewGroup.getId(),
+ BOTTOM);
+ constraintSet.connect(mViewFlipper.getId(), BOTTOM, PARENT_ID, BOTTOM,
+ viewFlipperBottomMargin);
constraintSet.centerHorizontally(mViewFlipper.getId(), PARENT_ID);
constraintSet.centerHorizontally(mUserSwitcherViewGroup.getId(), PARENT_ID);
constraintSet.setVerticalChainStyle(mViewFlipper.getId(), CHAIN_SPREAD);
@@ -1137,6 +1154,15 @@
}
}
+ private void inflateUserSwitcher() {
+ LayoutInflater.from(mView.getContext()).inflate(
+ R.layout.keyguard_bouncer_user_switcher,
+ mView,
+ true);
+ mUserSwitcherViewGroup = mView.findViewById(R.id.keyguard_bouncer_user_switcher);
+ mUserSwitcher = mView.findViewById(R.id.user_switcher_header);
+ }
+
interface UserSwitcherCallback {
void showUnlockToContinueMessage();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index fbb114c..01be33e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -58,7 +58,8 @@
import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
-import com.android.systemui.biometrics.SidefpsController;
+import com.android.systemui.biometrics.SideFpsController;
+import com.android.systemui.biometrics.SideFpsUiRequestSource;
import com.android.systemui.classifier.FalsingA11yDelegate;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
@@ -100,7 +101,7 @@
private final GlobalSettings mGlobalSettings;
private final FeatureFlags mFeatureFlags;
private final SessionTracker mSessionTracker;
- private final Optional<SidefpsController> mSidefpsController;
+ private final Optional<SideFpsController> mSideFpsController;
private final FalsingA11yDelegate mFalsingA11yDelegate;
private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED;
@@ -250,6 +251,11 @@
public void onUiModeChanged() {
reloadColors();
}
+
+ @Override
+ public void onDensityOrFontScaleChanged() {
+ KeyguardSecurityContainerController.this.onDensityOrFontScaleChanged();
+ }
};
private boolean mBouncerVisible = false;
private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
@@ -290,7 +296,7 @@
FeatureFlags featureFlags,
GlobalSettings globalSettings,
SessionTracker sessionTracker,
- Optional<SidefpsController> sidefpsController,
+ Optional<SideFpsController> sideFpsController,
FalsingA11yDelegate falsingA11yDelegate) {
super(view);
mLockPatternUtils = lockPatternUtils;
@@ -311,7 +317,7 @@
mFeatureFlags = featureFlags;
mGlobalSettings = globalSettings;
mSessionTracker = sessionTracker;
- mSidefpsController = sidefpsController;
+ mSideFpsController = sideFpsController;
mFalsingA11yDelegate = falsingA11yDelegate;
}
@@ -351,7 +357,7 @@
}
private void updateSideFpsVisibility() {
- if (!mSidefpsController.isPresent()) {
+ if (!mSideFpsController.isPresent()) {
return;
}
final boolean sfpsEnabled = getResources().getBoolean(
@@ -369,9 +375,9 @@
+ "needsStrongAuth=" + needsStrongAuth);
}
if (toShow) {
- mSidefpsController.get().show();
+ mSideFpsController.get().show(SideFpsUiRequestSource.PRIMARY_BOUNCER);
} else {
- mSidefpsController.get().hide();
+ mSideFpsController.get().hide(SideFpsUiRequestSource.PRIMARY_BOUNCER);
}
}
@@ -726,6 +732,14 @@
mView.reloadColors();
}
+ /** Handles density or font scale changes. */
+ private void onDensityOrFontScaleChanged() {
+ mSecurityViewFlipperController.onDensityOrFontScaleChanged();
+ mSecurityViewFlipperController.getSecurityView(mCurrentSecurityMode,
+ mKeyguardSecurityCallback);
+ mView.onDensityOrFontScaleChanged();
+ }
+
static class Factory {
private final KeyguardSecurityContainer mView;
@@ -745,7 +759,7 @@
private final FeatureFlags mFeatureFlags;
private final UserSwitcherController mUserSwitcherController;
private final SessionTracker mSessionTracker;
- private final Optional<SidefpsController> mSidefpsController;
+ private final Optional<SideFpsController> mSidefpsController;
private final FalsingA11yDelegate mFalsingA11yDelegate;
@Inject
@@ -766,7 +780,7 @@
FeatureFlags featureFlags,
GlobalSettings globalSettings,
SessionTracker sessionTracker,
- Optional<SidefpsController> sidefpsController,
+ Optional<SideFpsController> sidefpsController,
FalsingA11yDelegate falsingA11yDelegate) {
mView = view;
mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
index bddf4b0..25afe11 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
@@ -83,6 +83,13 @@
}
}
+ /** Handles density or font scale changes. */
+ public void onDensityOrFontScaleChanged() {
+ mView.removeAllViews();
+ mChildren.clear();
+ }
+
+
@VisibleForTesting
KeyguardInputViewController<KeyguardInputView> getSecurityView(SecurityMode securityMode,
KeyguardSecurityCallback keyguardSecurityCallback) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 83e23bd..8b9823b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -17,6 +17,7 @@
package com.android.keyguard;
import android.content.Context;
+import android.os.Trace;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
@@ -112,4 +113,11 @@
mKeyguardSlice.dump(pw, args);
}
}
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ Trace.beginSection("KeyguardStatusView#onMeasure");
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ Trace.endSection();
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 9d2dcf0..2ac93b5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -298,8 +298,8 @@
private boolean mCredentialAttempted;
private boolean mKeyguardGoingAway;
private boolean mGoingToSleep;
- private boolean mBouncerFullyShown;
- private boolean mBouncerIsOrWillBeShowing;
+ private boolean mPrimaryBouncerFullyShown;
+ private boolean mPrimaryBouncerIsOrWillBeShowing;
private boolean mUdfpsBouncerShowing;
private boolean mAuthInterruptActive;
private boolean mNeedsSlowUnlockTransition;
@@ -470,19 +470,8 @@
FACE_AUTH_TRIGGERED_TRUST_DISABLED);
}
- mLogger.logTrustChanged(wasTrusted, enabled, userId);
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onTrustChanged(userId);
- if (enabled && flags != 0) {
- cb.onTrustGrantedWithFlags(flags, userId);
- }
- }
- }
-
+ String message = null;
if (KeyguardUpdateMonitor.getCurrentUser() == userId) {
- CharSequence message = null;
final boolean userHasTrust = getUserHasTrust(userId);
if (userHasTrust && trustGrantedMessages != null) {
for (String msg : trustGrantedMessages) {
@@ -492,14 +481,17 @@
}
}
}
-
- if (message != null) {
- mLogger.logShowTrustGrantedMessage(message.toString());
- }
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.showTrustGrantedMessage(message);
+ }
+ mLogger.logTrustChanged(wasTrusted, enabled, userId);
+ if (message != null) {
+ mLogger.logShowTrustGrantedMessage(message.toString());
+ }
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onTrustChanged(userId);
+ if (enabled) {
+ cb.onTrustGrantedWithFlags(flags, userId, message);
}
}
}
@@ -782,7 +774,7 @@
mFingerprintCancelSignal = null;
updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
FACE_AUTH_UPDATED_FP_AUTHENTICATED);
- mLogger.d("onFingerprintAuthenticated");
+ mLogger.logFingerprintSuccess(userId, isStrongBiometric);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -1648,8 +1640,9 @@
public void onAuthenticationFailed() {
String reason =
mKeyguardBypassController.canBypass() ? "bypass"
- : mUdfpsBouncerShowing ? "udfpsBouncer" :
- mBouncerFullyShown ? "bouncer" : "udfpsFpDown";
+ : mUdfpsBouncerShowing ? "udfpsBouncer"
+ : mPrimaryBouncerFullyShown ? "bouncer"
+ : "udfpsFpDown";
requestActiveUnlock(
ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL,
"faceFailure-" + reason);
@@ -2041,7 +2034,7 @@
handleKeyguardReset();
break;
case MSG_KEYGUARD_BOUNCER_CHANGED:
- handleKeyguardBouncerChanged(msg.arg1, msg.arg2);
+ handlePrimaryBouncerChanged(msg.arg1, msg.arg2);
break;
case MSG_REPORT_EMERGENCY_CALL_ACTION:
handleReportEmergencyCallAction();
@@ -2511,7 +2504,7 @@
requestOrigin,
extraReason, canFaceBypass
|| mUdfpsBouncerShowing
- || mBouncerFullyShown
+ || mPrimaryBouncerFullyShown
|| mAuthController.isUdfpsFingerDown());
}
@@ -2532,7 +2525,7 @@
private boolean shouldTriggerActiveUnlock() {
// Triggers:
final boolean triggerActiveUnlockForAssistant = shouldTriggerActiveUnlockForAssistant();
- final boolean awakeKeyguard = mBouncerFullyShown || mUdfpsBouncerShowing
+ final boolean awakeKeyguard = mPrimaryBouncerFullyShown || mUdfpsBouncerShowing
|| (isKeyguardVisible() && !mGoingToSleep
&& mStatusBarState != StatusBarState.SHADE_LOCKED);
@@ -2611,7 +2604,7 @@
final boolean shouldListenKeyguardState =
isKeyguardVisible()
|| !mDeviceInteractive
- || (mBouncerIsOrWillBeShowing && !mKeyguardGoingAway)
+ || (mPrimaryBouncerIsOrWillBeShowing && !mKeyguardGoingAway)
|| mGoingToSleep
|| shouldListenForFingerprintAssistant
|| (mKeyguardOccluded && mIsDreaming)
@@ -2630,8 +2623,8 @@
&& mIsPrimaryUser
&& biometricEnabledForUser;
- final boolean shouldListenBouncerState =
- !(mFingerprintLockedOut && mBouncerIsOrWillBeShowing && mCredentialAttempted);
+ final boolean shouldListenBouncerState = !(mFingerprintLockedOut
+ && mPrimaryBouncerIsOrWillBeShowing && mCredentialAttempted);
final boolean isEncryptedOrLockdownForUser = isEncryptedOrLockdown(user);
@@ -2656,7 +2649,7 @@
user,
shouldListen,
biometricEnabledForUser,
- mBouncerIsOrWillBeShowing,
+ mPrimaryBouncerIsOrWillBeShowing,
userCanSkipBouncer,
mCredentialAttempted,
mDeviceInteractive,
@@ -2701,7 +2694,7 @@
|| containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
// TODO: always disallow when fp is already locked out?
- final boolean fpLockedout = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
+ final boolean fpLockedOut = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
final boolean canBypass = mKeyguardBypassController != null
&& mKeyguardBypassController.canBypass();
@@ -2713,7 +2706,7 @@
// Scan even when encrypted or timeout to show a preemptive bouncer when bypassing.
// Lock-down mode shouldn't scan, since it is more explicit.
boolean strongAuthAllowsScanning = (!isEncryptedOrTimedOut || canBypass
- && !mBouncerFullyShown);
+ && !mPrimaryBouncerFullyShown);
// If the device supports face detection (without authentication) and bypass is enabled,
// allow face scanning to happen if the device is in lockdown mode.
@@ -2730,12 +2723,11 @@
final boolean faceDisabledForUser = isFaceDisabled(user);
final boolean biometricEnabledForUser = mBiometricEnabledForUser.get(user);
final boolean shouldListenForFaceAssistant = shouldListenForFaceAssistant();
- final boolean fpOrFaceIsLockedOut = isFaceLockedOut() || fpLockedout;
// Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
// instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
final boolean shouldListen =
- (mBouncerFullyShown
+ (mPrimaryBouncerFullyShown
|| mAuthInterruptActive
|| mOccludingAppRequestingFace
|| awakeKeyguard
@@ -2748,7 +2740,10 @@
&& (!mSecureCameraLaunched || mOccludingAppRequestingFace)
&& !faceAuthenticated
&& !mGoingToSleep
- && !fpOrFaceIsLockedOut;
+ // We only care about fp locked out state and not face because we still trigger
+ // face auth even when face is locked out to show the user a message that face
+ // unlock was supposed to run but didn't
+ && !fpLockedOut;
// Aggregate relevant fields for debug logging.
maybeLogListenerModelData(
@@ -2759,11 +2754,11 @@
mAuthInterruptActive,
becauseCannotSkipBouncer,
biometricEnabledForUser,
- mBouncerFullyShown,
+ mPrimaryBouncerFullyShown,
faceAuthenticated,
faceDisabledForUser,
isFaceLockedOut(),
- fpLockedout,
+ fpLockedOut,
mGoingToSleep,
awakeKeyguard,
mKeyguardGoingAway,
@@ -3289,17 +3284,19 @@
/**
* Handle {@link #MSG_KEYGUARD_BOUNCER_CHANGED}
*
- * @see #sendKeyguardBouncerChanged(boolean, boolean)
+ * @see #sendPrimaryBouncerChanged(boolean, boolean)
*/
- private void handleKeyguardBouncerChanged(int bouncerIsOrWillBeShowing, int bouncerFullyShown) {
+ private void handlePrimaryBouncerChanged(int primaryBouncerIsOrWillBeShowing,
+ int primaryBouncerFullyShown) {
Assert.isMainThread();
- final boolean wasBouncerIsOrWillBeShowing = mBouncerIsOrWillBeShowing;
- final boolean wasBouncerFullyShown = mBouncerFullyShown;
- mBouncerIsOrWillBeShowing = bouncerIsOrWillBeShowing == 1;
- mBouncerFullyShown = bouncerFullyShown == 1;
- mLogger.logKeyguardBouncerChanged(mBouncerIsOrWillBeShowing, mBouncerFullyShown);
+ final boolean wasPrimaryBouncerIsOrWillBeShowing = mPrimaryBouncerIsOrWillBeShowing;
+ final boolean wasPrimaryBouncerFullyShown = mPrimaryBouncerFullyShown;
+ mPrimaryBouncerIsOrWillBeShowing = primaryBouncerIsOrWillBeShowing == 1;
+ mPrimaryBouncerFullyShown = primaryBouncerFullyShown == 1;
+ mLogger.logPrimaryKeyguardBouncerChanged(mPrimaryBouncerIsOrWillBeShowing,
+ mPrimaryBouncerFullyShown);
- if (mBouncerFullyShown) {
+ if (mPrimaryBouncerFullyShown) {
// If the bouncer is shown, always clear this flag. This can happen in the following
// situations: 1) Default camera with SHOW_WHEN_LOCKED is not chosen yet. 2) Secure
// camera requests dismiss keyguard (tapping on photos for example). When these happen,
@@ -3309,18 +3306,18 @@
mCredentialAttempted = false;
}
- if (wasBouncerIsOrWillBeShowing != mBouncerIsOrWillBeShowing) {
+ if (wasPrimaryBouncerIsOrWillBeShowing != mPrimaryBouncerIsOrWillBeShowing) {
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onKeyguardBouncerStateChanged(mBouncerIsOrWillBeShowing);
+ cb.onKeyguardBouncerStateChanged(mPrimaryBouncerIsOrWillBeShowing);
}
}
updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
}
- if (wasBouncerFullyShown != mBouncerFullyShown) {
- if (mBouncerFullyShown) {
+ if (wasPrimaryBouncerFullyShown != mPrimaryBouncerFullyShown) {
+ if (mPrimaryBouncerFullyShown) {
requestActiveUnlock(
ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT,
"bouncerFullyShown");
@@ -3328,7 +3325,7 @@
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onKeyguardBouncerFullyShowingChanged(mBouncerFullyShown);
+ cb.onKeyguardBouncerFullyShowingChanged(mPrimaryBouncerFullyShown);
}
}
updateFaceListeningState(BIOMETRIC_ACTION_UPDATE,
@@ -3479,14 +3476,15 @@
}
/**
- * @see #handleKeyguardBouncerChanged(int, int)
+ * @see #handlePrimaryBouncerChanged(int, int)
*/
- public void sendKeyguardBouncerChanged(boolean bouncerIsOrWillBeShowing,
- boolean bouncerFullyShown) {
- mLogger.logSendKeyguardBouncerChanged(bouncerIsOrWillBeShowing, bouncerFullyShown);
+ public void sendPrimaryBouncerChanged(boolean primaryBouncerIsOrWillBeShowing,
+ boolean primaryBouncerFullyShown) {
+ mLogger.logSendPrimaryBouncerChanged(primaryBouncerIsOrWillBeShowing,
+ primaryBouncerFullyShown);
Message message = mHandler.obtainMessage(MSG_KEYGUARD_BOUNCER_CHANGED);
- message.arg1 = bouncerIsOrWillBeShowing ? 1 : 0;
- message.arg2 = bouncerFullyShown ? 1 : 0;
+ message.arg1 = primaryBouncerIsOrWillBeShowing ? 1 : 0;
+ message.arg2 = primaryBouncerFullyShown ? 1 : 0;
message.sendToTarget();
}
@@ -3846,7 +3844,8 @@
if (isUdfpsSupported()) {
pw.println(" udfpsEnrolled=" + isUdfpsEnrolled());
pw.println(" shouldListenForUdfps=" + shouldListenForFingerprint(true));
- pw.println(" mBouncerIsOrWillBeShowing=" + mBouncerIsOrWillBeShowing);
+ pw.println(" mPrimaryBouncerIsOrWillBeShowing="
+ + mPrimaryBouncerIsOrWillBeShowing);
pw.println(" mStatusBarState=" + StatusBarState.toString(mStatusBarState));
pw.println(" mUdfpsBouncerShowing=" + mUdfpsBouncerShowing);
} else if (isSfpsSupported()) {
@@ -3878,7 +3877,7 @@
pw.println(" mFaceLockedOutPermanent=" + mFaceLockedOutPermanent);
pw.println(" enabledByUser=" + mBiometricEnabledForUser.get(userId));
pw.println(" mSecureCameraLaunched=" + mSecureCameraLaunched);
- pw.println(" mBouncerFullyShown=" + mBouncerFullyShown);
+ pw.println(" mPrimaryBouncerFullyShown=" + mPrimaryBouncerFullyShown);
pw.println(" mNeedsSlowUnlockTransition=" + mNeedsSlowUnlockTransition);
}
mListenModels.print(pw);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index c06e1dc..c5142f3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -174,14 +174,12 @@
public void onTrustManagedChanged(int userId) { }
/**
- * Called after trust was granted with non-zero flags.
+ * Called after trust was granted.
+ * @param userId of the user that has been granted trust
+ * @param message optional message the trust agent has provided to show that should indicate
+ * why trust was granted.
*/
- public void onTrustGrantedWithFlags(int flags, int userId) { }
-
- /**
- * Called when setting the trust granted message.
- */
- public void showTrustGrantedMessage(@Nullable CharSequence message) { }
+ public void onTrustGrantedWithFlags(int flags, int userId, @Nullable String message) { }
/**
* Called when a biometric has been acquired.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
index 90f0446..6c3c246 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
@@ -50,16 +50,11 @@
/**
* Resets the state of Keyguard View.
- * @param hideBouncerWhenShowing
+ * @param hideBouncerWhenShowing when true, hides the primary and alternate bouncers if showing.
*/
void reset(boolean hideBouncerWhenShowing);
/**
- * Stop showing any alternate auth methods.
- */
- void resetAlternateAuth(boolean forceUpdateScrim);
-
- /**
* Called when the device started going to sleep.
*/
default void onStartedGoingToSleep() {};
@@ -156,20 +151,24 @@
void notifyKeyguardAuthenticated(boolean strongAuth);
/**
- * Shows the Bouncer.
- *
+ * Shows the primary bouncer.
*/
- void showBouncer(boolean scrimmed);
+ void showPrimaryBouncer(boolean scrimmed);
/**
- * Returns {@code true} when the bouncer is currently showing
+ * When the primary bouncer is fully visible or is showing but animation didn't finish yet.
+ */
+ boolean primaryBouncerIsOrWillBeShowing();
+
+ /**
+ * Returns {@code true} when the primary bouncer or alternate bouncer is currently showing
*/
boolean isBouncerShowing();
/**
- * When bouncer is fully visible or it is showing but animation didn't finish yet.
+ * Stop showing the alternate bouncer, if showing.
*/
- boolean bouncerIsOrWillBeShowing();
+ void hideAlternateBouncer(boolean forceUpdateScrim);
// TODO: Deprecate registerStatusBar in KeyguardViewController interface. It is currently
// only used for testing purposes in StatusBarKeyguardViewManager, and it prevents us from
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index 0a82968..34a5ef7 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -158,6 +158,10 @@
return mLockIconCenter.y - mRadius;
}
+ float getLocationBottom() {
+ return mLockIconCenter.y + mRadius;
+ }
+
/**
* Updates the icon its default state where no visual is shown.
*/
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 8fbbd38..cf246b6 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -282,6 +282,10 @@
return mView.getLocationTop();
}
+ public float getBottom() {
+ return mView.getLocationBottom();
+ }
+
private void updateVisibility() {
if (mCancelDelayedUpdateVisibilityRunnable != null) {
mCancelDelayedUpdateVisibilityRunnable.run();
@@ -697,7 +701,7 @@
"lock-screen-lock-icon-longpress",
TOUCH_VIBRATION_ATTRIBUTES);
- mKeyguardViewController.showBouncer(/* scrim */ true);
+ mKeyguardViewController.showPrimaryBouncer(/* scrim */ true);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
index 49e9783..ef067b8 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
@@ -16,7 +16,7 @@
package com.android.keyguard.dagger;
-import static com.android.systemui.biometrics.SidefpsControllerKt.hasSideFpsSensor;
+import static com.android.systemui.biometrics.SideFpsControllerKt.hasSideFpsSensor;
import android.annotation.Nullable;
import android.hardware.fingerprint.FingerprintManager;
@@ -27,7 +27,7 @@
import com.android.keyguard.KeyguardSecurityContainer;
import com.android.keyguard.KeyguardSecurityViewFlipper;
import com.android.systemui.R;
-import com.android.systemui.biometrics.SidefpsController;
+import com.android.systemui.biometrics.SideFpsController;
import com.android.systemui.dagger.qualifiers.RootView;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
@@ -70,12 +70,12 @@
return containerView.findViewById(R.id.view_flipper);
}
- /** Provides {@link SidefpsController} if the device has the side fingerprint sensor. */
+ /** Provides {@link SideFpsController} if the device has the side fingerprint sensor. */
@Provides
@KeyguardBouncerScope
- static Optional<SidefpsController> providesOptionalSidefpsController(
+ static Optional<SideFpsController> providesOptionalSidefpsController(
@Nullable FingerprintManager fingerprintManager,
- Provider<SidefpsController> sidefpsControllerProvider) {
+ Provider<SideFpsController> sidefpsControllerProvider) {
if (!hasSideFpsSensor(fingerprintManager)) {
return Optional.empty();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index 3308f55..81b8dfe 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -153,19 +153,29 @@
{ "fingerprintRunningState: $int1" })
}
+ fun logFingerprintSuccess(userId: Int, isStrongBiometric: Boolean) {
+ logBuffer.log(TAG, DEBUG, {
+ int1 = userId
+ bool1 = isStrongBiometric
+ }, {"Fingerprint auth successful: userId: $int1, isStrongBiometric: $bool1"})
+ }
+
fun logInvalidSubId(subId: Int) {
logBuffer.log(TAG, INFO,
{ int1 = subId },
{ "Previously active sub id $int1 is now invalid, will remove" })
}
- fun logKeyguardBouncerChanged(bouncerIsOrWillBeShowing: Boolean, bouncerFullyShown: Boolean) {
+ fun logPrimaryKeyguardBouncerChanged(
+ primaryBouncerIsOrWillBeShowing: Boolean,
+ primaryBouncerFullyShown: Boolean
+ ) {
logBuffer.log(TAG, DEBUG, {
- bool1 = bouncerIsOrWillBeShowing
- bool2 = bouncerFullyShown
+ bool1 = primaryBouncerIsOrWillBeShowing
+ bool2 = primaryBouncerFullyShown
}, {
- "handleKeyguardBouncerChanged " +
- "bouncerIsOrWillBeShowing=$bool1 bouncerFullyShowing=$bool2"
+ "handlePrimaryBouncerChanged " +
+ "primaryBouncerIsOrWillBeShowing=$bool1 primaryBouncerFullyShown=$bool2"
})
}
@@ -222,16 +232,16 @@
{ "Retrying fingerprint attempt: $int1" })
}
- fun logSendKeyguardBouncerChanged(
- bouncerIsOrWillBeShowing: Boolean,
- bouncerFullyShown: Boolean,
+ fun logSendPrimaryBouncerChanged(
+ primaryBouncerIsOrWillBeShowing: Boolean,
+ primaryBouncerFullyShown: Boolean,
) {
logBuffer.log(TAG, DEBUG, {
- bool1 = bouncerIsOrWillBeShowing
- bool2 = bouncerFullyShown
+ bool1 = primaryBouncerIsOrWillBeShowing
+ bool2 = primaryBouncerFullyShown
}, {
- "sendKeyguardBouncerChanged bouncerIsOrWillBeShowing=$bool1 " +
- "bouncerFullyShown=$bool2"
+ "sendPrimaryBouncerChanged primaryBouncerIsOrWillBeShowing=$bool1 " +
+ "primaryBouncerFullyShown=$bool2"
})
}
diff --git a/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt b/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
index 3015710..eee705d 100644
--- a/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
@@ -26,8 +26,6 @@
import kotlin.math.roundToInt
-const val TAG = "CameraAvailabilityListener"
-
/**
* Listens for usage of the Camera and controls the ScreenDecorations transition to show extra
* protection around a display cutout based on config_frontBuiltInDisplayCutoutProtection and
diff --git a/packages/SystemUI/src/com/android/systemui/ChooserSelector.kt b/packages/SystemUI/src/com/android/systemui/ChooserSelector.kt
index a89cbf5..9ac45b3 100644
--- a/packages/SystemUI/src/com/android/systemui/ChooserSelector.kt
+++ b/packages/SystemUI/src/com/android/systemui/ChooserSelector.kt
@@ -4,30 +4,32 @@
import android.content.Context
import android.content.pm.PackageManager
import android.util.Log
+import com.android.internal.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.FlagListenable
import com.android.systemui.flags.Flags
-import javax.inject.Inject
+import com.android.systemui.settings.UserTracker
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
+import javax.inject.Inject
@SysUISingleton
class ChooserSelector @Inject constructor(
private val context: Context,
+ private val userTracker: UserTracker,
private val featureFlags: FeatureFlags,
@Application private val coroutineScope: CoroutineScope,
- @Background private val bgDispatcher: CoroutineDispatcher
+ @Background private val bgDispatcher: CoroutineDispatcher,
) : CoreStartable {
- private val packageManager = context.packageManager
private val chooserComponent = ComponentName.unflattenFromString(
- context.resources.getString(ChooserSelectorResourceHelper.CONFIG_CHOOSER_ACTIVITY))
+ context.resources.getString(R.string.config_chooserActivity))
override fun start() {
coroutineScope.launch {
@@ -56,10 +58,17 @@
} else {
PackageManager.COMPONENT_ENABLED_STATE_DISABLED
}
- try {
- packageManager.setComponentEnabledSetting(chooserComponent, newState, /* flags = */ 0)
- } catch (e: IllegalArgumentException) {
- Log.w("ChooserSelector", "Unable to set IntentResolver enabled=" + enabled, e)
+ userTracker.userProfiles.forEach {
+ try {
+ context.createContextAsUser(it.userHandle, /* flags = */ 0).packageManager
+ .setComponentEnabledSetting(chooserComponent, newState, /* flags = */ 0)
+ } catch (e: IllegalArgumentException) {
+ Log.w(
+ "ChooserSelector",
+ "Unable to set IntentResolver enabled=$enabled for user ${it.id}",
+ e,
+ )
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt b/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
index 5d52056..90ecb46 100644
--- a/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
+++ b/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
@@ -169,7 +169,7 @@
return
}
cutoutPath.reset()
- display.getDisplayInfo(displayInfo)
+ context.display?.getDisplayInfo(displayInfo)
displayInfo.displayCutout?.cutoutPath?.let { path -> cutoutPath.set(path) }
invalidate()
}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 11d579d..45f9385 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -170,6 +170,7 @@
private Display.Mode mDisplayMode;
@VisibleForTesting
protected DisplayInfo mDisplayInfo = new DisplayInfo();
+ private DisplayCutout mDisplayCutout;
@VisibleForTesting
protected void showCameraProtection(@NonNull Path protectionPath, @NonNull Rect bounds) {
@@ -384,6 +385,7 @@
mRotation = mDisplayInfo.rotation;
mDisplayMode = mDisplayInfo.getMode();
mDisplayUniqueId = mDisplayInfo.uniqueId;
+ mDisplayCutout = mDisplayInfo.displayCutout;
mRoundedCornerResDelegate = new RoundedCornerResDelegate(mContext.getResources(),
mDisplayUniqueId);
mRoundedCornerResDelegate.setPhysicalPixelDisplaySizeRatio(
@@ -1022,7 +1024,8 @@
mRoundedCornerResDelegate.dump(pw, args);
}
- private void updateConfiguration() {
+ @VisibleForTesting
+ void updateConfiguration() {
Preconditions.checkState(mHandler.getLooper().getThread() == Thread.currentThread(),
"must call on " + mHandler.getLooper().getThread()
+ ", but was " + Thread.currentThread());
@@ -1033,11 +1036,14 @@
mDotViewController.setNewRotation(newRotation);
}
final Display.Mode newMod = mDisplayInfo.getMode();
+ final DisplayCutout newCutout = mDisplayInfo.displayCutout;
if (!mPendingConfigChange
- && (newRotation != mRotation || displayModeChanged(mDisplayMode, newMod))) {
+ && (newRotation != mRotation || displayModeChanged(mDisplayMode, newMod)
+ || !Objects.equals(newCutout, mDisplayCutout))) {
mRotation = newRotation;
mDisplayMode = newMod;
+ mDisplayCutout = newCutout;
mRoundedCornerResDelegate.setPhysicalPixelDisplaySizeRatio(
getPhysicalPixelDisplaySizeRatio());
if (mScreenDecorHwcLayer != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
index a21f45f..632fcdc 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
@@ -97,7 +97,6 @@
.setDisplayAreaHelper(mWMComponent.getDisplayAreaHelper())
.setRecentTasks(mWMComponent.getRecentTasks())
.setBackAnimation(mWMComponent.getBackAnimation())
- .setFloatingTasks(mWMComponent.getFloatingTasks())
.setDesktopMode(mWMComponent.getDesktopMode());
// Only initialize when not starting from tests since this currently initializes some
@@ -118,7 +117,6 @@
.setStartingSurface(Optional.ofNullable(null))
.setRecentTasks(Optional.ofNullable(null))
.setBackAnimation(Optional.ofNullable(null))
- .setFloatingTasks(Optional.ofNullable(null))
.setDesktopMode(Optional.ofNullable(null));
}
mSysUIComponent = builder.build();
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt b/packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt
index 8920c92..8aa3040 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt
@@ -17,7 +17,7 @@
package com.android.systemui
import android.content.Context
-import com.android.systemui.dagger.DaggerGlobalRootComponent
+import com.android.systemui.dagger.DaggerReferenceGlobalRootComponent
import com.android.systemui.dagger.GlobalRootComponent
/**
@@ -25,6 +25,6 @@
*/
class SystemUIInitializerImpl(context: Context) : SystemUIInitializer(context) {
override fun getGlobalRootComponentBuilder(): GlobalRootComponent.Builder {
- return DaggerGlobalRootComponent.builder()
+ return DaggerReferenceGlobalRootComponent.builder()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
index 9af8300..de351ec 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
@@ -57,7 +57,7 @@
@FloatRange(from = 0.0, to = 1.0)
private static final float DEFAULT_POSITION_X_PERCENT = 1.0f;
@FloatRange(from = 0.0, to = 1.0)
- private static final float DEFAULT_POSITION_Y_PERCENT = 0.9f;
+ private static final float DEFAULT_POSITION_Y_PERCENT = 0.77f;
private final Context mContext;
private final AccessibilityFloatingMenuView mMenuView;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
index 57019de..4c52b33 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
@@ -50,7 +50,7 @@
private static final float DEFAULT_MENU_POSITION_X_PERCENT = 1.0f;
@FloatRange(from = 0.0, to = 1.0)
- private static final float DEFAULT_MENU_POSITION_Y_PERCENT = 0.9f;
+ private static final float DEFAULT_MENU_POSITION_Y_PERCENT = 0.77f;
private final Context mContext;
private final Handler mHandler = new Handler(Looper.getMainLooper());
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
index b40b356..b2a2a67 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
@@ -18,6 +18,7 @@
import android.annotation.RawRes
import android.content.Context
+import android.content.res.Configuration
import android.hardware.fingerprint.FingerprintManager
import android.view.DisplayInfo
import android.view.Surface
@@ -33,15 +34,19 @@
import com.android.systemui.biometrics.AuthBiometricView.STATE_HELP
import com.android.systemui.biometrics.AuthBiometricView.STATE_IDLE
import com.android.systemui.biometrics.AuthBiometricView.STATE_PENDING_CONFIRMATION
+import com.android.systemui.unfold.compat.ScreenSizeFoldProvider
+import com.android.systemui.unfold.updates.FoldProvider
/** Fingerprint only icon animator for BiometricPrompt. */
open class AuthBiometricFingerprintIconController(
context: Context,
iconView: LottieAnimationView,
protected val iconViewOverlay: LottieAnimationView
-) : AuthIconController(context, iconView) {
+) : AuthIconController(context, iconView), FoldProvider.FoldCallback {
+ private var isDeviceFolded: Boolean = false
private val isSideFps: Boolean
+ private val screenSizeFoldProvider: ScreenSizeFoldProvider = ScreenSizeFoldProvider(context)
var iconLayoutParamSize: Pair<Int, Int> = Pair(1, 1)
set(value) {
if (field == value) {
@@ -74,6 +79,8 @@
if (isSideFps && displayInfo.rotation == Surface.ROTATION_180) {
iconView.rotation = 180f
}
+ screenSizeFoldProvider.registerCallback(this, context.mainExecutor)
+ screenSizeFoldProvider.onConfigurationChange(context.resources.configuration)
}
private fun updateIconSideFps(@BiometricState lastState: Int, @BiometricState newState: Int) {
@@ -124,6 +131,10 @@
LottieColorUtils.applyDynamicColors(context, iconView)
}
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ screenSizeFoldProvider.onConfigurationChange(newConfig)
+ }
+
override fun updateIcon(@BiometricState lastState: Int, @BiometricState newState: Int) {
if (isSideFps) {
updateIconSideFps(lastState, newState)
@@ -191,11 +202,21 @@
@RawRes
private fun getSideFpsAnimationForTransition(rotation: Int): Int = when (rotation) {
- Surface.ROTATION_0 -> R.raw.biometricprompt_landscape_base
- Surface.ROTATION_90 -> R.raw.biometricprompt_portrait_base_topleft
- Surface.ROTATION_180 -> R.raw.biometricprompt_landscape_base
- Surface.ROTATION_270 -> R.raw.biometricprompt_portrait_base_bottomright
- else -> R.raw.biometricprompt_landscape_base
+ Surface.ROTATION_90 -> if (isDeviceFolded) {
+ R.raw.biometricprompt_folded_base_topleft
+ } else {
+ R.raw.biometricprompt_portrait_base_topleft
+ }
+ Surface.ROTATION_270 -> if (isDeviceFolded) {
+ R.raw.biometricprompt_folded_base_bottomright
+ } else {
+ R.raw.biometricprompt_portrait_base_bottomright
+ }
+ else -> if (isDeviceFolded) {
+ R.raw.biometricprompt_folded_base_default
+ } else {
+ R.raw.biometricprompt_landscape_base
+ }
}
@RawRes
@@ -273,4 +294,8 @@
}
else -> null
}
+
+ override fun onFoldUpdated(isFolded: Boolean) {
+ isDeviceFolded = isFolded
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricIconController.kt
index 15f487b..b3b6fa2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricIconController.kt
@@ -18,6 +18,7 @@
import android.annotation.DrawableRes
import android.content.Context
+import android.content.res.Configuration
import android.graphics.drawable.Animatable2
import android.graphics.drawable.AnimatedVectorDrawable
import android.graphics.drawable.Drawable
@@ -91,4 +92,6 @@
/** Called during [onAnimationEnd] if the controller is not [deactivated]. */
open fun handleAnimationEnd(drawable: Drawable) {}
+
+ open fun onConfigurationChanged(newConfig: Configuration) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index 0ac71c4..e12c170 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -27,6 +27,7 @@
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.content.Context;
+import android.content.res.Configuration;
import android.hardware.biometrics.BiometricAuthenticator.Modality;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.PromptInfo;
@@ -654,6 +655,12 @@
}
@Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ mIconController.onConfigurationChanged(newConfig);
+ }
+
+ @Override
protected void onFinishInflate() {
super.onFinishInflate();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index f74c721..815ac68 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -141,8 +141,7 @@
private final OnBackInvokedCallback mBackCallback = this::onBackInvoked;
private final @Background DelayableExecutor mBackgroundExecutor;
- private int mOrientation;
- private boolean mSkipFirstLostFocus = false;
+ private boolean mIsOrientationChanged = false;
// Non-null only if the dialog is in the act of dismissing and has not sent the reason yet.
@Nullable @AuthDialogCallback.DismissedReason private Integer mPendingCallbackReason;
@@ -491,6 +490,7 @@
@Override
public void onOrientationChanged() {
maybeUpdatePositionForUdfps(true /* invalidate */);
+ mIsOrientationChanged = true;
}
@Override
@@ -499,8 +499,8 @@
if (!hasWindowFocus) {
//it's a workaround to avoid closing BP incorrectly
//BP gets a onWindowFocusChanged(false) and then gets a onWindowFocusChanged(true)
- if (mSkipFirstLostFocus) {
- mSkipFirstLostFocus = false;
+ if (mIsOrientationChanged) {
+ mIsOrientationChanged = false;
return;
}
Log.v(TAG, "Lost window focus, dismissing the dialog");
@@ -512,9 +512,6 @@
public void onAttachedToWindow() {
super.onAttachedToWindow();
- //save the first orientation
- mOrientation = getResources().getConfiguration().orientation;
-
mWakefulnessLifecycle.addObserver(this);
if (Utils.isBiometricAllowed(mConfig.mPromptInfo)) {
@@ -670,7 +667,7 @@
}
if (savedState != null) {
- mSkipFirstLostFocus = savedState.getBoolean(
+ mIsOrientationChanged = savedState.getBoolean(
AuthDialog.KEY_BIOMETRIC_ORIENTATION_CHANGED);
}
@@ -764,9 +761,7 @@
mBiometricView != null && mCredentialView == null);
outState.putBoolean(AuthDialog.KEY_CREDENTIAL_SHOWING, mCredentialView != null);
- if (mOrientation != getResources().getConfiguration().orientation) {
- outState.putBoolean(AuthDialog.KEY_BIOMETRIC_ORIENTATION_CHANGED, true);
- }
+ outState.putBoolean(AuthDialog.KEY_BIOMETRIC_ORIENTATION_CHANGED, mIsOrientationChanged);
if (mBiometricView != null) {
mBiometricView.onSaveState(outState);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index c7c7f86..a7519cf 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -122,7 +122,7 @@
@Nullable private final FingerprintManager mFingerprintManager;
@Nullable private final FaceManager mFaceManager;
private final Provider<UdfpsController> mUdfpsControllerFactory;
- private final Provider<SidefpsController> mSidefpsControllerFactory;
+ private final Provider<SideFpsController> mSidefpsControllerFactory;
// TODO: these should be migrated out once ready
@NonNull private final Provider<BiometricPromptCredentialInteractor> mBiometricPromptInteractor;
@@ -148,7 +148,7 @@
@NonNull private final DisplayManager mDisplayManager;
@Nullable private UdfpsController mUdfpsController;
@Nullable private IUdfpsRefreshRateRequestCallback mUdfpsRefreshRateRequestCallback;
- @Nullable private SidefpsController mSidefpsController;
+ @Nullable private SideFpsController mSideFpsController;
@Nullable private IBiometricContextListener mBiometricContextListener;
@Nullable private UdfpsLogger mUdfpsLogger;
@VisibleForTesting IBiometricSysuiReceiver mReceiver;
@@ -306,7 +306,7 @@
mSidefpsProps = !sidefpsProps.isEmpty() ? sidefpsProps : null;
if (mSidefpsProps != null) {
- mSidefpsController = mSidefpsControllerFactory.get();
+ mSideFpsController = mSidefpsControllerFactory.get();
}
mFingerprintManager.registerBiometricStateListener(new BiometricStateListener() {
@@ -702,7 +702,7 @@
@Nullable FingerprintManager fingerprintManager,
@Nullable FaceManager faceManager,
Provider<UdfpsController> udfpsControllerFactory,
- Provider<SidefpsController> sidefpsControllerFactory,
+ Provider<SideFpsController> sidefpsControllerFactory,
@NonNull DisplayManager displayManager,
@NonNull WakefulnessLifecycle wakefulnessLifecycle,
@NonNull UserManager userManager,
@@ -1120,7 +1120,7 @@
* Whether the passed userId has enrolled SFPS.
*/
public boolean isSfpsEnrolled(int userId) {
- if (mSidefpsController == null) {
+ if (mSideFpsController == null) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
new file mode 100644
index 0000000..1c3dd45
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
@@ -0,0 +1,397 @@
+/*
+ * 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.biometrics
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.app.ActivityTaskManager
+import android.content.Context
+import android.graphics.PixelFormat
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffColorFilter
+import android.graphics.Rect
+import android.hardware.biometrics.BiometricOverlayConstants
+import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
+import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS
+import android.hardware.biometrics.SensorLocationInternal
+import android.hardware.display.DisplayManager
+import android.hardware.fingerprint.FingerprintManager
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
+import android.hardware.fingerprint.ISidefpsController
+import android.os.Handler
+import android.util.Log
+import android.util.RotationUtils
+import android.view.Display
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.Surface
+import android.view.View
+import android.view.View.AccessibilityDelegate
+import android.view.ViewPropertyAnimator
+import android.view.WindowInsets
+import android.view.WindowManager
+import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
+import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
+import android.view.accessibility.AccessibilityEvent
+import androidx.annotation.RawRes
+import com.airbnb.lottie.LottieAnimationView
+import com.airbnb.lottie.LottieProperty
+import com.airbnb.lottie.model.KeyPath
+import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.Dumpable
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.recents.OverviewProxyService
+import com.android.systemui.util.concurrency.DelayableExecutor
+import java.io.PrintWriter
+import javax.inject.Inject
+
+private const val TAG = "SideFpsController"
+
+/**
+ * Shows and hides the side fingerprint sensor (side-fps) overlay and handles side fps touch events.
+ */
+@SysUISingleton
+class SideFpsController
+@Inject
+constructor(
+ private val context: Context,
+ private val layoutInflater: LayoutInflater,
+ fingerprintManager: FingerprintManager?,
+ private val windowManager: WindowManager,
+ private val activityTaskManager: ActivityTaskManager,
+ overviewProxyService: OverviewProxyService,
+ displayManager: DisplayManager,
+ @Main private val mainExecutor: DelayableExecutor,
+ @Main private val handler: Handler,
+ dumpManager: DumpManager
+) : Dumpable {
+ val requests: HashSet<SideFpsUiRequestSource> = HashSet()
+
+ @VisibleForTesting
+ val sensorProps: FingerprintSensorPropertiesInternal =
+ fingerprintManager?.sideFpsSensorProperties
+ ?: throw IllegalStateException("no side fingerprint sensor")
+
+ @VisibleForTesting
+ val orientationListener =
+ BiometricDisplayListener(
+ context,
+ displayManager,
+ handler,
+ BiometricDisplayListener.SensorType.SideFingerprint(sensorProps)
+ ) { onOrientationChanged() }
+
+ @VisibleForTesting
+ val overviewProxyListener =
+ object : OverviewProxyService.OverviewProxyListener {
+ override fun onTaskbarStatusUpdated(visible: Boolean, stashed: Boolean) {
+ overlayView?.let { view ->
+ handler.postDelayed({ updateOverlayVisibility(view) }, 500)
+ }
+ }
+ }
+
+ private val animationDuration =
+ context.resources.getInteger(android.R.integer.config_mediumAnimTime).toLong()
+
+ private var overlayHideAnimator: ViewPropertyAnimator? = null
+
+ private var overlayView: View? = null
+ set(value) {
+ field?.let { oldView ->
+ windowManager.removeView(oldView)
+ orientationListener.disable()
+ }
+ overlayHideAnimator?.cancel()
+ overlayHideAnimator = null
+
+ field = value
+ field?.let { newView ->
+ windowManager.addView(newView, overlayViewParams)
+ updateOverlayVisibility(newView)
+ orientationListener.enable()
+ }
+ }
+ @VisibleForTesting
+ internal var overlayOffsets: SensorLocationInternal = SensorLocationInternal.DEFAULT
+
+ private val overlayViewParams =
+ WindowManager.LayoutParams(
+ WindowManager.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
+ Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS,
+ PixelFormat.TRANSLUCENT
+ )
+ .apply {
+ title = TAG
+ fitInsetsTypes = 0 // overrides default, avoiding status bars during layout
+ gravity = Gravity.TOP or Gravity.LEFT
+ layoutInDisplayCutoutMode =
+ WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ privateFlags = PRIVATE_FLAG_TRUSTED_OVERLAY or PRIVATE_FLAG_NO_MOVE_ANIMATION
+ }
+
+ init {
+ fingerprintManager?.setSidefpsController(
+ object : ISidefpsController.Stub() {
+ override fun show(
+ sensorId: Int,
+ @BiometricOverlayConstants.ShowReason reason: Int
+ ) =
+ if (reason.isReasonToAutoShow(activityTaskManager)) {
+ show(SideFpsUiRequestSource.AUTO_SHOW)
+ } else {
+ hide(SideFpsUiRequestSource.AUTO_SHOW)
+ }
+
+ override fun hide(sensorId: Int) = hide(SideFpsUiRequestSource.AUTO_SHOW)
+ }
+ )
+ overviewProxyService.addCallback(overviewProxyListener)
+ dumpManager.registerDumpable(this)
+ }
+
+ /** Shows the side fps overlay if not already shown. */
+ fun show(request: SideFpsUiRequestSource) {
+ requests.add(request)
+ mainExecutor.execute {
+ if (overlayView == null) {
+ createOverlayForDisplay()
+ } else {
+ Log.v(TAG, "overlay already shown")
+ }
+ }
+ }
+
+ /** Hides the fps overlay if shown. */
+ fun hide(request: SideFpsUiRequestSource) {
+ requests.remove(request)
+ mainExecutor.execute {
+ if (requests.isEmpty()) {
+ overlayView = null
+ }
+ }
+ }
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.println("requests:")
+ for (requestSource in requests) {
+ pw.println(" $requestSource.name")
+ }
+ }
+
+ private fun onOrientationChanged() {
+ if (overlayView != null) {
+ createOverlayForDisplay()
+ }
+ }
+
+ private fun createOverlayForDisplay() {
+ val view = layoutInflater.inflate(R.layout.sidefps_view, null, false)
+ overlayView = view
+ val display = context.display!!
+ val offsets =
+ sensorProps.getLocation(display.uniqueId).let { location ->
+ if (location == null) {
+ Log.w(TAG, "No location specified for display: ${display.uniqueId}")
+ }
+ location ?: sensorProps.location
+ }
+ overlayOffsets = offsets
+
+ val lottie = view.findViewById(R.id.sidefps_animation) as LottieAnimationView
+ view.rotation = display.asSideFpsAnimationRotation(offsets.isYAligned())
+ lottie.setAnimation(display.asSideFpsAnimation(offsets.isYAligned()))
+ lottie.addLottieOnCompositionLoadedListener {
+ // Check that view is not stale, and that overlayView has not been hidden/removed
+ if (overlayView != null && overlayView == view) {
+ updateOverlayParams(display, it.bounds)
+ }
+ }
+ lottie.addOverlayDynamicColor(context)
+
+ /**
+ * Intercepts TYPE_WINDOW_STATE_CHANGED accessibility event, preventing Talkback from
+ * speaking @string/accessibility_fingerprint_label twice when sensor location indicator is
+ * in focus
+ */
+ view.setAccessibilityDelegate(
+ object : AccessibilityDelegate() {
+ override fun dispatchPopulateAccessibilityEvent(
+ host: View,
+ event: AccessibilityEvent
+ ): Boolean {
+ return if (
+ event.getEventType() === AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
+ ) {
+ true
+ } else {
+ super.dispatchPopulateAccessibilityEvent(host, event)
+ }
+ }
+ }
+ )
+ }
+
+ @VisibleForTesting
+ internal fun updateOverlayParams(display: Display, bounds: Rect) {
+ val isNaturalOrientation = display.isNaturalOrientation()
+ val size = windowManager.maximumWindowMetrics.bounds
+ val displayWidth = if (isNaturalOrientation) size.width() else size.height()
+ val displayHeight = if (isNaturalOrientation) size.height() else size.width()
+ val boundsWidth = if (isNaturalOrientation) bounds.width() else bounds.height()
+ val boundsHeight = if (isNaturalOrientation) bounds.height() else bounds.width()
+ val sensorBounds =
+ if (overlayOffsets.isYAligned()) {
+ Rect(
+ displayWidth - boundsWidth,
+ overlayOffsets.sensorLocationY,
+ displayWidth,
+ overlayOffsets.sensorLocationY + boundsHeight
+ )
+ } else {
+ Rect(
+ overlayOffsets.sensorLocationX,
+ 0,
+ overlayOffsets.sensorLocationX + boundsWidth,
+ boundsHeight
+ )
+ }
+
+ RotationUtils.rotateBounds(
+ sensorBounds,
+ Rect(0, 0, displayWidth, displayHeight),
+ display.rotation
+ )
+
+ overlayViewParams.x = sensorBounds.left
+ overlayViewParams.y = sensorBounds.top
+ windowManager.updateViewLayout(overlayView, overlayViewParams)
+ }
+
+ private fun updateOverlayVisibility(view: View) {
+ if (view != overlayView) {
+ return
+ }
+ // hide after a few seconds if the sensor is oriented down and there are
+ // large overlapping system bars
+ val rotation = context.display?.rotation
+ if (
+ windowManager.currentWindowMetrics.windowInsets.hasBigNavigationBar() &&
+ ((rotation == Surface.ROTATION_270 && overlayOffsets.isYAligned()) ||
+ (rotation == Surface.ROTATION_180 && !overlayOffsets.isYAligned()))
+ ) {
+ overlayHideAnimator =
+ view
+ .animate()
+ .alpha(0f)
+ .setStartDelay(3_000)
+ .setDuration(animationDuration)
+ .setListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ view.visibility = View.GONE
+ overlayHideAnimator = null
+ }
+ }
+ )
+ } else {
+ overlayHideAnimator?.cancel()
+ overlayHideAnimator = null
+ view.alpha = 1f
+ view.visibility = View.VISIBLE
+ }
+ }
+}
+
+private val FingerprintManager?.sideFpsSensorProperties: FingerprintSensorPropertiesInternal?
+ get() = this?.sensorPropertiesInternal?.firstOrNull { it.isAnySidefpsType }
+
+/** Returns [True] when the device has a side fingerprint sensor. */
+fun FingerprintManager?.hasSideFpsSensor(): Boolean = this?.sideFpsSensorProperties != null
+
+@BiometricOverlayConstants.ShowReason
+private fun Int.isReasonToAutoShow(activityTaskManager: ActivityTaskManager): Boolean =
+ when (this) {
+ REASON_AUTH_KEYGUARD -> false
+ REASON_AUTH_SETTINGS ->
+ when (activityTaskManager.topClass()) {
+ // TODO(b/186176653): exclude fingerprint overlays from this list view
+ "com.android.settings.biometrics.fingerprint.FingerprintSettings" -> false
+ else -> true
+ }
+ else -> true
+ }
+
+private fun ActivityTaskManager.topClass(): String =
+ getTasks(1).firstOrNull()?.topActivity?.className ?: ""
+
+@RawRes
+private fun Display.asSideFpsAnimation(yAligned: Boolean): Int =
+ when (rotation) {
+ Surface.ROTATION_0 -> if (yAligned) R.raw.sfps_pulse else R.raw.sfps_pulse_landscape
+ Surface.ROTATION_180 -> if (yAligned) R.raw.sfps_pulse else R.raw.sfps_pulse_landscape
+ else -> if (yAligned) R.raw.sfps_pulse_landscape else R.raw.sfps_pulse
+ }
+
+private fun Display.asSideFpsAnimationRotation(yAligned: Boolean): Float =
+ when (rotation) {
+ Surface.ROTATION_90 -> if (yAligned) 0f else 180f
+ Surface.ROTATION_180 -> 180f
+ Surface.ROTATION_270 -> if (yAligned) 180f else 0f
+ else -> 0f
+ }
+
+private fun SensorLocationInternal.isYAligned(): Boolean = sensorLocationY != 0
+
+private fun Display.isNaturalOrientation(): Boolean =
+ rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180
+
+private fun WindowInsets.hasBigNavigationBar(): Boolean =
+ getInsets(WindowInsets.Type.navigationBars()).bottom >= 70
+
+private fun LottieAnimationView.addOverlayDynamicColor(context: Context) {
+ fun update() {
+ val c = context.getColor(R.color.biometric_dialog_accent)
+ for (key in listOf(".blue600", ".blue400")) {
+ addValueCallback(KeyPath(key, "**"), LottieProperty.COLOR_FILTER) {
+ PorterDuffColorFilter(c, PorterDuff.Mode.SRC_ATOP)
+ }
+ }
+ }
+
+ if (composition != null) {
+ update()
+ } else {
+ addLottieOnCompositionLoadedListener { update() }
+ }
+}
+
+/**
+ * The source of a request to show the side fps visual indicator. This is distinct from
+ * [BiometricOverlayConstants] which corrresponds with the reason fingerprint authentication is
+ * requested.
+ */
+enum class SideFpsUiRequestSource {
+ /** see [isReasonToAutoShow] */
+ AUTO_SHOW,
+ /** Pin, pattern or password bouncer */
+ PRIMARY_BOUNCER,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
deleted file mode 100644
index d03106b..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
+++ /dev/null
@@ -1,337 +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.biometrics
-
-import android.animation.Animator
-import android.animation.AnimatorListenerAdapter
-import android.app.ActivityTaskManager
-import android.content.Context
-import android.graphics.PixelFormat
-import android.graphics.PorterDuff
-import android.graphics.PorterDuffColorFilter
-import android.graphics.Rect
-import android.hardware.biometrics.BiometricOverlayConstants
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS
-import android.hardware.biometrics.SensorLocationInternal
-import android.hardware.display.DisplayManager
-import android.hardware.fingerprint.FingerprintManager
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
-import android.hardware.fingerprint.ISidefpsController
-import android.os.Handler
-import android.util.Log
-import android.util.RotationUtils
-import android.view.Display
-import android.view.Gravity
-import android.view.LayoutInflater
-import android.view.Surface
-import android.view.View
-import android.view.View.AccessibilityDelegate
-import android.view.ViewPropertyAnimator
-import android.view.WindowInsets
-import android.view.WindowManager
-import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
-import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
-import android.view.accessibility.AccessibilityEvent
-import androidx.annotation.RawRes
-import com.airbnb.lottie.LottieAnimationView
-import com.airbnb.lottie.LottieProperty
-import com.airbnb.lottie.model.KeyPath
-import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.R
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.recents.OverviewProxyService
-import com.android.systemui.util.concurrency.DelayableExecutor
-import javax.inject.Inject
-
-private const val TAG = "SidefpsController"
-
-/**
- * Shows and hides the side fingerprint sensor (side-fps) overlay and handles side fps touch events.
- */
-@SysUISingleton
-class SidefpsController @Inject constructor(
- private val context: Context,
- private val layoutInflater: LayoutInflater,
- fingerprintManager: FingerprintManager?,
- private val windowManager: WindowManager,
- private val activityTaskManager: ActivityTaskManager,
- overviewProxyService: OverviewProxyService,
- displayManager: DisplayManager,
- @Main private val mainExecutor: DelayableExecutor,
- @Main private val handler: Handler
-) {
- @VisibleForTesting
- val sensorProps: FingerprintSensorPropertiesInternal = fingerprintManager
- ?.sideFpsSensorProperties
- ?: throw IllegalStateException("no side fingerprint sensor")
-
- @VisibleForTesting
- val orientationListener = BiometricDisplayListener(
- context,
- displayManager,
- handler,
- BiometricDisplayListener.SensorType.SideFingerprint(sensorProps)
- ) { onOrientationChanged() }
-
- @VisibleForTesting
- val overviewProxyListener = object : OverviewProxyService.OverviewProxyListener {
- override fun onTaskbarStatusUpdated(visible: Boolean, stashed: Boolean) {
- overlayView?.let { view ->
- handler.postDelayed({ updateOverlayVisibility(view) }, 500)
- }
- }
- }
-
- private val animationDuration =
- context.resources.getInteger(android.R.integer.config_mediumAnimTime).toLong()
-
- private var overlayHideAnimator: ViewPropertyAnimator? = null
-
- private var overlayView: View? = null
- set(value) {
- field?.let { oldView ->
- windowManager.removeView(oldView)
- orientationListener.disable()
- }
- overlayHideAnimator?.cancel()
- overlayHideAnimator = null
-
- field = value
- field?.let { newView ->
- windowManager.addView(newView, overlayViewParams)
- updateOverlayVisibility(newView)
- orientationListener.enable()
- }
- }
- @VisibleForTesting
- internal var overlayOffsets: SensorLocationInternal = SensorLocationInternal.DEFAULT
-
- private val overlayViewParams = WindowManager.LayoutParams(
- WindowManager.LayoutParams.WRAP_CONTENT,
- WindowManager.LayoutParams.WRAP_CONTENT,
- WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
- Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS,
- PixelFormat.TRANSLUCENT
- ).apply {
- title = TAG
- fitInsetsTypes = 0 // overrides default, avoiding status bars during layout
- gravity = Gravity.TOP or Gravity.LEFT
- layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
- privateFlags = PRIVATE_FLAG_TRUSTED_OVERLAY or PRIVATE_FLAG_NO_MOVE_ANIMATION
- }
-
- init {
- fingerprintManager?.setSidefpsController(
- object : ISidefpsController.Stub() {
- override fun show(
- sensorId: Int,
- @BiometricOverlayConstants.ShowReason reason: Int
- ) = if (reason.isReasonToShow(activityTaskManager)) show() else hide()
-
- override fun hide(sensorId: Int) = hide()
- })
- overviewProxyService.addCallback(overviewProxyListener)
- }
-
- /** Shows the side fps overlay if not already shown. */
- fun show() {
- mainExecutor.execute {
- if (overlayView == null) {
- createOverlayForDisplay()
- } else {
- Log.v(TAG, "overlay already shown")
- }
- }
- }
-
- /** Hides the fps overlay if shown. */
- fun hide() {
- mainExecutor.execute { overlayView = null }
- }
-
- private fun onOrientationChanged() {
- if (overlayView != null) {
- createOverlayForDisplay()
- }
- }
-
- private fun createOverlayForDisplay() {
- val view = layoutInflater.inflate(R.layout.sidefps_view, null, false)
- overlayView = view
- val display = context.display!!
- val offsets = sensorProps.getLocation(display.uniqueId).let { location ->
- if (location == null) {
- Log.w(TAG, "No location specified for display: ${display.uniqueId}")
- }
- location ?: sensorProps.location
- }
- overlayOffsets = offsets
-
- val lottie = view.findViewById(R.id.sidefps_animation) as LottieAnimationView
- view.rotation = display.asSideFpsAnimationRotation(offsets.isYAligned())
- lottie.setAnimation(display.asSideFpsAnimation(offsets.isYAligned()))
- lottie.addLottieOnCompositionLoadedListener {
- // Check that view is not stale, and that overlayView has not been hidden/removed
- if (overlayView != null && overlayView == view) {
- updateOverlayParams(display, it.bounds)
- }
- }
- lottie.addOverlayDynamicColor(context)
-
- /**
- * Intercepts TYPE_WINDOW_STATE_CHANGED accessibility event, preventing Talkback from
- * speaking @string/accessibility_fingerprint_label twice when sensor location indicator
- * is in focus
- */
- view.setAccessibilityDelegate(object : AccessibilityDelegate() {
- override fun dispatchPopulateAccessibilityEvent(
- host: View,
- event: AccessibilityEvent
- ): Boolean {
- return if (event.getEventType() === AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
- true
- } else {
- super.dispatchPopulateAccessibilityEvent(host, event)
- }
- }
- })
- }
-
- @VisibleForTesting
- internal fun updateOverlayParams(display: Display, bounds: Rect) {
- val isNaturalOrientation = display.isNaturalOrientation()
- val size = windowManager.maximumWindowMetrics.bounds
- val displayWidth = if (isNaturalOrientation) size.width() else size.height()
- val displayHeight = if (isNaturalOrientation) size.height() else size.width()
- val boundsWidth = if (isNaturalOrientation) bounds.width() else bounds.height()
- val boundsHeight = if (isNaturalOrientation) bounds.height() else bounds.width()
- val sensorBounds = if (overlayOffsets.isYAligned()) {
- Rect(
- displayWidth - boundsWidth,
- overlayOffsets.sensorLocationY,
- displayWidth,
- overlayOffsets.sensorLocationY + boundsHeight
- )
- } else {
- Rect(
- overlayOffsets.sensorLocationX,
- 0,
- overlayOffsets.sensorLocationX + boundsWidth,
- boundsHeight
- )
- }
-
- RotationUtils.rotateBounds(
- sensorBounds,
- Rect(0, 0, displayWidth, displayHeight),
- display.rotation
- )
-
- overlayViewParams.x = sensorBounds.left
- overlayViewParams.y = sensorBounds.top
- windowManager.updateViewLayout(overlayView, overlayViewParams)
- }
-
- private fun updateOverlayVisibility(view: View) {
- if (view != overlayView) {
- return
- }
- // hide after a few seconds if the sensor is oriented down and there are
- // large overlapping system bars
- val rotation = context.display?.rotation
- if (windowManager.currentWindowMetrics.windowInsets.hasBigNavigationBar() &&
- ((rotation == Surface.ROTATION_270 && overlayOffsets.isYAligned()) ||
- (rotation == Surface.ROTATION_180 && !overlayOffsets.isYAligned()))) {
- overlayHideAnimator = view.animate()
- .alpha(0f)
- .setStartDelay(3_000)
- .setDuration(animationDuration)
- .setListener(object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator) {
- view.visibility = View.GONE
- overlayHideAnimator = null
- }
- })
- } else {
- overlayHideAnimator?.cancel()
- overlayHideAnimator = null
- view.alpha = 1f
- view.visibility = View.VISIBLE
- }
- }
-}
-
-private val FingerprintManager?.sideFpsSensorProperties: FingerprintSensorPropertiesInternal?
- get() = this?.sensorPropertiesInternal?.firstOrNull { it.isAnySidefpsType }
-
-/** Returns [True] when the device has a side fingerprint sensor. */
-fun FingerprintManager?.hasSideFpsSensor(): Boolean = this?.sideFpsSensorProperties != null
-
-@BiometricOverlayConstants.ShowReason
-private fun Int.isReasonToShow(activityTaskManager: ActivityTaskManager): Boolean = when (this) {
- REASON_AUTH_KEYGUARD -> false
- REASON_AUTH_SETTINGS -> when (activityTaskManager.topClass()) {
- // TODO(b/186176653): exclude fingerprint overlays from this list view
- "com.android.settings.biometrics.fingerprint.FingerprintSettings" -> false
- else -> true
- }
- else -> true
-}
-
-private fun ActivityTaskManager.topClass(): String =
- getTasks(1).firstOrNull()?.topActivity?.className ?: ""
-
-@RawRes
-private fun Display.asSideFpsAnimation(yAligned: Boolean): Int = when (rotation) {
- Surface.ROTATION_0 -> if (yAligned) R.raw.sfps_pulse else R.raw.sfps_pulse_landscape
- Surface.ROTATION_180 -> if (yAligned) R.raw.sfps_pulse else R.raw.sfps_pulse_landscape
- else -> if (yAligned) R.raw.sfps_pulse_landscape else R.raw.sfps_pulse
-}
-
-private fun Display.asSideFpsAnimationRotation(yAligned: Boolean): Float = when (rotation) {
- Surface.ROTATION_90 -> if (yAligned) 0f else 180f
- Surface.ROTATION_180 -> 180f
- Surface.ROTATION_270 -> if (yAligned) 180f else 0f
- else -> 0f
-}
-
-private fun SensorLocationInternal.isYAligned(): Boolean = sensorLocationY != 0
-
-private fun Display.isNaturalOrientation(): Boolean =
- rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180
-
-private fun WindowInsets.hasBigNavigationBar(): Boolean =
- getInsets(WindowInsets.Type.navigationBars()).bottom >= 70
-
-private fun LottieAnimationView.addOverlayDynamicColor(context: Context) {
- fun update() {
- val c = context.getColor(R.color.biometric_dialog_accent)
- for (key in listOf(".blue600", ".blue400")) {
- addValueCallback(
- KeyPath(key, "**"),
- LottieProperty.COLOR_FILTER
- ) { PorterDuffColorFilter(c, PorterDuff.Mode.SRC_ATOP) }
- }
- }
-
- if (composition != null) {
- update()
- } else {
- addLottieOnCompositionLoadedListener { update() }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 3631057..5469d29 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -69,7 +69,7 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.keyguard.domain.interactor.BouncerInteractor;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeExpansionStateManager;
@@ -142,7 +142,7 @@
@NonNull private final LatencyTracker mLatencyTracker;
@VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener;
@NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator;
- @NonNull private final BouncerInteractor mBouncerInteractor;
+ @NonNull private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
// sensors, this, in addition to a lot of the code here, will be updated.
@@ -235,7 +235,7 @@
mUdfpsDisplayMode, requestId, reason, callback,
(view, event, fromUdfpsView) -> onTouch(requestId, event,
fromUdfpsView), mActivityLaunchAnimator, mFeatureFlags,
- mBouncerInteractor)));
+ mPrimaryBouncerInteractor)));
}
@Override
@@ -354,14 +354,14 @@
if (!mOverlayParams.equals(overlayParams)) {
mOverlayParams = overlayParams;
- final boolean wasShowingAltAuth = mKeyguardViewManager.isShowingAlternateAuth();
+ final boolean wasShowingAltAuth = mKeyguardViewManager.isShowingAlternateBouncer();
// When the bounds change it's always necessary to re-create the overlay's window with
// new LayoutParams. If the overlay needs to be shown, this will re-create and show the
// overlay with the updated LayoutParams. Otherwise, the overlay will remain hidden.
redrawOverlay();
if (wasShowingAltAuth) {
- mKeyguardViewManager.showGenericBouncer(true);
+ mKeyguardViewManager.showBouncer(true);
}
}
}
@@ -660,7 +660,7 @@
@NonNull ActivityLaunchAnimator activityLaunchAnimator,
@NonNull Optional<AlternateUdfpsTouchProvider> alternateTouchProvider,
@NonNull @BiometricsBackground Executor biometricsExecutor,
- @NonNull BouncerInteractor bouncerInteractor) {
+ @NonNull PrimaryBouncerInteractor primaryBouncerInteractor) {
mContext = context;
mExecution = execution;
mVibrator = vibrator;
@@ -699,7 +699,7 @@
false /* resetLockoutRequiresHardwareAuthToken */);
mBiometricExecutor = biometricsExecutor;
- mBouncerInteractor = bouncerInteractor;
+ mPrimaryBouncerInteractor = primaryBouncerInteractor;
mDumpManager.registerDumpable(TAG, this);
@@ -791,8 +791,8 @@
onFingerUp(mOverlay.getRequestId(), oldView);
}
final boolean removed = mOverlay.hide();
- if (mKeyguardViewManager.isShowingAlternateAuth()) {
- mKeyguardViewManager.resetAlternateAuth(true);
+ if (mKeyguardViewManager.isShowingAlternateBouncer()) {
+ mKeyguardViewManager.hideAlternateBouncer(true);
}
Log.v(TAG, "hideUdfpsOverlay | removing window: " + removed);
} else {
@@ -832,7 +832,7 @@
Log.v(TAG, "aod lock icon long-press rejected by the falsing manager.");
return;
}
- mKeyguardViewManager.showBouncer(true);
+ mKeyguardViewManager.showPrimaryBouncer(true);
// play the same haptic as the LockIconViewController longpress
mVibrator.vibrate(
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index d70861a..0bb24f8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -49,7 +49,7 @@
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.keyguard.domain.interactor.BouncerInteractor
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -95,7 +95,7 @@
private val onTouch: (View, MotionEvent, Boolean) -> Boolean,
private val activityLaunchAnimator: ActivityLaunchAnimator,
private val featureFlags: FeatureFlags,
- private val bouncerInteractor: BouncerInteractor,
+ private val primaryBouncerInteractor: PrimaryBouncerInteractor,
private val isDebuggable: Boolean = Build.IS_DEBUGGABLE
) {
/** The view, when [isShowing], or null. */
@@ -252,7 +252,7 @@
controller,
activityLaunchAnimator,
featureFlags,
- bouncerInteractor
+ primaryBouncerInteractor
)
}
REASON_AUTH_BP -> {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt
index 5bae2dc..91967f9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt
@@ -31,7 +31,7 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.domain.interactor.BouncerInteractor
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.ShadeExpansionListener
@@ -40,9 +40,9 @@
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
import com.android.systemui.statusbar.phone.KeyguardBouncer
-import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback
+import com.android.systemui.statusbar.phone.KeyguardBouncer.PrimaryBouncerExpansionCallback
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.AlternateAuthInterceptor
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.AlternateBouncer
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.KeyguardViewManagerCallback
import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
@@ -73,7 +73,7 @@
private val udfpsController: UdfpsController,
private val activityLaunchAnimator: ActivityLaunchAnimator,
featureFlags: FeatureFlags,
- private val bouncerInteractor: BouncerInteractor
+ private val primaryBouncerInteractor: PrimaryBouncerInteractor
) :
UdfpsAnimationViewController<UdfpsKeyguardView>(
view,
@@ -146,8 +146,8 @@
}
}
- private val bouncerExpansionCallback: BouncerExpansionCallback =
- object : BouncerExpansionCallback {
+ private val mPrimaryBouncerExpansionCallback: PrimaryBouncerExpansionCallback =
+ object : PrimaryBouncerExpansionCallback {
override fun onExpansionChanged(expansion: Float) {
inputBouncerHiddenAmount = expansion
updateAlpha()
@@ -180,7 +180,7 @@
private val shadeExpansionListener = ShadeExpansionListener { (fraction) ->
panelExpansionFraction =
- if (keyguardViewManager.isBouncerInTransit) {
+ if (keyguardViewManager.isPrimaryBouncerInTransit) {
aboutToShowBouncerProgress(fraction)
} else {
fraction
@@ -237,17 +237,17 @@
}
}
- private val alternateAuthInterceptor: AlternateAuthInterceptor =
- object : AlternateAuthInterceptor {
- override fun showAlternateAuthBouncer(): Boolean {
+ private val mAlternateBouncer: AlternateBouncer =
+ object : AlternateBouncer {
+ override fun showAlternateBouncer(): Boolean {
return showUdfpsBouncer(true)
}
- override fun hideAlternateAuthBouncer(): Boolean {
+ override fun hideAlternateBouncer(): Boolean {
return showUdfpsBouncer(false)
}
- override fun isShowingAlternateAuthBouncer(): Boolean {
+ override fun isShowingAlternateBouncer(): Boolean {
return showingUdfpsBouncer
}
@@ -268,7 +268,7 @@
override fun onInit() {
super.onInit()
- keyguardViewManager.setAlternateAuthInterceptor(alternateAuthInterceptor)
+ keyguardViewManager.setAlternateBouncer(mAlternateBouncer)
}
init {
@@ -285,7 +285,7 @@
@VisibleForTesting
internal suspend fun listenForBouncerExpansion(scope: CoroutineScope): Job {
return scope.launch {
- bouncerInteractor.bouncerExpansion.collect { bouncerExpansion: Float ->
+ primaryBouncerInteractor.bouncerExpansion.collect { bouncerExpansion: Float ->
inputBouncerExpansion = bouncerExpansion
updateAlpha()
updatePauseAuth()
@@ -306,10 +306,10 @@
qsExpansion = keyguardViewManager.qsExpansion
keyguardViewManager.addCallback(statusBarKeyguardViewManagerCallback)
if (!isModernBouncerEnabled) {
- val bouncer = keyguardViewManager.bouncer
+ val bouncer = keyguardViewManager.primaryBouncer
bouncer?.expansion?.let {
- bouncerExpansionCallback.onExpansionChanged(it)
- bouncer.addBouncerExpansionCallback(bouncerExpansionCallback)
+ mPrimaryBouncerExpansionCallback.onExpansionChanged(it)
+ bouncer.addBouncerExpansionCallback(mPrimaryBouncerExpansionCallback)
}
updateBouncerHiddenAmount()
}
@@ -319,7 +319,7 @@
view.updatePadding()
updateAlpha()
updatePauseAuth()
- keyguardViewManager.setAlternateAuthInterceptor(alternateAuthInterceptor)
+ keyguardViewManager.setAlternateBouncer(mAlternateBouncer)
lockScreenShadeTransitionController.udfpsKeyguardViewController = this
activityLaunchAnimator.addListener(activityLaunchAnimatorListener)
}
@@ -329,7 +329,7 @@
faceDetectRunning = false
keyguardStateController.removeCallback(keyguardStateControllerCallback)
statusBarStateController.removeCallback(stateListener)
- keyguardViewManager.removeAlternateAuthInterceptor(alternateAuthInterceptor)
+ keyguardViewManager.removeAlternateAuthInterceptor(mAlternateBouncer)
keyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false)
configurationController.removeCallback(configurationListener)
shadeExpansionStateManager.removeExpansionListener(shadeExpansionListener)
@@ -339,7 +339,9 @@
activityLaunchAnimator.removeListener(activityLaunchAnimatorListener)
keyguardViewManager.removeCallback(statusBarKeyguardViewManagerCallback)
if (!isModernBouncerEnabled) {
- keyguardViewManager.bouncer?.removeBouncerExpansionCallback(bouncerExpansionCallback)
+ keyguardViewManager.primaryBouncer?.removeBouncerExpansionCallback(
+ mPrimaryBouncerExpansionCallback
+ )
}
}
@@ -442,7 +444,7 @@
return if (isModernBouncerEnabled) {
inputBouncerExpansion == 1f
} else {
- keyguardViewManager.isBouncerShowing && !keyguardViewManager.isShowingAlternateAuth
+ keyguardViewManager.isBouncerShowing && !keyguardViewManager.isShowingAlternateBouncer
}
}
@@ -462,7 +464,7 @@
*/
private fun maybeShowInputBouncer() {
if (showingUdfpsBouncer && hasUdfpsBouncerShownWithMinTime()) {
- keyguardViewManager.showBouncer(true)
+ keyguardViewManager.showPrimaryBouncer(true)
}
}
@@ -535,8 +537,8 @@
if (isModernBouncerEnabled) {
return
}
- val altBouncerShowing = keyguardViewManager.isShowingAlternateAuth
- if (altBouncerShowing || !keyguardViewManager.bouncerIsOrWillBeShowing()) {
+ val altBouncerShowing = keyguardViewManager.isShowingAlternateBouncer
+ if (altBouncerShowing || !keyguardViewManager.primaryBouncerIsOrWillBeShowing()) {
inputBouncerHiddenAmount = 1f
} else if (keyguardViewManager.isBouncerShowing) {
// input bouncer is fully showing
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
index 2245d84..beaccba 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
@@ -34,6 +34,8 @@
import com.android.systemui.classifier.FalsingDataProvider.SessionListener;
import com.android.systemui.classifier.HistoryTracker.BeliefListener;
import com.android.systemui.dagger.qualifiers.TestHarness;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -65,6 +67,7 @@
private static final double FALSE_BELIEF_THRESHOLD = 0.9;
private final FalsingDataProvider mDataProvider;
+ private final LongTapClassifier mLongTapClassifier;
private final SingleTapClassifier mSingleTapClassifier;
private final DoubleTapClassifier mDoubleTapClassifier;
private final HistoryTracker mHistoryTracker;
@@ -73,6 +76,7 @@
private final boolean mTestHarness;
private final MetricsLogger mMetricsLogger;
private int mIsFalseTouchCalls;
+ private FeatureFlags mFeatureFlags;
private static final Queue<String> RECENT_INFO_LOG =
new ArrayDeque<>(RECENT_INFO_LOG_SIZE + 1);
private static final Queue<DebugSwipeRecord> RECENT_SWIPES =
@@ -175,19 +179,23 @@
public BrightLineFalsingManager(FalsingDataProvider falsingDataProvider,
MetricsLogger metricsLogger,
@Named(BRIGHT_LINE_GESTURE_CLASSIFERS) Set<FalsingClassifier> classifiers,
- SingleTapClassifier singleTapClassifier, DoubleTapClassifier doubleTapClassifier,
- HistoryTracker historyTracker, KeyguardStateController keyguardStateController,
+ SingleTapClassifier singleTapClassifier, LongTapClassifier longTapClassifier,
+ DoubleTapClassifier doubleTapClassifier, HistoryTracker historyTracker,
+ KeyguardStateController keyguardStateController,
AccessibilityManager accessibilityManager,
- @TestHarness boolean testHarness) {
+ @TestHarness boolean testHarness,
+ FeatureFlags featureFlags) {
mDataProvider = falsingDataProvider;
mMetricsLogger = metricsLogger;
mClassifiers = classifiers;
mSingleTapClassifier = singleTapClassifier;
+ mLongTapClassifier = longTapClassifier;
mDoubleTapClassifier = doubleTapClassifier;
mHistoryTracker = historyTracker;
mKeyguardStateController = keyguardStateController;
mAccessibilityManager = accessibilityManager;
mTestHarness = testHarness;
+ mFeatureFlags = featureFlags;
mDataProvider.addSessionListener(mSessionListener);
mDataProvider.addGestureCompleteListener(mGestureFinalizedListener);
@@ -313,6 +321,58 @@
}
@Override
+ public boolean isFalseLongTap(@Penalty int penalty) {
+ if (!mFeatureFlags.isEnabled(Flags.FALSING_FOR_LONG_TAPS)) {
+ return false;
+ }
+
+ checkDestroyed();
+
+ if (skipFalsing(GENERIC)) {
+ mPriorResults = getPassedResult(1);
+ logDebug("Skipped falsing");
+ return false;
+ }
+
+ double falsePenalty = 0;
+ switch(penalty) {
+ case NO_PENALTY:
+ falsePenalty = 0;
+ break;
+ case LOW_PENALTY:
+ falsePenalty = 0.1;
+ break;
+ case MODERATE_PENALTY:
+ falsePenalty = 0.3;
+ break;
+ case HIGH_PENALTY:
+ falsePenalty = 0.6;
+ break;
+ }
+
+ FalsingClassifier.Result longTapResult =
+ mLongTapClassifier.isTap(mDataProvider.getRecentMotionEvents().isEmpty()
+ ? mDataProvider.getPriorMotionEvents()
+ : mDataProvider.getRecentMotionEvents(), falsePenalty);
+ mPriorResults = Collections.singleton(longTapResult);
+
+ if (!longTapResult.isFalse()) {
+ if (mDataProvider.isJustUnlockedWithFace()) {
+ // Immediately pass if a face is detected.
+ mPriorResults = getPassedResult(1);
+ logDebug("False Long Tap: false (face detected)");
+ } else {
+ mPriorResults = getPassedResult(0.1);
+ logDebug("False Long Tap: false (default)");
+ }
+ return false;
+ } else {
+ logDebug("False Long Tap: " + longTapResult.isFalse() + " (simple)");
+ return longTapResult.isFalse();
+ }
+ }
+
+ @Override
public boolean isFalseDoubleTap() {
checkDestroyed();
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index 5d04b5f..c4723e8 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -139,6 +139,11 @@
}
@Override
+ public boolean isFalseLongTap(int penalty) {
+ return mInternalFalsingManager.isFalseLongTap(penalty);
+ }
+
+ @Override
public boolean isFalseDoubleTap() {
return mInternalFalsingManager.isFalseDoubleTap();
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java
index 7b7f17e..5302af9 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java
@@ -40,6 +40,7 @@
public interface FalsingModule {
String BRIGHT_LINE_GESTURE_CLASSIFERS = "bright_line_gesture_classifiers";
String SINGLE_TAP_TOUCH_SLOP = "falsing_single_tap_touch_slop";
+ String LONG_TAP_TOUCH_SLOP = "falsing_long_tap_slop";
String DOUBLE_TAP_TOUCH_SLOP = "falsing_double_tap_touch_slop";
String DOUBLE_TAP_TIMEOUT_MS = "falsing_double_tap_timeout_ms";
@@ -81,4 +82,11 @@
static float providesSingleTapTouchSlop(ViewConfiguration viewConfiguration) {
return viewConfiguration.getScaledTouchSlop();
}
+
+ /** */
+ @Provides
+ @Named(LONG_TAP_TOUCH_SLOP)
+ static float providesLongTapTouchSlop(ViewConfiguration viewConfiguration) {
+ return viewConfiguration.getScaledTouchSlop() * 1.25f;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/LongTapClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/LongTapClassifier.java
new file mode 100644
index 0000000..1963e69
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/LongTapClassifier.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.classifier;
+
+import static com.android.systemui.classifier.FalsingModule.LONG_TAP_TOUCH_SLOP;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * Falsing classifier that accepts or rejects a gesture as a long tap.
+ */
+public class LongTapClassifier extends TapClassifier{
+
+ @Inject
+ LongTapClassifier(FalsingDataProvider dataProvider,
+ @Named(LONG_TAP_TOUCH_SLOP) float touchSlop) {
+ super(dataProvider, touchSlop);
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java
index bd6fbfb..7a7401d 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java
@@ -18,57 +18,17 @@
import static com.android.systemui.classifier.FalsingModule.SINGLE_TAP_TOUCH_SLOP;
-import android.view.MotionEvent;
-
-import java.util.List;
-
import javax.inject.Inject;
import javax.inject.Named;
/**
- * Falsing classifier that accepts or rejects a single gesture as a tap.
+ * Falsing classifier that accepts or rejects a gesture as a single tap.
*/
-public class SingleTapClassifier extends FalsingClassifier {
- private final float mTouchSlop;
+public class SingleTapClassifier extends TapClassifier {
@Inject
SingleTapClassifier(FalsingDataProvider dataProvider,
@Named(SINGLE_TAP_TOUCH_SLOP) float touchSlop) {
- super(dataProvider);
- mTouchSlop = touchSlop;
- }
-
- @Override
- Result calculateFalsingResult(
- @Classifier.InteractionType int interactionType,
- double historyBelief, double historyConfidence) {
- return isTap(getRecentMotionEvents(), 0.5);
- }
-
- /** Given a list of {@link android.view.MotionEvent}'s, returns true if the look like a tap. */
- public Result isTap(List<MotionEvent> motionEvents, double falsePenalty) {
- if (motionEvents.isEmpty()) {
- return falsed(0, "no motion events");
- }
- float downX = motionEvents.get(0).getX();
- float downY = motionEvents.get(0).getY();
-
- for (MotionEvent event : motionEvents) {
- String reason;
- if (Math.abs(event.getX() - downX) >= mTouchSlop) {
- reason = "dX too big for a tap: "
- + Math.abs(event.getX() - downX)
- + "vs "
- + mTouchSlop;
- return falsed(falsePenalty, reason);
- } else if (Math.abs(event.getY() - downY) >= mTouchSlop) {
- reason = "dY too big for a tap: "
- + Math.abs(event.getY() - downY)
- + " vs "
- + mTouchSlop;
- return falsed(falsePenalty, reason);
- }
- }
- return Result.passed(0);
+ super(dataProvider, touchSlop);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/TapClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/TapClassifier.java
new file mode 100644
index 0000000..e24cfaa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/TapClassifier.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.classifier;
+
+import android.view.MotionEvent;
+
+import java.util.List;
+
+/**
+ * Falsing classifier that accepts or rejects a gesture as a tap.
+ */
+public abstract class TapClassifier extends FalsingClassifier{
+ private final float mTouchSlop;
+
+ TapClassifier(FalsingDataProvider dataProvider,
+ float touchSlop) {
+ super(dataProvider);
+ mTouchSlop = touchSlop;
+ }
+
+ @Override
+ Result calculateFalsingResult(
+ @Classifier.InteractionType int interactionType,
+ double historyBelief, double historyConfidence) {
+ return isTap(getRecentMotionEvents(), 0.5);
+ }
+
+ /** Given a list of {@link android.view.MotionEvent}'s, returns true if the look like a tap. */
+ public Result isTap(List<MotionEvent> motionEvents, double falsePenalty) {
+ if (motionEvents.isEmpty()) {
+ return falsed(0, "no motion events");
+ }
+ float downX = motionEvents.get(0).getX();
+ float downY = motionEvents.get(0).getY();
+
+ for (MotionEvent event : motionEvents) {
+ String reason;
+ if (Math.abs(event.getX() - downX) >= mTouchSlop) {
+ reason = "dX too big for a tap: "
+ + Math.abs(event.getX() - downX)
+ + "vs "
+ + mTouchSlop;
+ return falsed(falsePenalty, reason);
+ } else if (Math.abs(event.getY() - downY) >= mTouchSlop) {
+ reason = "dY too big for a tap: "
+ + Math.abs(event.getY() - downY)
+ + " vs "
+ + mTouchSlop;
+ return falsed(falsePenalty, reason);
+ }
+ }
+ return Result.passed(0);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt
index 588ef5c..4dfcd63 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt
@@ -16,16 +16,120 @@
package com.android.systemui.controls
+import android.Manifest
+import android.content.ComponentName
import android.content.Context
+import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE
+import android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+import android.content.pm.ResolveInfo
import android.content.pm.ServiceInfo
+import android.os.UserHandle
+import android.service.controls.ControlsProviderService
+import androidx.annotation.WorkerThread
import com.android.settingslib.applications.DefaultAppInfo
+import java.util.Objects
class ControlsServiceInfo(
- context: Context,
+ private val context: Context,
val serviceInfo: ServiceInfo
) : DefaultAppInfo(
context,
context.packageManager,
context.userId,
serviceInfo.componentName
-)
\ No newline at end of file
+) {
+ private val _panelActivity: ComponentName?
+
+ init {
+ val metadata = serviceInfo.metaData
+ ?.getString(ControlsProviderService.META_DATA_PANEL_ACTIVITY) ?: ""
+ val unflatenned = ComponentName.unflattenFromString(metadata)
+ if (unflatenned != null && unflatenned.packageName == componentName.packageName) {
+ _panelActivity = unflatenned
+ } else {
+ _panelActivity = null
+ }
+ }
+
+ /**
+ * Component name of an activity that will be shown embedded in the device controls space
+ * instead of using the controls rendered by SystemUI.
+ *
+ * The activity must be in the same package, exported, enabled and protected by the
+ * [Manifest.permission.BIND_CONTROLS] permission.
+ */
+ var panelActivity: ComponentName? = null
+ private set
+
+ private var resolved: Boolean = false
+
+ @WorkerThread
+ fun resolvePanelActivity() {
+ if (resolved) return
+ resolved = true
+ panelActivity = _panelActivity?.let {
+ val resolveInfos = mPm.queryIntentActivitiesAsUser(
+ Intent().setComponent(it),
+ PackageManager.ResolveInfoFlags.of(
+ MATCH_DIRECT_BOOT_AWARE.toLong() or
+ MATCH_DIRECT_BOOT_UNAWARE.toLong()
+ ),
+ UserHandle.of(userId)
+ )
+ if (resolveInfos.isNotEmpty() && verifyResolveInfo(resolveInfos[0])) {
+ it
+ } else {
+ null
+ }
+ }
+ }
+
+ /**
+ * Verifies that the panel activity is enabled, exported and protected by the correct
+ * permission. This last check is to prevent apps from forgetting to protect the activity, as
+ * they won't be able to see the panel until they do.
+ */
+ @WorkerThread
+ private fun verifyResolveInfo(resolveInfo: ResolveInfo): Boolean {
+ return resolveInfo.activityInfo?.let {
+ it.permission == Manifest.permission.BIND_CONTROLS &&
+ it.exported && isComponentActuallyEnabled(it)
+ } ?: false
+ }
+
+ @WorkerThread
+ private fun isComponentActuallyEnabled(activityInfo: ActivityInfo): Boolean {
+ return when (mPm.getComponentEnabledSetting(activityInfo.componentName)) {
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED -> true
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED -> false
+ PackageManager.COMPONENT_ENABLED_STATE_DEFAULT -> activityInfo.enabled
+ else -> false
+ }
+ }
+
+ override fun equals(other: Any?): Boolean {
+ return other is ControlsServiceInfo &&
+ userId == other.userId &&
+ componentName == other.componentName &&
+ panelActivity == other.panelActivity
+ }
+
+ override fun hashCode(): Int {
+ return Objects.hash(userId, componentName, panelActivity)
+ }
+
+ fun copy(): ControlsServiceInfo {
+ return ControlsServiceInfo(context, serviceInfo).also {
+ it.panelActivity = this.panelActivity
+ }
+ }
+
+ override fun toString(): String {
+ return """
+ ControlsServiceInfo(serviceInfo=$serviceInfo, panelActivity=$panelActivity, resolved=$resolved)
+ """.trimIndent()
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
index 5e8ce6d..b11103a 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
@@ -20,11 +20,14 @@
import android.content.ComponentName
import android.content.Intent
import android.os.Bundle
+import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.view.ViewStub
import android.widget.Button
import android.widget.TextView
+import android.window.OnBackInvokedCallback
+import android.window.OnBackInvokedDispatcher
import androidx.activity.ComponentActivity
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper
@@ -42,7 +45,7 @@
/**
* Activity for rearranging and removing controls for a given structure
*/
-class ControlsEditingActivity @Inject constructor(
+open class ControlsEditingActivity @Inject constructor(
private val controller: ControlsControllerImpl,
private val broadcastDispatcher: BroadcastDispatcher,
private val customIconCache: CustomIconCache,
@@ -50,8 +53,9 @@
) : ComponentActivity() {
companion object {
+ private const val DEBUG = false
private const val TAG = "ControlsEditingActivity"
- private const val EXTRA_STRUCTURE = ControlsFavoritingActivity.EXTRA_STRUCTURE
+ const val EXTRA_STRUCTURE = ControlsFavoritingActivity.EXTRA_STRUCTURE
private val SUBTITLE_ID = R.string.controls_favorite_rearrange
private val EMPTY_TEXT_ID = R.string.controls_favorite_removed
}
@@ -73,6 +77,13 @@
}
}
+ private val mOnBackInvokedCallback = OnBackInvokedCallback {
+ if (DEBUG) {
+ Log.d(TAG, "Predictive Back dispatcher called mOnBackInvokedCallback")
+ }
+ onBackPressed()
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -94,11 +105,22 @@
setUpList()
currentUserTracker.startTracking()
+
+ if (DEBUG) {
+ Log.d(TAG, "Registered onBackInvokedCallback")
+ }
+ onBackInvokedDispatcher.registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT, mOnBackInvokedCallback)
}
override fun onStop() {
super.onStop()
currentUserTracker.stopTracking()
+
+ if (DEBUG) {
+ Log.d(TAG, "Unregistered onBackInvokedCallback")
+ }
+ onBackInvokedDispatcher.unregisterOnBackInvokedCallback(mOnBackInvokedCallback)
}
override fun onBackPressed() {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
index be572c5..9b2a728 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -24,6 +24,7 @@
import android.content.res.Configuration
import android.os.Bundle
import android.text.TextUtils
+import android.util.Log
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
@@ -32,6 +33,8 @@
import android.widget.FrameLayout
import android.widget.TextView
import android.widget.Toast
+import android.window.OnBackInvokedCallback
+import android.window.OnBackInvokedDispatcher
import androidx.activity.ComponentActivity
import androidx.viewpager2.widget.ViewPager2
import com.android.systemui.Prefs
@@ -50,7 +53,7 @@
import java.util.function.Consumer
import javax.inject.Inject
-class ControlsFavoritingActivity @Inject constructor(
+open class ControlsFavoritingActivity @Inject constructor(
@Main private val executor: Executor,
private val controller: ControlsControllerImpl,
private val listingController: ControlsListingController,
@@ -59,6 +62,7 @@
) : ComponentActivity() {
companion object {
+ private const val DEBUG = false
private const val TAG = "ControlsFavoritingActivity"
// If provided and no structure is available, use as the title
@@ -67,7 +71,7 @@
// If provided, show this structure page first
const val EXTRA_STRUCTURE = "extra_structure"
const val EXTRA_SINGLE_STRUCTURE = "extra_single_structure"
- internal const val EXTRA_FROM_PROVIDER_SELECTOR = "extra_from_provider_selector"
+ const val EXTRA_FROM_PROVIDER_SELECTOR = "extra_from_provider_selector"
private const val TOOLTIP_PREFS_KEY = Prefs.Key.CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT
private const val TOOLTIP_MAX_SHOWN = 2
}
@@ -102,6 +106,13 @@
}
}
+ private val mOnBackInvokedCallback = OnBackInvokedCallback {
+ if (DEBUG) {
+ Log.d(TAG, "Predictive Back dispatcher called mOnBackInvokedCallback")
+ }
+ onBackPressed()
+ }
+
private val listingCallback = object : ControlsListingController.ControlsListingCallback {
override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {
@@ -346,13 +357,19 @@
override fun onPause() {
super.onPause()
mTooltipManager?.hide(false)
- }
+ }
override fun onStart() {
super.onStart()
listingController.addCallback(listingCallback)
currentUserTracker.startTracking()
+
+ if (DEBUG) {
+ Log.d(TAG, "Registered onBackInvokedCallback")
+ }
+ onBackInvokedDispatcher.registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT, mOnBackInvokedCallback)
}
override fun onResume() {
@@ -365,13 +382,19 @@
loadControls()
isPagerLoaded = true
}
- }
+ }
override fun onStop() {
super.onStop()
listingController.removeCallback(listingCallback)
currentUserTracker.stopTracking()
+
+ if (DEBUG) {
+ Log.d(TAG, "Unregistered onBackInvokedCallback")
+ }
+ onBackInvokedDispatcher.unregisterOnBackInvokedCallback(
+ mOnBackInvokedCallback)
}
override fun onConfigurationChanged(newConfig: Configuration) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
index 2d76ff2..115edd11 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
@@ -18,17 +18,23 @@
import android.content.ComponentName
import android.content.Context
-import android.content.pm.ServiceInfo
import android.os.UserHandle
import android.service.controls.ControlsProviderService
import android.util.Log
import com.android.internal.annotations.VisibleForTesting
import com.android.settingslib.applications.ServiceListing
import com.android.settingslib.widget.CandidateInfo
+import com.android.systemui.Dumpable
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.asIndenting
+import com.android.systemui.util.indentIfPossible
+import java.io.PrintWriter
import java.util.concurrent.Executor
import java.util.concurrent.atomic.AtomicInteger
import javax.inject.Inject
@@ -57,16 +63,19 @@
private val context: Context,
@Background private val backgroundExecutor: Executor,
private val serviceListingBuilder: (Context) -> ServiceListing,
- userTracker: UserTracker
-) : ControlsListingController {
+ private val userTracker: UserTracker,
+ dumpManager: DumpManager,
+ featureFlags: FeatureFlags
+) : ControlsListingController, Dumpable {
@Inject
- constructor(context: Context, executor: Executor, userTracker: UserTracker): this(
- context,
- executor,
- ::createServiceListing,
- userTracker
- )
+ constructor(
+ context: Context,
+ @Background executor: Executor,
+ userTracker: UserTracker,
+ dumpManager: DumpManager,
+ featureFlags: FeatureFlags
+ ) : this(context, executor, ::createServiceListing, userTracker, dumpManager, featureFlags)
private var serviceListing = serviceListingBuilder(context)
// All operations in background thread
@@ -76,27 +85,25 @@
private const val TAG = "ControlsListingControllerImpl"
}
- private var availableComponents = emptySet<ComponentName>()
- private var availableServices = emptyList<ServiceInfo>()
+ private var availableServices = emptyList<ControlsServiceInfo>()
private var userChangeInProgress = AtomicInteger(0)
override var currentUserId = userTracker.userId
private set
private val serviceListingCallback = ServiceListing.Callback {
- val newServices = it.toList()
- val newComponents =
- newServices.mapTo(mutableSetOf<ComponentName>(), { s -> s.getComponentName() })
-
backgroundExecutor.execute {
if (userChangeInProgress.get() > 0) return@execute
- if (!newComponents.equals(availableComponents)) {
- Log.d(TAG, "ServiceConfig reloaded, count: ${newComponents.size}")
- availableComponents = newComponents
+ Log.d(TAG, "ServiceConfig reloaded, count: ${it.size}")
+ val newServices = it.map { ControlsServiceInfo(userTracker.userContext, it) }
+ if (featureFlags.isEnabled(Flags.USE_APP_PANELS)) {
+ newServices.forEach(ControlsServiceInfo::resolvePanelActivity)
+ }
+
+ if (newServices != availableServices) {
availableServices = newServices
- val currentServices = getCurrentServices()
callbacks.forEach {
- it.onServicesUpdated(currentServices)
+ it.onServicesUpdated(getCurrentServices())
}
}
}
@@ -104,6 +111,7 @@
init {
Log.d(TAG, "Initializing")
+ dumpManager.registerDumpable(TAG, this)
serviceListing.addCallback(serviceListingCallback)
serviceListing.setListening(true)
serviceListing.reload()
@@ -165,7 +173,7 @@
* [ControlsProviderService]
*/
override fun getCurrentServices(): List<ControlsServiceInfo> =
- availableServices.map { ControlsServiceInfo(context, it) }
+ availableServices.map(ControlsServiceInfo::copy)
/**
* Get the localized label for the component.
@@ -174,7 +182,15 @@
* @return a label as returned by [CandidateInfo.loadLabel] or `null`.
*/
override fun getAppLabel(name: ComponentName): CharSequence? {
- return getCurrentServices().firstOrNull { it.componentName == name }
+ return availableServices.firstOrNull { it.componentName == name }
?.loadLabel()
}
+
+ override fun dump(writer: PrintWriter, args: Array<out String>) {
+ writer.println("ControlsListingController:")
+ writer.asIndenting().indentIfPossible {
+ println("Callbacks: $callbacks")
+ println("Services: ${getCurrentServices()}")
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
index b26615f..47690a7 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
@@ -20,16 +20,18 @@
import android.content.ComponentName
import android.content.Intent
import android.os.Bundle
+import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewStub
import android.widget.Button
import android.widget.TextView
+import android.window.OnBackInvokedCallback
+import android.window.OnBackInvokedDispatcher
import androidx.activity.ComponentActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
-import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver
import com.android.systemui.R
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.controls.controller.ControlsController
@@ -44,7 +46,7 @@
/**
* Activity to select an application to favorite the [Control] provided by them.
*/
-class ControlsProviderSelectorActivity @Inject constructor(
+open class ControlsProviderSelectorActivity @Inject constructor(
@Main private val executor: Executor,
@Background private val backExecutor: Executor,
private val listingController: ControlsListingController,
@@ -54,6 +56,7 @@
) : ComponentActivity() {
companion object {
+ private const val DEBUG = false
private const val TAG = "ControlsProviderSelectorActivity"
const val BACK_SHOULD_EXIT = "back_should_exit"
}
@@ -70,6 +73,13 @@
}
}
+ private val mOnBackInvokedCallback = OnBackInvokedCallback {
+ if (DEBUG) {
+ Log.d(TAG, "Predictive Back dispatcher called mOnBackInvokedCallback")
+ }
+ onBackPressed()
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -141,11 +151,22 @@
}
})
}
+
+ if (DEBUG) {
+ Log.d(TAG, "Registered onBackInvokedCallback")
+ }
+ onBackInvokedDispatcher.registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT, mOnBackInvokedCallback)
}
override fun onStop() {
super.onStop()
currentUserTracker.stopTracking()
+
+ if (DEBUG) {
+ Log.d(TAG, "Unregistered onBackInvokedCallback")
+ }
+ onBackInvokedDispatcher.unregisterOnBackInvokedCallback(mOnBackInvokedCallback)
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
index 9e33ee1..fe89c9a 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
@@ -27,7 +27,9 @@
import dagger.Component;
/**
- * Root component for Dagger injection.
+ * Base root component for Dagger injection.
+ *
+ * See {@link ReferenceGlobalRootComponent} for the one actually used by AOSP.
*/
@Singleton
@Component(modules = {GlobalModule.class})
@@ -51,7 +53,7 @@
WMComponent.Builder getWMComponentBuilder();
/**
- * Builder for a {@link SysUIComponent}, which makes it a subcomponent of this class.
+ * Builder for a {@link ReferenceSysUIComponent}, which makes it a subcomponent of this class.
*/
SysUIComponent.Builder getSysUIComponent();
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceGlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceGlobalRootComponent.java
new file mode 100644
index 0000000..be93c9f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceGlobalRootComponent.java
@@ -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.
+ */
+
+package com.android.systemui.dagger;
+
+import javax.inject.Singleton;
+
+import dagger.Component;
+
+/**
+ * Root component for Dagger injection used in AOSP.
+ */
+@Singleton
+@Component(modules = {GlobalModule.class})
+public interface ReferenceGlobalRootComponent extends GlobalRootComponent {
+
+ /**
+ * Builder for a ReferenceGlobalRootComponent.
+ */
+ @Component.Builder
+ interface Builder extends GlobalRootComponent.Builder {
+ ReferenceGlobalRootComponent build();
+ }
+
+ /**
+ * Builder for a {@link ReferenceSysUIComponent}, which makes it a subcomponent of this class.
+ */
+ ReferenceSysUIComponent.Builder getSysUIComponent();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
new file mode 100644
index 0000000..d3555ee
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dagger;
+
+import com.android.systemui.keyguard.KeyguardQuickAffordanceProvider;
+import com.android.systemui.statusbar.QsFrameTranslateModule;
+
+import dagger.Subcomponent;
+
+/**
+ * Dagger Subcomponent for Core SysUI used in AOSP.
+ */
+@SysUISingleton
+@Subcomponent(modules = {
+ DefaultComponentBinder.class,
+ DependencyProvider.class,
+ QsFrameTranslateModule.class,
+ SystemUIBinder.class,
+ SystemUIModule.class,
+ SystemUICoreStartableModule.class,
+ ReferenceSystemUIModule.class})
+public interface ReferenceSysUIComponent extends SysUIComponent {
+
+ /**
+ * Builder for a ReferenceSysUIComponent.
+ */
+ @SysUISingleton
+ @Subcomponent.Builder
+ interface Builder extends SysUIComponent.Builder {
+ ReferenceSysUIComponent build();
+ }
+
+ /**
+ * Member injection into the supplied argument.
+ */
+ void inject(KeyguardQuickAffordanceProvider keyguardQuickAffordanceProvider);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 2bee75e..d0c5007 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -45,6 +45,7 @@
import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeControllerImpl;
+import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
@@ -164,7 +165,8 @@
ConfigurationController configurationController,
@Main Handler handler,
AccessibilityManagerWrapper accessibilityManagerWrapper,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger,
+ ShadeExpansionStateManager shadeExpansionStateManager) {
return new HeadsUpManagerPhone(
context,
headsUpManagerLogger,
@@ -175,7 +177,8 @@
configurationController,
handler,
accessibilityManagerWrapper,
- uiEventLogger
+ uiEventLogger,
+ shadeExpansionStateManager
);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index d05bd51..a14b0ee 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -40,7 +40,6 @@
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
-import com.android.wm.shell.floating.FloatingTasks;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.recents.RecentTasks;
@@ -58,7 +57,9 @@
import dagger.Subcomponent;
/**
- * Dagger Subcomponent for Core SysUI.
+ * An example Dagger Subcomponent for Core SysUI.
+ *
+ * See {@link ReferenceSysUIComponent} for the one actually used by AOSP.
*/
@SysUISingleton
@Subcomponent(modules = {
@@ -111,9 +112,6 @@
Builder setBackAnimation(Optional<BackAnimation> b);
@BindsInstance
- Builder setFloatingTasks(Optional<FloatingTasks> f);
-
- @BindsInstance
Builder setDesktopMode(Optional<DesktopMode> d);
SysUIComponent build();
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 271a8c5ff..bcf5e7a 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -41,6 +41,7 @@
import com.android.systemui.doze.dagger.DozeComponent;
import com.android.systemui.dreams.dagger.DreamModule;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.FlagsModule;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.data.BouncerViewModule;
@@ -240,6 +241,7 @@
CommonNotifCollection notifCollection,
NotifPipeline notifPipeline,
SysUiState sysUiState,
+ FeatureFlags featureFlags,
@Main Executor sysuiMainExecutor) {
return Optional.ofNullable(BubblesManager.create(context,
bubblesOptional,
@@ -256,6 +258,7 @@
notifCollection,
notifPipeline,
sysUiState,
+ featureFlags,
sysuiMainExecutor));
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index 096f969..d756f3a 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -32,7 +32,6 @@
import com.android.wm.shell.dagger.WMSingleton;
import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
-import com.android.wm.shell.floating.FloatingTasks;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.recents.RecentTasks;
@@ -111,9 +110,6 @@
@WMSingleton
Optional<BackAnimation> getBackAnimation();
- @WMSingleton
- Optional<FloatingTasks> getFloatingTasks();
-
/**
* Optional {@link DesktopMode} component for interacting with desktop mode.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index d0d0184..5c6d248 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -35,7 +35,7 @@
import com.android.systemui.dreams.complication.ComplicationHostViewController;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.dreams.dagger.DreamOverlayModule;
-import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor;
import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -76,14 +76,14 @@
// Main thread handler used to schedule periodic tasks (e.g. burn-in protection updates).
private final Handler mHandler;
private final int mDreamOverlayMaxTranslationY;
- private final BouncerCallbackInteractor mBouncerCallbackInteractor;
+ private final PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor;
private long mJitterStartTimeMillis;
private boolean mBouncerAnimating;
- private final KeyguardBouncer.BouncerExpansionCallback mBouncerExpansionCallback =
- new KeyguardBouncer.BouncerExpansionCallback() {
+ private final KeyguardBouncer.PrimaryBouncerExpansionCallback mBouncerExpansionCallback =
+ new KeyguardBouncer.PrimaryBouncerExpansionCallback() {
@Override
public void onStartingToShow() {
@@ -136,7 +136,7 @@
@Named(DreamOverlayModule.BURN_IN_PROTECTION_UPDATE_INTERVAL) long
burnInProtectionUpdateInterval,
@Named(DreamOverlayModule.MILLIS_UNTIL_FULL_JITTER) long millisUntilFullJitter,
- BouncerCallbackInteractor bouncerCallbackInteractor,
+ PrimaryBouncerCallbackInteractor primaryBouncerCallbackInteractor,
DreamOverlayAnimationsController animationsController,
DreamOverlayStateController stateController) {
super(containerView);
@@ -160,7 +160,7 @@
mMaxBurnInOffset = maxBurnInOffset;
mBurnInProtectionUpdateInterval = burnInProtectionUpdateInterval;
mMillisUntilFullJitter = millisUntilFullJitter;
- mBouncerCallbackInteractor = bouncerCallbackInteractor;
+ mPrimaryBouncerCallbackInteractor = primaryBouncerCallbackInteractor;
}
@Override
@@ -173,11 +173,11 @@
protected void onViewAttached() {
mJitterStartTimeMillis = System.currentTimeMillis();
mHandler.postDelayed(this::updateBurnInOffsets, mBurnInProtectionUpdateInterval);
- final KeyguardBouncer bouncer = mStatusBarKeyguardViewManager.getBouncer();
+ final KeyguardBouncer bouncer = mStatusBarKeyguardViewManager.getPrimaryBouncer();
if (bouncer != null) {
bouncer.addBouncerExpansionCallback(mBouncerExpansionCallback);
}
- mBouncerCallbackInteractor.addBouncerExpansionCallback(mBouncerExpansionCallback);
+ mPrimaryBouncerCallbackInteractor.addBouncerExpansionCallback(mBouncerExpansionCallback);
// Start dream entry animations. Skip animations for low light clock.
if (!mStateController.isLowLightActive()) {
@@ -188,11 +188,11 @@
@Override
protected void onViewDetached() {
mHandler.removeCallbacks(this::updateBurnInOffsets);
- final KeyguardBouncer bouncer = mStatusBarKeyguardViewManager.getBouncer();
+ final KeyguardBouncer bouncer = mStatusBarKeyguardViewManager.getPrimaryBouncer();
if (bouncer != null) {
bouncer.removeBouncerExpansionCallback(mBouncerExpansionCallback);
}
- mBouncerCallbackInteractor.removeBouncerExpansionCallback(mBouncerExpansionCallback);
+ mPrimaryBouncerCallbackInteractor.removeBouncerExpansionCallback(mBouncerExpansionCallback);
mDreamOverlayAnimationsController.cancelRunningEntryAnimations();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
index 5694f6d..440dcbc 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
@@ -194,7 +194,9 @@
break;
}
- if (!isRoot) {
+ // Add margin if specified by the complication. Otherwise add default margin
+ // between complications.
+ if (mLayoutParams.isMarginSpecified() || !isRoot) {
final int margin = mLayoutParams.getMargin(mDefaultMargin);
switch(direction) {
case ComplicationLayoutParams.DIRECTION_DOWN:
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java
index a21eb19..2b32d34 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java
@@ -261,6 +261,13 @@
}
/**
+ * Returns whether margin has been specified by the complication.
+ */
+ public boolean isMarginSpecified() {
+ return mMargin != MARGIN_UNSPECIFIED;
+ }
+
+ /**
* Returns the margin to apply between complications, or the given default if no margin is
* specified.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
index 7d2ce51..69b85b5 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
@@ -48,9 +48,9 @@
int DREAM_CLOCK_TIME_COMPLICATION_WEIGHT = 1;
int DREAM_SMARTSPACE_COMPLICATION_WEIGHT = 0;
- int DREAM_MEDIA_COMPLICATION_WEIGHT = -1;
- int DREAM_HOME_CONTROLS_CHIP_COMPLICATION_WEIGHT = 1;
- int DREAM_MEDIA_ENTRY_COMPLICATION_WEIGHT = 0;
+ int DREAM_MEDIA_COMPLICATION_WEIGHT = 0;
+ int DREAM_HOME_CONTROLS_CHIP_COMPLICATION_WEIGHT = 2;
+ int DREAM_MEDIA_ENTRY_COMPLICATION_WEIGHT = 1;
/**
* Provides layout parameters for the clock time complication.
@@ -60,10 +60,11 @@
static ComplicationLayoutParams provideClockTimeLayoutParams() {
return new ComplicationLayoutParams(0,
ViewGroup.LayoutParams.WRAP_CONTENT,
- ComplicationLayoutParams.POSITION_TOP
+ ComplicationLayoutParams.POSITION_BOTTOM
| ComplicationLayoutParams.POSITION_START,
- ComplicationLayoutParams.DIRECTION_DOWN,
- DREAM_CLOCK_TIME_COMPLICATION_WEIGHT);
+ ComplicationLayoutParams.DIRECTION_UP,
+ DREAM_CLOCK_TIME_COMPLICATION_WEIGHT,
+ 0 /*margin*/);
}
/**
@@ -77,8 +78,10 @@
res.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height),
ComplicationLayoutParams.POSITION_BOTTOM
| ComplicationLayoutParams.POSITION_START,
- ComplicationLayoutParams.DIRECTION_END,
- DREAM_HOME_CONTROLS_CHIP_COMPLICATION_WEIGHT);
+ ComplicationLayoutParams.DIRECTION_UP,
+ DREAM_HOME_CONTROLS_CHIP_COMPLICATION_WEIGHT,
+ // Add margin to the bottom of home controls to horizontally align with smartspace.
+ res.getDimensionPixelSize(R.dimen.dream_overlay_complication_clock_time_padding));
}
/**
@@ -101,14 +104,13 @@
*/
@Provides
@Named(DREAM_SMARTSPACE_LAYOUT_PARAMS)
- static ComplicationLayoutParams provideSmartspaceLayoutParams() {
+ static ComplicationLayoutParams provideSmartspaceLayoutParams(@Main Resources res) {
return new ComplicationLayoutParams(0,
ViewGroup.LayoutParams.WRAP_CONTENT,
- ComplicationLayoutParams.POSITION_TOP
+ ComplicationLayoutParams.POSITION_BOTTOM
| ComplicationLayoutParams.POSITION_START,
- ComplicationLayoutParams.DIRECTION_DOWN,
+ ComplicationLayoutParams.DIRECTION_END,
DREAM_SMARTSPACE_COMPLICATION_WEIGHT,
- 0,
- true /*snapToGuide*/);
+ res.getDimensionPixelSize(R.dimen.dream_overlay_complication_smartspace_padding));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
index 0dba4ff..92cdcf9 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
@@ -116,7 +116,7 @@
if (mCapture) {
// Since the user is dragging the bouncer up, set scrimmed to false.
- mStatusBarKeyguardViewManager.showBouncer(false);
+ mStatusBarKeyguardViewManager.showPrimaryBouncer(false);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
index fb4fc92..95e7ad96 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
@@ -34,9 +34,6 @@
fun isEnabled(flag: ResourceBooleanFlag): Boolean
/** Returns a boolean value for the given flag. */
- fun isEnabled(flag: DeviceConfigBooleanFlag): Boolean
-
- /** Returns a boolean value for the given flag. */
fun isEnabled(flag: SysPropBooleanFlag): Boolean
/** Returns a string value for the given flag. */
@@ -44,4 +41,10 @@
/** Returns a string value for the given flag. */
fun getString(flag: ResourceStringFlag): String
+
+ /** Returns an int value for a given flag/ */
+ fun getInt(flag: IntFlag): Int
+
+ /** Returns an int value for a given flag/ */
+ fun getInt(flag: ResourceIntFlag): Int
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
index 1f061e9..b03ae59 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
@@ -40,7 +40,6 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.settings.SecureSettings;
import org.jetbrains.annotations.NotNull;
@@ -77,11 +76,11 @@
private final SecureSettings mSecureSettings;
private final Resources mResources;
private final SystemPropertiesHelper mSystemProperties;
- private final DeviceConfigProxy mDeviceConfigProxy;
private final ServerFlagReader mServerFlagReader;
private final Map<Integer, Flag<?>> mAllFlags;
private final Map<Integer, Boolean> mBooleanFlagCache = new TreeMap<>();
private final Map<Integer, String> mStringFlagCache = new TreeMap<>();
+ private final Map<Integer, Integer> mIntFlagCache = new TreeMap<>();
private final Restarter mRestarter;
private final ServerFlagReader.ChangeListener mOnPropertiesChanged =
@@ -99,7 +98,6 @@
SecureSettings secureSettings,
SystemPropertiesHelper systemProperties,
@Main Resources resources,
- DeviceConfigProxy deviceConfigProxy,
ServerFlagReader serverFlagReader,
@Named(ALL_FLAGS) Map<Integer, Flag<?>> allFlags,
Restarter restarter) {
@@ -108,7 +106,6 @@
mSecureSettings = secureSettings;
mResources = resources;
mSystemProperties = systemProperties;
- mDeviceConfigProxy = deviceConfigProxy;
mServerFlagReader = serverFlagReader;
mAllFlags = allFlags;
mRestarter = restarter;
@@ -140,7 +137,7 @@
int id = flag.getId();
if (!mBooleanFlagCache.containsKey(id)) {
mBooleanFlagCache.put(id,
- readFlagValue(id, flag.getDefault()));
+ readBooleanFlagInternal(flag, flag.getDefault()));
}
return mBooleanFlagCache.get(id);
@@ -151,19 +148,7 @@
int id = flag.getId();
if (!mBooleanFlagCache.containsKey(id)) {
mBooleanFlagCache.put(id,
- readFlagValue(id, mResources.getBoolean(flag.getResourceId())));
- }
-
- return mBooleanFlagCache.get(id);
- }
-
- @Override
- public boolean isEnabled(@NonNull DeviceConfigBooleanFlag flag) {
- int id = flag.getId();
- if (!mBooleanFlagCache.containsKey(id)) {
- boolean deviceConfigValue = mDeviceConfigProxy.getBoolean(flag.getNamespace(),
- flag.getName(), flag.getDefault());
- mBooleanFlagCache.put(id, readFlagValue(id, deviceConfigValue));
+ readBooleanFlagInternal(flag, mResources.getBoolean(flag.getResourceId())));
}
return mBooleanFlagCache.get(id);
@@ -179,7 +164,7 @@
id,
mSystemProperties.getBoolean(
flag.getName(),
- readFlagValue(id, flag.getDefault())));
+ readBooleanFlagInternal(flag, flag.getDefault())));
}
return mBooleanFlagCache.get(id);
@@ -191,7 +176,7 @@
int id = flag.getId();
if (!mStringFlagCache.containsKey(id)) {
mStringFlagCache.put(id,
- readFlagValue(id, flag.getDefault(), StringFlagSerializer.INSTANCE));
+ readFlagValueInternal(id, flag.getDefault(), StringFlagSerializer.INSTANCE));
}
return mStringFlagCache.get(id);
@@ -203,27 +188,57 @@
int id = flag.getId();
if (!mStringFlagCache.containsKey(id)) {
mStringFlagCache.put(id,
- readFlagValue(id, mResources.getString(flag.getResourceId()),
+ readFlagValueInternal(id, mResources.getString(flag.getResourceId()),
StringFlagSerializer.INSTANCE));
}
return mStringFlagCache.get(id);
}
- /** Specific override for Boolean flags that checks against the teamfood list. */
- private boolean readFlagValue(int id, boolean defaultValue) {
- Boolean result = readBooleanFlagOverride(id);
- boolean hasServerOverride = mServerFlagReader.hasOverride(id);
+
+ @NonNull
+ @Override
+ public int getInt(@NonNull IntFlag flag) {
+ int id = flag.getId();
+ if (!mIntFlagCache.containsKey(id)) {
+ mIntFlagCache.put(id,
+ readFlagValueInternal(id, flag.getDefault(), IntFlagSerializer.INSTANCE));
+ }
+
+ return mIntFlagCache.get(id);
+ }
+
+ @NonNull
+ @Override
+ public int getInt(@NonNull ResourceIntFlag flag) {
+ int id = flag.getId();
+ if (!mIntFlagCache.containsKey(id)) {
+ mIntFlagCache.put(id,
+ readFlagValueInternal(id, mResources.getInteger(flag.getResourceId()),
+ IntFlagSerializer.INSTANCE));
+ }
+
+ return mIntFlagCache.get(id);
+ }
+
+ /** Specific override for Boolean flags that checks against the teamfood list.*/
+ private boolean readBooleanFlagInternal(Flag<Boolean> flag, boolean defaultValue) {
+ Boolean result = readBooleanFlagOverride(flag.getId());
+ boolean hasServerOverride = mServerFlagReader.hasOverride(
+ flag.getNamespace(), flag.getName());
// Only check for teamfood if the default is false
// and there is no server override.
- if (!hasServerOverride && !defaultValue && result == null && id != Flags.TEAMFOOD.getId()) {
- if (mAllFlags.containsKey(id) && mAllFlags.get(id).getTeamfood()) {
- return isEnabled(Flags.TEAMFOOD);
- }
+ if (!hasServerOverride
+ && !defaultValue
+ && result == null
+ && flag.getId() != Flags.TEAMFOOD.getId()
+ && flag.getTeamfood()) {
+ return isEnabled(Flags.TEAMFOOD);
}
- return result == null ? mServerFlagReader.readServerOverride(id, defaultValue) : result;
+ return result == null ? mServerFlagReader.readServerOverride(
+ flag.getNamespace(), flag.getName(), defaultValue) : result;
}
private Boolean readBooleanFlagOverride(int id) {
@@ -231,7 +246,8 @@
}
@NonNull
- private <T> T readFlagValue(int id, @NonNull T defaultValue, FlagSerializer<T> serializer) {
+ private <T> T readFlagValueInternal(
+ int id, @NonNull T defaultValue, FlagSerializer<T> serializer) {
requireNonNull(defaultValue, "defaultValue");
T result = readFlagValueInternal(id, serializer);
return result == null ? defaultValue : result;
@@ -330,8 +346,6 @@
setFlagValue(flag.getId(), value, BooleanFlagSerializer.INSTANCE);
} else if (flag instanceof ResourceBooleanFlag) {
setFlagValue(flag.getId(), value, BooleanFlagSerializer.INSTANCE);
- } else if (flag instanceof DeviceConfigBooleanFlag) {
- setFlagValue(flag.getId(), value, BooleanFlagSerializer.INSTANCE);
} else if (flag instanceof SysPropBooleanFlag) {
// Store SysProp flags in SystemProperties where they can read by outside parties.
mSystemProperties.setBoolean(((SysPropBooleanFlag) flag).getName(), value);
@@ -352,6 +366,16 @@
}
}
+ void setIntFlagInternal(Flag<?> flag, int value) {
+ if (flag instanceof IntFlag) {
+ setFlagValue(flag.getId(), value, IntFlagSerializer.INSTANCE);
+ } else if (flag instanceof ResourceIntFlag) {
+ setFlagValue(flag.getId(), value, IntFlagSerializer.INSTANCE);
+ } else {
+ throw new IllegalArgumentException("Unknown flag type");
+ }
+ }
+
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -439,9 +463,6 @@
} else if (f instanceof ResourceBooleanFlag) {
enabled = isEnabled((ResourceBooleanFlag) f);
overridden = readBooleanFlagOverride(f.getId()) != null;
- } else if (f instanceof DeviceConfigBooleanFlag) {
- enabled = isEnabled((DeviceConfigBooleanFlag) f);
- overridden = false;
} else if (f instanceof SysPropBooleanFlag) {
// TODO(b/223379190): Teamfood not supported for sysprop flags yet.
enabled = isEnabled((SysPropBooleanFlag) f);
@@ -454,9 +475,11 @@
}
if (enabled) {
- return new ReleasedFlag(f.getId(), teamfood, overridden);
+ return new ReleasedFlag(
+ f.getId(), f.getName(), f.getNamespace(), teamfood, overridden);
} else {
- return new UnreleasedFlag(f.getId(), teamfood, overridden);
+ return new UnreleasedFlag(
+ f.getId(), f.getName(), f.getNamespace(), teamfood, overridden);
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
index 30cad5f..3c83682 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
@@ -101,7 +101,7 @@
@Override
public boolean isEnabled(@NotNull ReleasedFlag flag) {
- return mServerFlagReader.readServerOverride(flag.getId(), true);
+ return mServerFlagReader.readServerOverride(flag.getNamespace(), flag.getName(), true);
}
@Override
@@ -115,18 +115,6 @@
}
@Override
- public boolean isEnabled(@NonNull DeviceConfigBooleanFlag flag) {
- int cacheIndex = mBooleanCache.indexOfKey(flag.getId());
- if (cacheIndex < 0) {
- boolean deviceConfigValue = mDeviceConfigProxy.getBoolean(flag.getNamespace(),
- flag.getName(), flag.getDefault());
- return isEnabled(flag.getId(), deviceConfigValue);
- }
-
- return mBooleanCache.valueAt(cacheIndex);
- }
-
- @Override
public boolean isEnabled(SysPropBooleanFlag flag) {
int cacheIndex = mBooleanCache.indexOfKey(flag.getId());
if (cacheIndex < 0) {
@@ -165,13 +153,25 @@
return defaultValue;
}
+ @NonNull
+ @Override
+ public int getInt(@NonNull IntFlag flag) {
+ return flag.getDefault();
+ }
+
+ @NonNull
+ @Override
+ public int getInt(@NonNull ResourceIntFlag flag) {
+ return mResources.getInteger(flag.getResourceId());
+ }
+
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("can override: false");
- Map<Integer, Flag<?>> knownFlags = Flags.collectFlags();
- for (Map.Entry<Integer, Flag<?>> idToFlag : knownFlags.entrySet()) {
- int id = idToFlag.getKey();
- Flag<?> flag = idToFlag.getValue();
+ Map<String, Flag<?>> knownFlags = FlagsFactory.INSTANCE.getKnownFlags();
+ for (Map.Entry<String, Flag<?>> nameToFlag : knownFlags.entrySet()) {
+ Flag<?> flag = nameToFlag.getValue();
+ int id = flag.getId();
boolean def = false;
if (mBooleanCache.indexOfKey(flag.getId()) < 0) {
if (flag instanceof SysPropBooleanFlag) {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java b/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java
index 1e93c0b7..b7fc0e4 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java
@@ -23,7 +23,6 @@
import com.android.systemui.statusbar.commandline.Command;
import java.io.PrintWriter;
-import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
@@ -38,6 +37,7 @@
private final List<String> mOnCommands = List.of("true", "on", "1", "enabled");
private final List<String> mOffCommands = List.of("false", "off", "0", "disable");
+ private final List<String> mSetCommands = List.of("set", "put");
private final FeatureFlagsDebug mFeatureFlags;
private final Map<Integer, Flag<?>> mAllFlags;
@@ -60,12 +60,6 @@
return;
}
- if (args.size() > 2) {
- pw.println("Invalid number of arguments.");
- help(pw);
- return;
- }
-
int id = 0;
try {
id = Integer.parseInt(args.get(0));
@@ -85,48 +79,113 @@
Flag<?> flag = mAllFlags.get(id);
String cmd = "";
- if (args.size() == 2) {
+ if (args.size() > 1) {
cmd = args.get(1).toLowerCase();
}
if ("erase".equals(cmd) || "reset".equals(cmd)) {
+ if (args.size() > 2) {
+ pw.println("Invalid number of arguments to reset a flag.");
+ help(pw);
+ return;
+ }
+
mFeatureFlags.eraseFlag(flag);
return;
}
- boolean newValue = true;
- if (args.size() == 1 || "toggle".equals(cmd)) {
- boolean enabled = isBooleanFlagEnabled(flag);
-
- if (args.size() == 1) {
- pw.println("Flag " + id + " is " + enabled);
+ boolean shouldSet = true;
+ if (args.size() == 1) {
+ shouldSet = false;
+ }
+ if (isBooleanFlag(flag)) {
+ if (args.size() > 2) {
+ pw.println("Invalid number of arguments for a boolean flag.");
+ help(pw);
return;
}
-
- newValue = !enabled;
- } else {
- newValue = mOnCommands.contains(cmd);
- if (!newValue && !mOffCommands.contains(cmd)) {
+ boolean newValue = isBooleanFlagEnabled(flag);
+ if ("toggle".equals(cmd)) {
+ newValue = !newValue;
+ } else if (mOnCommands.contains(cmd)) {
+ newValue = true;
+ } else if (mOffCommands.contains(cmd)) {
+ newValue = false;
+ } else if (shouldSet) {
pw.println("Invalid on/off argument supplied");
help(pw);
return;
}
- }
- pw.flush(); // Next command will restart sysui, so flush before we do so.
- mFeatureFlags.setBooleanFlagInternal(flag, newValue);
+ pw.println("Flag " + id + " is " + newValue);
+ pw.flush(); // Next command will restart sysui, so flush before we do so.
+ if (shouldSet) {
+ mFeatureFlags.setBooleanFlagInternal(flag, newValue);
+ }
+ return;
+
+ } else if (isStringFlag(flag)) {
+ if (shouldSet) {
+ if (args.size() != 3) {
+ pw.println("Invalid number of arguments a StringFlag.");
+ help(pw);
+ return;
+ } else if (!mSetCommands.contains(cmd)) {
+ pw.println("Unknown command: " + cmd);
+ help(pw);
+ return;
+ }
+ String value = args.get(2);
+ pw.println("Setting Flag " + id + " to " + value);
+ pw.flush(); // Next command will restart sysui, so flush before we do so.
+ mFeatureFlags.setStringFlagInternal(flag, args.get(2));
+ } else {
+ pw.println("Flag " + id + " is " + getStringFlag(flag));
+ }
+ return;
+ } else if (isIntFlag(flag)) {
+ if (shouldSet) {
+ if (args.size() != 3) {
+ pw.println("Invalid number of arguments for an IntFlag.");
+ help(pw);
+ return;
+ } else if (!mSetCommands.contains(cmd)) {
+ pw.println("Unknown command: " + cmd);
+ help(pw);
+ return;
+ }
+ int value = Integer.parseInt(args.get(2));
+ pw.println("Setting Flag " + id + " to " + value);
+ pw.flush(); // Next command will restart sysui, so flush before we do so.
+ mFeatureFlags.setIntFlagInternal(flag, value);
+ } else {
+ pw.println("Flag " + id + " is " + getIntFlag(flag));
+ }
+ return;
+ }
}
@Override
public void help(PrintWriter pw) {
- pw.println(
- "Usage: adb shell cmd statusbar flag <id> "
+ pw.println("Usage: adb shell cmd statusbar flag <id> [options]");
+ pw.println();
+ pw.println(" Boolean Flag Options: "
+ "[true|false|1|0|on|off|enable|disable|toggle|erase|reset]");
+ pw.println(" String Flag Options: [set|put \"<value>\"]");
+ pw.println(" Int Flag Options: [set|put <value>]");
+ pw.println();
pw.println("The id can either be a numeric integer or the corresponding field name");
pw.println(
"If no argument is supplied after the id, the flags runtime value is output");
}
+ private boolean isBooleanFlag(Flag<?> flag) {
+ return (flag instanceof BooleanFlag)
+ || (flag instanceof ResourceBooleanFlag)
+ || (flag instanceof SysPropFlag)
+ || (flag instanceof DeviceConfigBooleanFlag);
+ }
+
private boolean isBooleanFlagEnabled(Flag<?> flag) {
if (flag instanceof ReleasedFlag) {
return mFeatureFlags.isEnabled((ReleasedFlag) flag);
@@ -141,34 +200,51 @@
return false;
}
+ private boolean isStringFlag(Flag<?> flag) {
+ return (flag instanceof StringFlag) || (flag instanceof ResourceStringFlag);
+ }
+
+ private String getStringFlag(Flag<?> flag) {
+ if (flag instanceof StringFlag) {
+ return mFeatureFlags.getString((StringFlag) flag);
+ } else if (flag instanceof ResourceStringFlag) {
+ return mFeatureFlags.getString((ResourceStringFlag) flag);
+ }
+
+ return "";
+ }
+
+ private boolean isIntFlag(Flag<?> flag) {
+ return (flag instanceof IntFlag) || (flag instanceof ResourceIntFlag);
+ }
+
+ private int getIntFlag(Flag<?> flag) {
+ if (flag instanceof IntFlag) {
+ return mFeatureFlags.getInt((IntFlag) flag);
+ } else if (flag instanceof ResourceIntFlag) {
+ return mFeatureFlags.getInt((ResourceIntFlag) flag);
+ }
+
+ return 0;
+ }
+
private int flagNameToId(String flagName) {
- List<Field> fields = Flags.getFlagFields();
- for (Field field : fields) {
- if (flagName.equals(field.getName())) {
- return fieldToId(field);
+ Map<String, Flag<?>> flagFields = FlagsFactory.INSTANCE.getKnownFlags();
+ for (String fieldName : flagFields.keySet()) {
+ if (flagName.equals(fieldName)) {
+ return flagFields.get(fieldName).getId();
}
}
return 0;
}
- private int fieldToId(Field field) {
- try {
- Flag<?> flag = (Flag<?>) field.get(null);
- return flag.getId();
- } catch (IllegalAccessException e) {
- // no-op
- }
-
- return 0;
- }
-
private void printKnownFlags(PrintWriter pw) {
- List<Field> fields = Flags.getFlagFields();
+ Map<String, Flag<?>> fields = FlagsFactory.INSTANCE.getKnownFlags();
int longestFieldName = 0;
- for (Field field : fields) {
- longestFieldName = Math.max(longestFieldName, field.getName().length());
+ for (String fieldName : fields.keySet()) {
+ longestFieldName = Math.max(longestFieldName, fieldName.length());
}
pw.println("Known Flags:");
@@ -176,23 +252,32 @@
for (int i = 0; i < longestFieldName - "Flag Name".length() + 1; i++) {
pw.print(" ");
}
- pw.println("ID Enabled?");
+ pw.println("ID Value");
for (int i = 0; i < longestFieldName; i++) {
pw.print("=");
}
pw.println(" ==== ========");
- for (Field field : fields) {
- int id = fieldToId(field);
+ for (String fieldName : fields.keySet()) {
+ Flag<?> flag = fields.get(fieldName);
+ int id = flag.getId();
if (id == 0 || !mAllFlags.containsKey(id)) {
continue;
}
- pw.print(field.getName());
- int fieldWidth = field.getName().length();
+ pw.print(fieldName);
+ int fieldWidth = fieldName.length();
for (int i = 0; i < longestFieldName - fieldWidth + 1; i++) {
pw.print(" ");
}
pw.printf("%-4d ", id);
- pw.println(isBooleanFlagEnabled(mAllFlags.get(id)));
+ if (isBooleanFlag(flag)) {
+ pw.println(isBooleanFlagEnabled(mAllFlags.get(id)));
+ } else if (isStringFlag(flag)) {
+ pw.println(getStringFlag(flag));
+ } else if (isIntFlag(flag)) {
+ pw.println(getIntFlag(flag));
+ } else {
+ pw.println("<unknown flag type>");
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index bcea262..c4cc338 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -18,7 +18,10 @@
import android.provider.DeviceConfig
import com.android.internal.annotations.Keep
import com.android.systemui.R
-import java.lang.reflect.Field
+import com.android.systemui.flags.FlagsFactory.releasedFlag
+import com.android.systemui.flags.FlagsFactory.resourceBooleanFlag
+import com.android.systemui.flags.FlagsFactory.sysPropBooleanFlag
+import com.android.systemui.flags.FlagsFactory.unreleasedFlag
/**
* List of [Flag] objects for use in SystemUI.
@@ -33,63 +36,80 @@
* See [FeatureFlagsDebug] for instructions on flipping the flags via adb.
*/
object Flags {
- @JvmField val TEAMFOOD = UnreleasedFlag(1)
+ @JvmField val TEAMFOOD = unreleasedFlag(1, "teamfood")
// 100 - notification
// TODO(b/254512751): Tracking Bug
- val NOTIFICATION_PIPELINE_DEVELOPER_LOGGING = UnreleasedFlag(103)
+ val NOTIFICATION_PIPELINE_DEVELOPER_LOGGING =
+ unreleasedFlag(103, "notification_pipeline_developer_logging")
// TODO(b/254512732): Tracking Bug
- @JvmField val NSSL_DEBUG_LINES = UnreleasedFlag(105)
+ @JvmField val NSSL_DEBUG_LINES = unreleasedFlag(105, "nssl_debug_lines")
// TODO(b/254512505): Tracking Bug
- @JvmField val NSSL_DEBUG_REMOVE_ANIMATION = UnreleasedFlag(106)
+ @JvmField val NSSL_DEBUG_REMOVE_ANIMATION = unreleasedFlag(106, "nssl_debug_remove_animation")
// TODO(b/254512624): Tracking Bug
@JvmField
val NOTIFICATION_DRAG_TO_CONTENTS =
- ResourceBooleanFlag(108, R.bool.config_notificationToContents)
+ resourceBooleanFlag(
+ 108,
+ R.bool.config_notificationToContents,
+ "notification_drag_to_contents"
+ )
// TODO(b/254512517): Tracking Bug
- val FSI_REQUIRES_KEYGUARD = UnreleasedFlag(110, teamfood = true)
+ val FSI_REQUIRES_KEYGUARD = unreleasedFlag(110, "fsi_requires_keyguard", teamfood = true)
// TODO(b/254512538): Tracking Bug
- val INSTANT_VOICE_REPLY = UnreleasedFlag(111, teamfood = true)
+ val INSTANT_VOICE_REPLY = unreleasedFlag(111, "instant_voice_reply", teamfood = true)
// TODO(b/254512425): Tracking Bug
- val NOTIFICATION_MEMORY_MONITOR_ENABLED = ReleasedFlag(112)
+ val NOTIFICATION_MEMORY_MONITOR_ENABLED =
+ releasedFlag(112, "notification_memory_monitor_enabled")
// TODO(b/254512731): Tracking Bug
- @JvmField val NOTIFICATION_DISMISSAL_FADE = UnreleasedFlag(113, teamfood = true)
- val STABILITY_INDEX_FIX = UnreleasedFlag(114, teamfood = true)
- val SEMI_STABLE_SORT = UnreleasedFlag(115, teamfood = true)
- @JvmField val NOTIFICATION_GROUP_CORNER = UnreleasedFlag(116, teamfood = true)
- // next id: 117
+ @JvmField
+ val NOTIFICATION_DISMISSAL_FADE =
+ unreleasedFlag(113, "notification_dismissal_fade", teamfood = true)
+ val STABILITY_INDEX_FIX = unreleasedFlag(114, "stability_index_fix", teamfood = true)
+ val SEMI_STABLE_SORT = unreleasedFlag(115, "semi_stable_sort", teamfood = true)
+
+ @JvmField
+ val NOTIFICATION_GROUP_CORNER =
+ unreleasedFlag(116, "notification_group_corner", teamfood = true)
+
+ // TODO(b/257506350): Tracking Bug
+ val FSI_CHROME = unreleasedFlag(117, "fsi_chrome")
+
+ // next id: 118
// 200 - keyguard/lockscreen
// ** Flag retired **
// public static final BooleanFlag KEYGUARD_LAYOUT =
// new BooleanFlag(200, true);
// TODO(b/254512713): Tracking Bug
- @JvmField val LOCKSCREEN_ANIMATIONS = ReleasedFlag(201)
+ @JvmField val LOCKSCREEN_ANIMATIONS = releasedFlag(201, "lockscreen_animations")
// TODO(b/254512750): Tracking Bug
- val NEW_UNLOCK_SWIPE_ANIMATION = ReleasedFlag(202)
- val CHARGING_RIPPLE = ResourceBooleanFlag(203, R.bool.flag_charging_ripple)
+ val NEW_UNLOCK_SWIPE_ANIMATION = releasedFlag(202, "new_unlock_swipe_animation")
+ val CHARGING_RIPPLE = resourceBooleanFlag(203, R.bool.flag_charging_ripple, "charging_ripple")
// TODO(b/254512281): Tracking Bug
@JvmField
- val BOUNCER_USER_SWITCHER = ResourceBooleanFlag(204, R.bool.config_enableBouncerUserSwitcher)
+ val BOUNCER_USER_SWITCHER =
+ resourceBooleanFlag(204, R.bool.config_enableBouncerUserSwitcher, "bouncer_user_switcher")
// TODO(b/254512676): Tracking Bug
- @JvmField val LOCKSCREEN_CUSTOM_CLOCKS = UnreleasedFlag(207, teamfood = true)
+ @JvmField
+ val LOCKSCREEN_CUSTOM_CLOCKS = unreleasedFlag(207, "lockscreen_custom_clocks", teamfood = true)
/**
* Flag to enable the usage of the new bouncer data source. This is a refactor of and eventual
* replacement of KeyguardBouncer.java.
*/
// TODO(b/254512385): Tracking Bug
- @JvmField val MODERN_BOUNCER = ReleasedFlag(208)
+ @JvmField val MODERN_BOUNCER = releasedFlag(208, "modern_bouncer")
/**
* Whether the user interactor and repository should use `UserSwitcherController`.
@@ -98,7 +118,8 @@
* framework APIs.
*/
// TODO(b/254513286): Tracking Bug
- val USER_INTERACTOR_AND_REPO_USE_CONTROLLER = UnreleasedFlag(210)
+ val USER_INTERACTOR_AND_REPO_USE_CONTROLLER =
+ unreleasedFlag(210, "user_interactor_and_repo_use_controller")
/**
* Whether `UserSwitcherController` should use the user interactor.
@@ -110,24 +131,26 @@
* would created a cycle between controller -> interactor -> controller.
*/
// TODO(b/254513102): Tracking Bug
- val USER_CONTROLLER_USES_INTERACTOR = ReleasedFlag(211)
+ val USER_CONTROLLER_USES_INTERACTOR = releasedFlag(211, "user_controller_uses_interactor")
/**
* Whether the clock on a wide lock screen should use the new "stepping" animation for moving
* the digits when the clock moves.
*/
- @JvmField val STEP_CLOCK_ANIMATION = UnreleasedFlag(212)
+ @JvmField val STEP_CLOCK_ANIMATION = unreleasedFlag(212, "step_clock_animation")
/**
* Migration from the legacy isDozing/dozeAmount paths to the new KeyguardTransitionRepository
* will occur in stages. This is one stage of many to come.
*/
// TODO(b/255607168): Tracking Bug
- @JvmField val DOZING_MIGRATION_1 = UnreleasedFlag(213)
+ @JvmField val DOZING_MIGRATION_1 = unreleasedFlag(213, "dozing_migration_1")
- @JvmField val NEW_ELLIPSE_DETECTION = UnreleasedFlag(214)
+ // TODO(b/252897742): Tracking Bug
+ @JvmField val NEW_ELLIPSE_DETECTION = unreleasedFlag(214, "new_ellipse_detection")
- @JvmField val NEW_UDFPS_OVERLAY = UnreleasedFlag(215)
+ // TODO(b/252897742): Tracking Bug
+ @JvmField val NEW_UDFPS_OVERLAY = unreleasedFlag(215, "new_udfps_overlay")
/**
* Whether to enable the code powering customizable lock screen quick affordances.
@@ -135,260 +158,260 @@
* Note that this flag does not enable individual implementations of quick affordances like the
* new camera quick affordance. Look for individual flags for those.
*/
- @JvmField val CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES = UnreleasedFlag(216, teamfood = false)
+ @JvmField
+ val CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES =
+ unreleasedFlag(216, "customizable_lock_screen_quick_affordances", teamfood = false)
+
+ /** Shows chipbar UI whenever the device is unlocked by ActiveUnlock (watch). */
+ // TODO(b/240196500): Tracking Bug
+ @JvmField val ACTIVE_UNLOCK_CHIPBAR = unreleasedFlag(217, "active_unlock_chipbar")
// 300 - power menu
// TODO(b/254512600): Tracking Bug
- @JvmField val POWER_MENU_LITE = ReleasedFlag(300)
+ @JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
// 400 - smartspace
// TODO(b/254513100): Tracking Bug
- val SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED = ReleasedFlag(401)
- val SMARTSPACE = ResourceBooleanFlag(402, R.bool.flag_smartspace)
+ val SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED =
+ releasedFlag(401, "smartspace_shared_element_transition_enabled")
+ val SMARTSPACE = resourceBooleanFlag(402, R.bool.flag_smartspace, "smartspace")
// 500 - quick settings
- @Deprecated("Not needed anymore") val NEW_USER_SWITCHER = ReleasedFlag(500)
// TODO(b/254512321): Tracking Bug
- @JvmField val COMBINED_QS_HEADERS = UnreleasedFlag(501, teamfood = true)
- val PEOPLE_TILE = ResourceBooleanFlag(502, R.bool.flag_conversations)
+ @JvmField val COMBINED_QS_HEADERS = releasedFlag(501, "combined_qs_headers")
+ val PEOPLE_TILE = resourceBooleanFlag(502, R.bool.flag_conversations, "people_tile")
+
@JvmField
val QS_USER_DETAIL_SHORTCUT =
- ResourceBooleanFlag(503, R.bool.flag_lockscreen_qs_user_detail_shortcut)
-
- // TODO(b/254512699): Tracking Bug
- @Deprecated("Not needed anymore") val NEW_FOOTER = ReleasedFlag(504)
+ resourceBooleanFlag(
+ 503,
+ R.bool.flag_lockscreen_qs_user_detail_shortcut,
+ "qs_user_detail_shortcut"
+ )
// TODO(b/254512747): Tracking Bug
- val NEW_HEADER = UnreleasedFlag(505, teamfood = true)
+ val NEW_HEADER = releasedFlag(505, "new_header")
// TODO(b/254512383): Tracking Bug
@JvmField
val FULL_SCREEN_USER_SWITCHER =
- ResourceBooleanFlag(506, R.bool.config_enableFullscreenUserSwitcher)
+ resourceBooleanFlag(
+ 506,
+ R.bool.config_enableFullscreenUserSwitcher,
+ "full_screen_user_switcher"
+ )
// TODO(b/254512678): Tracking Bug
- @JvmField val NEW_FOOTER_ACTIONS = ReleasedFlag(507)
+ @JvmField val NEW_FOOTER_ACTIONS = releasedFlag(507, "new_footer_actions")
// TODO(b/244064524): Tracking Bug
- @JvmField val QS_SECONDARY_DATA_SUB_INFO = UnreleasedFlag(508, teamfood = true)
+ @JvmField
+ val QS_SECONDARY_DATA_SUB_INFO =
+ unreleasedFlag(508, "qs_secondary_data_sub_info", teamfood = true)
// 600- status bar
// TODO(b/254513246): Tracking Bug
- val STATUS_BAR_USER_SWITCHER = ResourceBooleanFlag(602, R.bool.flag_user_switcher_chip)
+ val STATUS_BAR_USER_SWITCHER =
+ resourceBooleanFlag(602, R.bool.flag_user_switcher_chip, "status_bar_user_switcher")
// TODO(b/254512623): Tracking Bug
@Deprecated("Replaced by mobile and wifi specific flags.")
- val NEW_STATUS_BAR_PIPELINE_BACKEND = UnreleasedFlag(604, teamfood = false)
+ val NEW_STATUS_BAR_PIPELINE_BACKEND =
+ unreleasedFlag(604, "new_status_bar_pipeline_backend", teamfood = false)
// TODO(b/254512660): Tracking Bug
@Deprecated("Replaced by mobile and wifi specific flags.")
- val NEW_STATUS_BAR_PIPELINE_FRONTEND = UnreleasedFlag(605, teamfood = false)
+ val NEW_STATUS_BAR_PIPELINE_FRONTEND =
+ unreleasedFlag(605, "new_status_bar_pipeline_frontend", teamfood = false)
// TODO(b/256614753): Tracking Bug
- val NEW_STATUS_BAR_MOBILE_ICONS = UnreleasedFlag(606)
+ val NEW_STATUS_BAR_MOBILE_ICONS = unreleasedFlag(606, "new_status_bar_mobile_icons")
// TODO(b/256614210): Tracking Bug
- val NEW_STATUS_BAR_WIFI_ICON = UnreleasedFlag(607)
+ val NEW_STATUS_BAR_WIFI_ICON = unreleasedFlag(607, "new_status_bar_wifi_icon")
// TODO(b/256614751): Tracking Bug
- val NEW_STATUS_BAR_MOBILE_ICONS_BACKEND = UnreleasedFlag(608)
+ val NEW_STATUS_BAR_MOBILE_ICONS_BACKEND =
+ unreleasedFlag(608, "new_status_bar_mobile_icons_backend")
// TODO(b/256613548): Tracking Bug
- val NEW_STATUS_BAR_WIFI_ICON_BACKEND = UnreleasedFlag(609)
+ val NEW_STATUS_BAR_WIFI_ICON_BACKEND = unreleasedFlag(609, "new_status_bar_wifi_icon_backend")
// 700 - dialer/calls
// TODO(b/254512734): Tracking Bug
- val ONGOING_CALL_STATUS_BAR_CHIP = ReleasedFlag(700)
+ val ONGOING_CALL_STATUS_BAR_CHIP = releasedFlag(700, "ongoing_call_status_bar_chip")
// TODO(b/254512681): Tracking Bug
- val ONGOING_CALL_IN_IMMERSIVE = ReleasedFlag(701)
+ val ONGOING_CALL_IN_IMMERSIVE = releasedFlag(701, "ongoing_call_in_immersive")
// TODO(b/254512753): Tracking Bug
- val ONGOING_CALL_IN_IMMERSIVE_CHIP_TAP = ReleasedFlag(702)
+ val ONGOING_CALL_IN_IMMERSIVE_CHIP_TAP = releasedFlag(702, "ongoing_call_in_immersive_chip_tap")
// 800 - general visual/theme
- @JvmField val MONET = ResourceBooleanFlag(800, R.bool.flag_monet)
+ @JvmField val MONET = resourceBooleanFlag(800, R.bool.flag_monet, "monet")
// 801 - region sampling
// TODO(b/254512848): Tracking Bug
- val REGION_SAMPLING = UnreleasedFlag(801)
+ val REGION_SAMPLING = unreleasedFlag(801, "region_sampling")
// 802 - wallpaper rendering
// TODO(b/254512923): Tracking Bug
- @JvmField val USE_CANVAS_RENDERER = UnreleasedFlag(802, teamfood = true)
+ @JvmField val USE_CANVAS_RENDERER = unreleasedFlag(802, "use_canvas_renderer")
// 803 - screen contents translation
// TODO(b/254513187): Tracking Bug
- val SCREEN_CONTENTS_TRANSLATION = UnreleasedFlag(803)
+ val SCREEN_CONTENTS_TRANSLATION = unreleasedFlag(803, "screen_contents_translation")
// 804 - monochromatic themes
- @JvmField val MONOCHROMATIC_THEMES = UnreleasedFlag(804)
+ @JvmField
+ val MONOCHROMATIC_THEMES =
+ sysPropBooleanFlag(804, "persist.sysui.monochromatic", default = false)
// 900 - media
// TODO(b/254512697): Tracking Bug
- val MEDIA_TAP_TO_TRANSFER = ReleasedFlag(900)
+ val MEDIA_TAP_TO_TRANSFER = releasedFlag(900, "media_tap_to_transfer")
// TODO(b/254512502): Tracking Bug
- val MEDIA_SESSION_ACTIONS = UnreleasedFlag(901)
+ val MEDIA_SESSION_ACTIONS = unreleasedFlag(901, "media_session_actions")
// TODO(b/254512726): Tracking Bug
- val MEDIA_NEARBY_DEVICES = ReleasedFlag(903)
+ val MEDIA_NEARBY_DEVICES = releasedFlag(903, "media_nearby_devices")
// TODO(b/254512695): Tracking Bug
- val MEDIA_MUTE_AWAIT = ReleasedFlag(904)
+ val MEDIA_MUTE_AWAIT = releasedFlag(904, "media_mute_await")
// TODO(b/254512654): Tracking Bug
- @JvmField val DREAM_MEDIA_COMPLICATION = UnreleasedFlag(905)
+ @JvmField val DREAM_MEDIA_COMPLICATION = unreleasedFlag(905, "dream_media_complication")
// TODO(b/254512673): Tracking Bug
- @JvmField val DREAM_MEDIA_TAP_TO_OPEN = UnreleasedFlag(906)
+ @JvmField val DREAM_MEDIA_TAP_TO_OPEN = unreleasedFlag(906, "dream_media_tap_to_open")
// TODO(b/254513168): Tracking Bug
- @JvmField val UMO_SURFACE_RIPPLE = UnreleasedFlag(907)
+ @JvmField val UMO_SURFACE_RIPPLE = unreleasedFlag(907, "umo_surface_ripple")
// 1000 - dock
- val SIMULATE_DOCK_THROUGH_CHARGING = ReleasedFlag(1000)
+ val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag(1000, "simulate_dock_through_charging")
// TODO(b/254512758): Tracking Bug
- @JvmField val ROUNDED_BOX_RIPPLE = ReleasedFlag(1002)
+ @JvmField val ROUNDED_BOX_RIPPLE = releasedFlag(1002, "rounded_box_ripple")
// 1100 - windowing
- @JvmField
@Keep
+ @JvmField
val WM_ENABLE_SHELL_TRANSITIONS =
- SysPropBooleanFlag(1100, "persist.wm.debug.shell_transit", false)
-
- /** b/170163464: animate bubbles expanded view collapse with home gesture */
- @JvmField
- @Keep
- val BUBBLES_HOME_GESTURE =
- SysPropBooleanFlag(1101, "persist.wm.debug.bubbles_home_gesture", true)
+ sysPropBooleanFlag(1100, "persist.wm.debug.shell_transit", default = true)
// TODO(b/254513207): Tracking Bug
- @JvmField
@Keep
+ @JvmField
val WM_ENABLE_PARTIAL_SCREEN_SHARING =
- DeviceConfigBooleanFlag(
+ unreleasedFlag(
1102,
- "record_task_content",
- DeviceConfig.NAMESPACE_WINDOW_MANAGER,
- false,
+ name = "record_task_content",
+ namespace = DeviceConfig.NAMESPACE_WINDOW_MANAGER,
teamfood = true
)
// TODO(b/254512674): Tracking Bug
- @JvmField
@Keep
- val HIDE_NAVBAR_WINDOW = SysPropBooleanFlag(1103, "persist.wm.debug.hide_navbar_window", false)
+ @JvmField
+ val HIDE_NAVBAR_WINDOW =
+ sysPropBooleanFlag(1103, "persist.wm.debug.hide_navbar_window", default = false)
- @JvmField
@Keep
- val WM_DESKTOP_WINDOWING = SysPropBooleanFlag(1104, "persist.wm.debug.desktop_mode", false)
+ @JvmField
+ val WM_DESKTOP_WINDOWING =
+ sysPropBooleanFlag(1104, "persist.wm.debug.desktop_mode", default = false)
- @JvmField
@Keep
- val WM_CAPTION_ON_SHELL = SysPropBooleanFlag(1105, "persist.wm.debug.caption_on_shell", false)
+ @JvmField
+ val WM_CAPTION_ON_SHELL =
+ sysPropBooleanFlag(1105, "persist.wm.debug.caption_on_shell", default = false)
- @JvmField
@Keep
- val FLOATING_TASKS_ENABLED = SysPropBooleanFlag(1106, "persist.wm.debug.floating_tasks", false)
-
@JvmField
- @Keep
- val SHOW_FLOATING_TASKS_AS_BUBBLES =
- SysPropBooleanFlag(1107, "persist.wm.debug.floating_tasks_as_bubbles", false)
-
- @JvmField
- @Keep
val ENABLE_FLING_TO_DISMISS_BUBBLE =
- SysPropBooleanFlag(1108, "persist.wm.debug.fling_to_dismiss_bubble", true)
+ sysPropBooleanFlag(1108, "persist.wm.debug.fling_to_dismiss_bubble", default = true)
- @JvmField
@Keep
+ @JvmField
val ENABLE_FLING_TO_DISMISS_PIP =
- SysPropBooleanFlag(1109, "persist.wm.debug.fling_to_dismiss_pip", true)
+ sysPropBooleanFlag(1109, "persist.wm.debug.fling_to_dismiss_pip", default = true)
- @JvmField
@Keep
+ @JvmField
val ENABLE_PIP_KEEP_CLEAR_ALGORITHM =
- SysPropBooleanFlag(1110, "persist.wm.debug.enable_pip_keep_clear_algorithm", false)
+ sysPropBooleanFlag(
+ 1110,
+ "persist.wm.debug.enable_pip_keep_clear_algorithm",
+ default = false
+ )
+
+ // TODO(b/256873975): Tracking Bug
+ @JvmField @Keep val WM_BUBBLE_BAR = unreleasedFlag(1111, "wm_bubble_bar")
// 1200 - predictive back
- @JvmField
@Keep
+ @JvmField
val WM_ENABLE_PREDICTIVE_BACK =
- SysPropBooleanFlag(1200, "persist.wm.debug.predictive_back", true)
+ sysPropBooleanFlag(1200, "persist.wm.debug.predictive_back", default = true)
- @JvmField
@Keep
+ @JvmField
val WM_ENABLE_PREDICTIVE_BACK_ANIM =
- SysPropBooleanFlag(1201, "persist.wm.debug.predictive_back_anim", false)
+ sysPropBooleanFlag(1201, "persist.wm.debug.predictive_back_anim", default = false)
- @JvmField
@Keep
+ @JvmField
val WM_ALWAYS_ENFORCE_PREDICTIVE_BACK =
- SysPropBooleanFlag(1202, "persist.wm.debug.predictive_back_always_enforce", false)
+ sysPropBooleanFlag(1202, "persist.wm.debug.predictive_back_always_enforce", default = false)
// TODO(b/254512728): Tracking Bug
- @JvmField val NEW_BACK_AFFORDANCE = UnreleasedFlag(1203, teamfood = false)
+ @JvmField
+ val NEW_BACK_AFFORDANCE = unreleasedFlag(1203, "new_back_affordance", teamfood = false)
// 1300 - screenshots
// TODO(b/254512719): Tracking Bug
- @JvmField val SCREENSHOT_REQUEST_PROCESSOR = UnreleasedFlag(1300, true)
+ @JvmField
+ val SCREENSHOT_REQUEST_PROCESSOR =
+ unreleasedFlag(1300, "screenshot_request_processor", teamfood = true)
// TODO(b/254513155): Tracking Bug
- @JvmField val SCREENSHOT_WORK_PROFILE_POLICY = UnreleasedFlag(1301)
+ @JvmField
+ val SCREENSHOT_WORK_PROFILE_POLICY = unreleasedFlag(1301, "screenshot_work_profile_policy")
// 1400 - columbus
// TODO(b/254512756): Tracking Bug
- val QUICK_TAP_IN_PCC = ReleasedFlag(1400)
+ val QUICK_TAP_IN_PCC = releasedFlag(1400, "quick_tap_in_pcc")
// 1500 - chooser
// TODO(b/254512507): Tracking Bug
- val CHOOSER_UNBUNDLED = UnreleasedFlag(1500, teamfood = true)
+ val CHOOSER_UNBUNDLED = unreleasedFlag(1500, "chooser_unbundled", teamfood = true)
// 1600 - accessibility
- @JvmField val A11Y_FLOATING_MENU_FLING_SPRING_ANIMATIONS = UnreleasedFlag(1600)
+ @JvmField
+ val A11Y_FLOATING_MENU_FLING_SPRING_ANIMATIONS =
+ unreleasedFlag(1600, "a11y_floating_menu_fling_spring_animations")
// 1700 - clipboard
- @JvmField val CLIPBOARD_OVERLAY_REFACTOR = UnreleasedFlag(1700, true)
- @JvmField val CLIPBOARD_REMOTE_BEHAVIOR = UnreleasedFlag(1701)
+ @JvmField
+ val CLIPBOARD_OVERLAY_REFACTOR =
+ unreleasedFlag(1700, "clipboard_overlay_refactor", teamfood = true)
+ @JvmField val CLIPBOARD_REMOTE_BEHAVIOR = unreleasedFlag(1701, "clipboard_remote_behavior")
// 1800 - shade container
- @JvmField val LEAVE_SHADE_OPEN_FOR_BUGREPORT = UnreleasedFlag(1800, teamfood = true)
+ @JvmField
+ val LEAVE_SHADE_OPEN_FOR_BUGREPORT =
+ unreleasedFlag(1800, "leave_shade_open_for_bugreport", teamfood = true)
// 1900 - note task
- @JvmField val NOTE_TASKS = SysPropBooleanFlag(1900, "persist.sysui.debug.note_tasks")
+ @JvmField val NOTE_TASKS = sysPropBooleanFlag(1900, "persist.sysui.debug.note_tasks")
// 2000 - device controls
- @Keep val USE_APP_PANELS = UnreleasedFlag(2000, true)
+ @Keep @JvmField val USE_APP_PANELS = unreleasedFlag(2000, "use_app_panels", teamfood = true)
- // Pay no attention to the reflection behind the curtain.
- // ========================== Curtain ==========================
- // | |
- // | . . . . . . . . . . . . . . . . . . . |
- @JvmStatic
- fun collectFlags(): Map<Int, Flag<*>> {
- return flagFields
- .map { field ->
- // field[null] returns the current value of the field.
- // See java.lang.Field#get
- val flag = field[null] as Flag<*>
- flag.id to flag
- }
- .toMap()
- }
-
- // | . . . . . . . . . . . . . . . . . . . |
- @JvmStatic
- val flagFields: List<Field>
- get() {
- return Flags::class.java.fields.filter { f ->
- Flag::class.java.isAssignableFrom(f.type)
- }
- }
- // | |
- // \_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/
+ // 2100 - Falsing Manager
+ @JvmField val FALSING_FOR_LONG_TAPS = releasedFlag(2100, "falsing_for_long_taps")
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagsCommonModule.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagsCommonModule.kt
index e1f4944..18d7bcf 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagsCommonModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagsCommonModule.kt
@@ -30,7 +30,7 @@
@Provides
@Named(ALL_FLAGS)
fun providesAllFlags(): Map<Int, Flag<*>> {
- return Flags.collectFlags()
+ return FlagsFactory.knownFlags.map { it.value.id to it.value }.toMap()
}
@JvmStatic
diff --git a/packages/SystemUI/src/com/android/systemui/flags/OWNERS b/packages/SystemUI/src/com/android/systemui/flags/OWNERS
new file mode 100644
index 0000000..c9d2db1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/flags/OWNERS
@@ -0,0 +1,12 @@
+set noparent
+
+# Bug component: 1203176
+
+mankoff@google.com # send reviews here
+
+pixel@google.com
+juliacr@google.com
+cinek@google.com
+alexflo@google.com
+dsandler@android.com
+adamcohen@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt b/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt
index 694fa01..ae05c46 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt
@@ -27,11 +27,10 @@
interface ServerFlagReader {
/** Returns true if there is a server-side setting stored. */
- fun hasOverride(flagId: Int): Boolean
+ fun hasOverride(namespace: String, name: String): Boolean
/** Returns any stored server-side setting or the default if not set. */
- fun readServerOverride(flagId: Int, default: Boolean): Boolean
-
+ fun readServerOverride(namespace: String, name: String, default: Boolean): Boolean
/** Register a listener for changes to any of the passed in flags. */
fun listenForChanges(values: Collection<Flag<*>>, listener: ChangeListener)
@@ -68,19 +67,19 @@
}
}
- override fun hasOverride(flagId: Int): Boolean =
- deviceConfig.getProperty(
+ override fun hasOverride(namespace: String, name: String): Boolean =
+ !namespace.isBlank() && !name.isBlank() && deviceConfig.getProperty(
namespace,
- getServerOverrideName(flagId)
+ name
) != null
- override fun readServerOverride(flagId: Int, default: Boolean): Boolean {
- return deviceConfig.getBoolean(
+
+ override fun readServerOverride(namespace: String, name: String, default: Boolean): Boolean =
+ !namespace.isBlank() && !name.isBlank() && deviceConfig.getBoolean(
namespace,
- getServerOverrideName(flagId),
+ name,
default
)
- }
override fun listenForChanges(
flags: Collection<Flag<*>>,
@@ -121,24 +120,24 @@
}
class ServerFlagReaderFake : ServerFlagReader {
- private val flagMap: MutableMap<Int, Boolean> = mutableMapOf()
+ private val flagMap: MutableMap<String, Boolean> = mutableMapOf()
private val listeners =
mutableListOf<Pair<ServerFlagReader.ChangeListener, Collection<Flag<*>>>>()
- override fun hasOverride(flagId: Int): Boolean {
- return flagMap.containsKey(flagId)
+ override fun hasOverride(namespace: String, name: String): Boolean {
+ return flagMap.containsKey(name)
}
- override fun readServerOverride(flagId: Int, default: Boolean): Boolean {
- return flagMap.getOrDefault(flagId, default)
+ override fun readServerOverride(namespace: String, name: String, default: Boolean): Boolean {
+ return flagMap.getOrDefault(name, default)
}
- fun setFlagValue(flagId: Int, value: Boolean) {
- flagMap.put(flagId, value)
+ fun setFlagValue(namespace: String, name: String, value: Boolean) {
+ flagMap.put(name, value)
for ((listener, flags) in listeners) {
flagLoop@ for (flag in flags) {
- if (flagId == flag.id) {
+ if (name == flag.name) {
listener.onChange()
break@flagLoop
}
@@ -146,8 +145,8 @@
}
}
- fun eraseFlag(flagId: Int) {
- flagMap.remove(flagId)
+ fun eraseFlag(namespace: String, name: String) {
+ flagMap.remove(name)
}
override fun listenForChanges(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt
new file mode 100644
index 0000000..0f4581c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard
+
+import android.content.ContentProvider
+import android.content.ContentValues
+import android.content.Context
+import android.content.UriMatcher
+import android.content.pm.ProviderInfo
+import android.database.Cursor
+import android.database.MatrixCursor
+import android.net.Uri
+import android.util.Log
+import com.android.systemui.SystemUIAppComponentFactoryBase
+import com.android.systemui.SystemUIAppComponentFactoryBase.ContextAvailableCallback
+import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
+import com.android.systemui.shared.keyguard.data.content.KeyguardQuickAffordanceProviderContract as Contract
+import javax.inject.Inject
+import kotlinx.coroutines.runBlocking
+
+class KeyguardQuickAffordanceProvider :
+ ContentProvider(), SystemUIAppComponentFactoryBase.ContextInitializer {
+
+ @Inject lateinit var interactor: KeyguardQuickAffordanceInteractor
+
+ private lateinit var contextAvailableCallback: ContextAvailableCallback
+
+ private val uriMatcher =
+ UriMatcher(UriMatcher.NO_MATCH).apply {
+ addURI(
+ Contract.AUTHORITY,
+ Contract.SlotTable.TABLE_NAME,
+ MATCH_CODE_ALL_SLOTS,
+ )
+ addURI(
+ Contract.AUTHORITY,
+ Contract.AffordanceTable.TABLE_NAME,
+ MATCH_CODE_ALL_AFFORDANCES,
+ )
+ addURI(
+ Contract.AUTHORITY,
+ Contract.SelectionTable.TABLE_NAME,
+ MATCH_CODE_ALL_SELECTIONS,
+ )
+ }
+
+ override fun onCreate(): Boolean {
+ return true
+ }
+
+ override fun attachInfo(context: Context?, info: ProviderInfo?) {
+ contextAvailableCallback.onContextAvailable(checkNotNull(context))
+ super.attachInfo(context, info)
+ }
+
+ override fun setContextAvailableCallback(callback: ContextAvailableCallback) {
+ contextAvailableCallback = callback
+ }
+
+ override fun getType(uri: Uri): String? {
+ val prefix =
+ when (uriMatcher.match(uri)) {
+ MATCH_CODE_ALL_SLOTS,
+ MATCH_CODE_ALL_AFFORDANCES,
+ MATCH_CODE_ALL_SELECTIONS -> "vnd.android.cursor.dir/vnd."
+ else -> null
+ }
+
+ val tableName =
+ when (uriMatcher.match(uri)) {
+ MATCH_CODE_ALL_SLOTS -> Contract.SlotTable.TABLE_NAME
+ MATCH_CODE_ALL_AFFORDANCES -> Contract.AffordanceTable.TABLE_NAME
+ MATCH_CODE_ALL_SELECTIONS -> Contract.SelectionTable.TABLE_NAME
+ else -> null
+ }
+
+ if (prefix == null || tableName == null) {
+ return null
+ }
+
+ return "$prefix${Contract.AUTHORITY}.$tableName"
+ }
+
+ override fun insert(uri: Uri, values: ContentValues?): Uri? {
+ if (uriMatcher.match(uri) != MATCH_CODE_ALL_SELECTIONS) {
+ throw UnsupportedOperationException()
+ }
+
+ return insertSelection(values)
+ }
+
+ override fun query(
+ uri: Uri,
+ projection: Array<out String>?,
+ selection: String?,
+ selectionArgs: Array<out String>?,
+ sortOrder: String?,
+ ): Cursor? {
+ return when (uriMatcher.match(uri)) {
+ MATCH_CODE_ALL_AFFORDANCES -> queryAffordances()
+ MATCH_CODE_ALL_SLOTS -> querySlots()
+ MATCH_CODE_ALL_SELECTIONS -> querySelections()
+ else -> null
+ }
+ }
+
+ override fun update(
+ uri: Uri,
+ values: ContentValues?,
+ selection: String?,
+ selectionArgs: Array<out String>?,
+ ): Int {
+ Log.e(TAG, "Update is not supported!")
+ return 0
+ }
+
+ override fun delete(
+ uri: Uri,
+ selection: String?,
+ selectionArgs: Array<out String>?,
+ ): Int {
+ if (uriMatcher.match(uri) != MATCH_CODE_ALL_SELECTIONS) {
+ throw UnsupportedOperationException()
+ }
+
+ return deleteSelection(uri, selectionArgs)
+ }
+
+ private fun insertSelection(values: ContentValues?): Uri? {
+ if (values == null) {
+ throw IllegalArgumentException("Cannot insert selection, no values passed in!")
+ }
+
+ if (!values.containsKey(Contract.SelectionTable.Columns.SLOT_ID)) {
+ throw IllegalArgumentException(
+ "Cannot insert selection, " +
+ "\"${Contract.SelectionTable.Columns.SLOT_ID}\" not specified!"
+ )
+ }
+
+ if (!values.containsKey(Contract.SelectionTable.Columns.AFFORDANCE_ID)) {
+ throw IllegalArgumentException(
+ "Cannot insert selection, " +
+ "\"${Contract.SelectionTable.Columns.AFFORDANCE_ID}\" not specified!"
+ )
+ }
+
+ val slotId = values.getAsString(Contract.SelectionTable.Columns.SLOT_ID)
+ val affordanceId = values.getAsString(Contract.SelectionTable.Columns.AFFORDANCE_ID)
+
+ if (slotId.isNullOrEmpty()) {
+ throw IllegalArgumentException("Cannot insert selection, slot ID was empty!")
+ }
+
+ if (affordanceId.isNullOrEmpty()) {
+ throw IllegalArgumentException("Cannot insert selection, affordance ID was empty!")
+ }
+
+ val success = runBlocking {
+ interactor.select(
+ slotId = slotId,
+ affordanceId = affordanceId,
+ )
+ }
+
+ return if (success) {
+ Log.d(TAG, "Successfully selected $affordanceId for slot $slotId")
+ context?.contentResolver?.notifyChange(Contract.SelectionTable.URI, null)
+ Contract.SelectionTable.URI
+ } else {
+ Log.d(TAG, "Failed to select $affordanceId for slot $slotId")
+ null
+ }
+ }
+
+ private fun querySelections(): Cursor {
+ return MatrixCursor(
+ arrayOf(
+ Contract.SelectionTable.Columns.SLOT_ID,
+ Contract.SelectionTable.Columns.AFFORDANCE_ID,
+ )
+ )
+ .apply {
+ val affordanceIdsBySlotId = runBlocking { interactor.getSelections() }
+ affordanceIdsBySlotId.entries.forEach { (slotId, affordanceIds) ->
+ affordanceIds.forEach { affordanceId ->
+ addRow(
+ arrayOf(
+ slotId,
+ affordanceId,
+ )
+ )
+ }
+ }
+ }
+ }
+
+ private fun queryAffordances(): Cursor {
+ return MatrixCursor(
+ arrayOf(
+ Contract.AffordanceTable.Columns.ID,
+ Contract.AffordanceTable.Columns.NAME,
+ Contract.AffordanceTable.Columns.ICON,
+ )
+ )
+ .apply {
+ interactor.getAffordancePickerRepresentations().forEach { representation ->
+ addRow(
+ arrayOf(
+ representation.id,
+ representation.name,
+ representation.iconResourceId,
+ )
+ )
+ }
+ }
+ }
+
+ private fun querySlots(): Cursor {
+ return MatrixCursor(
+ arrayOf(
+ Contract.SlotTable.Columns.ID,
+ Contract.SlotTable.Columns.CAPACITY,
+ )
+ )
+ .apply {
+ interactor.getSlotPickerRepresentations().forEach { representation ->
+ addRow(
+ arrayOf(
+ representation.id,
+ representation.maxSelectedAffordances,
+ )
+ )
+ }
+ }
+ }
+
+ private fun deleteSelection(
+ uri: Uri,
+ selectionArgs: Array<out String>?,
+ ): Int {
+ if (selectionArgs == null) {
+ throw IllegalArgumentException(
+ "Cannot delete selection, selection arguments not included!"
+ )
+ }
+
+ val (slotId, affordanceId) =
+ when (selectionArgs.size) {
+ 1 -> Pair(selectionArgs[0], null)
+ 2 -> Pair(selectionArgs[0], selectionArgs[1])
+ else ->
+ throw IllegalArgumentException(
+ "Cannot delete selection, selection arguments has wrong size, expected to" +
+ " have 1 or 2 arguments, had ${selectionArgs.size} instead!"
+ )
+ }
+
+ val deleted = runBlocking {
+ interactor.unselect(
+ slotId = slotId,
+ affordanceId = affordanceId,
+ )
+ }
+
+ return if (deleted) {
+ Log.d(TAG, "Successfully unselected $affordanceId for slot $slotId")
+ context?.contentResolver?.notifyChange(uri, null)
+ 1
+ } else {
+ Log.d(TAG, "Failed to unselect $affordanceId for slot $slotId")
+ 0
+ }
+ }
+
+ companion object {
+ private const val TAG = "KeyguardQuickAffordanceProvider"
+ private const val MATCH_CODE_ALL_SLOTS = 1
+ private const val MATCH_CODE_ALL_AFFORDANCES = 2
+ private const val MATCH_CODE_ALL_SELECTIONS = 3
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 41abb62..d52efab 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -125,6 +125,7 @@
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
@@ -266,6 +267,7 @@
private final Executor mUiBgExecutor;
private final ScreenOffAnimationController mScreenOffAnimationController;
private final Lazy<NotificationShadeDepthController> mNotificationShadeDepthController;
+ private final Lazy<ShadeController> mShadeController;
private boolean mSystemReady;
private boolean mBootCompleted;
@@ -1150,6 +1152,7 @@
ScreenOnCoordinator screenOnCoordinator,
InteractionJankMonitor interactionJankMonitor,
DreamOverlayStateController dreamOverlayStateController,
+ Lazy<ShadeController> shadeControllerLazy,
Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy,
Lazy<ActivityLaunchAnimator> activityLaunchAnimator) {
mContext = context;
@@ -1165,6 +1168,7 @@
mTrustManager = trustManager;
mUserSwitcherController = userSwitcherController;
mKeyguardDisplayManager = keyguardDisplayManager;
+ mShadeController = shadeControllerLazy;
dumpManager.registerDumpable(getClass().getName(), this);
mDeviceConfig = deviceConfig;
mScreenOnCoordinator = screenOnCoordinator;
@@ -1725,7 +1729,7 @@
try {
callback.onKeyguardExitResult(true);
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
+ Slog.w(TAG, "Failed to call onKeyguardExitResult(true)", e);
}
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index f08463b..78a7c9e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -47,6 +47,7 @@
import com.android.systemui.keyguard.domain.interactor.StartKeyguardTransitionModule;
import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceModule;
import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -108,6 +109,7 @@
ScreenOnCoordinator screenOnCoordinator,
InteractionJankMonitor interactionJankMonitor,
DreamOverlayStateController dreamOverlayStateController,
+ Lazy<ShadeController> shadeController,
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
Lazy<ActivityLaunchAnimator> activityLaunchAnimator) {
return new KeyguardViewMediator(
@@ -135,6 +137,7 @@
screenOnCoordinator,
interactionJankMonitor,
dreamOverlayStateController,
+ shadeController,
notificationShadeWindowController,
activityLaunchAnimator);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
index 0046256..9a90fe7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
@@ -16,19 +16,20 @@
package com.android.systemui.keyguard.data.repository
-import android.hardware.biometrics.BiometricSourceType
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.keyguard.ViewMediatorCallback
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
import com.android.systemui.statusbar.phone.KeyguardBouncer
import javax.inject.Inject
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
-/** Encapsulates app state for the lock screen bouncer. */
+/** Encapsulates app state for the lock screen primary and alternate bouncer. */
@SysUISingleton
class KeyguardBouncerRepository
@Inject
@@ -36,12 +37,24 @@
private val viewMediatorCallback: ViewMediatorCallback,
keyguardUpdateMonitor: KeyguardUpdateMonitor,
) {
- var bouncerPromptReason: Int? = null
- /** Determines if we want to instantaneously show the bouncer instead of translating. */
- private val _isScrimmed = MutableStateFlow(false)
- val isScrimmed = _isScrimmed.asStateFlow()
+ /** Values associated with the PrimaryBouncer (pin/pattern/password) input. */
+ private val _primaryBouncerVisible = MutableStateFlow(false)
+ val primaryBouncerVisible = _primaryBouncerVisible.asStateFlow()
+ private val _primaryBouncerShow = MutableStateFlow<KeyguardBouncerModel?>(null)
+ val primaryBouncerShow = _primaryBouncerShow.asStateFlow()
+ private val _primaryBouncerShowingSoon = MutableStateFlow(false)
+ val primaryBouncerShowingSoon = _primaryBouncerShowingSoon.asStateFlow()
+ private val _primaryBouncerHide = MutableStateFlow(false)
+ val primaryBouncerHide = _primaryBouncerHide.asStateFlow()
+ private val _primaryBouncerStartingToHide = MutableStateFlow(false)
+ val primaryBouncerStartingToHide = _primaryBouncerStartingToHide.asStateFlow()
+ private val _primaryBouncerDisappearAnimation = MutableStateFlow<Runnable?>(null)
+ val primaryBouncerStartingDisappearAnimation = _primaryBouncerDisappearAnimation.asStateFlow()
+ /** Determines if we want to instantaneously show the primary bouncer instead of translating. */
+ private val _primaryBouncerScrimmed = MutableStateFlow(false)
+ val primaryBouncerScrimmed = _primaryBouncerScrimmed.asStateFlow()
/**
- * Set how much of the panel is showing on the screen.
+ * Set how much of the notification panel is showing on the screen.
* ```
* 0f = panel fully hidden = bouncer fully showing
* 1f = panel fully showing = bouncer fully hidden
@@ -49,84 +62,60 @@
*/
private val _panelExpansionAmount = MutableStateFlow(KeyguardBouncer.EXPANSION_HIDDEN)
val panelExpansionAmount = _panelExpansionAmount.asStateFlow()
- private val _isVisible = MutableStateFlow(false)
- val isVisible = _isVisible.asStateFlow()
- private val _show = MutableStateFlow<KeyguardBouncerModel?>(null)
- val show = _show.asStateFlow()
- private val _showingSoon = MutableStateFlow(false)
- val showingSoon = _showingSoon.asStateFlow()
- private val _hide = MutableStateFlow(false)
- val hide = _hide.asStateFlow()
- private val _startingToHide = MutableStateFlow(false)
- val startingToHide = _startingToHide.asStateFlow()
- private val _disappearAnimation = MutableStateFlow<Runnable?>(null)
- val startingDisappearAnimation = _disappearAnimation.asStateFlow()
private val _keyguardPosition = MutableStateFlow(0f)
val keyguardPosition = _keyguardPosition.asStateFlow()
- private val _resourceUpdateRequests = MutableStateFlow(false)
- val resourceUpdateRequests = _resourceUpdateRequests.asStateFlow()
- private val _showMessage = MutableStateFlow<BouncerShowMessageModel?>(null)
- val showMessage = _showMessage.asStateFlow()
+ private val _onScreenTurnedOff = MutableStateFlow(false)
+ val onScreenTurnedOff = _onScreenTurnedOff.asStateFlow()
+ private val _isBackButtonEnabled = MutableStateFlow<Boolean?>(null)
+ val isBackButtonEnabled = _isBackButtonEnabled.asStateFlow()
private val _keyguardAuthenticated = MutableStateFlow<Boolean?>(null)
/** Determines if user is already unlocked */
val keyguardAuthenticated = _keyguardAuthenticated.asStateFlow()
- private val _isBackButtonEnabled = MutableStateFlow<Boolean?>(null)
- val isBackButtonEnabled = _isBackButtonEnabled.asStateFlow()
- private val _onScreenTurnedOff = MutableStateFlow(false)
- val onScreenTurnedOff = _onScreenTurnedOff.asStateFlow()
-
+ private val _showMessage =
+ MutableSharedFlow<BouncerShowMessageModel?>(
+ replay = 1,
+ onBufferOverflow = BufferOverflow.DROP_OLDEST
+ )
+ val showMessage = _showMessage.asSharedFlow()
+ private val _resourceUpdateRequests = MutableStateFlow(false)
+ val resourceUpdateRequests = _resourceUpdateRequests.asStateFlow()
+ val bouncerPromptReason: Int
+ get() = viewMediatorCallback.bouncerPromptReason
val bouncerErrorMessage: CharSequence?
get() = viewMediatorCallback.consumeCustomMessage()
- init {
- val callback =
- object : KeyguardUpdateMonitorCallback() {
- override fun onStrongAuthStateChanged(userId: Int) {
- bouncerPromptReason = viewMediatorCallback.bouncerPromptReason
- }
-
- override fun onLockedOutStateChanged(type: BiometricSourceType) {
- if (type == BiometricSourceType.FINGERPRINT) {
- bouncerPromptReason = viewMediatorCallback.bouncerPromptReason
- }
- }
- }
-
- keyguardUpdateMonitor.registerCallback(callback)
+ fun setPrimaryScrimmed(isScrimmed: Boolean) {
+ _primaryBouncerScrimmed.value = isScrimmed
}
- fun setScrimmed(isScrimmed: Boolean) {
- _isScrimmed.value = isScrimmed
+ fun setPrimaryVisible(isVisible: Boolean) {
+ _primaryBouncerVisible.value = isVisible
+ }
+
+ fun setPrimaryShow(keyguardBouncerModel: KeyguardBouncerModel?) {
+ _primaryBouncerShow.value = keyguardBouncerModel
+ }
+
+ fun setPrimaryShowingSoon(showingSoon: Boolean) {
+ _primaryBouncerShowingSoon.value = showingSoon
+ }
+
+ fun setPrimaryHide(hide: Boolean) {
+ _primaryBouncerHide.value = hide
+ }
+
+ fun setPrimaryStartingToHide(startingToHide: Boolean) {
+ _primaryBouncerStartingToHide.value = startingToHide
+ }
+
+ fun setPrimaryStartDisappearAnimation(runnable: Runnable?) {
+ _primaryBouncerDisappearAnimation.value = runnable
}
fun setPanelExpansion(panelExpansion: Float) {
_panelExpansionAmount.value = panelExpansion
}
- fun setVisible(isVisible: Boolean) {
- _isVisible.value = isVisible
- }
-
- fun setShow(keyguardBouncerModel: KeyguardBouncerModel?) {
- _show.value = keyguardBouncerModel
- }
-
- fun setShowingSoon(showingSoon: Boolean) {
- _showingSoon.value = showingSoon
- }
-
- fun setHide(hide: Boolean) {
- _hide.value = hide
- }
-
- fun setStartingToHide(startingToHide: Boolean) {
- _startingToHide.value = startingToHide
- }
-
- fun setStartDisappearAnimation(runnable: Runnable?) {
- _disappearAnimation.value = runnable
- }
-
fun setKeyguardPosition(keyguardPosition: Float) {
_keyguardPosition.value = keyguardPosition
}
@@ -136,7 +125,7 @@
}
fun setShowMessage(bouncerShowMessageModel: BouncerShowMessageModel?) {
- _showMessage.value = bouncerShowMessageModel
+ _showMessage.tryEmit(bouncerShowMessageModel)
}
fun setKeyguardAuthenticated(keyguardAuthenticated: Boolean?) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index c867c6e..9d5d8bb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -16,6 +16,8 @@
package com.android.systemui.keyguard.data.repository
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.common.shared.model.Position
@@ -68,6 +70,9 @@
*/
val isKeyguardShowing: Flow<Boolean>
+ /** Observable for the signal that keyguard is about to go away. */
+ val isKeyguardGoingAway: Flow<Boolean>
+
/** Observable for whether the bouncer is showing. */
val isBouncerShowing: Flow<Boolean>
@@ -84,6 +89,14 @@
val isDozing: Flow<Boolean>
/**
+ * Observable for whether the device is dreaming.
+ *
+ * Dozing/AOD is a specific type of dream, but it is also possible for other non-systemui dreams
+ * to be active, such as screensavers.
+ */
+ val isDreaming: Flow<Boolean>
+
+ /**
* Observable for the amount of doze we are currently in.
*
* While in doze state, this amount can change - driving a cycle of animations designed to avoid
@@ -123,6 +136,11 @@
* Sets the relative offset of the lock-screen clock from its natural position on the screen.
*/
fun setClockPosition(x: Int, y: Int)
+
+ /**
+ * Returns whether the keyguard bottom area should be constrained to the top of the lock icon
+ */
+ fun isUdfpsSupported(): Boolean
}
/** Encapsulates application state for the keyguard. */
@@ -131,10 +149,11 @@
@Inject
constructor(
statusBarStateController: StatusBarStateController,
- private val keyguardStateController: KeyguardStateController,
dozeHost: DozeHost,
wakefulnessLifecycle: WakefulnessLifecycle,
biometricUnlockController: BiometricUnlockController,
+ private val keyguardStateController: KeyguardStateController,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
) : KeyguardRepository {
private val _animateBottomAreaDozingTransitions = MutableStateFlow(false)
override val animateBottomAreaDozingTransitions =
@@ -169,6 +188,29 @@
awaitClose { keyguardStateController.removeCallback(callback) }
}
+ override val isKeyguardGoingAway: Flow<Boolean> = conflatedCallbackFlow {
+ val callback =
+ object : KeyguardStateController.Callback {
+ override fun onKeyguardGoingAwayChanged() {
+ trySendWithFailureLogging(
+ keyguardStateController.isKeyguardGoingAway,
+ TAG,
+ "updated isKeyguardGoingAway"
+ )
+ }
+ }
+
+ keyguardStateController.addCallback(callback)
+ // Adding the callback does not send an initial update.
+ trySendWithFailureLogging(
+ keyguardStateController.isKeyguardGoingAway,
+ TAG,
+ "initial isKeyguardGoingAway"
+ )
+
+ awaitClose { keyguardStateController.removeCallback(callback) }
+ }
+
override val isBouncerShowing: Flow<Boolean> = conflatedCallbackFlow {
val callback =
object : KeyguardStateController.Callback {
@@ -211,6 +253,25 @@
}
.distinctUntilChanged()
+ override val isDreaming: Flow<Boolean> =
+ conflatedCallbackFlow {
+ val callback =
+ object : KeyguardUpdateMonitorCallback() {
+ override fun onDreamingStateChanged(isDreaming: Boolean) {
+ trySendWithFailureLogging(isDreaming, TAG, "updated isDreaming")
+ }
+ }
+ keyguardUpdateMonitor.registerCallback(callback)
+ trySendWithFailureLogging(
+ keyguardUpdateMonitor.isDreaming,
+ TAG,
+ "initial isDreaming",
+ )
+
+ awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
+ }
+ .distinctUntilChanged()
+
override val dozeAmount: Flow<Float> = conflatedCallbackFlow {
val callback =
object : StatusBarStateController.StateListener {
@@ -311,6 +372,8 @@
_clockPosition.value = Position(x, y)
}
+ override fun isUdfpsSupported(): Boolean = keyguardUpdateMonitor.isUdfpsSupported
+
private fun statusBarStateIntToObject(value: Int): StatusBarState {
return when (value) {
0 -> StatusBarState.SHADE
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
index e3d1a27..bce7d92 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
@@ -94,11 +94,13 @@
*/
private val _transitions =
MutableSharedFlow<TransitionStep>(
+ replay = 2,
extraBufferCapacity = 10,
- onBufferOverflow = BufferOverflow.DROP_OLDEST
+ onBufferOverflow = BufferOverflow.DROP_OLDEST,
)
override val transitions = _transitions.asSharedFlow().distinctUntilChanged()
private var lastStep: TransitionStep = TransitionStep()
+ private var lastAnimator: ValueAnimator? = null
/*
* When manual control of the transition is requested, a unique [UUID] is used as the handle
@@ -106,19 +108,39 @@
*/
private var updateTransitionId: UUID? = null
+ init {
+ // Seed with transitions signaling a boot into lockscreen state
+ emitTransition(
+ TransitionStep(
+ KeyguardState.OFF,
+ KeyguardState.LOCKSCREEN,
+ 0f,
+ TransitionState.STARTED,
+ )
+ )
+ emitTransition(
+ TransitionStep(
+ KeyguardState.OFF,
+ KeyguardState.LOCKSCREEN,
+ 1f,
+ TransitionState.FINISHED,
+ )
+ )
+ }
+
override fun startTransition(info: TransitionInfo): UUID? {
if (lastStep.transitionState != TransitionState.FINISHED) {
- // Open questions:
- // * Queue of transitions? buffer of 1?
- // * Are transitions cancellable if a new one is triggered?
- // * What validation does this need to do?
- Log.wtf(TAG, "Transition still active: $lastStep")
- return null
+ Log.i(TAG, "Transition still active: $lastStep, canceling")
}
+ val startingValue = 1f - lastStep.value
+ lastAnimator?.cancel()
+ lastAnimator = info.animator
+
info.animator?.let { animator ->
// An animator was provided, so use it to run the transition
- animator.setFloatValues(0f, 1f)
+ animator.setFloatValues(startingValue, 1f)
+ animator.duration = ((1f - startingValue) * animator.duration).toLong()
val updateListener =
object : AnimatorUpdateListener {
override fun onAnimationUpdate(animation: ValueAnimator) {
@@ -134,15 +156,24 @@
val adapter =
object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator) {
- emitTransition(TransitionStep(info, 0f, TransitionState.STARTED))
+ emitTransition(TransitionStep(info, startingValue, TransitionState.STARTED))
}
override fun onAnimationCancel(animation: Animator) {
- Log.i(TAG, "Cancelling transition: $info")
+ endAnimation(animation, lastStep.value, TransitionState.CANCELED)
}
override fun onAnimationEnd(animation: Animator) {
- emitTransition(TransitionStep(info, 1f, TransitionState.FINISHED))
+ endAnimation(animation, 1f, TransitionState.FINISHED)
+ }
+
+ private fun endAnimation(
+ animation: Animator,
+ value: Float,
+ state: TransitionState
+ ) {
+ emitTransition(TransitionStep(info, value, state))
animator.removeListener(this)
animator.removeUpdateListener(updateListener)
+ lastAnimator = null
}
}
animator.addListener(adapter)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
index 0aeff7f..e5521c7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
@@ -20,10 +20,11 @@
import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.keyguard.shared.model.WakefulnessModel.Companion.isSleepingOrStartingToSleep
+import com.android.systemui.keyguard.shared.model.WakefulnessModel.Companion.isWakingOrStartingToWake
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -35,18 +36,30 @@
@Inject
constructor(
@Application private val scope: CoroutineScope,
- private val keyguardRepository: KeyguardRepository,
+ private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionRepository: KeyguardTransitionRepository,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
) : TransitionInteractor("AOD<->LOCKSCREEN") {
override fun start() {
scope.launch {
- keyguardRepository.isDozing
- .sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
+ /*
+ * Listening to the startedKeyguardTransitionStep (last started step) allows this code
+ * to interrupt an active transition, as long as they were either going to LOCKSCREEN or
+ * AOD state. One example is when the user presses the power button in the middle of an
+ * active transition.
+ */
+ keyguardInteractor.wakefulnessState
+ .sample(
+ keyguardTransitionInteractor.startedKeyguardTransitionStep,
+ { a, b -> Pair(a, b) }
+ )
.collect { pair ->
- val (isDozing, keyguardState) = pair
- if (isDozing && keyguardState == KeyguardState.LOCKSCREEN) {
+ val (wakefulnessState, lastStartedStep) = pair
+ if (
+ isSleepingOrStartingToSleep(wakefulnessState) &&
+ lastStartedStep.to == KeyguardState.LOCKSCREEN
+ ) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
name,
@@ -55,7 +68,10 @@
getAnimator(),
)
)
- } else if (!isDozing && keyguardState == KeyguardState.AOD) {
+ } else if (
+ isWakingOrStartingToWake(wakefulnessState) &&
+ lastStartedStep.to == KeyguardState.AOD
+ ) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
name,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerToGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerToGoneTransitionInteractor.kt
new file mode 100644
index 0000000..dd29673
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerToGoneTransitionInteractor.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.animation.ValueAnimator
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.shade.data.repository.ShadeRepository
+import com.android.systemui.util.kotlin.sample
+import java.util.UUID
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+
+@SysUISingleton
+class BouncerToGoneTransitionInteractor
+@Inject
+constructor(
+ @Application private val scope: CoroutineScope,
+ private val keyguardInteractor: KeyguardInteractor,
+ private val shadeRepository: ShadeRepository,
+ private val keyguardTransitionRepository: KeyguardTransitionRepository,
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor
+) : TransitionInteractor("BOUNCER->GONE") {
+
+ private var transitionId: UUID? = null
+
+ override fun start() {
+ listenForKeyguardGoingAway()
+ }
+
+ private fun listenForKeyguardGoingAway() {
+ scope.launch {
+ keyguardInteractor.isKeyguardGoingAway
+ .sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
+ .collect { pair ->
+ val (isKeyguardGoingAway, keyguardState) = pair
+ if (isKeyguardGoingAway && keyguardState == KeyguardState.BOUNCER) {
+ keyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ ownerName = name,
+ from = KeyguardState.BOUNCER,
+ to = KeyguardState.GONE,
+ animator = getAnimator(),
+ )
+ )
+ }
+ }
+ }
+ }
+
+ private fun getAnimator(): ValueAnimator {
+ return ValueAnimator().apply {
+ setInterpolator(Interpolators.LINEAR)
+ setDuration(TRANSITION_DURATION_MS)
+ }
+ }
+
+ companion object {
+ private const val TRANSITION_DURATION_MS = 300L
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingLockscreenTransitionInteractor.kt
new file mode 100644
index 0000000..c44cda4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingLockscreenTransitionInteractor.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.animation.ValueAnimator
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+
+@SysUISingleton
+class DreamingLockscreenTransitionInteractor
+@Inject
+constructor(
+ @Application private val scope: CoroutineScope,
+ private val keyguardInteractor: KeyguardInteractor,
+ private val keyguardTransitionRepository: KeyguardTransitionRepository,
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+) : TransitionInteractor("DREAMING<->LOCKSCREEN") {
+
+ override fun start() {
+ scope.launch {
+ keyguardInteractor.isDreaming
+ .sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
+ .collect { pair ->
+ val (isDreaming, keyguardState) = pair
+ if (isDreaming && keyguardState == KeyguardState.LOCKSCREEN) {
+ keyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ name,
+ KeyguardState.LOCKSCREEN,
+ KeyguardState.DREAMING,
+ getAnimator(),
+ )
+ )
+ } else if (!isDreaming && keyguardState == KeyguardState.DREAMING) {
+ keyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ name,
+ KeyguardState.DREAMING,
+ KeyguardState.LOCKSCREEN,
+ getAnimator(),
+ )
+ )
+ }
+ }
+ }
+ }
+
+ private fun getAnimator(): ValueAnimator {
+ return ValueAnimator().apply {
+ setInterpolator(Interpolators.LINEAR)
+ setDuration(TRANSITION_DURATION_MS)
+ }
+ }
+
+ companion object {
+ private const val TRANSITION_DURATION_MS = 500L
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingToAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingToAodTransitionInteractor.kt
new file mode 100644
index 0000000..9e2b724
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingToAodTransitionInteractor.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.animation.ValueAnimator
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.keyguard.shared.model.WakefulnessModel.Companion.isSleepingOrStartingToSleep
+import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+
+@SysUISingleton
+class DreamingToAodTransitionInteractor
+@Inject
+constructor(
+ @Application private val scope: CoroutineScope,
+ private val keyguardInteractor: KeyguardInteractor,
+ private val keyguardTransitionRepository: KeyguardTransitionRepository,
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+) : TransitionInteractor("DREAMING->AOD") {
+
+ override fun start() {
+ scope.launch {
+ keyguardInteractor.wakefulnessState
+ .sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
+ .collect { pair ->
+ val (wakefulnessState, keyguardState) = pair
+ if (
+ isSleepingOrStartingToSleep(wakefulnessState) &&
+ keyguardState == KeyguardState.DREAMING
+ ) {
+ keyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ name,
+ KeyguardState.DREAMING,
+ KeyguardState.AOD,
+ getAnimator(),
+ )
+ )
+ }
+ }
+ }
+ }
+
+ private fun getAnimator(): ValueAnimator {
+ return ValueAnimator().apply {
+ setInterpolator(Interpolators.LINEAR)
+ setDuration(TRANSITION_DURATION_MS)
+ }
+ }
+
+ companion object {
+ private const val TRANSITION_DURATION_MS = 300L
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractor.kt
index ede50b0..d2a7486 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractor.kt
@@ -48,4 +48,9 @@
fun setAnimateDozingTransitions(animate: Boolean) {
repository.setAnimateDozingTransitions(animate)
}
+
+ /**
+ * Returns whether the keyguard bottom area should be constrained to the top of the lock icon
+ */
+ fun shouldConstrainToTopOfLockIcon(): Boolean = repository.isUdfpsSupported()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 614ff8d..5a1c702 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -41,8 +41,15 @@
val dozeAmount: Flow<Float> = repository.dozeAmount
/** Whether the system is in doze mode. */
val isDozing: Flow<Boolean> = repository.isDozing
+ /**
+ * Whether the system is dreaming. [isDreaming] will be always be true when [isDozing] is true,
+ * but not vice-versa.
+ */
+ val isDreaming: Flow<Boolean> = repository.isDreaming
/** Whether the keyguard is showing or not. */
val isKeyguardShowing: Flow<Boolean> = repository.isKeyguardShowing
+ /** Whether the keyguard is going away. */
+ val isKeyguardGoingAway: Flow<Boolean> = repository.isKeyguardGoingAway
/** Whether the bouncer is showing or not. */
val isBouncerShowing: Flow<Boolean> = repository.isBouncerShowing
/** The device wake/sleep state */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
index 57fb4a1..58a8093 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
@@ -41,12 +41,24 @@
}
scope.launch {
+ keyguardInteractor.isBouncerShowing.collect { logger.v("Bouncer showing", it) }
+ }
+
+ scope.launch { keyguardInteractor.isDozing.collect { logger.v("isDozing", it) } }
+
+ scope.launch {
interactor.finishedKeyguardTransitionStep.collect {
logger.i("Finished transition", it)
}
}
scope.launch {
+ interactor.canceledKeyguardTransitionStep.collect {
+ logger.i("Canceled transition", it)
+ }
+ }
+
+ scope.launch {
interactor.startedKeyguardTransitionStep.collect { logger.i("Started transition", it) }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
index a7c6d44..43dd358e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
@@ -42,6 +42,9 @@
is GoneAodTransitionInteractor -> Log.d(TAG, "Started $it")
is LockscreenGoneTransitionInteractor -> Log.d(TAG, "Started $it")
is AodToGoneTransitionInteractor -> Log.d(TAG, "Started $it")
+ is BouncerToGoneTransitionInteractor -> Log.d(TAG, "Started $it")
+ is DreamingLockscreenTransitionInteractor -> Log.d(TAG, "Started $it")
+ is DreamingToAodTransitionInteractor -> Log.d(TAG, "Started $it")
}
it.start()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 749183e..54a4f49 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -57,6 +57,14 @@
lockscreenToAodTransition,
)
+ /* The last [TransitionStep] with a [TransitionState] of STARTED */
+ val startedKeyguardTransitionStep: Flow<TransitionStep> =
+ repository.transitions.filter { step -> step.transitionState == TransitionState.STARTED }
+
+ /* The last [TransitionStep] with a [TransitionState] of CANCELED */
+ val canceledKeyguardTransitionStep: Flow<TransitionStep> =
+ repository.transitions.filter { step -> step.transitionState == TransitionState.CANCELED }
+
/* The last [TransitionStep] with a [TransitionState] of FINISHED */
val finishedKeyguardTransitionStep: Flow<TransitionStep> =
repository.transitions.filter { step -> step.transitionState == TransitionState.FINISHED }
@@ -64,8 +72,4 @@
/* The last completed [KeyguardState] transition */
val finishedKeyguardState: Flow<KeyguardState> =
finishedKeyguardTransitionStep.map { step -> step.to }
-
- /* The last [TransitionStep] with a [TransitionState] of STARTED */
- val startedKeyguardTransitionStep: Flow<TransitionStep> =
- repository.transitions.filter { step -> step.transitionState == TransitionState.STARTED }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
index fd4814d..cca2d56 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
@@ -56,10 +56,20 @@
private fun listenForBouncerHiding() {
scope.launch {
keyguardInteractor.isBouncerShowing
- .sample(keyguardInteractor.wakefulnessState, { a, b -> Pair(a, b) })
- .collect { pair ->
- val (isBouncerShowing, wakefulnessState) = pair
- if (!isBouncerShowing) {
+ .sample(
+ combine(
+ keyguardInteractor.wakefulnessState,
+ keyguardTransitionInteractor.startedKeyguardTransitionStep,
+ ) { a, b ->
+ Pair(a, b)
+ },
+ { a, bc -> Triple(a, bc.first, bc.second) }
+ )
+ .collect { triple ->
+ val (isBouncerShowing, wakefulnessState, lastStartedTransitionStep) = triple
+ if (
+ !isBouncerShowing && lastStartedTransitionStep.to == KeyguardState.BOUNCER
+ ) {
val to =
if (
wakefulnessState == WakefulnessModel.STARTING_TO_SLEEP ||
@@ -90,10 +100,10 @@
combine(
keyguardTransitionInteractor.finishedKeyguardState,
keyguardInteractor.statusBarState,
- ) { keyguardState, statusBarState ->
- Pair(keyguardState, statusBarState)
+ ) { a, b ->
+ Pair(a, b)
},
- { shadeModel, pair -> Triple(shadeModel, pair.first, pair.second) }
+ { a, bc -> Triple(a, bc.first, bc.second) }
)
.collect { triple ->
val (shadeModel, keyguardState, statusBarState) = triple
@@ -116,8 +126,7 @@
)
} else {
// TODO (b/251849525): Remove statusbarstate check when that state is
- // integrated
- // into KeyguardTransitionRepository
+ // integrated into KeyguardTransitionRepository
if (
keyguardState == KeyguardState.LOCKSCREEN &&
shadeModel.isUserDragging &&
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenGoneTransitionInteractor.kt
index 6c1adbd..4100f7a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenGoneTransitionInteractor.kt
@@ -23,6 +23,7 @@
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collect
@@ -34,23 +35,27 @@
constructor(
@Application private val scope: CoroutineScope,
private val keyguardInteractor: KeyguardInteractor,
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val keyguardTransitionRepository: KeyguardTransitionRepository,
) : TransitionInteractor("LOCKSCREEN->GONE") {
override fun start() {
scope.launch {
- keyguardInteractor.isKeyguardShowing.collect { isShowing ->
- if (!isShowing) {
- keyguardTransitionRepository.startTransition(
- TransitionInfo(
- name,
- KeyguardState.LOCKSCREEN,
- KeyguardState.GONE,
- getAnimator(),
+ keyguardInteractor.isKeyguardGoingAway
+ .sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
+ .collect { pair ->
+ val (isKeyguardGoingAway, keyguardState) = pair
+ if (!isKeyguardGoingAway && keyguardState == KeyguardState.LOCKSCREEN) {
+ keyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ name,
+ KeyguardState.LOCKSCREEN,
+ KeyguardState.GONE,
+ getAnimator(),
+ )
)
- )
+ }
}
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractor.kt
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractor.kt
rename to packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractor.kt
index 10c7a37..c5e49c6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractor.kt
@@ -24,9 +24,9 @@
/** Interactor to add and remove callbacks for the bouncer. */
@SysUISingleton
-class BouncerCallbackInteractor @Inject constructor() {
+class PrimaryBouncerCallbackInteractor @Inject constructor() {
private var resetCallbacks = ListenerSet<KeyguardBouncer.KeyguardResetCallback>()
- private var expansionCallbacks = ArrayList<KeyguardBouncer.BouncerExpansionCallback>()
+ private var expansionCallbacks = ArrayList<KeyguardBouncer.PrimaryBouncerExpansionCallback>()
/** Add a KeyguardResetCallback. */
fun addKeyguardResetCallback(callback: KeyguardBouncer.KeyguardResetCallback) {
resetCallbacks.addIfAbsent(callback)
@@ -38,7 +38,7 @@
}
/** Adds a callback to listen to bouncer expansion updates. */
- fun addBouncerExpansionCallback(callback: KeyguardBouncer.BouncerExpansionCallback) {
+ fun addBouncerExpansionCallback(callback: KeyguardBouncer.PrimaryBouncerExpansionCallback) {
if (!expansionCallbacks.contains(callback)) {
expansionCallbacks.add(callback)
}
@@ -48,7 +48,7 @@
* Removes a previously added callback. If the callback was never added, this method does
* nothing.
*/
- fun removeBouncerExpansionCallback(callback: KeyguardBouncer.BouncerExpansionCallback) {
+ fun removeBouncerExpansionCallback(callback: KeyguardBouncer.PrimaryBouncerExpansionCallback) {
expansionCallbacks.remove(callback)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
similarity index 75%
rename from packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractor.kt
rename to packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
index dbb0352..910cdf2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
@@ -44,24 +44,27 @@
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
-/** Encapsulates business logic for interacting with the lock-screen bouncer. */
+/**
+ * Encapsulates business logic for interacting with the lock-screen primary (pin/pattern/password)
+ * bouncer.
+ */
@SysUISingleton
-class BouncerInteractor
+class PrimaryBouncerInteractor
@Inject
constructor(
private val repository: KeyguardBouncerRepository,
- private val bouncerView: BouncerView,
+ private val primaryBouncerView: BouncerView,
@Main private val mainHandler: Handler,
private val keyguardStateController: KeyguardStateController,
private val keyguardSecurityModel: KeyguardSecurityModel,
- private val callbackInteractor: BouncerCallbackInteractor,
+ private val primaryBouncerCallbackInteractor: PrimaryBouncerCallbackInteractor,
private val falsingCollector: FalsingCollector,
private val dismissCallbackRegistry: DismissCallbackRegistry,
keyguardBypassController: KeyguardBypassController,
keyguardUpdateMonitor: KeyguardUpdateMonitor,
) {
/** Whether we want to wait for face auth. */
- private val bouncerFaceDelay =
+ private val primaryBouncerFaceDelay =
keyguardStateController.isFaceAuthEnabled &&
!keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
KeyguardUpdateMonitor.getCurrentUser()
@@ -70,38 +73,39 @@
!keyguardUpdateMonitor.userNeedsStrongAuth() &&
!keyguardBypassController.bypassEnabled
- /** Runnable to show the bouncer. */
+ /** Runnable to show the primary bouncer. */
val showRunnable = Runnable {
- repository.setVisible(true)
- repository.setShow(
+ repository.setPrimaryVisible(true)
+ repository.setPrimaryShow(
KeyguardBouncerModel(
promptReason = repository.bouncerPromptReason ?: 0,
errorMessage = repository.bouncerErrorMessage,
expansionAmount = repository.panelExpansionAmount.value
)
)
- repository.setShowingSoon(false)
+ repository.setPrimaryShowingSoon(false)
}
val keyguardAuthenticated: Flow<Boolean> = repository.keyguardAuthenticated.filterNotNull()
val screenTurnedOff: Flow<Unit> = repository.onScreenTurnedOff.filter { it }.map {}
- val show: Flow<KeyguardBouncerModel> = repository.show.filterNotNull()
- val hide: Flow<Unit> = repository.hide.filter { it }.map {}
- val startingToHide: Flow<Unit> = repository.startingToHide.filter { it }.map {}
- val isVisible: Flow<Boolean> = repository.isVisible
+ val show: Flow<KeyguardBouncerModel> = repository.primaryBouncerShow.filterNotNull()
+ val hide: Flow<Unit> = repository.primaryBouncerHide.filter { it }.map {}
+ val startingToHide: Flow<Unit> = repository.primaryBouncerStartingToHide.filter { it }.map {}
+ val isVisible: Flow<Boolean> = repository.primaryBouncerVisible
val isBackButtonEnabled: Flow<Boolean> = repository.isBackButtonEnabled.filterNotNull()
val showMessage: Flow<BouncerShowMessageModel> = repository.showMessage.filterNotNull()
val startingDisappearAnimation: Flow<Runnable> =
- repository.startingDisappearAnimation.filterNotNull()
+ repository.primaryBouncerStartingDisappearAnimation.filterNotNull()
val resourceUpdateRequests: Flow<Boolean> = repository.resourceUpdateRequests.filter { it }
val keyguardPosition: Flow<Float> = repository.keyguardPosition
val panelExpansionAmount: Flow<Float> = repository.panelExpansionAmount
/** 0f = bouncer fully hidden. 1f = bouncer fully visible. */
- val bouncerExpansion: Flow<Float> = //
- combine(repository.panelExpansionAmount, repository.isVisible) { expansionAmount, isVisible
- ->
- if (isVisible) {
- 1f - expansionAmount
+ val bouncerExpansion: Flow<Float> =
+ combine(repository.panelExpansionAmount, repository.primaryBouncerVisible) {
+ panelExpansion,
+ primaryBouncerVisible ->
+ if (primaryBouncerVisible) {
+ 1f - panelExpansion
} else {
0f
}
@@ -113,16 +117,16 @@
@JvmOverloads
fun show(isScrimmed: Boolean) {
// Reset some states as we show the bouncer.
- repository.setShowMessage(null)
repository.setOnScreenTurnedOff(false)
repository.setKeyguardAuthenticated(null)
- repository.setHide(false)
- repository.setStartingToHide(false)
+ repository.setPrimaryHide(false)
+ repository.setPrimaryStartingToHide(false)
val resumeBouncer =
- (repository.isVisible.value || repository.showingSoon.value) && needsFullscreenBouncer()
+ (repository.primaryBouncerVisible.value ||
+ repository.primaryBouncerShowingSoon.value) && needsFullscreenBouncer()
- if (!resumeBouncer && repository.show.value != null) {
+ if (!resumeBouncer && repository.primaryBouncerShow.value != null) {
// If bouncer is visible, the bouncer is already showing.
return
}
@@ -134,29 +138,29 @@
}
Trace.beginSection("KeyguardBouncer#show")
- repository.setScrimmed(isScrimmed)
+ repository.setPrimaryScrimmed(isScrimmed)
if (isScrimmed) {
setPanelExpansion(KeyguardBouncer.EXPANSION_VISIBLE)
}
if (resumeBouncer) {
- bouncerView.delegate?.resume()
+ primaryBouncerView.delegate?.resume()
// Bouncer is showing the next security screen and we just need to prompt a resume.
return
}
- if (bouncerView.delegate?.showNextSecurityScreenOrFinish() == true) {
+ if (primaryBouncerView.delegate?.showNextSecurityScreenOrFinish() == true) {
// Keyguard is done.
return
}
- repository.setShowingSoon(true)
- if (bouncerFaceDelay) {
+ repository.setPrimaryShowingSoon(true)
+ if (primaryBouncerFaceDelay) {
mainHandler.postDelayed(showRunnable, 1200L)
} else {
DejankUtils.postAfterTraversal(showRunnable)
}
keyguardStateController.notifyBouncerShowing(true)
- callbackInteractor.dispatchStartingToShow()
+ primaryBouncerCallbackInteractor.dispatchStartingToShow()
Trace.endSection()
}
@@ -174,10 +178,10 @@
falsingCollector.onBouncerHidden()
keyguardStateController.notifyBouncerShowing(false /* showing */)
cancelShowRunnable()
- repository.setShowingSoon(false)
- repository.setVisible(false)
- repository.setHide(true)
- repository.setShow(null)
+ repository.setPrimaryShowingSoon(false)
+ repository.setPrimaryVisible(false)
+ repository.setPrimaryHide(true)
+ repository.setPrimaryShow(null)
Trace.endSection()
}
@@ -191,7 +195,7 @@
fun setPanelExpansion(expansion: Float) {
val oldExpansion = repository.panelExpansionAmount.value
val expansionChanged = oldExpansion != expansion
- if (repository.startingDisappearAnimation.value == null) {
+ if (repository.primaryBouncerStartingDisappearAnimation.value == null) {
repository.setPanelExpansion(expansion)
}
@@ -200,25 +204,28 @@
oldExpansion != KeyguardBouncer.EXPANSION_VISIBLE
) {
falsingCollector.onBouncerShown()
- callbackInteractor.dispatchFullyShown()
+ primaryBouncerCallbackInteractor.dispatchFullyShown()
} else if (
expansion == KeyguardBouncer.EXPANSION_HIDDEN &&
oldExpansion != KeyguardBouncer.EXPANSION_HIDDEN
) {
- repository.setVisible(false)
- repository.setShow(null)
- falsingCollector.onBouncerHidden()
- DejankUtils.postAfterTraversal { callbackInteractor.dispatchReset() }
- callbackInteractor.dispatchFullyHidden()
+ /*
+ * There are cases where #hide() was not invoked, such as when
+ * NotificationPanelViewController controls the hide animation. Make sure the state gets
+ * updated by calling #hide() directly.
+ */
+ hide()
+ DejankUtils.postAfterTraversal { primaryBouncerCallbackInteractor.dispatchReset() }
+ primaryBouncerCallbackInteractor.dispatchFullyHidden()
} else if (
expansion != KeyguardBouncer.EXPANSION_VISIBLE &&
oldExpansion == KeyguardBouncer.EXPANSION_VISIBLE
) {
- callbackInteractor.dispatchStartingToHide()
- repository.setStartingToHide(true)
+ primaryBouncerCallbackInteractor.dispatchStartingToHide()
+ repository.setPrimaryStartingToHide(true)
}
if (expansionChanged) {
- callbackInteractor.dispatchExpansionChanged(expansion)
+ primaryBouncerCallbackInteractor.dispatchExpansionChanged(expansion)
}
}
@@ -236,7 +243,7 @@
onDismissAction: ActivityStarter.OnDismissAction?,
cancelAction: Runnable?
) {
- bouncerView.delegate?.setDismissAction(onDismissAction, cancelAction)
+ primaryBouncerView.delegate?.setDismissAction(onDismissAction, cancelAction)
}
/** Update the resources of the views. */
@@ -266,7 +273,7 @@
/** Notify that view visibility has changed. */
fun notifyBouncerVisibilityHasChanged(visibility: Int) {
- callbackInteractor.dispatchVisibilityChanged(visibility)
+ primaryBouncerCallbackInteractor.dispatchVisibilityChanged(visibility)
}
/** Notify that the resources have been updated */
@@ -283,38 +290,39 @@
fun startDisappearAnimation(runnable: Runnable) {
val finishRunnable = Runnable {
runnable.run()
- repository.setStartDisappearAnimation(null)
+ repository.setPrimaryStartDisappearAnimation(null)
}
- repository.setStartDisappearAnimation(finishRunnable)
+ repository.setPrimaryStartDisappearAnimation(finishRunnable)
}
/** Returns whether bouncer is fully showing. */
fun isFullyShowing(): Boolean {
- return (repository.showingSoon.value || repository.isVisible.value) &&
+ return (repository.primaryBouncerShowingSoon.value ||
+ repository.primaryBouncerVisible.value) &&
repository.panelExpansionAmount.value == KeyguardBouncer.EXPANSION_VISIBLE &&
- repository.startingDisappearAnimation.value == null
+ repository.primaryBouncerStartingDisappearAnimation.value == null
}
/** Returns whether bouncer is scrimmed. */
fun isScrimmed(): Boolean {
- return repository.isScrimmed.value
+ return repository.primaryBouncerScrimmed.value
}
/** If bouncer expansion is between 0f and 1f non-inclusive. */
fun isInTransit(): Boolean {
- return repository.showingSoon.value ||
+ return repository.primaryBouncerShowingSoon.value ||
repository.panelExpansionAmount.value != KeyguardBouncer.EXPANSION_HIDDEN &&
repository.panelExpansionAmount.value != KeyguardBouncer.EXPANSION_VISIBLE
}
/** Return whether bouncer is animating away. */
fun isAnimatingAway(): Boolean {
- return repository.startingDisappearAnimation.value != null
+ return repository.primaryBouncerStartingDisappearAnimation.value != null
}
/** Return whether bouncer will dismiss with actions */
fun willDismissWithAction(): Boolean {
- return bouncerView.delegate?.willDismissWithActions() == true
+ return primaryBouncerView.delegate?.willDismissWithActions() == true
}
/** Returns whether the bouncer should be full screen. */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
index 37f33af..dbffeab 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
@@ -46,5 +46,19 @@
@Binds
@IntoSet
+ abstract fun bouncerGone(impl: BouncerToGoneTransitionInteractor): TransitionInteractor
+
+ @Binds
+ @IntoSet
abstract fun lockscreenGone(impl: LockscreenGoneTransitionInteractor): TransitionInteractor
+
+ @Binds
+ @IntoSet
+ abstract fun dreamingLockscreen(
+ impl: DreamingLockscreenTransitionInteractor
+ ): TransitionInteractor
+
+ @Binds
+ @IntoSet
+ abstract fun dreamingToAod(impl: DreamingToAodTransitionInteractor): TransitionInteractor
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
index 7958033..dd908c4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
@@ -17,12 +17,29 @@
/** List of all possible states to transition to/from */
enum class KeyguardState {
- /**
- * For initialization as well as when the security method is set to NONE, indicating that
- * the keyguard should never be shown.
+ /*
+ * The display is completely off, as well as any sensors that would trigger the device to wake
+ * up.
*/
- NONE,
- /* Always-on Display. The device is in a low-power mode with a minimal UI visible */
+ OFF,
+ /**
+ * The device has entered a special low-power mode within SystemUI. Doze is technically a
+ * special dream service implementation. No UI is visible. In this state, a least some
+ * low-powered sensors such as lift to wake or tap to wake are enabled, or wake screen for
+ * notifications is enabled, allowing the device to quickly wake up.
+ */
+ DOZING,
+ /*
+ * A device state after the device times out, which can be from both LOCKSCREEN or GONE states.
+ * DOZING is an example of special version of this state. Dreams may be implemented by third
+ * parties to present their own UI over keyguard, like a screensaver.
+ */
+ DREAMING,
+ /**
+ * The device has entered a special low-power mode within SystemUI, also called the Always-on
+ * Display (AOD). A minimal UI is presented to show critical information. If the device is in
+ * low-power mode without a UI, then it is DOZING.
+ */
AOD,
/*
* The security screen prompt UI, containing PIN, Password, Pattern, and all FPS
@@ -34,7 +51,6 @@
* unlocked if SWIPE security method is used, or if face lockscreen bypass is false.
*/
LOCKSCREEN,
-
/*
* Keyguard is no longer visible. In most cases the user has just authenticated and keyguard
* is being removed, but there are other cases where the user is swiping away keyguard, such as
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt
index 0e0465b..38a93b5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt
@@ -17,7 +17,12 @@
/** Possible states for a running transition between [State] */
enum class TransitionState {
+ /* Transition has begun. */
STARTED,
+ /* Transition is actively running. */
RUNNING,
- FINISHED
+ /* Transition has completed successfully. */
+ FINISHED,
+ /* Transition has been interrupted, and not completed successfully. */
+ CANCELED,
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
index 732a6f7..767fd58 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
@@ -17,8 +17,8 @@
/** This information will flow from the [KeyguardTransitionRepository] to control the UI layer */
data class TransitionStep(
- val from: KeyguardState = KeyguardState.NONE,
- val to: KeyguardState = KeyguardState.NONE,
+ val from: KeyguardState = KeyguardState.OFF,
+ val to: KeyguardState = KeyguardState.OFF,
val value: Float = 0f, // constrained [0.0, 1.0]
val transitionState: TransitionState = TransitionState.FINISHED,
val ownerName: String = "",
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt
index 64f834d..92040f4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt
@@ -24,5 +24,15 @@
/** Device is now fully awake and interactive. */
AWAKE,
/** Signal that the device is now going to sleep. */
- STARTING_TO_SLEEP,
+ STARTING_TO_SLEEP;
+
+ companion object {
+ fun isSleepingOrStartingToSleep(model: WakefulnessModel): Boolean {
+ return model == ASLEEP || model == STARTING_TO_SLEEP
+ }
+
+ fun isWakingOrStartingToWake(model: WakefulnessModel): Boolean {
+ return model == AWAKE || model == STARTING_TO_WAKE
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index 2c99ca5..3276b6d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -27,6 +27,8 @@
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.LockIconViewController
import com.android.settingslib.Utils
import com.android.systemui.R
import com.android.systemui.animation.Expandable
@@ -69,6 +71,11 @@
/** Notifies that device configuration has changed. */
fun onConfigurationChanged()
+
+ /**
+ * Returns whether the keyguard bottom area should be constrained to the top of the lock icon
+ */
+ fun shouldConstrainToTopOfLockIcon(): Boolean
}
/** Binds the view to the view-model, continuing to update the former based on the latter. */
@@ -208,6 +215,9 @@
override fun onConfigurationChanged() {
configurationBasedDimensions.value = loadFromResources(view)
}
+
+ override fun shouldConstrainToTopOfLockIcon(): Boolean =
+ viewModel.shouldConstrainToTopOfLockIcon()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
index a22958b..7739a45 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
@@ -94,6 +94,10 @@
viewModel.setBouncerViewDelegate(delegate)
launch {
viewModel.show.collect {
+ hostViewController.showPromptReason(it.promptReason)
+ it.errorMessage?.let { errorMessage ->
+ hostViewController.showErrorMessage(errorMessage)
+ }
hostViewController.showPrimarySecurityScreen()
hostViewController.appear(
SystemBarUtils.getStatusBarHeight(view.context)
@@ -102,18 +106,6 @@
}
launch {
- viewModel.showPromptReason.collect { prompt ->
- hostViewController.showPromptReason(prompt)
- }
- }
-
- launch {
- viewModel.showBouncerErrorMessage.collect { errorMessage ->
- hostViewController.showErrorMessage(errorMessage)
- }
- }
-
- launch {
viewModel.showWithFullExpansion.collect { model ->
hostViewController.resetSecurityContainer()
hostViewController.showPromptReason(model.promptReason)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
index b6b2304..227796f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
@@ -90,6 +90,12 @@
.distinctUntilChanged()
}
+ /**
+ * Returns whether the keyguard bottom area should be constrained to the top of the lock icon
+ */
+ fun shouldConstrainToTopOfLockIcon(): Boolean =
+ bottomAreaInteractor.shouldConstrainToTopOfLockIcon()
+
private fun button(
position: KeyguardQuickAffordancePosition
): Flow<KeyguardQuickAffordanceViewModel> {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
index 9a92843..526ae74 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
@@ -19,14 +19,13 @@
import android.view.View
import com.android.systemui.keyguard.data.BouncerView
import com.android.systemui.keyguard.data.BouncerViewDelegate
-import com.android.systemui.keyguard.domain.interactor.BouncerInteractor
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_VISIBLE
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
/** Models UI state for the lock screen bouncer; handles user input. */
@@ -34,7 +33,7 @@
@Inject
constructor(
private val view: BouncerView,
- private val interactor: BouncerInteractor,
+ private val interactor: PrimaryBouncerInteractor,
) {
/** Observe on bouncer expansion amount. */
val bouncerExpansionAmount: Flow<Float> = interactor.panelExpansionAmount
@@ -45,13 +44,6 @@
/** Observe whether bouncer is showing. */
val show: Flow<KeyguardBouncerModel> = interactor.show
- /** Observe bouncer prompt when bouncer is showing. */
- val showPromptReason: Flow<Int> = interactor.show.map { it.promptReason }
-
- /** Observe bouncer error message when bouncer is showing. */
- val showBouncerErrorMessage: Flow<CharSequence> =
- interactor.show.map { it.errorMessage }.filterNotNull()
-
/** Observe visible expansion when bouncer is showing. */
val showWithFullExpansion: Flow<KeyguardBouncerModel> =
interactor.show.filter { it.expansionAmount == EXPANSION_VISIBLE }
diff --git a/core/java/com/android/internal/app/LogAccessDialogActivity.java b/packages/SystemUI/src/com/android/systemui/logcat/LogAccessDialogActivity.java
similarity index 88%
rename from core/java/com/android/internal/app/LogAccessDialogActivity.java
rename to packages/SystemUI/src/com/android/systemui/logcat/LogAccessDialogActivity.java
index 4adb867..a88a4ca 100644
--- a/core/java/com/android/internal/app/LogAccessDialogActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/logcat/LogAccessDialogActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.app;
+package com.android.systemui.logcat;
import android.annotation.StyleRes;
import android.app.Activity;
@@ -23,7 +23,6 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -43,7 +42,9 @@
import android.widget.Button;
import android.widget.TextView;
-import com.android.internal.R;
+import com.android.internal.app.ILogAccessDialogCallback;
+import com.android.systemui.R;
+
/**
* Dialog responsible for obtaining user consent per-use log access
@@ -93,10 +94,7 @@
mAlertLearnMore = getResources().getString(R.string.log_access_confirmation_learn_more);
// create View
- boolean isDarkTheme = (getResources().getConfiguration().uiMode
- & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
- int themeId = isDarkTheme ? android.R.style.Theme_DeviceDefault_Dialog_Alert :
- android.R.style.Theme_DeviceDefault_Light_Dialog_Alert;
+ int themeId = R.style.LogAccessDialogTheme;
mAlertView = createView(themeId);
// create AlertDialog
@@ -177,8 +175,7 @@
PackageManager.MATCH_DIRECT_BOOT_AUTO,
UserHandle.getUserId(uid)).loadLabel(pm);
- String titleString = context.getString(
- com.android.internal.R.string.log_access_confirmation_title, appLabel);
+ String titleString = context.getString(R.string.log_access_confirmation_title, appLabel);
return titleString;
}
@@ -235,15 +232,12 @@
@Override
public void onClick(View view) {
try {
- switch (view.getId()) {
- case R.id.log_access_dialog_allow_button:
- mCallback.approveAccessForClient(mUid, mPackageName);
- finish();
- break;
- case R.id.log_access_dialog_deny_button:
- declineLogAccess();
- finish();
- break;
+ if (view.getId() == R.id.log_access_dialog_allow_button) {
+ mCallback.approveAccessForClient(mUid, mPackageName);
+ finish();
+ } else if (view.getId() == R.id.log_access_dialog_allow_button) {
+ declineLogAccess();
+ finish();
}
} catch (RemoteException e) {
finish();
diff --git a/packages/SystemUI/src/com/android/systemui/logcat/OWNERS b/packages/SystemUI/src/com/android/systemui/logcat/OWNERS
new file mode 100644
index 0000000..9c0c414
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/logcat/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 1218649
+file:platform/frameworks/base:/services/core/java/com/android/server/logcat/OWNERS
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
index be357ee..ceb4845 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
@@ -79,8 +79,7 @@
val queryIntent = Intent(Intent.ACTION_MAIN).apply { addCategory(Intent.CATEGORY_LAUNCHER) }
intent.putExtra(Intent.EXTRA_INTENT, queryIntent)
- // TODO(b/240939253): update copies
- val title = getString(R.string.media_projection_dialog_service_title)
+ val title = getString(R.string.media_projection_permission_app_selector_title)
intent.putExtra(Intent.EXTRA_TITLE, title)
super.onCreate(bundle)
controller.init()
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
index 397bffc..22f91f3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
@@ -18,6 +18,9 @@
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+import static com.android.systemui.screenrecord.ScreenShareOptionKt.ENTIRE_SCREEN;
+import static com.android.systemui.screenrecord.ScreenShareOptionKt.SINGLE_APP;
+
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
@@ -44,6 +47,8 @@
import com.android.systemui.R;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.screenrecord.MediaProjectionPermissionDialog;
+import com.android.systemui.screenrecord.ScreenShareOption;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.util.Utils;
@@ -102,7 +107,9 @@
CharSequence dialogText = null;
CharSequence dialogTitle = null;
+ String appName = null;
if (Utils.isHeadlessRemoteDisplayProvider(packageManager, mPackageName)) {
+ // TODO(b/253438807): handle special app name
dialogText = getString(R.string.media_projection_dialog_service_text);
dialogTitle = getString(R.string.media_projection_dialog_service_title);
} else {
@@ -132,7 +139,7 @@
String unsanitizedAppName = TextUtils.ellipsize(label,
paint, MAX_APP_NAME_SIZE_PX, TextUtils.TruncateAt.END).toString();
- String appName = BidiFormatter.getInstance().unicodeWrap(unsanitizedAppName);
+ appName = BidiFormatter.getInstance().unicodeWrap(unsanitizedAppName);
String actionText = getString(R.string.media_projection_dialog_text, appName);
SpannableString message = new SpannableString(actionText);
@@ -146,27 +153,28 @@
dialogTitle = getString(R.string.media_projection_dialog_title, appName);
}
- AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this,
- R.style.Theme_SystemUI_Dialog)
- .setTitle(dialogTitle)
- .setIcon(R.drawable.ic_media_projection_permission)
- .setMessage(dialogText)
- .setPositiveButton(R.string.media_projection_action_text, this)
- .setNeutralButton(android.R.string.cancel, this)
- .setOnCancelListener(this);
-
if (isPartialScreenSharingEnabled()) {
- // This is a temporary entry point before we have a new permission dialog
- // TODO(b/233183090): this activity should be redesigned to have a dropdown selector
- dialogBuilder.setNegativeButton("App", this);
+ mDialog = new MediaProjectionPermissionDialog(this, () -> {
+ ScreenShareOption selectedOption =
+ ((MediaProjectionPermissionDialog) mDialog).getSelectedScreenShareOption();
+ grantMediaProjectionPermission(selectedOption.getMode());
+ }, appName);
+ } else {
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this,
+ R.style.Theme_SystemUI_Dialog)
+ .setTitle(dialogTitle)
+ .setIcon(R.drawable.ic_media_projection_permission)
+ .setMessage(dialogText)
+ .setPositiveButton(R.string.media_projection_action_text, this)
+ .setNeutralButton(android.R.string.cancel, this);
+ mDialog = dialogBuilder.create();
}
- mDialog = dialogBuilder.create();
-
SystemUIDialog.registerDismissListener(mDialog);
SystemUIDialog.applyFlags(mDialog);
SystemUIDialog.setDialogSize(mDialog);
+ mDialog.setOnCancelListener(this);
mDialog.create();
mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setFilterTouchesWhenObscured(true);
@@ -186,12 +194,17 @@
@Override
public void onClick(DialogInterface dialog, int which) {
+ if (which == AlertDialog.BUTTON_POSITIVE) {
+ grantMediaProjectionPermission(ENTIRE_SCREEN);
+ }
+ }
+
+ private void grantMediaProjectionPermission(int screenShareMode) {
try {
- if (which == AlertDialog.BUTTON_POSITIVE) {
+ if (screenShareMode == ENTIRE_SCREEN) {
setResult(RESULT_OK, getMediaProjectionIntent(mUid, mPackageName));
}
-
- if (isPartialScreenSharingEnabled() && which == AlertDialog.BUTTON_NEGATIVE) {
+ if (isPartialScreenSharingEnabled() && screenShareMode == SINGLE_APP) {
IMediaProjection projection = createProjection(mUid, mPackageName);
final Intent intent = new Intent(this, MediaProjectionAppSelectorActivity.class);
intent.putExtra(MediaProjectionManager.EXTRA_MEDIA_PROJECTION,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataProvider.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataProvider.kt
index a7ed69a..cacb3e2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataProvider.kt
@@ -29,7 +29,6 @@
private val smartspaceMediaTargetListeners: MutableList<SmartspaceTargetListener> =
mutableListOf()
- private var smartspaceMediaTargets: List<SmartspaceTarget> = listOf()
override fun registerListener(smartspaceTargetListener: SmartspaceTargetListener) {
smartspaceMediaTargetListeners.add(smartspaceTargetListener)
@@ -41,22 +40,7 @@
/** Updates Smartspace data and propagates it to any listeners. */
override fun onTargetsAvailable(targets: List<SmartspaceTarget>) {
- // Filter out non-media targets.
- val mediaTargets = mutableListOf<SmartspaceTarget>()
- for (target in targets) {
- val smartspaceTarget = target
- if (smartspaceTarget.featureType == SmartspaceTarget.FEATURE_MEDIA) {
- mediaTargets.add(smartspaceTarget)
- }
- }
-
- if (!mediaTargets.isEmpty()) {
- Log.d(TAG, "Forwarding Smartspace media updates $mediaTargets")
- }
-
- smartspaceMediaTargets = mediaTargets
- smartspaceMediaTargetListeners.forEach {
- it.onSmartspaceTargetsUpdated(smartspaceMediaTargets)
- }
+ Log.d(TAG, "Forwarding Smartspace updates $targets")
+ smartspaceMediaTargetListeners.forEach { it.onSmartspaceTargetsUpdated(targets) }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
index e38c1ba..8aaee81 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
@@ -610,7 +610,12 @@
// are
// elements in mediaPlayers.
if (MediaPlayerData.players().size != mediaContent.childCount) {
- Log.wtf(TAG, "Size of players list and number of views in carousel are out of sync")
+ Log.e(
+ TAG,
+ "Size of players list and number of views in carousel are out of sync. " +
+ "Players size is ${MediaPlayerData.players().size}. " +
+ "View count is ${mediaContent.childCount}."
+ )
}
return existingPlayer == null
}
@@ -667,7 +672,12 @@
// are
// elements in mediaPlayers.
if (MediaPlayerData.players().size != mediaContent.childCount) {
- Log.wtf(TAG, "Size of players list and number of views in carousel are out of sync")
+ Log.e(
+ TAG,
+ "Size of players list and number of views in carousel are out of sync. " +
+ "Players size is ${MediaPlayerData.players().size}. " +
+ "View count is ${mediaContent.childCount}."
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
index 5b14cf3..215fa03 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
@@ -373,6 +373,7 @@
mMediaViewController.attach(player, MediaViewController.TYPE.PLAYER);
vh.getPlayer().setOnLongClickListener(v -> {
+ if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) return true;
if (!mMediaViewController.isGutsVisible()) {
openGuts();
return true;
@@ -423,6 +424,7 @@
mMediaViewController.attach(recommendations, MediaViewController.TYPE.RECOMMENDATION);
mRecommendationViewHolder.getRecommendations().setOnLongClickListener(v -> {
+ if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) return true;
if (!mMediaViewController.isGutsVisible()) {
openGuts();
return true;
@@ -1191,6 +1193,7 @@
setSmartspaceRecItemOnClickListener(mediaCoverContainer, recommendation, itemIndex);
// Bubble up the long-click event to the card.
mediaCoverContainer.setOnLongClickListener(v -> {
+ if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) return true;
View parent = (View) v.getParent();
if (parent != null) {
parent.performLongClick();
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
index dd9d35b..55fce59 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
@@ -35,25 +35,40 @@
private val mediaOutputBroadcastDialogFactory: MediaOutputBroadcastDialogFactory
) : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
- if (TextUtils.equals(MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG,
- intent.action)) {
- val packageName: String? =
- intent.getStringExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME)
- if (!TextUtils.isEmpty(packageName)) {
- mediaOutputDialogFactory.create(packageName!!, false)
- } else if (DEBUG) {
- Log.e(TAG, "Unable to launch media output dialog. Package name is empty.")
+ when {
+ TextUtils.equals(Intent.ACTION_SHOW_OUTPUT_SWITCHER, intent.action) -> {
+ val packageName: String? = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME)
+ launchMediaOutputDialogIfPossible(packageName)
}
- } else if (TextUtils.equals(
- MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG,
- intent.action)) {
- val packageName: String? =
+ TextUtils.equals(
+ MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG, intent.action) -> {
+ val packageName: String? =
intent.getStringExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME)
- if (!TextUtils.isEmpty(packageName)) {
- mediaOutputBroadcastDialogFactory.create(packageName!!, false)
- } else if (DEBUG) {
- Log.e(TAG, "Unable to launch media output broadcast dialog. Package name is empty.")
+ launchMediaOutputDialogIfPossible(packageName)
}
+ TextUtils.equals(
+ MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG,
+ intent.action) -> {
+ val packageName: String? =
+ intent.getStringExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME)
+ launchMediaOutputBroadcastDialogIfPossible(packageName)
+ }
+ }
+ }
+
+ private fun launchMediaOutputDialogIfPossible(packageName: String?) {
+ if (!packageName.isNullOrEmpty()) {
+ mediaOutputDialogFactory.create(packageName, false)
+ } else if (DEBUG) {
+ Log.e(TAG, "Unable to launch media output dialog. Package name is empty.")
+ }
+ }
+
+ private fun launchMediaOutputBroadcastDialogIfPossible(packageName: String?) {
+ if (!packageName.isNullOrEmpty()) {
+ mediaOutputBroadcastDialogFactory.create(packageName, false)
+ } else if (DEBUG) {
+ Log.e(TAG, "Unable to launch media output broadcast dialog. Package name is empty.")
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
index a4a96806..647beb9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
@@ -61,7 +61,7 @@
@SuppressLint("WrongConstant") // sysui allowed to call STATUS_BAR_SERVICE
val statusBarManager = context.getSystemService(Context.STATUS_BAR_SERVICE)
as StatusBarManager
- val routeInfo = MediaRoute2Info.Builder("id", args[0])
+ val routeInfo = MediaRoute2Info.Builder(if (args.size >= 4) args[3] else "id", args[0])
.addFeature("feature")
val useAppIcon = !(args.size >= 3 && args[2] == "useAppIcon=false")
if (useAppIcon) {
@@ -107,7 +107,7 @@
override fun help(pw: PrintWriter) {
pw.println("Usage: adb shell cmd statusbar $SENDER_COMMAND " +
- "<deviceName> <chipState> useAppIcon=[true|false]")
+ "<deviceName> <chipState> useAppIcon=[true|false] <id>")
}
}
@@ -127,8 +127,10 @@
@SuppressLint("WrongConstant") // sysui is allowed to call STATUS_BAR_SERVICE
val statusBarManager = context.getSystemService(Context.STATUS_BAR_SERVICE)
as StatusBarManager
- val routeInfo = MediaRoute2Info.Builder("id", "Test Name")
- .addFeature("feature")
+ val routeInfo = MediaRoute2Info.Builder(
+ if (args.size >= 3) args[2] else "id",
+ "Test Name"
+ ).addFeature("feature")
val useAppIcon = !(args.size >= 2 && args[1] == "useAppIcon=false")
if (useAppIcon) {
routeInfo.setClientPackageName(TEST_PACKAGE_NAME)
@@ -144,7 +146,7 @@
override fun help(pw: PrintWriter) {
pw.println("Usage: adb shell cmd statusbar $RECEIVER_COMMAND " +
- "<chipState> useAppIcon=[true|false]")
+ "<chipState> useAppIcon=[true|false] <id>")
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 662d059..691953a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -45,6 +45,7 @@
import com.android.systemui.util.animation.AnimationUtil.Companion.frames
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.view.ViewUtil
+import com.android.systemui.util.wakelock.WakeLock
import javax.inject.Inject
/**
@@ -68,6 +69,7 @@
private val mediaTttFlags: MediaTttFlags,
private val uiEventLogger: MediaTttReceiverUiEventLogger,
private val viewUtil: ViewUtil,
+ wakeLockBuilder: WakeLock.Builder,
) : TemporaryViewDisplayController<ChipReceiverInfo, MediaTttLogger>(
context,
logger,
@@ -77,6 +79,7 @@
configurationController,
powerManager,
R.layout.media_ttt_chip_receiver,
+ wakeLockBuilder,
) {
@SuppressLint("WrongConstant") // We're allowed to use LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
override val windowLayoutParams = commonWindowLayoutParams.apply {
@@ -118,18 +121,32 @@
uiEventLogger.logReceiverStateChange(chipState)
if (chipState == ChipStateReceiver.FAR_FROM_SENDER) {
- removeView(removalReason = ChipStateReceiver.FAR_FROM_SENDER.name)
+ removeView(routeInfo.id, removalReason = ChipStateReceiver.FAR_FROM_SENDER.name)
return
}
if (appIcon == null) {
- displayView(ChipReceiverInfo(routeInfo, appIconDrawableOverride = null, appName))
+ displayView(
+ ChipReceiverInfo(
+ routeInfo,
+ appIconDrawableOverride = null,
+ appName,
+ id = routeInfo.id,
+ )
+ )
return
}
appIcon.loadDrawableAsync(
context,
Icon.OnDrawableLoadedListener { drawable ->
- displayView(ChipReceiverInfo(routeInfo, drawable, appName))
+ displayView(
+ ChipReceiverInfo(
+ routeInfo,
+ drawable,
+ appName,
+ id = routeInfo.id,
+ )
+ )
},
// Notify the listener on the main handler since the listener will update
// the UI.
@@ -231,4 +248,5 @@
val appNameOverride: CharSequence?,
override val windowTitle: String = MediaTttUtils.WINDOW_TITLE_RECEIVER,
override val wakeReason: String = MediaTttUtils.WAKE_REASON_RECEIVER,
+ override val id: String,
) : TemporaryViewInfo()
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
index d1ea2d0..bb7bc6f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
@@ -108,7 +108,7 @@
}
displayedState = null
- chipbarCoordinator.removeView(removalReason)
+ chipbarCoordinator.removeView(routeInfo.id, removalReason)
} else {
displayedState = chipState
chipbarCoordinator.displayView(
@@ -162,6 +162,7 @@
windowTitle = MediaTttUtils.WINDOW_TITLE_SENDER,
wakeReason = MediaTttUtils.WAKE_REASON_SENDER,
timeoutMs = chipStateSender.timeout,
+ id = routeInfo.id,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
index 7fd100f..6c41caa 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -19,6 +19,7 @@
import android.app.Activity
import android.content.ComponentName
import android.content.Context
+import com.android.launcher3.icons.IconFactory
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.media.MediaProjectionAppSelectorActivity
import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerThumbnailLoader
@@ -92,6 +93,11 @@
): ConfigurationController = ConfigurationControllerImpl(activity)
@Provides
+ fun bindIconFactory(
+ context: Context
+ ): IconFactory = IconFactory.obtain(context)
+
+ @Provides
@MediaProjectionAppSelector
@MediaProjectionAppSelectorScope
fun provideCoroutineScope(@Application applicationScope: CoroutineScope): CoroutineScope =
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt
index 0927f3b..b85d628 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt
@@ -19,13 +19,14 @@
import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageManager
-import android.content.pm.PackageManager.ComponentInfoFlags
import android.graphics.drawable.Drawable
import android.os.UserHandle
import com.android.launcher3.icons.BaseIconFactory.IconOptions
import com.android.launcher3.icons.IconFactory
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.shared.system.PackageManagerWrapper
import javax.inject.Inject
+import javax.inject.Provider
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
@@ -38,14 +39,18 @@
constructor(
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val context: Context,
- private val packageManager: PackageManager
+ // Use wrapper to access hidden API that allows to get ActivityInfo for any user id
+ private val packageManagerWrapper: PackageManagerWrapper,
+ private val packageManager: PackageManager,
+ private val iconFactoryProvider: Provider<IconFactory>
) : AppIconLoader {
override suspend fun loadIcon(userId: Int, component: ComponentName): Drawable? =
withContext(backgroundDispatcher) {
- IconFactory.obtain(context).use<IconFactory, Drawable?> { iconFactory ->
- val activityInfo = packageManager
- .getActivityInfo(component, ComponentInfoFlags.of(0))
+ iconFactoryProvider.get().use<IconFactory, Drawable?> { iconFactory ->
+ val activityInfo =
+ packageManagerWrapper.getActivityInfo(component, userId)
+ ?: return@withContext null
val icon = activityInfo.loadIcon(packageManager) ?: return@withContext null
val userHandler = UserHandle.of(userId)
val options = IconOptions().apply { setUser(userHandler) }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
index b682bd1..d4991f9 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
@@ -148,6 +148,7 @@
val currentRotation: Int = display.rotation
val displayWidthPx = windowMetrics.bounds.width()
+ val displayHeightPx = windowMetrics.bounds.height()
val isRtl = layoutDirection == LAYOUT_DIRECTION_RTL
val isTablet = isTablet(context)
val taskbarSize =
@@ -163,6 +164,7 @@
measuredWidth,
measuredHeight,
displayWidthPx,
+ displayHeightPx,
taskbarSize,
isTablet,
currentRotation,
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index d247f24..b964b76 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -22,7 +22,7 @@
import android.view.KeyEvent
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.util.kotlin.getOrNull
-import com.android.wm.shell.floating.FloatingTasks
+import com.android.wm.shell.bubbles.Bubbles
import java.util.Optional
import javax.inject.Inject
@@ -39,7 +39,7 @@
constructor(
private val context: Context,
private val intentResolver: NoteTaskIntentResolver,
- private val optionalFloatingTasks: Optional<FloatingTasks>,
+ private val optionalBubbles: Optional<Bubbles>,
private val optionalKeyguardManager: Optional<KeyguardManager>,
private val optionalUserManager: Optional<UserManager>,
@NoteTaskEnabledKey private val isEnabled: Boolean,
@@ -54,7 +54,7 @@
}
private fun showNoteTask() {
- val floatingTasks = optionalFloatingTasks.getOrNull() ?: return
+ val bubbles = optionalBubbles.getOrNull() ?: return
val keyguardManager = optionalKeyguardManager.getOrNull() ?: return
val userManager = optionalUserManager.getOrNull() ?: return
val intent = intentResolver.resolveIntent() ?: return
@@ -66,7 +66,7 @@
context.startActivity(intent)
} else {
// TODO(b/254606432): Should include Intent.EXTRA_FLOATING_WINDOW_MODE parameter.
- floatingTasks.showOrSetStashed(intent)
+ bubbles.showAppBubble(intent)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
index d84717d..0a5b600 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
@@ -17,7 +17,7 @@
package com.android.systemui.notetask
import com.android.systemui.statusbar.CommandQueue
-import com.android.wm.shell.floating.FloatingTasks
+import com.android.wm.shell.bubbles.Bubbles
import dagger.Lazy
import java.util.Optional
import javax.inject.Inject
@@ -26,7 +26,7 @@
internal class NoteTaskInitializer
@Inject
constructor(
- private val optionalFloatingTasks: Optional<FloatingTasks>,
+ private val optionalBubbles: Optional<Bubbles>,
private val lazyNoteTaskController: Lazy<NoteTaskController>,
private val commandQueue: CommandQueue,
@NoteTaskEnabledKey private val isEnabled: Boolean,
@@ -40,7 +40,7 @@
}
fun initialize() {
- if (isEnabled && optionalFloatingTasks.isPresent) {
+ if (isEnabled && optionalBubbles.isPresent) {
commandQueue.addCallback(callbacks)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
index dc79f40..6f645b5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
@@ -26,6 +26,7 @@
import javax.inject.Inject
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
interface ChipVisibilityListener {
fun onChipVisibilityRefreshed(visible: Boolean)
@@ -54,7 +55,8 @@
private val activityStarter: ActivityStarter,
private val appOpsController: AppOpsController,
private val broadcastDispatcher: BroadcastDispatcher,
- private val safetyCenterManager: SafetyCenterManager
+ private val safetyCenterManager: SafetyCenterManager,
+ private val deviceProvisionedController: DeviceProvisionedController
) {
var chipVisibilityListener: ChipVisibilityListener? = null
@@ -134,6 +136,8 @@
fun onParentVisible() {
privacyChip.setOnClickListener {
+ // Do not expand dialog while device is not provisioned
+ if (!deviceProvisionedController.isDeviceProvisioned) return@setOnClickListener
// If the privacy chip is visible, it means there were some indicators
uiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_CLICK)
if (safetyCenterEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index ef87fb4..dc9dcc2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -29,6 +29,7 @@
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.qs.customize.QSCustomizer;
+import com.android.systemui.util.LargeScreenUtils;
import java.io.PrintWriter;
@@ -52,6 +53,7 @@
private boolean mQsDisabled;
private int mContentHorizontalPadding = -1;
private boolean mClippingEnabled;
+ private boolean mUseCombinedHeaders;
public QSContainerImpl(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -66,6 +68,10 @@
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
}
+ void setUseCombinedHeaders(boolean useCombinedHeaders) {
+ mUseCombinedHeaders = useCombinedHeaders;
+ }
+
@Override
public boolean hasOverlappingRendering() {
return false;
@@ -143,9 +149,15 @@
void updateResources(QSPanelController qsPanelController,
QuickStatusBarHeaderController quickStatusBarHeaderController) {
+ int topPadding = QSUtils.getQsHeaderSystemIconsAreaHeight(mContext);
+ if (mUseCombinedHeaders
+ && !LargeScreenUtils.shouldUseLargeScreenShadeHeader(mContext.getResources())) {
+ topPadding = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.large_screen_shade_header_height);
+ }
mQSPanelContainer.setPaddingRelative(
mQSPanelContainer.getPaddingStart(),
- QSUtils.getQsHeaderSystemIconsAreaHeight(mContext),
+ topPadding,
mQSPanelContainer.getPaddingEnd(),
mQSPanelContainer.getPaddingBottom());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
index dea7bb5..28b4c822 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
@@ -22,6 +22,8 @@
import android.view.MotionEvent;
import android.view.View;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -37,7 +39,6 @@
private final ConfigurationController mConfigurationController;
private final FalsingManager mFalsingManager;
private final NonInterceptingScrollView mQSPanelContainer;
-
private final ConfigurationController.ConfigurationListener mConfigurationListener =
new ConfigurationController.ConfigurationListener() {
@Override
@@ -65,13 +66,15 @@
QSPanelController qsPanelController,
QuickStatusBarHeaderController quickStatusBarHeaderController,
ConfigurationController configurationController,
- FalsingManager falsingManager) {
+ FalsingManager falsingManager,
+ FeatureFlags featureFlags) {
super(view);
mQsPanelController = qsPanelController;
mQuickStatusBarHeaderController = quickStatusBarHeaderController;
mConfigurationController = configurationController;
mFalsingManager = falsingManager;
mQSPanelContainer = mView.getQSPanelContainer();
+ view.setUseCombinedHeaders(featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 920a108..d9be281 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -515,7 +515,13 @@
public void setExpanded(boolean expanded) {
if (DEBUG) Log.d(TAG, "setExpanded " + expanded);
mQsExpanded = expanded;
- updateQsPanelControllerListening();
+ if (mInSplitShade && mQsExpanded) {
+ // in split shade QS is expanded immediately when shade expansion starts and then we
+ // also need to listen to changes - otherwise QS is updated only once its fully expanded
+ setListening(true);
+ } else {
+ updateQsPanelControllerListening();
+ }
updateQsState();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index abc0ade..64962b4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -237,7 +237,7 @@
* @return if bouncer is in transit
*/
public boolean isBouncerInTransit() {
- return mStatusBarKeyguardViewManager.isBouncerInTransit();
+ return mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 2a80de0..dd88c83 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -25,6 +25,7 @@
import android.content.res.Configuration;
import android.content.res.Configuration.Orientation;
import android.metrics.LogMaker;
+import android.util.Log;
import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
@@ -38,6 +39,7 @@
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileViewImpl;
import com.android.systemui.util.LargeScreenUtils;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.animation.DisappearParameters;
@@ -237,6 +239,16 @@
private void addTile(final QSTile tile, boolean collapsedView) {
final TileRecord r =
new TileRecord(tile, mHost.createTileView(getContext(), tile, collapsedView));
+ // TODO(b/250618218): Remove the QSLogger in QSTileViewImpl once we know the root cause of
+ // b/250618218.
+ try {
+ QSTileViewImpl qsTileView = (QSTileViewImpl) (r.tileView);
+ if (qsTileView != null) {
+ qsTileView.setQsLogger(mQSLogger);
+ }
+ } catch (ClassCastException e) {
+ Log.e(TAG, "Failed to cast QSTileView to QSTileViewImpl", e);
+ }
mView.addTile(r);
mRecords.add(r);
mCachedSpecs = getTilesSpecs();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 27d9da6..946fe54 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -288,8 +288,15 @@
}
MarginLayoutParams qqsLP = (MarginLayoutParams) mHeaderQsPanel.getLayoutParams();
- qqsLP.topMargin = largeScreenHeaderActive || !mUseCombinedQSHeader ? mContext.getResources()
- .getDimensionPixelSize(R.dimen.qqs_layout_margin_top) : qsOffsetHeight;
+ if (largeScreenHeaderActive) {
+ qqsLP.topMargin = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.qqs_layout_margin_top);
+ } else if (!mUseCombinedQSHeader) {
+ qqsLP.topMargin = qsOffsetHeight;
+ } else {
+ qqsLP.topMargin = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.large_screen_shade_header_min_height);
+ }
mHeaderQsPanel.setLayoutParams(qqsLP);
updateBatteryMode();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
index 931dc8d..9f6317f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
@@ -129,12 +129,36 @@
})
}
- fun logInternetTileUpdate(lastType: Int, callback: String) {
+ fun logInternetTileUpdate(tileSpec: String, lastType: Int, callback: String) {
log(VERBOSE, {
+ str1 = tileSpec
int1 = lastType
- str1 = callback
+ str2 = callback
}, {
- "mLastTileState=$int1, Callback=$str1."
+ "[$str1] mLastTileState=$int1, Callback=$str2."
+ })
+ }
+
+ // TODO(b/250618218): Remove this method once we know the root cause of b/250618218.
+ fun logTileBackgroundColorUpdateIfInternetTile(
+ tileSpec: String,
+ state: Int,
+ disabledByPolicy: Boolean,
+ color: Int
+ ) {
+ // This method is added to further debug b/250618218 which has only been observed from the
+ // InternetTile, so we are only logging the background color change for the InternetTile
+ // to avoid spamming the QSLogger.
+ if (tileSpec != "internet") {
+ return
+ }
+ log(VERBOSE, {
+ str1 = tileSpec
+ int1 = state
+ bool1 = disabledByPolicy
+ int2 = color
+ }, {
+ "[$str1] state=$int1, disabledByPolicy=$bool1, color=$int2."
})
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index 1f92b12..cd69f4e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -140,16 +140,21 @@
iv.setTag(R.id.qs_icon_tag, icon);
iv.setTag(R.id.qs_slash_tag, state.slash);
iv.setPadding(0, padding, 0, padding);
- if (shouldAnimate && d instanceof Animatable2) {
+ if (d instanceof Animatable2) {
Animatable2 a = (Animatable2) d;
a.start();
- if (state.isTransient) {
- a.registerAnimationCallback(new AnimationCallback() {
- @Override
- public void onAnimationEnd(Drawable drawable) {
- a.start();
- }
- });
+ if (shouldAnimate) {
+ if (state.isTransient) {
+ a.registerAnimationCallback(new AnimationCallback() {
+ @Override
+ public void onAnimationEnd(Drawable drawable) {
+ a.start();
+ }
+ });
+ }
+ } else {
+ // Sends animator to end of animation. Needs to be called after calling start.
+ a.stop();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 972b243..b355d4b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -50,6 +50,7 @@
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.qs.QSTile.BooleanState
import com.android.systemui.plugins.qs.QSTileView
+import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSIconViewImpl.QS_ANIM_LENGTH
import java.util.Objects
@@ -116,7 +117,7 @@
protected lateinit var sideView: ViewGroup
private lateinit var customDrawableView: ImageView
private lateinit var chevronView: ImageView
-
+ private var mQsLogger: QSLogger? = null
protected var showRippleEffect = true
private lateinit var ripple: RippleDrawable
@@ -188,6 +189,10 @@
updateHeight()
}
+ fun setQsLogger(qsLogger: QSLogger) {
+ mQsLogger = qsLogger
+ }
+
fun updateResources() {
FontSizeUtils.updateFontSize(label, R.dimen.qs_tile_text_size)
FontSizeUtils.updateFontSize(secondaryLabel, R.dimen.qs_tile_text_size)
@@ -493,6 +498,11 @@
// Colors
if (state.state != lastState || state.disabledByPolicy || lastDisabledByPolicy) {
singleAnimator.cancel()
+ mQsLogger?.logTileBackgroundColorUpdateIfInternetTile(
+ state.spec,
+ state.state,
+ state.disabledByPolicy,
+ getBackgroundColorForState(state.state, state.disabledByPolicy))
if (allowAnimations) {
singleAnimator.setValues(
colorValuesHolder(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index ae46477..350d8b0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -387,7 +387,8 @@
@Override
protected void handleUpdateState(SignalState state, Object arg) {
- mQSLogger.logInternetTileUpdate(mLastTileState, arg == null ? "null" : arg.toString());
+ mQSLogger.logInternetTileUpdate(
+ getTileSpec(), mLastTileState, arg == null ? "null" : arg.toString());
if (arg instanceof CellularCallbackInfo) {
mLastTileState = LAST_STATE_CELLULAR;
handleUpdateCellularState(state, arg);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
index b415022..376d3d8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
@@ -115,7 +115,7 @@
state.label = mContext.getString(R.string.qr_code_scanner_title);
state.contentDescription = state.label;
state.icon = ResourceIcon.get(R.drawable.ic_qr_code_scanner);
- state.state = mQRCodeScannerController.isEnabledForQuickSettings() ? Tile.STATE_ACTIVE
+ state.state = mQRCodeScannerController.isEnabledForQuickSettings() ? Tile.STATE_INACTIVE
: Tile.STATE_UNAVAILABLE;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index f63f044..64a8a14 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -16,6 +16,7 @@
package com.android.systemui.qs.tiles;
+import android.app.Dialog;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
@@ -43,7 +44,6 @@
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.screenrecord.RecordingController;
-import com.android.systemui.screenrecord.ScreenRecordDialog;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -170,9 +170,9 @@
mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
getHost().collapsePanels();
};
- ScreenRecordDialog dialog = mController.createScreenRecordDialog(mContext, mFlags,
- mDialogLaunchAnimator, mActivityStarter, onStartRecordingClicked);
+ Dialog dialog = mController.createScreenRecordDialog(mContext, mFlags,
+ mDialogLaunchAnimator, mActivityStarter, onStartRecordingClicked);
ActivityStarter.OnDismissAction dismissAction = () -> {
if (shouldAnimateFromView) {
mDialogLaunchAnimator.showFromView(dialog, view, new DialogCuj(
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 46c4f41..ba97297 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -108,7 +108,6 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
-import java.util.function.BiConsumer;
import java.util.function.Supplier;
import javax.inject.Inject;
@@ -470,8 +469,6 @@
};
private final StatusBarWindowCallback mStatusBarWindowCallback = this::onStatusBarStateChanged;
- private final BiConsumer<Rect, Rect> mSplitScreenBoundsChangeListener =
- this::notifySplitScreenBoundsChanged;
// This is the death handler for the binder from the launcher service
private final IBinder.DeathRecipient mOverviewServiceDeathRcpt
@@ -839,26 +836,6 @@
}
}
- /**
- * Notifies the Launcher of split screen size changes
- *
- * @param secondaryWindowBounds Bounds of the secondary window including the insets
- * @param secondaryWindowInsets stable insets received by the secondary window
- */
- public void notifySplitScreenBoundsChanged(
- Rect secondaryWindowBounds, Rect secondaryWindowInsets) {
- try {
- if (mOverviewProxy != null) {
- mOverviewProxy.onSplitScreenSecondaryBoundsChanged(
- secondaryWindowBounds, secondaryWindowInsets);
- } else {
- Log.e(TAG_OPS, "Failed to get overview proxy for split screen bounds.");
- }
- } catch (RemoteException e) {
- Log.e(TAG_OPS, "Failed to call onSplitScreenSecondaryBoundsChanged()", e);
- }
- }
-
private final ScreenLifecycle.Observer mLifecycleObserver = new ScreenLifecycle.Observer() {
/**
* Notifies the Launcher that screen turned on and ready to use
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
new file mode 100644
index 0000000..f4d59a8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.screenrecord
+
+import android.content.Context
+import android.os.Bundle
+import android.view.Gravity
+import android.view.View
+import android.view.ViewStub
+import android.view.WindowManager
+import android.widget.AdapterView
+import android.widget.ArrayAdapter
+import android.widget.Spinner
+import android.widget.TextView
+import androidx.annotation.LayoutRes
+import androidx.annotation.StringRes
+import com.android.systemui.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
+
+/** Base permission dialog for screen share and recording */
+open class BaseScreenSharePermissionDialog(
+ context: Context?,
+ private val screenShareOptions: List<ScreenShareOption>,
+ private val appName: String?
+) : SystemUIDialog(context), AdapterView.OnItemSelectedListener {
+ private lateinit var dialogTitle: TextView
+ private lateinit var startButton: TextView
+ private lateinit var warning: TextView
+ private lateinit var screenShareModeSpinner: Spinner
+ var selectedScreenShareOption: ScreenShareOption = screenShareOptions.first()
+
+ public override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ window.apply {
+ addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS)
+ setGravity(Gravity.CENTER)
+ }
+ setContentView(R.layout.screen_share_dialog)
+ dialogTitle = findViewById(R.id.screen_share_dialog_title)
+ warning = findViewById(R.id.text_warning)
+ startButton = findViewById(R.id.button_start)
+ findViewById<TextView>(R.id.button_cancel).setOnClickListener { dismiss() }
+ initScreenShareOptions()
+ createOptionsView(getOptionsViewLayoutId())
+ }
+
+ protected fun initScreenShareOptions() {
+ selectedScreenShareOption = screenShareOptions.first()
+ warning.text = warningText
+ initScreenShareSpinner()
+ }
+
+ private val warningText: String
+ get() = context.getString(selectedScreenShareOption.warningText, appName)
+
+ private fun initScreenShareSpinner() {
+ val options = screenShareOptions.map { context.getString(it.spinnerText) }.toTypedArray()
+ val adapter =
+ ArrayAdapter(context.applicationContext, android.R.layout.simple_spinner_item, options)
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
+ screenShareModeSpinner = findViewById(R.id.screen_share_mode_spinner)
+ screenShareModeSpinner.adapter = adapter
+ screenShareModeSpinner.onItemSelectedListener = this
+ }
+
+ override fun onItemSelected(adapterView: AdapterView<*>?, view: View, pos: Int, id: Long) {
+ selectedScreenShareOption = screenShareOptions[pos]
+ warning.text = warningText
+ }
+
+ override fun onNothingSelected(parent: AdapterView<*>?) {}
+
+ /** Protected methods for the text updates & functionality */
+ protected fun setDialogTitle(@StringRes stringId: Int) {
+ val title = context.getString(stringId, appName)
+ dialogTitle.text = title
+ }
+
+ protected fun setStartButtonText(@StringRes stringId: Int) {
+ startButton.setText(stringId)
+ }
+
+ protected fun setStartButtonOnClickListener(listener: View.OnClickListener?) {
+ startButton.setOnClickListener(listener)
+ }
+
+ // Create additional options that is shown under the share mode spinner
+ // Eg. the audio and tap toggles in SysUI Recorder
+ @LayoutRes protected open fun getOptionsViewLayoutId(): Int? = null
+
+ private fun createOptionsView(@LayoutRes layoutId: Int?) {
+ if (layoutId == null) return
+ val stub = findViewById<View>(R.id.options_stub) as ViewStub
+ stub.layoutResource = layoutId
+ stub.inflate()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt
new file mode 100644
index 0000000..15b0bc4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.screenrecord
+
+import android.content.Context
+import android.os.Bundle
+import com.android.systemui.R
+
+/** Dialog to select screen recording options */
+class MediaProjectionPermissionDialog(
+ context: Context?,
+ private val onStartRecordingClicked: Runnable,
+ appName: String?
+) : BaseScreenSharePermissionDialog(context, createOptionList(), appName) {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setDialogTitle(R.string.media_projection_permission_dialog_title)
+ setStartButtonText(R.string.media_projection_permission_dialog_continue)
+ setStartButtonOnClickListener {
+ // Note that it is important to run this callback before dismissing, so that the
+ // callback can disable the dialog exit animation if it wants to.
+ onStartRecordingClicked.run()
+ dismiss()
+ }
+ }
+
+ companion object {
+ private fun createOptionList(): List<ScreenShareOption> {
+ return listOf(
+ ScreenShareOption(
+ SINGLE_APP,
+ R.string.media_projection_permission_dialog_option_single_app,
+ R.string.media_projection_permission_dialog_warning_single_app
+ ),
+ ScreenShareOption(
+ ENTIRE_SCREEN,
+ R.string.media_projection_permission_dialog_option_entire_screen,
+ R.string.media_projection_permission_dialog_warning_entire_screen
+ )
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index 1083f22..ce4e0ec 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -16,6 +16,7 @@
package com.android.systemui.screenrecord;
+import android.app.Dialog;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -33,6 +34,7 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.policy.CallbackController;
@@ -97,11 +99,15 @@
}
/** Create a dialog to show screen recording options to the user. */
- public ScreenRecordDialog createScreenRecordDialog(Context context, FeatureFlags flags,
- DialogLaunchAnimator dialogLaunchAnimator, ActivityStarter activityStarter,
- @Nullable Runnable onStartRecordingClicked) {
- return new ScreenRecordDialog(context, this, activityStarter, mUserContextProvider,
- flags, dialogLaunchAnimator, onStartRecordingClicked);
+ public Dialog createScreenRecordDialog(Context context, FeatureFlags flags,
+ DialogLaunchAnimator dialogLaunchAnimator,
+ ActivityStarter activityStarter,
+ @Nullable Runnable onStartRecordingClicked) {
+ return flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)
+ ? new ScreenRecordPermissionDialog(context, this, activityStarter,
+ dialogLaunchAnimator, mUserContextProvider, onStartRecordingClicked)
+ : new ScreenRecordDialog(context, this, activityStarter,
+ mUserContextProvider, flags, dialogLaunchAnimator, onStartRecordingClicked);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
new file mode 100644
index 0000000..19bb15a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.screenrecord
+
+import android.app.Activity
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.os.ResultReceiver
+import android.view.View
+import android.view.View.GONE
+import android.view.View.VISIBLE
+import android.widget.AdapterView
+import android.widget.ArrayAdapter
+import android.widget.Spinner
+import android.widget.Switch
+import androidx.annotation.LayoutRes
+import com.android.systemui.R
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.media.MediaProjectionAppSelectorActivity
+import com.android.systemui.media.MediaProjectionCaptureTarget
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.settings.UserContextProvider
+
+/** Dialog to select screen recording options */
+class ScreenRecordPermissionDialog(
+ context: Context?,
+ private val controller: RecordingController,
+ private val activityStarter: ActivityStarter,
+ private val dialogLaunchAnimator: DialogLaunchAnimator,
+ private val userContextProvider: UserContextProvider,
+ private val onStartRecordingClicked: Runnable?
+) : BaseScreenSharePermissionDialog(context, createOptionList(), null) {
+ private lateinit var tapsSwitch: Switch
+ private lateinit var tapsView: View
+ private lateinit var audioSwitch: Switch
+ private lateinit var options: Spinner
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setDialogTitle(R.string.screenrecord_start_label)
+ setStartButtonText(R.string.screenrecord_start_recording)
+ setStartButtonOnClickListener { v: View? ->
+ onStartRecordingClicked?.run()
+ if (selectedScreenShareOption.mode == ENTIRE_SCREEN) {
+ requestScreenCapture(/* captureTarget= */ null)
+ }
+ if (selectedScreenShareOption.mode == SINGLE_APP) {
+ val intent = Intent(context, MediaProjectionAppSelectorActivity::class.java)
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+
+ // We can't start activity for result here so we use result receiver to get
+ // the selected target to capture
+ intent.putExtra(
+ MediaProjectionAppSelectorActivity.EXTRA_CAPTURE_REGION_RESULT_RECEIVER,
+ CaptureTargetResultReceiver()
+ )
+ val animationController = dialogLaunchAnimator.createActivityLaunchController(v!!)
+ if (animationController == null) {
+ dismiss()
+ }
+ activityStarter.startActivity(intent, /* dismissShade= */ true, animationController)
+ }
+ dismiss()
+ }
+ initRecordOptionsView()
+ }
+
+ @LayoutRes override fun getOptionsViewLayoutId(): Int = R.layout.screen_record_options
+
+ private fun initRecordOptionsView() {
+ audioSwitch = findViewById(R.id.screenrecord_audio_switch)
+ tapsSwitch = findViewById(R.id.screenrecord_taps_switch)
+ tapsView = findViewById(R.id.show_taps)
+ updateTapsViewVisibility()
+ options = findViewById(R.id.screen_recording_options)
+ val a: ArrayAdapter<*> =
+ ScreenRecordingAdapter(context, android.R.layout.simple_spinner_dropdown_item, MODES)
+ a.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
+ options.adapter = a
+ options.setOnItemClickListenerInt { _: AdapterView<*>?, _: View?, _: Int, _: Long ->
+ audioSwitch.isChecked = true
+ }
+ }
+
+ override fun onItemSelected(adapterView: AdapterView<*>?, view: View, pos: Int, id: Long) {
+ super.onItemSelected(adapterView, view, pos, id)
+ updateTapsViewVisibility()
+ }
+
+ private fun updateTapsViewVisibility() {
+ tapsView.visibility = if (selectedScreenShareOption.mode == SINGLE_APP) GONE else VISIBLE
+ }
+
+ /**
+ * Starts screen capture after some countdown
+ * @param captureTarget target to capture (could be e.g. a task) or null to record the whole
+ * screen
+ */
+ private fun requestScreenCapture(captureTarget: MediaProjectionCaptureTarget?) {
+ val userContext = userContextProvider.userContext
+ val showTaps = selectedScreenShareOption.mode != SINGLE_APP && tapsSwitch.isChecked
+ val audioMode =
+ if (audioSwitch.isChecked) options.selectedItem as ScreenRecordingAudioSource
+ else ScreenRecordingAudioSource.NONE
+ val startIntent =
+ PendingIntent.getForegroundService(
+ userContext,
+ RecordingService.REQUEST_CODE,
+ RecordingService.getStartIntent(
+ userContext,
+ Activity.RESULT_OK,
+ audioMode.ordinal,
+ showTaps,
+ captureTarget
+ ),
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ )
+ val stopIntent =
+ PendingIntent.getService(
+ userContext,
+ RecordingService.REQUEST_CODE,
+ RecordingService.getStopIntent(userContext),
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ )
+ controller.startCountdown(DELAY_MS, INTERVAL_MS, startIntent, stopIntent)
+ }
+
+ private inner class CaptureTargetResultReceiver() :
+ ResultReceiver(Handler(Looper.getMainLooper())) {
+ override fun onReceiveResult(resultCode: Int, resultData: Bundle) {
+ if (resultCode == Activity.RESULT_OK) {
+ val captureTarget =
+ resultData.getParcelable(
+ MediaProjectionAppSelectorActivity.KEY_CAPTURE_TARGET,
+ MediaProjectionCaptureTarget::class.java
+ )
+
+ // Start recording of the selected target
+ requestScreenCapture(captureTarget)
+ }
+ }
+ }
+
+ companion object {
+ private val MODES =
+ listOf(
+ ScreenRecordingAudioSource.INTERNAL,
+ ScreenRecordingAudioSource.MIC,
+ ScreenRecordingAudioSource.MIC_AND_INTERNAL
+ )
+ private const val DELAY_MS: Long = 3000
+ private const val INTERVAL_MS: Long = 1000
+ private fun createOptionList(): List<ScreenShareOption> {
+ return listOf(
+ ScreenShareOption(
+ SINGLE_APP,
+ R.string.screenrecord_option_single_app,
+ R.string.screenrecord_warning_single_app
+ ),
+ ScreenShareOption(
+ ENTIRE_SCREEN,
+ R.string.screenrecord_option_entire_screen,
+ R.string.screenrecord_warning_entire_screen
+ )
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt
new file mode 100644
index 0000000..914d29a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.screenrecord
+
+import androidx.annotation.IntDef
+import androidx.annotation.StringRes
+import kotlin.annotation.Retention
+
+@Retention(AnnotationRetention.SOURCE)
+@IntDef(SINGLE_APP, ENTIRE_SCREEN)
+annotation class ScreenShareMode
+
+const val SINGLE_APP = 0
+const val ENTIRE_SCREEN = 1
+
+class ScreenShareOption(
+ @ScreenShareMode val mode: Int,
+ @StringRes val spinnerText: Int,
+ @StringRes val warningText: Int
+)
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
index 5961635..01e32b7a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
@@ -32,7 +32,7 @@
import com.android.internal.infra.ServiceConnector
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
import javax.inject.Inject
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineDispatcher
@@ -45,7 +45,7 @@
@Inject
constructor(
@Application private val applicationScope: CoroutineScope,
- @Background private val bgDispatcher: CoroutineDispatcher,
+ @Main private val mainDispatcher: CoroutineDispatcher,
private val context: Context,
) {
/**
@@ -70,23 +70,21 @@
userId: Int,
overrideTransition: Boolean,
) {
- withContext(bgDispatcher) {
- dismissKeyguard()
+ dismissKeyguard()
- if (userId == UserHandle.myUserId()) {
- context.startActivity(intent, bundle)
- } else {
- launchCrossProfileIntent(userId, intent, bundle)
- }
+ if (userId == UserHandle.myUserId()) {
+ withContext(mainDispatcher) { context.startActivity(intent, bundle) }
+ } else {
+ launchCrossProfileIntent(userId, intent, bundle)
+ }
- if (overrideTransition) {
- val runner = RemoteAnimationAdapter(SCREENSHOT_REMOTE_RUNNER, 0, 0)
- try {
- WindowManagerGlobal.getWindowManagerService()
- .overridePendingAppTransitionRemote(runner, Display.DEFAULT_DISPLAY)
- } catch (e: Exception) {
- Log.e(TAG, "Error overriding screenshot app transition", e)
- }
+ if (overrideTransition) {
+ val runner = RemoteAnimationAdapter(SCREENSHOT_REMOTE_RUNNER, 0, 0)
+ try {
+ WindowManagerGlobal.getWindowManagerService()
+ .overridePendingAppTransitionRemote(runner, Display.DEFAULT_DISPLAY)
+ } catch (e: Exception) {
+ Log.e(TAG, "Error overriding screenshot app transition", e)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
index 8bf956b..5450db9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
@@ -46,6 +46,8 @@
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.screenshot.ScrollCaptureController.LongScreenshot;
import com.google.common.util.concurrent.ListenableFuture;
@@ -67,6 +69,7 @@
private static final String TAG = LogConfig.logTag(LongScreenshotActivity.class);
public static final String EXTRA_CAPTURE_RESPONSE = "capture-response";
+ public static final String EXTRA_SCREENSHOT_USER_HANDLE = "screenshot-userhandle";
private static final String KEY_SAVED_IMAGE_PATH = "saved-image-path";
private final UiEventLogger mUiEventLogger;
@@ -74,6 +77,8 @@
private final Executor mBackgroundExecutor;
private final ImageExporter mImageExporter;
private final LongScreenshotData mLongScreenshotHolder;
+ private final ActionIntentExecutor mActionExecutor;
+ private final FeatureFlags mFeatureFlags;
private ImageView mPreview;
private ImageView mTransitionView;
@@ -85,6 +90,7 @@
private CropView mCropView;
private MagnifierView mMagnifierView;
private ScrollCaptureResponse mScrollCaptureResponse;
+ private UserHandle mScreenshotUserHandle;
private File mSavedImagePath;
private ListenableFuture<File> mCacheSaveFuture;
@@ -103,12 +109,15 @@
@Inject
public LongScreenshotActivity(UiEventLogger uiEventLogger, ImageExporter imageExporter,
@Main Executor mainExecutor, @Background Executor bgExecutor,
- LongScreenshotData longScreenshotHolder) {
+ LongScreenshotData longScreenshotHolder, ActionIntentExecutor actionExecutor,
+ FeatureFlags featureFlags) {
mUiEventLogger = uiEventLogger;
mUiExecutor = mainExecutor;
mBackgroundExecutor = bgExecutor;
mImageExporter = imageExporter;
mLongScreenshotHolder = longScreenshotHolder;
+ mActionExecutor = actionExecutor;
+ mFeatureFlags = featureFlags;
}
@@ -139,6 +148,11 @@
Intent intent = getIntent();
mScrollCaptureResponse = intent.getParcelableExtra(EXTRA_CAPTURE_RESPONSE);
+ mScreenshotUserHandle = intent.getParcelableExtra(EXTRA_SCREENSHOT_USER_HANDLE,
+ UserHandle.class);
+ if (mScreenshotUserHandle == null) {
+ mScreenshotUserHandle = Process.myUserHandle();
+ }
if (savedInstanceState != null) {
String savedImagePath = savedInstanceState.getString(KEY_SAVED_IMAGE_PATH);
@@ -318,36 +332,51 @@
}
private void doEdit(Uri uri) {
- String editorPackage = getString(R.string.config_screenshotEditor);
- Intent intent = new Intent(Intent.ACTION_EDIT);
- if (!TextUtils.isEmpty(editorPackage)) {
- intent.setComponent(ComponentName.unflattenFromString(editorPackage));
- }
- intent.setDataAndType(uri, "image/png");
- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
- | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ if (mFeatureFlags.isEnabled(Flags.SCREENSHOT_WORK_PROFILE_POLICY) && mScreenshotUserHandle
+ != Process.myUserHandle()) {
+ // TODO: Fix transition for work profile. Omitting it in the meantime.
+ mActionExecutor.launchIntentAsync(
+ ActionIntentCreator.INSTANCE.createEditIntent(uri, this),
+ null,
+ mScreenshotUserHandle.getIdentifier(), false);
+ } else {
+ String editorPackage = getString(R.string.config_screenshotEditor);
+ Intent intent = new Intent(Intent.ACTION_EDIT);
+ if (!TextUtils.isEmpty(editorPackage)) {
+ intent.setComponent(ComponentName.unflattenFromString(editorPackage));
+ }
+ intent.setDataAndType(uri, "image/png");
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- mTransitionView.setImageBitmap(mOutputBitmap);
- mTransitionView.setVisibility(View.VISIBLE);
- mTransitionView.setTransitionName(
- ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME);
- // TODO: listen for transition completing instead of finishing onStop
- mTransitionStarted = true;
- startActivity(intent,
- ActivityOptions.makeSceneTransitionAnimation(this, mTransitionView,
- ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME).toBundle());
+ mTransitionView.setImageBitmap(mOutputBitmap);
+ mTransitionView.setVisibility(View.VISIBLE);
+ mTransitionView.setTransitionName(
+ ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME);
+ // TODO: listen for transition completing instead of finishing onStop
+ mTransitionStarted = true;
+ startActivity(intent,
+ ActivityOptions.makeSceneTransitionAnimation(this, mTransitionView,
+ ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME).toBundle());
+ }
}
private void doShare(Uri uri) {
- Intent intent = new Intent(Intent.ACTION_SEND);
- intent.setType("image/png");
- intent.putExtra(Intent.EXTRA_STREAM, uri);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
- | Intent.FLAG_GRANT_READ_URI_PERMISSION);
- Intent sharingChooserIntent = Intent.createChooser(intent, null)
- .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ if (mFeatureFlags.isEnabled(Flags.SCREENSHOT_WORK_PROFILE_POLICY)) {
+ Intent shareIntent = ActionIntentCreator.INSTANCE.createShareIntent(uri, null);
+ mActionExecutor.launchIntentAsync(shareIntent, null,
+ mScreenshotUserHandle.getIdentifier(), false);
+ } else {
+ Intent intent = new Intent(Intent.ACTION_SEND);
+ intent.setType("image/png");
+ intent.putExtra(Intent.EXTRA_STREAM, uri);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
+ | Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ Intent sharingChooserIntent = Intent.createChooser(intent, null)
+ .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- startActivityAsUser(sharingChooserIntent, UserHandle.CURRENT);
+ startActivityAsUser(sharingChooserIntent, UserHandle.CURRENT);
+ }
}
private void onClicked(View v) {
@@ -389,8 +418,8 @@
mOutputBitmap = renderBitmap(drawable, bounds);
ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export(
mBackgroundExecutor, UUID.randomUUID(), mOutputBitmap, ZonedDateTime.now(),
- // TODO: Owner must match the owner of the captured window.
- Process.myUserHandle());
+ mFeatureFlags.isEnabled(Flags.SCREENSHOT_WORK_PROFILE_POLICY)
+ ? mScreenshotUserHandle : Process.myUserHandle());
exportFuture.addListener(() -> onExportCompleted(action, exportFuture), mUiExecutor);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 9b5295d..d94c827 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -63,6 +63,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -276,6 +277,7 @@
mScreenshotNotificationSmartActionsProvider;
private final TimeoutHandler mScreenshotHandler;
private final ActionIntentExecutor mActionExecutor;
+ private final UserManager mUserManager;
private ScreenshotView mScreenshotView;
private Bitmap mScreenBitmap;
@@ -314,7 +316,8 @@
TimeoutHandler timeoutHandler,
BroadcastSender broadcastSender,
ScreenshotNotificationSmartActionsProvider screenshotNotificationSmartActionsProvider,
- ActionIntentExecutor actionExecutor
+ ActionIntentExecutor actionExecutor,
+ UserManager userManager
) {
mScreenshotSmartActions = screenshotSmartActions;
mNotificationsController = screenshotNotificationsController;
@@ -345,6 +348,7 @@
mWindowManager = mContext.getSystemService(WindowManager.class);
mFlags = flags;
mActionExecutor = actionExecutor;
+ mUserManager = userManager;
mAccessibilityManager = AccessibilityManager.getInstance(mContext);
@@ -587,7 +591,7 @@
// Wait until this window is attached to request because it is
// the reference used to locate the target window (below).
withWindowAttached(() -> {
- requestScrollCapture();
+ requestScrollCapture(owner);
mWindow.peekDecorView().getViewRootImpl().setActivityConfigCallback(
new ViewRootImpl.ActivityConfigCallback() {
@Override
@@ -599,11 +603,11 @@
mScreenshotView.hideScrollChip();
// Delay scroll capture eval a bit to allow the underlying activity
// to set up in the new orientation.
- mScreenshotHandler.postDelayed(
- ScreenshotController.this::requestScrollCapture, 150);
+ mScreenshotHandler.postDelayed(() -> {
+ requestScrollCapture(owner);
+ }, 150);
mScreenshotView.updateInsets(
- mWindowManager.getCurrentWindowMetrics()
- .getWindowInsets());
+ mWindowManager.getCurrentWindowMetrics().getWindowInsets());
// Screenshot animation calculations won't be valid anymore,
// so just end
if (mScreenshotAnimation != null
@@ -651,7 +655,7 @@
mScreenshotHandler.cancelTimeout(); // restarted after animation
}
- private void requestScrollCapture() {
+ private void requestScrollCapture(UserHandle owner) {
if (!allowLongScreenshots()) {
Log.d(TAG, "Long screenshots not supported on this device");
return;
@@ -664,10 +668,11 @@
mScrollCaptureClient.request(DEFAULT_DISPLAY);
mLastScrollCaptureRequest = future;
mLastScrollCaptureRequest.addListener(() ->
- onScrollCaptureResponseReady(future), mMainExecutor);
+ onScrollCaptureResponseReady(future, owner), mMainExecutor);
}
- private void onScrollCaptureResponseReady(Future<ScrollCaptureResponse> responseFuture) {
+ private void onScrollCaptureResponseReady(Future<ScrollCaptureResponse> responseFuture,
+ UserHandle owner) {
try {
if (mLastScrollCaptureResponse != null) {
mLastScrollCaptureResponse.close();
@@ -697,7 +702,7 @@
mScreenshotView.prepareScrollingTransition(response, mScreenBitmap, newScreenshot,
mScreenshotTakenInPortrait);
// delay starting scroll capture to make sure the scrim is up before the app moves
- mScreenshotView.post(() -> runBatchScrollCapture(response));
+ mScreenshotView.post(() -> runBatchScrollCapture(response, owner));
});
} catch (InterruptedException | ExecutionException e) {
Log.e(TAG, "requestScrollCapture failed", e);
@@ -706,7 +711,7 @@
ListenableFuture<ScrollCaptureController.LongScreenshot> mLongScreenshotFuture;
- private void runBatchScrollCapture(ScrollCaptureResponse response) {
+ private void runBatchScrollCapture(ScrollCaptureResponse response, UserHandle owner) {
// Clear the reference to prevent close() in dismissScreenshot
mLastScrollCaptureResponse = null;
@@ -740,6 +745,8 @@
longScreenshot));
final Intent intent = new Intent(mContext, LongScreenshotActivity.class);
+ intent.putExtra(LongScreenshotActivity.EXTRA_SCREENSHOT_USER_HANDLE,
+ owner);
intent.setFlags(
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
@@ -975,16 +982,25 @@
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
- mScreenshotView.setChipIntents(imageData);
+ doPostAnimation(imageData);
}
});
} else {
- mScreenshotView.setChipIntents(imageData);
+ doPostAnimation(imageData);
}
});
}
}
+ private void doPostAnimation(ScreenshotController.SavedImageData imageData) {
+ mScreenshotView.setChipIntents(imageData);
+ if (mFlags.isEnabled(SCREENSHOT_WORK_PROFILE_POLICY)
+ && mUserManager.isManagedProfile(imageData.owner.getIdentifier())) {
+ // TODO: Read app from configuration
+ mScreenshotView.showWorkProfileMessage("Files");
+ }
+ }
+
/**
* Sets up the action shade and its entrance animation, once we get the Quick Share action data.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
index 8b5a24c..c891686 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
@@ -89,7 +89,9 @@
@UiEvent(doc = "User has saved a long screenshot to a file")
SCREENSHOT_LONG_SCREENSHOT_SAVED(910),
@UiEvent(doc = "User has discarded the result of a long screenshot")
- SCREENSHOT_LONG_SCREENSHOT_EXIT(911);
+ SCREENSHOT_LONG_SCREENSHOT_EXIT(911),
+ @UiEvent(doc = "A screenshot has been taken and saved to work profile")
+ SCREENSHOT_SAVED_TO_WORK_PROFILE(1240);
private final int mId;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
index c41e2bc..4cb91e1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
@@ -15,12 +15,17 @@
*/
package com.android.systemui.screenshot
-import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.util.Log
+import androidx.lifecycle.LifecycleService
+import androidx.lifecycle.lifecycleScope
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.statusbar.phone.CentralSurfaces
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import java.util.Optional
import javax.inject.Inject
@@ -30,7 +35,8 @@
internal class ScreenshotProxyService @Inject constructor(
private val mExpansionMgr: ShadeExpansionStateManager,
private val mCentralSurfacesOptional: Optional<CentralSurfaces>,
-) : Service() {
+ @Main private val mMainDispatcher: CoroutineDispatcher,
+) : LifecycleService() {
private val mBinder: IBinder = object : IScreenshotProxy.Stub() {
/**
@@ -43,20 +49,28 @@
}
override fun dismissKeyguard(callback: IOnDoneCallback) {
- if (mCentralSurfacesOptional.isPresent) {
- mCentralSurfacesOptional.get().executeRunnableDismissingKeyguard(
- Runnable {
- callback.onDone(true)
- }, null,
- true /* dismissShade */, true /* afterKeyguardGone */,
- true /* deferred */
- )
- } else {
- callback.onDone(false)
+ lifecycleScope.launch {
+ executeAfterDismissing(callback)
}
}
}
+ private suspend fun executeAfterDismissing(callback: IOnDoneCallback) =
+ withContext(mMainDispatcher) {
+ mCentralSurfacesOptional.ifPresentOrElse(
+ {
+ it.executeRunnableDismissingKeyguard(
+ Runnable {
+ callback.onDone(true)
+ }, null,
+ true /* dismissShade */, true /* afterKeyguardGone */,
+ true /* deferred */
+ )
+ },
+ { callback.onDone(false) }
+ )
+ }
+
override fun onBind(intent: Intent): IBinder? {
Log.d(TAG, "onBind: $intent")
return mBinder
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 27331ae..0a4b550 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -80,6 +80,7 @@
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import android.widget.TextView;
import androidx.constraintlayout.widget.ConstraintLayout;
@@ -137,6 +138,8 @@
private ImageView mScrollingScrim;
private DraggableConstraintLayout mScreenshotStatic;
+ private ViewGroup mMessageContainer;
+ private TextView mMessageContent;
private ImageView mScreenshotPreview;
private ImageView mScreenshotBadge;
private View mScreenshotPreviewBorder;
@@ -340,10 +343,26 @@
}
}
+ /**
+ * Show a notification under the screenshot view indicating that a work profile screenshot has
+ * been taken and which app can be used to view it.
+ *
+ * @param appName The name of the app to use to view screenshots
+ */
+ void showWorkProfileMessage(String appName) {
+ mMessageContent.setText(
+ mContext.getString(R.string.screenshot_work_profile_notification, appName));
+ mMessageContainer.setVisibility(VISIBLE);
+ }
+
@Override // View
protected void onFinishInflate() {
mScrollingScrim = requireNonNull(findViewById(R.id.screenshot_scrolling_scrim));
mScreenshotStatic = requireNonNull(findViewById(R.id.screenshot_static));
+ mMessageContainer =
+ requireNonNull(mScreenshotStatic.findViewById(R.id.screenshot_message_container));
+ mMessageContent =
+ requireNonNull(mMessageContainer.findViewById(R.id.screenshot_message_content));
mScreenshotPreview = requireNonNull(findViewById(R.id.screenshot_preview));
mScreenshotPreviewBorder = requireNonNull(
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index cd5647e..200288b 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -107,6 +107,7 @@
val filter = IntentFilter().apply {
addAction(Intent.ACTION_USER_SWITCHED)
+ addAction(Intent.ACTION_USER_INFO_CHANGED)
// These get called when a managed profile goes in or out of quiet mode.
addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
@@ -124,6 +125,7 @@
Intent.ACTION_USER_SWITCHED -> {
handleSwitchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL))
}
+ Intent.ACTION_USER_INFO_CHANGED,
Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
Intent.ACTION_MANAGED_PROFILE_REMOVED,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/CameraLauncher.java b/packages/SystemUI/src/com/android/systemui/shade/CameraLauncher.java
new file mode 100644
index 0000000..fc61e90
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/CameraLauncher.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade;
+
+import com.android.systemui.camera.CameraGestureHelper;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
+
+import javax.inject.Inject;
+
+/** Handles launching camera from Shade. */
+@SysUISingleton
+public class CameraLauncher {
+ private final CameraGestureHelper mCameraGestureHelper;
+ private final KeyguardBypassController mKeyguardBypassController;
+
+ private boolean mLaunchingAffordance;
+
+ @Inject
+ public CameraLauncher(
+ CameraGestureHelper cameraGestureHelper,
+ KeyguardBypassController keyguardBypassController
+ ) {
+ mCameraGestureHelper = cameraGestureHelper;
+ mKeyguardBypassController = keyguardBypassController;
+ }
+
+ /** Launches the camera. */
+ public void launchCamera(int source, boolean isShadeFullyCollapsed) {
+ if (!isShadeFullyCollapsed) {
+ setLaunchingAffordance(true);
+ }
+
+ mCameraGestureHelper.launchCamera(source);
+ }
+
+ /**
+ * Set whether we are currently launching an affordance. This is currently only set when
+ * launched via a camera gesture.
+ */
+ public void setLaunchingAffordance(boolean launchingAffordance) {
+ mLaunchingAffordance = launchingAffordance;
+ mKeyguardBypassController.setLaunchingAffordance(launchingAffordance);
+ }
+
+ /**
+ * Return true when a bottom affordance is launching an occluded activity with a splash screen.
+ */
+ public boolean isLaunchingAffordance() {
+ return mLaunchingAffordance;
+ }
+
+ /**
+ * Whether the camera application can be launched for the camera launch gesture.
+ */
+ public boolean canCameraGestureBeLaunched(int barState) {
+ return mCameraGestureHelper.canCameraGestureBeLaunched(barState);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java b/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java
new file mode 100644
index 0000000..ae303eb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade;
+
+import android.annotation.NonNull;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
+
+import com.android.keyguard.LockIconViewController;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Drawable for NotificationPanelViewController.
+ */
+public class DebugDrawable extends Drawable {
+
+ private final NotificationPanelViewController mNotificationPanelViewController;
+ private final NotificationPanelView mView;
+ private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
+ private final LockIconViewController mLockIconViewController;
+ private final Set<Integer> mDebugTextUsedYPositions;
+ private final Paint mDebugPaint;
+
+ public DebugDrawable(
+ NotificationPanelViewController notificationPanelViewController,
+ NotificationPanelView notificationPanelView,
+ NotificationStackScrollLayoutController notificationStackScrollLayoutController,
+ LockIconViewController lockIconViewController
+ ) {
+ mNotificationPanelViewController = notificationPanelViewController;
+ mView = notificationPanelView;
+ mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
+ mLockIconViewController = lockIconViewController;
+ mDebugTextUsedYPositions = new HashSet<>();
+ mDebugPaint = new Paint();
+ }
+
+ @Override
+ public void draw(@androidx.annotation.NonNull @NonNull Canvas canvas) {
+ mDebugTextUsedYPositions.clear();
+
+ mDebugPaint.setColor(Color.RED);
+ mDebugPaint.setStrokeWidth(2);
+ mDebugPaint.setStyle(Paint.Style.STROKE);
+ mDebugPaint.setTextSize(24);
+ String headerDebugInfo = mNotificationPanelViewController.getHeaderDebugInfo();
+ if (headerDebugInfo != null) canvas.drawText(headerDebugInfo, 50, 100, mDebugPaint);
+
+ drawDebugInfo(canvas, mNotificationPanelViewController.getMaxPanelHeight(),
+ Color.RED, "getMaxPanelHeight()");
+ drawDebugInfo(canvas, (int) mNotificationPanelViewController.getExpandedHeight(),
+ Color.BLUE, "getExpandedHeight()");
+ drawDebugInfo(canvas, mNotificationPanelViewController.calculatePanelHeightQsExpanded(),
+ Color.GREEN, "calculatePanelHeightQsExpanded()");
+ drawDebugInfo(canvas, mNotificationPanelViewController.calculatePanelHeightQsExpanded(),
+ Color.YELLOW, "calculatePanelHeightShade()");
+ drawDebugInfo(canvas,
+ (int) mNotificationPanelViewController.calculateNotificationsTopPadding(),
+ Color.MAGENTA, "calculateNotificationsTopPadding()");
+ drawDebugInfo(canvas, mNotificationPanelViewController.getClockPositionResult().clockY,
+ Color.GRAY, "mClockPositionResult.clockY");
+ drawDebugInfo(canvas, (int) mLockIconViewController.getTop(), Color.GRAY,
+ "mLockIconViewController.getTop()");
+
+ if (mNotificationPanelViewController.getKeyguardShowing()) {
+ // Notifications have the space between those two lines.
+ drawDebugInfo(canvas,
+ mNotificationStackScrollLayoutController.getTop()
+ + (int) mNotificationPanelViewController
+ .getKeyguardNotificationTopPadding(),
+ Color.RED, "NSSL.getTop() + mKeyguardNotificationTopPadding");
+
+ drawDebugInfo(canvas, mNotificationStackScrollLayoutController.getBottom()
+ - (int) mNotificationPanelViewController
+ .getKeyguardNotificationBottomPadding(),
+ Color.RED, "NSSL.getBottom() - mKeyguardNotificationBottomPadding");
+ }
+
+ mDebugPaint.setColor(Color.CYAN);
+ canvas.drawLine(0,
+ mNotificationPanelViewController.getClockPositionResult().stackScrollerPadding,
+ mView.getWidth(), mNotificationStackScrollLayoutController.getTopPadding(),
+ mDebugPaint);
+ }
+
+ private void drawDebugInfo(Canvas canvas, int y, int color, String label) {
+ mDebugPaint.setColor(color);
+ canvas.drawLine(/* startX= */ 0, /* startY= */ y, /* stopX= */ mView.getWidth(),
+ /* stopY= */ y, mDebugPaint);
+ canvas.drawText(label + " = " + y + "px", /* x= */ 0,
+ /* y= */ computeDebugYTextPosition(y), mDebugPaint);
+ }
+
+ private int computeDebugYTextPosition(int lineY) {
+ if (lineY - mDebugPaint.getTextSize() < 0) {
+ // Avoiding drawing out of bounds
+ lineY += mDebugPaint.getTextSize();
+ }
+ int textY = lineY;
+ while (mDebugTextUsedYPositions.contains(textY)) {
+ textY = (int) (textY + mDebugPaint.getTextSize());
+ }
+ mDebugTextUsedYPositions.add(textY);
+ return textY;
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.UNKNOWN;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
index 6b540aa..63d0d16 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
@@ -246,6 +246,8 @@
qsCarrierGroup.updateTextAppearance(R.style.TextAppearance_QS_Status_Carriers)
if (header is MotionLayout) {
loadConstraints()
+ header.minHeight = resources
+ .getDimensionPixelSize(R.dimen.large_screen_shade_header_min_height)
lastInsets?.let { updateConstraintsForInsets(header, it) }
}
updateResources()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 3a624ca..ceef8c8 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -56,15 +56,10 @@
import android.content.ContentResolver;
import android.content.res.Resources;
import android.database.ContentObserver;
-import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.ColorFilter;
import android.graphics.Insets;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Region;
-import android.graphics.drawable.Drawable;
import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Bundle;
@@ -123,17 +118,18 @@
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
import com.android.systemui.DejankUtils;
+import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.animation.LaunchAnimator;
import com.android.systemui.biometrics.AuthController;
-import com.android.systemui.camera.CameraGestureHelper;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeLog;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.dump.DumpsysTableLogger;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
@@ -229,17 +225,15 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashSet;
import java.util.List;
import java.util.Optional;
-import java.util.Set;
import java.util.function.Consumer;
import javax.inject.Inject;
import javax.inject.Provider;
@CentralSurfacesComponent.CentralSurfacesScope
-public final class NotificationPanelViewController {
+public final class NotificationPanelViewController implements Dumpable {
public static final String TAG = NotificationPanelView.class.getSimpleName();
public static final float FLING_MAX_LENGTH_SECONDS = 0.6f;
@@ -455,7 +449,6 @@
* need to take this into account in our panel height calculation.
*/
private boolean mQsAnimatorExpand;
- private boolean mIsLaunchTransitionFinished;
private ValueAnimator mQsSizeChangeAnimator;
private boolean mQsScrimEnabled = true;
private boolean mQsTouchAboveFalsingThreshold;
@@ -470,7 +463,6 @@
private boolean mCollapsedOnDown;
private boolean mClosingWithAlphaFadeOut;
private boolean mHeadsUpAnimatingAway;
- private boolean mLaunchingAffordance;
private final FalsingManager mFalsingManager;
private final FalsingCollector mFalsingCollector;
@@ -581,7 +573,7 @@
/** Whether the current animator is resetting the pulse expansion after a drag down. */
private boolean mIsPulseExpansionResetAnimator;
- private final Rect mKeyguardStatusAreaClipBounds = new Rect();
+ private final Rect mLastQsClipBounds = new Rect();
private final Region mQsInterceptRegion = new Region();
/** Alpha of the views which only show on the keyguard but not in shade / shade locked. */
private float mKeyguardOnlyContentAlpha = 1.0f;
@@ -621,7 +613,6 @@
private final NotificationListContainer mNotificationListContainer;
private final NotificationStackSizeCalculator mNotificationStackSizeCalculator;
private final NPVCDownEventState.Buffer mLastDownEvents;
- private final CameraGestureHelper mCameraGestureHelper;
private final KeyguardBottomAreaViewModel mKeyguardBottomAreaViewModel;
private final KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor;
private float mMinExpandHeight;
@@ -749,9 +740,9 @@
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
ShadeTransitionController shadeTransitionController,
SystemClock systemClock,
- CameraGestureHelper cameraGestureHelper,
KeyguardBottomAreaViewModel keyguardBottomAreaViewModel,
- KeyguardBottomAreaInteractor keyguardBottomAreaInteractor) {
+ KeyguardBottomAreaInteractor keyguardBottomAreaInteractor,
+ DumpManager dumpManager) {
keyguardStateController.addCallback(new KeyguardStateController.Callback() {
@Override
public void onKeyguardFadingAwayChanged() {
@@ -900,7 +891,8 @@
mView.setOnApplyWindowInsetsListener((v, insets) -> onApplyShadeWindowInsets(insets));
if (DEBUG_DRAWABLE) {
- mView.getOverlay().add(new DebugDrawable());
+ mView.getOverlay().add(new DebugDrawable(this, mView,
+ mNotificationStackScrollLayoutController, mLockIconViewController));
}
mKeyguardUnfoldTransition = unfoldComponent.map(
@@ -928,8 +920,8 @@
unlockAnimationStarted(playingCannedAnimation, isWakeAndUnlock, startDelay);
}
});
- mCameraGestureHelper = cameraGestureHelper;
mKeyguardBottomAreaInteractor = keyguardBottomAreaInteractor;
+ dumpManager.registerDumpable(this);
}
private void unlockAnimationFinished() {
@@ -1153,8 +1145,15 @@
mLargeScreenShadeHeaderHeight =
mResources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height);
- mQuickQsHeaderHeight = mUseLargeScreenShadeHeader ? mLargeScreenShadeHeaderHeight :
- SystemBarUtils.getQuickQsOffsetHeight(mView.getContext());
+ // TODO: When the flag is eventually removed, it means that we have a single view that is
+ // the same height in QQS and in Large Screen (large_screen_shade_header_height). Eventually
+ // the concept of largeScreenHeader or quickQsHeader will disappear outside of the class
+ // that controls the view as the offset needs to be the same regardless.
+ if (mUseLargeScreenShadeHeader || mFeatureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)) {
+ mQuickQsHeaderHeight = mLargeScreenShadeHeaderHeight;
+ } else {
+ mQuickQsHeaderHeight = SystemBarUtils.getQuickQsOffsetHeight(mView.getContext());
+ }
int topMargin = mUseLargeScreenShadeHeader ? mLargeScreenShadeHeaderHeight :
mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_top);
mLargeScreenShadeHeaderController.setLargeScreenActive(mUseLargeScreenShadeHeader);
@@ -1309,7 +1308,11 @@
}
private void initBottomArea() {
- mKeyguardBottomArea.init(mKeyguardBottomAreaViewModel, mFalsingManager);
+ mKeyguardBottomArea.init(
+ mKeyguardBottomAreaViewModel,
+ mFalsingManager,
+ mLockIconViewController
+ );
}
@VisibleForTesting
@@ -1506,6 +1509,10 @@
updateClock();
}
+ public KeyguardClockPositionAlgorithm.Result getClockPositionResult() {
+ return mClockPositionResult;
+ }
+
@ClockSize
private int computeDesiredClockSize() {
if (mSplitShadeEnabled) {
@@ -1753,7 +1760,6 @@
}
public void resetViews(boolean animate) {
- mIsLaunchTransitionFinished = false;
mCentralSurfaces.getGutsManager().closeAndSaveGuts(true /* leavebehind */, true /* force */,
true /* controls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
if (animate && !isFullyCollapsed()) {
@@ -2803,7 +2809,7 @@
*/
private void applyQSClippingBounds(int left, int top, int right, int bottom,
boolean qsVisible) {
- if (!mAnimateNextNotificationBounds || mKeyguardStatusAreaClipBounds.isEmpty()) {
+ if (!mAnimateNextNotificationBounds || mLastQsClipBounds.isEmpty()) {
if (mQsClippingAnimation != null) {
// update the end position of the animator
mQsClippingAnimationEndBounds.set(left, top, right, bottom);
@@ -2812,10 +2818,10 @@
}
} else {
mQsClippingAnimationEndBounds.set(left, top, right, bottom);
- final int startLeft = mKeyguardStatusAreaClipBounds.left;
- final int startTop = mKeyguardStatusAreaClipBounds.top;
- final int startRight = mKeyguardStatusAreaClipBounds.right;
- final int startBottom = mKeyguardStatusAreaClipBounds.bottom;
+ final int startLeft = mLastQsClipBounds.left;
+ final int startTop = mLastQsClipBounds.top;
+ final int startRight = mLastQsClipBounds.right;
+ final int startBottom = mLastQsClipBounds.bottom;
if (mQsClippingAnimation != null) {
mQsClippingAnimation.cancel();
}
@@ -2852,12 +2858,10 @@
private void applyQSClippingImmediately(int left, int top, int right, int bottom,
boolean qsVisible) {
- // Fancy clipping for quick settings
int radius = mScrimCornerRadius;
boolean clipStatusView = false;
+ mLastQsClipBounds.set(left, top, right, bottom);
if (mIsFullWidth) {
- // The padding on this area is large enough that we can use a cheaper clipping strategy
- mKeyguardStatusAreaClipBounds.set(left, top, right, bottom);
clipStatusView = qsVisible;
float screenCornerRadius = mRecordingController.isRecording() ? 0 : mScreenCornerRadius;
radius = (int) MathUtils.lerp(screenCornerRadius, mScrimCornerRadius,
@@ -2892,8 +2896,8 @@
radius,
qsVisible && !mSplitShadeEnabled);
}
- mKeyguardStatusViewController.setClipBounds(
- clipStatusView ? mKeyguardStatusAreaClipBounds : null);
+ // The padding on this area is large enough that we can use a cheaper clipping strategy
+ mKeyguardStatusViewController.setClipBounds(clipStatusView ? mLastQsClipBounds : null);
if (!qsVisible && mSplitShadeEnabled) {
// On the lockscreen when qs isn't visible, we don't want the bounds of the shade to
// be visible, otherwise you can see the bounds once swiping up to see bouncer
@@ -2971,7 +2975,7 @@
}
}
- private float calculateNotificationsTopPadding() {
+ float calculateNotificationsTopPadding() {
if (mSplitShadeEnabled) {
return mKeyguardShowing ? getKeyguardNotificationStaticPadding() : 0;
}
@@ -3005,6 +3009,18 @@
}
}
+ public boolean getKeyguardShowing() {
+ return mKeyguardShowing;
+ }
+
+ public float getKeyguardNotificationTopPadding() {
+ return mKeyguardNotificationTopPadding;
+ }
+
+ public float getKeyguardNotificationBottomPadding() {
+ return mKeyguardNotificationBottomPadding;
+ }
+
/** Returns the topPadding of notifications when on keyguard not respecting QS expansion. */
private int getKeyguardNotificationStaticPadding() {
if (!mKeyguardShowing) {
@@ -3274,7 +3290,6 @@
return !mSplitShadeEnabled && (isInSettings() || mIsPanelCollapseOnQQS);
}
- @VisibleForTesting
int getMaxPanelHeight() {
int min = mStatusBarMinHeight;
if (!(mBarState == KEYGUARD)
@@ -3372,11 +3387,7 @@
boolean isExpanded = !isFullyCollapsed() || mExpectingSynthesizedDown;
if (mPanelExpanded != isExpanded) {
mPanelExpanded = isExpanded;
-
- mHeadsUpManager.setIsPanelExpanded(isExpanded);
- mStatusBarTouchableRegionManager.setPanelExpanded(isExpanded);
- mCentralSurfaces.setPanelExpanded(isExpanded);
-
+ mShadeExpansionStateManager.onShadeExpansionFullyChanged(isExpanded);
if (!isExpanded && mQs != null && mQs.isCustomizing()) {
mQs.closeCustomizer();
}
@@ -3400,7 +3411,7 @@
}
}
- private int calculatePanelHeightQsExpanded() {
+ int calculatePanelHeightQsExpanded() {
float
notificationHeight =
mNotificationStackScrollLayoutController.getHeight()
@@ -3779,10 +3790,6 @@
mQs.closeCustomizer();
}
- public boolean isLaunchTransitionFinished() {
- return mIsLaunchTransitionFinished;
- }
-
public void setIsLaunchAnimationRunning(boolean running) {
boolean wasRunning = mIsLaunchAnimationRunning;
mIsLaunchAnimationRunning = running;
@@ -3934,6 +3941,10 @@
}
}
+ public int getBarState() {
+ return mBarState;
+ }
+
private boolean isOnKeyguard() {
return mBarState == KEYGUARD;
}
@@ -3979,35 +3990,6 @@
&& mBarState == StatusBarState.SHADE;
}
- /** Launches the camera. */
- public void launchCamera(int source) {
- if (!isFullyCollapsed()) {
- setLaunchingAffordance(true);
- }
-
- mCameraGestureHelper.launchCamera(source);
- }
-
- public void onAffordanceLaunchEnded() {
- setLaunchingAffordance(false);
- }
-
- /** Set whether we are currently launching an affordance (i.e. camera gesture). */
- private void setLaunchingAffordance(boolean launchingAffordance) {
- mLaunchingAffordance = launchingAffordance;
- mKeyguardBypassController.setLaunchingAffordance(launchingAffordance);
- }
-
- /** Returns whether a bottom affordance is launching an occluded activity with splash screen. */
- public boolean isLaunchingAffordanceWithPreview() {
- return mLaunchingAffordance;
- }
-
- /** Whether the camera application can be launched by the camera launch gesture. */
- public boolean canCameraGestureBeLaunched() {
- return mCameraGestureHelper.canCameraGestureBeLaunched(mBarState);
- }
-
public boolean hideStatusBarIconsWhenExpanded() {
if (mIsLaunchAnimationRunning) {
return mHideIconsDuringLaunchAnimation;
@@ -4273,29 +4255,184 @@
mBlockingExpansionForCurrentTouch = mTracking;
}
+ @Override
public void dump(PrintWriter pw, String[] args) {
- pw.println(String.format("[PanelView(%s): expandedHeight=%f maxPanelHeight=%d closing=%s"
- + " tracking=%s timeAnim=%s%s "
- + "touchDisabled=%s" + "]",
- this.getClass().getSimpleName(), getExpandedHeight(), getMaxPanelHeight(),
- mClosing ? "T" : "f", mTracking ? "T" : "f", mHeightAnimator,
- ((mHeightAnimator != null && mHeightAnimator.isStarted()) ? " (started)" : ""),
- mTouchDisabled ? "T" : "f"));
+ pw.println(TAG + ":");
IndentingPrintWriter ipw = asIndenting(pw);
ipw.increaseIndent();
+
+ ipw.print("mDownTime="); ipw.println(mDownTime);
+ ipw.print("mTouchSlopExceededBeforeDown="); ipw.println(mTouchSlopExceededBeforeDown);
+ ipw.print("mIsLaunchAnimationRunning="); ipw.println(mIsLaunchAnimationRunning);
+ ipw.print("mOverExpansion="); ipw.println(mOverExpansion);
+ ipw.print("mExpandedHeight="); ipw.println(mExpandedHeight);
+ ipw.print("mTracking="); ipw.println(mTracking);
+ ipw.print("mHintAnimationRunning="); ipw.println(mHintAnimationRunning);
+ ipw.print("mExpanding="); ipw.println(mExpanding);
+ ipw.print("mSplitShadeEnabled="); ipw.println(mSplitShadeEnabled);
+ ipw.print("mKeyguardNotificationBottomPadding=");
+ ipw.println(mKeyguardNotificationBottomPadding);
+ ipw.print("mKeyguardNotificationTopPadding="); ipw.println(mKeyguardNotificationTopPadding);
+ ipw.print("mMaxAllowedKeyguardNotifications=");
+ ipw.println(mMaxAllowedKeyguardNotifications);
+ ipw.print("mAnimateNextPositionUpdate="); ipw.println(mAnimateNextPositionUpdate);
+ ipw.print("mQuickQsHeaderHeight="); ipw.println(mQuickQsHeaderHeight);
+ ipw.print("mQsTrackingPointer="); ipw.println(mQsTrackingPointer);
+ ipw.print("mQsTracking="); ipw.println(mQsTracking);
+ ipw.print("mConflictingQsExpansionGesture="); ipw.println(mConflictingQsExpansionGesture);
+ ipw.print("mPanelExpanded="); ipw.println(mPanelExpanded);
+ ipw.print("mQsExpanded="); ipw.println(mQsExpanded);
+ ipw.print("mQsExpandedWhenExpandingStarted="); ipw.println(mQsExpandedWhenExpandingStarted);
+ ipw.print("mQsFullyExpanded="); ipw.println(mQsFullyExpanded);
+ ipw.print("mKeyguardShowing="); ipw.println(mKeyguardShowing);
+ ipw.print("mKeyguardQsUserSwitchEnabled="); ipw.println(mKeyguardQsUserSwitchEnabled);
+ ipw.print("mKeyguardUserSwitcherEnabled="); ipw.println(mKeyguardUserSwitcherEnabled);
+ ipw.print("mDozing="); ipw.println(mDozing);
+ ipw.print("mDozingOnDown="); ipw.println(mDozingOnDown);
+ ipw.print("mBouncerShowing="); ipw.println(mBouncerShowing);
+ ipw.print("mBarState="); ipw.println(mBarState);
+ ipw.print("mInitialHeightOnTouch="); ipw.println(mInitialHeightOnTouch);
+ ipw.print("mInitialTouchX="); ipw.println(mInitialTouchX);
+ ipw.print("mInitialTouchY="); ipw.println(mInitialTouchY);
+ ipw.print("mQsExpansionHeight="); ipw.println(mQsExpansionHeight);
+ ipw.print("mQsMinExpansionHeight="); ipw.println(mQsMinExpansionHeight);
+ ipw.print("mQsMaxExpansionHeight="); ipw.println(mQsMaxExpansionHeight);
+ ipw.print("mQsPeekHeight="); ipw.println(mQsPeekHeight);
+ ipw.print("mStackScrollerOverscrolling="); ipw.println(mStackScrollerOverscrolling);
+ ipw.print("mQsExpansionFromOverscroll="); ipw.println(mQsExpansionFromOverscroll);
+ ipw.print("mLastOverscroll="); ipw.println(mLastOverscroll);
+ ipw.print("mQsExpansionEnabledPolicy="); ipw.println(mQsExpansionEnabledPolicy);
+ ipw.print("mQsExpansionEnabledAmbient="); ipw.println(mQsExpansionEnabledAmbient);
+ ipw.print("mStatusBarMinHeight="); ipw.println(mStatusBarMinHeight);
+ ipw.print("mStatusBarHeaderHeightKeyguard="); ipw.println(mStatusBarHeaderHeightKeyguard);
+ ipw.print("mOverStretchAmount="); ipw.println(mOverStretchAmount);
+ ipw.print("mDownX="); ipw.println(mDownX);
+ ipw.print("mDownY="); ipw.println(mDownY);
+ ipw.print("mDisplayTopInset="); ipw.println(mDisplayTopInset);
+ ipw.print("mDisplayRightInset="); ipw.println(mDisplayRightInset);
+ ipw.print("mLargeScreenShadeHeaderHeight="); ipw.println(mLargeScreenShadeHeaderHeight);
+ ipw.print("mSplitShadeNotificationsScrimMarginBottom=");
+ ipw.println(mSplitShadeNotificationsScrimMarginBottom);
+ ipw.print("mIsExpanding="); ipw.println(mIsExpanding);
+ ipw.print("mQsExpandImmediate="); ipw.println(mQsExpandImmediate);
+ ipw.print("mTwoFingerQsExpandPossible="); ipw.println(mTwoFingerQsExpandPossible);
+ ipw.print("mHeaderDebugInfo="); ipw.println(mHeaderDebugInfo);
+ ipw.print("mQsAnimatorExpand="); ipw.println(mQsAnimatorExpand);
+ ipw.print("mQsScrimEnabled="); ipw.println(mQsScrimEnabled);
+ ipw.print("mQsTouchAboveFalsingThreshold="); ipw.println(mQsTouchAboveFalsingThreshold);
+ ipw.print("mQsFalsingThreshold="); ipw.println(mQsFalsingThreshold);
+ ipw.print("mHeadsUpStartHeight="); ipw.println(mHeadsUpStartHeight);
+ ipw.print("mListenForHeadsUp="); ipw.println(mListenForHeadsUp);
+ ipw.print("mNavigationBarBottomHeight="); ipw.println(mNavigationBarBottomHeight);
+ ipw.print("mExpandingFromHeadsUp="); ipw.println(mExpandingFromHeadsUp);
+ ipw.print("mCollapsedOnDown="); ipw.println(mCollapsedOnDown);
+ ipw.print("mClosingWithAlphaFadeOut="); ipw.println(mClosingWithAlphaFadeOut);
+ ipw.print("mHeadsUpAnimatingAway="); ipw.println(mHeadsUpAnimatingAway);
+ ipw.print("mShowIconsWhenExpanded="); ipw.println(mShowIconsWhenExpanded);
+ ipw.print("mIndicationBottomPadding="); ipw.println(mIndicationBottomPadding);
+ ipw.print("mAmbientIndicationBottomPadding="); ipw.println(mAmbientIndicationBottomPadding);
+ ipw.print("mIsFullWidth="); ipw.println(mIsFullWidth);
+ ipw.print("mBlockingExpansionForCurrentTouch=");
+ ipw.println(mBlockingExpansionForCurrentTouch);
+ ipw.print("mExpectingSynthesizedDown="); ipw.println(mExpectingSynthesizedDown);
+ ipw.print("mLastEventSynthesizedDown="); ipw.println(mLastEventSynthesizedDown);
+ ipw.print("mInterpolatedDarkAmount="); ipw.println(mInterpolatedDarkAmount);
+ ipw.print("mLinearDarkAmount="); ipw.println(mLinearDarkAmount);
+ ipw.print("mPulsing="); ipw.println(mPulsing);
+ ipw.print("mHideIconsDuringLaunchAnimation="); ipw.println(mHideIconsDuringLaunchAnimation);
+ ipw.print("mStackScrollerMeasuringPass="); ipw.println(mStackScrollerMeasuringPass);
+ ipw.print("mPanelAlpha="); ipw.println(mPanelAlpha);
+ ipw.print("mBottomAreaShadeAlpha="); ipw.println(mBottomAreaShadeAlpha);
+ ipw.print("mHeadsUpInset="); ipw.println(mHeadsUpInset);
+ ipw.print("mHeadsUpPinnedMode="); ipw.println(mHeadsUpPinnedMode);
+ ipw.print("mAllowExpandForSmallExpansion="); ipw.println(mAllowExpandForSmallExpansion);
+ ipw.print("mLockscreenNotificationQSPadding=");
+ ipw.println(mLockscreenNotificationQSPadding);
+ ipw.print("mTransitioningToFullShadeProgress=");
+ ipw.println(mTransitioningToFullShadeProgress);
+ ipw.print("mTransitionToFullShadeQSPosition=");
+ ipw.println(mTransitionToFullShadeQSPosition);
+ ipw.print("mDistanceForQSFullShadeTransition=");
+ ipw.println(mDistanceForQSFullShadeTransition);
+ ipw.print("mQsTranslationForFullShadeTransition=");
+ ipw.println(mQsTranslationForFullShadeTransition);
+ ipw.print("mMaxOverscrollAmountForPulse="); ipw.println(mMaxOverscrollAmountForPulse);
+ ipw.print("mAnimateNextNotificationBounds="); ipw.println(mAnimateNextNotificationBounds);
+ ipw.print("mNotificationBoundsAnimationDelay=");
+ ipw.println(mNotificationBoundsAnimationDelay);
+ ipw.print("mNotificationBoundsAnimationDuration=");
+ ipw.println(mNotificationBoundsAnimationDuration);
+ ipw.print("mIsPanelCollapseOnQQS="); ipw.println(mIsPanelCollapseOnQQS);
+ ipw.print("mAnimatingQS="); ipw.println(mAnimatingQS);
+ ipw.print("mIsQsTranslationResetAnimator="); ipw.println(mIsQsTranslationResetAnimator);
+ ipw.print("mIsPulseExpansionResetAnimator="); ipw.println(mIsPulseExpansionResetAnimator);
+ ipw.print("mKeyguardOnlyContentAlpha="); ipw.println(mKeyguardOnlyContentAlpha);
+ ipw.print("mKeyguardOnlyTransitionTranslationY=");
+ ipw.println(mKeyguardOnlyTransitionTranslationY);
+ ipw.print("mUdfpsMaxYBurnInOffset="); ipw.println(mUdfpsMaxYBurnInOffset);
+ ipw.print("mIsGestureNavigation="); ipw.println(mIsGestureNavigation);
+ ipw.print("mOldLayoutDirection="); ipw.println(mOldLayoutDirection);
+ ipw.print("mScrimCornerRadius="); ipw.println(mScrimCornerRadius);
+ ipw.print("mScreenCornerRadius="); ipw.println(mScreenCornerRadius);
+ ipw.print("mQSAnimatingHiddenFromCollapsed="); ipw.println(mQSAnimatingHiddenFromCollapsed);
+ ipw.print("mUseLargeScreenShadeHeader="); ipw.println(mUseLargeScreenShadeHeader);
+ ipw.print("mEnableQsClipping="); ipw.println(mEnableQsClipping);
+ ipw.print("mQsClipTop="); ipw.println(mQsClipTop);
+ ipw.print("mQsClipBottom="); ipw.println(mQsClipBottom);
+ ipw.print("mQsVisible="); ipw.println(mQsVisible);
+ ipw.print("mMinFraction="); ipw.println(mMinFraction);
+ ipw.print("mStatusViewCentered="); ipw.println(mStatusViewCentered);
+ ipw.print("mSplitShadeFullTransitionDistance=");
+ ipw.println(mSplitShadeFullTransitionDistance);
+ ipw.print("mSplitShadeScrimTransitionDistance=");
+ ipw.println(mSplitShadeScrimTransitionDistance);
+ ipw.print("mMinExpandHeight="); ipw.println(mMinExpandHeight);
+ ipw.print("mPanelUpdateWhenAnimatorEnds="); ipw.println(mPanelUpdateWhenAnimatorEnds);
+ ipw.print("mHasVibratedOnOpen="); ipw.println(mHasVibratedOnOpen);
+ ipw.print("mFixedDuration="); ipw.println(mFixedDuration);
+ ipw.print("mPanelFlingOvershootAmount="); ipw.println(mPanelFlingOvershootAmount);
+ ipw.print("mLastGesturedOverExpansion="); ipw.println(mLastGesturedOverExpansion);
+ ipw.print("mIsSpringBackAnimation="); ipw.println(mIsSpringBackAnimation);
+ ipw.print("mInSplitShade="); ipw.println(mInSplitShade);
+ ipw.print("mHintDistance="); ipw.println(mHintDistance);
+ ipw.print("mInitialOffsetOnTouch="); ipw.println(mInitialOffsetOnTouch);
+ ipw.print("mCollapsedAndHeadsUpOnDown="); ipw.println(mCollapsedAndHeadsUpOnDown);
+ ipw.print("mExpandedFraction="); ipw.println(mExpandedFraction);
+ ipw.print("mExpansionDragDownAmountPx="); ipw.println(mExpansionDragDownAmountPx);
+ ipw.print("mPanelClosedOnDown="); ipw.println(mPanelClosedOnDown);
+ ipw.print("mHasLayoutedSinceDown="); ipw.println(mHasLayoutedSinceDown);
+ ipw.print("mUpdateFlingVelocity="); ipw.println(mUpdateFlingVelocity);
+ ipw.print("mUpdateFlingOnLayout="); ipw.println(mUpdateFlingOnLayout);
+ ipw.print("mClosing="); ipw.println(mClosing);
+ ipw.print("mTouchSlopExceeded="); ipw.println(mTouchSlopExceeded);
+ ipw.print("mTrackingPointer="); ipw.println(mTrackingPointer);
+ ipw.print("mTouchSlop="); ipw.println(mTouchSlop);
+ ipw.print("mSlopMultiplier="); ipw.println(mSlopMultiplier);
+ ipw.print("mTouchAboveFalsingThreshold="); ipw.println(mTouchAboveFalsingThreshold);
+ ipw.print("mTouchStartedInEmptyArea="); ipw.println(mTouchStartedInEmptyArea);
+ ipw.print("mMotionAborted="); ipw.println(mMotionAborted);
+ ipw.print("mUpwardsWhenThresholdReached="); ipw.println(mUpwardsWhenThresholdReached);
+ ipw.print("mAnimatingOnDown="); ipw.println(mAnimatingOnDown);
+ ipw.print("mHandlingPointerUp="); ipw.println(mHandlingPointerUp);
+ ipw.print("mInstantExpanding="); ipw.println(mInstantExpanding);
+ ipw.print("mAnimateAfterExpanding="); ipw.println(mAnimateAfterExpanding);
+ ipw.print("mIsFlinging="); ipw.println(mIsFlinging);
+ ipw.print("mViewName="); ipw.println(mViewName);
+ ipw.print("mInitialExpandY="); ipw.println(mInitialExpandY);
+ ipw.print("mInitialExpandX="); ipw.println(mInitialExpandX);
+ ipw.print("mTouchDisabled="); ipw.println(mTouchDisabled);
+ ipw.print("mInitialTouchFromKeyguard="); ipw.println(mInitialTouchFromKeyguard);
+ ipw.print("mNextCollapseSpeedUpFactor="); ipw.println(mNextCollapseSpeedUpFactor);
+ ipw.print("mGestureWaitForTouchSlop="); ipw.println(mGestureWaitForTouchSlop);
+ ipw.print("mIgnoreXTouchSlop="); ipw.println(mIgnoreXTouchSlop);
+ ipw.print("mExpandLatencyTracking="); ipw.println(mExpandLatencyTracking);
+ ipw.print("mExpandLatencyTracking="); ipw.println(mExpandLatencyTracking);
ipw.println("gestureExclusionRect:" + calculateGestureExclusionRect());
- ipw.println("applyQSClippingImmediately: top(" + mQsClipTop + ") bottom(" + mQsClipBottom
- + ")");
- ipw.println("qsVisible:" + mQsVisible);
new DumpsysTableLogger(
TAG,
NPVCDownEventState.TABLE_HEADERS,
mLastDownEvents.toList()
).printTableData(ipw);
- ipw.decreaseIndent();
- if (mKeyguardStatusBarViewController != null) {
- mKeyguardStatusBarViewController.dump(pw, args);
- }
}
@@ -4368,6 +4505,10 @@
if (DEBUG_DRAWABLE) mHeaderDebugInfo = text;
}
+ public String getHeaderDebugInfo() {
+ return mHeaderDebugInfo;
+ }
+
public void onThemeChanged() {
mConfigurationListener.onThemeChanged();
}
@@ -4643,7 +4784,7 @@
mUpdateFlingVelocity = vel;
}
} else if (!mCentralSurfaces.isBouncerShowing()
- && !mStatusBarKeyguardViewManager.isShowingAlternateAuth()
+ && !mStatusBarKeyguardViewManager.isShowingAlternateBouncer()
&& !mKeyguardStateController.isKeyguardGoingAway()) {
onEmptySpaceClick();
onTrackingStopped(true);
@@ -4817,7 +4958,6 @@
setExpandedHeight(getMaxPanelTransitionDistance() * frac);
}
- @VisibleForTesting
float getExpandedHeight() {
return mExpandedHeight;
}
@@ -5519,89 +5659,6 @@
}
}
- private final class DebugDrawable extends Drawable {
- private final Set<Integer> mDebugTextUsedYPositions = new HashSet<>();
- private final Paint mDebugPaint = new Paint();
-
- @Override
- public void draw(@NonNull Canvas canvas) {
- mDebugTextUsedYPositions.clear();
-
- mDebugPaint.setColor(Color.RED);
- mDebugPaint.setStrokeWidth(2);
- mDebugPaint.setStyle(Paint.Style.STROKE);
- mDebugPaint.setTextSize(24);
- if (mHeaderDebugInfo != null) canvas.drawText(mHeaderDebugInfo, 50, 100, mDebugPaint);
-
- drawDebugInfo(canvas, getMaxPanelHeight(), Color.RED, "getMaxPanelHeight()");
- drawDebugInfo(canvas, (int) getExpandedHeight(), Color.BLUE, "getExpandedHeight()");
- drawDebugInfo(canvas, calculatePanelHeightQsExpanded(), Color.GREEN,
- "calculatePanelHeightQsExpanded()");
- drawDebugInfo(canvas, calculatePanelHeightShade(), Color.YELLOW,
- "calculatePanelHeightShade()");
- drawDebugInfo(canvas, (int) calculateNotificationsTopPadding(), Color.MAGENTA,
- "calculateNotificationsTopPadding()");
- drawDebugInfo(canvas, mClockPositionResult.clockY, Color.GRAY,
- "mClockPositionResult.clockY");
- drawDebugInfo(canvas, (int) mLockIconViewController.getTop(), Color.GRAY,
- "mLockIconViewController.getTop()");
-
- if (mKeyguardShowing) {
- // Notifications have the space between those two lines.
- drawDebugInfo(canvas,
- mNotificationStackScrollLayoutController.getTop() +
- (int) mKeyguardNotificationTopPadding,
- Color.RED,
- "NSSL.getTop() + mKeyguardNotificationTopPadding");
-
- drawDebugInfo(canvas, mNotificationStackScrollLayoutController.getBottom() -
- (int) mKeyguardNotificationBottomPadding,
- Color.RED,
- "NSSL.getBottom() - mKeyguardNotificationBottomPadding");
- }
-
- mDebugPaint.setColor(Color.CYAN);
- canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, mView.getWidth(),
- mNotificationStackScrollLayoutController.getTopPadding(), mDebugPaint);
- }
-
- private void drawDebugInfo(Canvas canvas, int y, int color, String label) {
- mDebugPaint.setColor(color);
- canvas.drawLine(/* startX= */ 0, /* startY= */ y, /* stopX= */ mView.getWidth(),
- /* stopY= */ y, mDebugPaint);
- canvas.drawText(label + " = " + y + "px", /* x= */ 0,
- /* y= */ computeDebugYTextPosition(y), mDebugPaint);
- }
-
- private int computeDebugYTextPosition(int lineY) {
- if (lineY - mDebugPaint.getTextSize() < 0) {
- // Avoiding drawing out of bounds
- lineY += mDebugPaint.getTextSize();
- }
- int textY = lineY;
- while (mDebugTextUsedYPositions.contains(textY)) {
- textY = (int) (textY + mDebugPaint.getTextSize());
- }
- mDebugTextUsedYPositions.add(textY);
- return textY;
- }
-
- @Override
- public void setAlpha(int alpha) {
-
- }
-
- @Override
- public void setColorFilter(ColorFilter colorFilter) {
-
- }
-
- @Override
- public int getOpacity() {
- return PixelFormat.UNKNOWN;
- }
- }
-
@NonNull
private WindowInsets onApplyShadeWindowInsets(WindowInsets insets) {
// the same types of insets that are handled in NotificationShadeWindowView
@@ -6111,7 +6168,7 @@
== AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD.getId()
|| action
== AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP.getId()) {
- mStatusBarKeyguardViewManager.showBouncer(true);
+ mStatusBarKeyguardViewManager.showPrimaryBouncer(true);
return true;
}
return super.performAccessibilityAction(host, action, args);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 66a22f4..b719177 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -44,6 +44,7 @@
import android.view.WindowManager.LayoutParams;
import android.view.WindowManagerGlobal;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
@@ -158,6 +159,7 @@
SysuiStatusBarStateController.RANK_STATUS_BAR_WINDOW_CONTROLLER);
configurationController.addCallback(this);
shadeExpansionStateManager.addQsExpansionListener(this::onQsExpansionChanged);
+ shadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged);
float desiredPreferredRefreshRate = context.getResources()
.getInteger(R.integer.config_keyguardRefreshRate);
@@ -204,6 +206,14 @@
}
}
+ @VisibleForTesting
+ void onShadeExpansionFullyChanged(Boolean isExpanded) {
+ if (mCurrentState.mPanelExpanded != isExpanded) {
+ mCurrentState.mPanelExpanded = isExpanded;
+ apply(mCurrentState);
+ }
+ }
+
/**
* Register a listener to monitor scrims visibility
* @param listener A listener to monitor scrims visibility
@@ -699,15 +709,6 @@
}
@Override
- public void setPanelExpanded(boolean isExpanded) {
- if (mCurrentState.mPanelExpanded == isExpanded) {
- return;
- }
- mCurrentState.mPanelExpanded = isExpanded;
- apply(mCurrentState);
- }
-
- @Override
public void onRemoteInputActive(boolean remoteInputActive) {
mCurrentState.mRemoteInputActive = remoteInputActive;
apply(mCurrentState);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
index e52170e..400b0ba 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
@@ -16,6 +16,7 @@
package com.android.systemui.shade;
+import static android.os.Trace.TRACE_TAG_ALWAYS;
import static android.view.WindowInsets.Type.systemBars;
import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG;
@@ -33,6 +34,7 @@
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Trace;
import android.util.AttributeSet;
import android.view.ActionMode;
import android.view.DisplayCutout;
@@ -299,6 +301,19 @@
return mode;
}
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ Trace.beginSection("NotificationShadeWindowView#onMeasure");
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ Trace.endSection();
+ }
+
+ @Override
+ public void requestLayout() {
+ Trace.instant(TRACE_TAG_ALWAYS, "NotificationShadeWindowView#requestLayout");
+ super.requestLayout();
+ }
+
private class ActionModeCallback2Wrapper extends ActionMode.Callback2 {
private final ActionMode.Callback mWrapped;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 1e63b2d..bb67280c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -284,7 +284,7 @@
return true;
}
- if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) {
+ if (mStatusBarKeyguardViewManager.isShowingAlternateBouncer()) {
// capture all touches if the alt auth bouncer is showing
return true;
}
@@ -322,7 +322,7 @@
handled = !mService.isPulsing();
}
- if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) {
+ if (mStatusBarKeyguardViewManager.isShowingAlternateBouncer()) {
// eat the touch
handled = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
index 667392c..a1767cc 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
@@ -34,6 +34,7 @@
class ShadeExpansionStateManager @Inject constructor() : ShadeStateEvents {
private val expansionListeners = CopyOnWriteArrayList<ShadeExpansionListener>()
+ private val fullExpansionListeners = CopyOnWriteArrayList<ShadeFullExpansionListener>()
private val qsExpansionListeners = CopyOnWriteArrayList<ShadeQsExpansionListener>()
private val stateListeners = CopyOnWriteArrayList<ShadeStateListener>()
private val shadeStateEventsListeners = CopyOnWriteArrayList<ShadeStateEventsListener>()
@@ -62,6 +63,15 @@
expansionListeners.remove(listener)
}
+ fun addFullExpansionListener(listener: ShadeFullExpansionListener) {
+ fullExpansionListeners.add(listener)
+ listener.onShadeExpansionFullyChanged(qsExpanded)
+ }
+
+ fun removeFullExpansionListener(listener: ShadeFullExpansionListener) {
+ fullExpansionListeners.remove(listener)
+ }
+
fun addQsExpansionListener(listener: ShadeQsExpansionListener) {
qsExpansionListeners.add(listener)
listener.onQsExpansionChanged(qsExpanded)
@@ -156,6 +166,13 @@
qsExpansionListeners.forEach { it.onQsExpansionChanged(qsExpanded) }
}
+ fun onShadeExpansionFullyChanged(isExpanded: Boolean) {
+ this.expanded = isExpanded
+
+ debugLog("expanded=$isExpanded")
+ fullExpansionListeners.forEach { it.onShadeExpansionFullyChanged(isExpanded) }
+ }
+
/** Updates the panel state if necessary. */
fun updateState(@PanelState state: Int) {
debugLog(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeFullExpansionListener.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeFullExpansionListener.kt
new file mode 100644
index 0000000..6d13e19
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeFullExpansionListener.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade
+
+/** A listener interface to be notified of expansion events for the notification shade. */
+fun interface ShadeFullExpansionListener {
+ /** Invoked whenever the shade expansion changes, when it is fully collapsed or expanded */
+ fun onShadeExpansionFullyChanged(isExpanded: Boolean)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 073ab8b..15f4b12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -21,6 +21,7 @@
import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_NAMED_MANAGEMENT_DISCLOSURE;
import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK;
import static android.hardware.biometrics.BiometricSourceType.FACE;
+import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
@@ -50,7 +51,6 @@
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
-import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
@@ -74,12 +74,12 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.widget.LockPatternUtils;
-import com.android.internal.widget.ViewClippingUtil;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.Utils;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.R;
+import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FaceHelpMessageDeferral;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
@@ -138,6 +138,7 @@
private final KeyguardStateController mKeyguardStateController;
protected final StatusBarStateController mStatusBarStateController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final AuthController mAuthController;
private ViewGroup mIndicationArea;
private KeyguardIndicationTextView mTopIndicationView;
private KeyguardIndicationTextView mLockScreenIndicationView;
@@ -188,14 +189,7 @@
private KeyguardUpdateMonitorCallback mUpdateMonitorCallback;
private boolean mDozing;
- private final ViewClippingUtil.ClippingParameters mClippingParams =
- new ViewClippingUtil.ClippingParameters() {
- @Override
- public boolean shouldFinish(View view) {
- return view == mIndicationArea;
- }
- };
- private ScreenLifecycle mScreenLifecycle;
+ private final ScreenLifecycle mScreenLifecycle;
private final ScreenLifecycle.Observer mScreenObserver =
new ScreenLifecycle.Observer() {
@Override
@@ -209,6 +203,7 @@
}
}
};
+ private boolean mFaceLockedOutThisAuthSession;
/**
* Creates a new KeyguardIndicationController and registers callbacks.
@@ -229,6 +224,7 @@
@Main DelayableExecutor executor,
@Background DelayableExecutor bgExecutor,
FalsingManager falsingManager,
+ AuthController authController,
LockPatternUtils lockPatternUtils,
ScreenLifecycle screenLifecycle,
KeyguardBypassController keyguardBypassController,
@@ -248,6 +244,7 @@
mExecutor = executor;
mBackgroundExecutor = bgExecutor;
mLockPatternUtils = lockPatternUtils;
+ mAuthController = authController;
mFalsingManager = falsingManager;
mKeyguardBypassController = keyguardBypassController;
mAccessibilityManager = accessibilityManager;
@@ -619,7 +616,6 @@
if (mFalsingManager.isFalseTap(LOW_PENALTY)) {
return;
}
- int currentUserId = getCurrentUser();
mDevicePolicyManager.logoutUser();
})
.build(),
@@ -676,7 +672,7 @@
hideTransientIndication();
}
updateDeviceEntryIndication(false);
- } else if (!visible) {
+ } else {
// If we unlock and return to keyguard quickly, previous error should not be shown
hideTransientIndication();
}
@@ -764,7 +760,7 @@
* logic.
*/
private void showBiometricMessage(CharSequence biometricMessage,
- CharSequence biometricMessageFollowUp) {
+ @Nullable CharSequence biometricMessageFollowUp) {
if (TextUtils.equals(biometricMessage, mBiometricMessage)) {
return;
}
@@ -929,7 +925,7 @@
}
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
- if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) {
+ if (mStatusBarKeyguardViewManager.isShowingAlternateBouncer()) {
return; // udfps affordance is highlighted, no need to show action to unlock
} else if (mKeyguardUpdateMonitor.isFaceEnrolled()) {
String message = mContext.getString(R.string.keyguard_retry);
@@ -1072,17 +1068,12 @@
&& msgId != BIOMETRIC_HELP_FACE_NOT_RECOGNIZED;
final boolean faceAuthFailed = biometricSourceType == FACE
&& msgId == BIOMETRIC_HELP_FACE_NOT_RECOGNIZED; // ran through matcher & failed
- final boolean isUnlockWithFingerprintPossible =
- mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
- getCurrentUser());
+ final boolean isUnlockWithFingerprintPossible = canUnlockWithFingerprint();
final boolean isCoExFaceAcquisitionMessage =
faceAuthSoftError && isUnlockWithFingerprintPossible;
if (isCoExFaceAcquisitionMessage && !mCoExFaceAcquisitionMsgIdsToShow.contains(msgId)) {
- if (DEBUG) {
- Log.d(TAG, "skip showing msgId=" + msgId + " helpString=" + helpString
- + ", due to co-ex logic");
- }
- return;
+ debugLog("skip showing msgId=" + msgId + " helpString=" + helpString
+ + ", due to co-ex logic");
} else if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
mStatusBarKeyguardViewManager.setKeyguardMessage(helpString,
mInitialTextColorState);
@@ -1120,74 +1111,45 @@
}
@Override
- public void onBiometricError(int msgId, String errString,
- BiometricSourceType biometricSourceType) {
- CharSequence deferredFaceMessage = null;
- if (biometricSourceType == FACE) {
- if (msgId == BiometricFaceConstants.FACE_ERROR_TIMEOUT) {
- deferredFaceMessage = mFaceAcquiredMessageDeferral.getDeferredMessage();
- if (DEBUG) {
- Log.d(TAG, "showDeferredFaceMessage msgId=" + deferredFaceMessage);
- }
- }
- mFaceAcquiredMessageDeferral.reset();
- }
-
- if (shouldSuppressBiometricError(msgId, biometricSourceType, mKeyguardUpdateMonitor)) {
- if (DEBUG) {
- Log.d(TAG, "suppressingBiometricError msgId=" + msgId
- + " source=" + biometricSourceType);
- }
- } else if (biometricSourceType == FACE && msgId == FaceManager.FACE_ERROR_TIMEOUT) {
- // Co-ex: show deferred message OR nothing
- if (mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
- KeyguardUpdateMonitor.getCurrentUser())) {
- // if we're on the lock screen (bouncer isn't showing), show the deferred msg
- if (deferredFaceMessage != null
- && !mStatusBarKeyguardViewManager.isBouncerShowing()) {
- showBiometricMessage(
- deferredFaceMessage,
- mContext.getString(R.string.keyguard_suggest_fingerprint)
- );
- return;
- }
-
- // otherwise, don't show any message
- if (DEBUG) {
- Log.d(TAG, "skip showing FACE_ERROR_TIMEOUT due to co-ex logic");
- }
- return;
- }
-
- // Face-only: The face timeout message is not very actionable, let's ask the user to
- // manually retry.
- if (deferredFaceMessage != null) {
- showBiometricMessage(
- deferredFaceMessage,
- mContext.getString(R.string.keyguard_unlock)
- );
- } else {
- // suggest swiping up to unlock (try face auth again or swipe up to bouncer)
- showActionToUnlock();
- }
- } else if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
- mStatusBarKeyguardViewManager.setKeyguardMessage(errString, mInitialTextColorState);
- } else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
- showBiometricMessage(errString);
- } else {
- mBiometricErrorMessageToShowOnScreenOn = errString;
+ public void onLockedOutStateChanged(BiometricSourceType biometricSourceType) {
+ if (biometricSourceType == FACE && !mKeyguardUpdateMonitor.isFaceLockedOut()) {
+ mFaceLockedOutThisAuthSession = false;
}
}
- private boolean shouldSuppressBiometricError(int msgId,
- BiometricSourceType biometricSourceType, KeyguardUpdateMonitor updateMonitor) {
- if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
- return shouldSuppressFingerprintError(msgId, updateMonitor);
- }
+ @Override
+ public void onBiometricError(int msgId, String errString,
+ BiometricSourceType biometricSourceType) {
if (biometricSourceType == FACE) {
- return shouldSuppressFaceError(msgId, updateMonitor);
+ onFaceAuthError(msgId, errString);
+ } else if (biometricSourceType == FINGERPRINT) {
+ onFingerprintAuthError(msgId, errString);
}
- return false;
+ }
+
+ private void onFaceAuthError(int msgId, String errString) {
+ CharSequence deferredFaceMessage = mFaceAcquiredMessageDeferral.getDeferredMessage();
+ mFaceAcquiredMessageDeferral.reset();
+ if (shouldSuppressFaceError(msgId, mKeyguardUpdateMonitor)) {
+ debugLog("suppressingFaceError msgId=" + msgId + " errString= " + errString);
+ return;
+ }
+ if (msgId == FaceManager.FACE_ERROR_TIMEOUT) {
+ handleFaceAuthTimeoutError(deferredFaceMessage);
+ } else if (isLockoutError(msgId)) {
+ handleFaceLockoutError(errString);
+ } else {
+ showErrorMessageNowOrLater(errString, null);
+ }
+ }
+
+ private void onFingerprintAuthError(int msgId, String errString) {
+ if (shouldSuppressFingerprintError(msgId, mKeyguardUpdateMonitor)) {
+ debugLog("suppressingFingerprintError msgId=" + msgId
+ + " errString= " + errString);
+ } else {
+ showErrorMessageNowOrLater(errString, null);
+ }
}
private boolean shouldSuppressFingerprintError(int msgId,
@@ -1197,7 +1159,7 @@
// pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the
// check of whether non-strong biometric is allowed
return ((!updateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)
- && msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
+ && !isLockoutError(msgId))
|| msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED
|| msgId == FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED
|| msgId == FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED);
@@ -1217,16 +1179,14 @@
@Override
public void onTrustChanged(int userId) {
- if (getCurrentUser() != userId) {
- return;
- }
+ if (!isCurrentUser(userId)) return;
updateDeviceEntryIndication(false);
}
@Override
- public void showTrustGrantedMessage(CharSequence message) {
- mTrustGrantedIndication = message;
- updateDeviceEntryIndication(false);
+ public void onTrustGrantedWithFlags(int flags, int userId, @Nullable String message) {
+ if (!isCurrentUser(userId)) return;
+ showTrustGrantedMessage(flags, message);
}
@Override
@@ -1286,7 +1246,92 @@
}
}
- private StatusBarStateController.StateListener mStatusBarStateListener =
+ private boolean isCurrentUser(int userId) {
+ return getCurrentUser() == userId;
+ }
+
+ void showTrustGrantedMessage(int flags, @Nullable CharSequence message) {
+ mTrustGrantedIndication = message;
+ updateDeviceEntryIndication(false);
+ }
+
+ private void handleFaceLockoutError(String errString) {
+ int followupMsgId = canUnlockWithFingerprint() ? R.string.keyguard_suggest_fingerprint
+ : R.string.keyguard_unlock;
+ String followupMessage = mContext.getString(followupMsgId);
+ // Lockout error can happen multiple times in a session because we trigger face auth
+ // even when it is locked out so that the user is aware that face unlock would have
+ // triggered but didn't because it is locked out.
+
+ // On first lockout we show the error message from FaceManager, which tells the user they
+ // had too many unsuccessful attempts.
+ if (!mFaceLockedOutThisAuthSession) {
+ mFaceLockedOutThisAuthSession = true;
+ showErrorMessageNowOrLater(errString, followupMessage);
+ } else if (!mAuthController.isUdfpsFingerDown()) {
+ // On subsequent lockouts, we show a more generic locked out message.
+ showErrorMessageNowOrLater(
+ mContext.getString(R.string.keyguard_face_unlock_unavailable),
+ followupMessage);
+ }
+ }
+
+ private static boolean isLockoutError(int msgId) {
+ return msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT
+ || msgId == FaceManager.FACE_ERROR_LOCKOUT;
+ }
+
+ private void handleFaceAuthTimeoutError(@Nullable CharSequence deferredFaceMessage) {
+ debugLog("showDeferredFaceMessage msgId=" + deferredFaceMessage);
+ if (canUnlockWithFingerprint()) {
+ // Co-ex: show deferred message OR nothing
+ // if we're on the lock screen (bouncer isn't showing), show the deferred msg
+ if (deferredFaceMessage != null
+ && !mStatusBarKeyguardViewManager.isBouncerShowing()) {
+ showBiometricMessage(
+ deferredFaceMessage,
+ mContext.getString(R.string.keyguard_suggest_fingerprint)
+ );
+ } else {
+ // otherwise, don't show any message
+ debugLog("skip showing FACE_ERROR_TIMEOUT due to co-ex logic");
+ }
+ } else if (deferredFaceMessage != null) {
+ // Face-only: The face timeout message is not very actionable, let's ask the
+ // user to manually retry.
+ showBiometricMessage(
+ deferredFaceMessage,
+ mContext.getString(R.string.keyguard_unlock)
+ );
+ } else {
+ // Face-only
+ // suggest swiping up to unlock (try face auth again or swipe up to bouncer)
+ showActionToUnlock();
+ }
+ }
+
+ private boolean canUnlockWithFingerprint() {
+ return mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
+ KeyguardUpdateMonitor.getCurrentUser());
+ }
+
+ private void debugLog(String logMsg) {
+ if (DEBUG) {
+ Log.d(TAG, logMsg);
+ }
+ }
+
+ private void showErrorMessageNowOrLater(String errString, @Nullable String followUpMsg) {
+ if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
+ mStatusBarKeyguardViewManager.setKeyguardMessage(errString, mInitialTextColorState);
+ } else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
+ showBiometricMessage(errString, followUpMsg);
+ } else {
+ mBiometricErrorMessageToShowOnScreenOn = errString;
+ }
+ }
+
+ private final StatusBarStateController.StateListener mStatusBarStateListener =
new StatusBarStateController.StateListener() {
@Override
public void onStateChanged(int newState) {
@@ -1307,7 +1352,7 @@
}
};
- private KeyguardStateController.Callback mKeyguardStateCallback =
+ private final KeyguardStateController.Callback mKeyguardStateCallback =
new KeyguardStateController.Callback() {
@Override
public void onUnlockedChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
index 9d2750f..bc456d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
@@ -18,6 +18,8 @@
import com.android.systemui.animation.Interpolators
import com.android.systemui.statusbar.LightRevealEffect.Companion.getPercentPastThreshold
import com.android.systemui.util.getColorWithAlpha
+import com.android.systemui.util.leak.RotationUtils
+import com.android.systemui.util.leak.RotationUtils.Rotation
import java.util.function.Consumer
/**
@@ -67,22 +69,19 @@
override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {
val interpolatedAmount = INTERPOLATOR.getInterpolation(amount)
val ovalWidthIncreaseAmount =
- getPercentPastThreshold(interpolatedAmount, WIDEN_OVAL_THRESHOLD)
+ getPercentPastThreshold(interpolatedAmount, WIDEN_OVAL_THRESHOLD)
val initialWidthMultiplier = (1f - OVAL_INITIAL_WIDTH_PERCENT) / 2f
with(scrim) {
- revealGradientEndColorAlpha = 1f - getPercentPastThreshold(
- amount, FADE_END_COLOR_OUT_THRESHOLD)
+ revealGradientEndColorAlpha =
+ 1f - getPercentPastThreshold(amount, FADE_END_COLOR_OUT_THRESHOLD)
setRevealGradientBounds(
- scrim.width * initialWidthMultiplier +
- -scrim.width * ovalWidthIncreaseAmount,
- scrim.height * OVAL_INITIAL_TOP_PERCENT -
- scrim.height * interpolatedAmount,
- scrim.width * (1f - initialWidthMultiplier) +
- scrim.width * ovalWidthIncreaseAmount,
- scrim.height * OVAL_INITIAL_BOTTOM_PERCENT +
- scrim.height * interpolatedAmount)
+ scrim.width * initialWidthMultiplier + -scrim.width * ovalWidthIncreaseAmount,
+ scrim.height * OVAL_INITIAL_TOP_PERCENT - scrim.height * interpolatedAmount,
+ scrim.width * (1f - initialWidthMultiplier) + scrim.width * ovalWidthIncreaseAmount,
+ scrim.height * OVAL_INITIAL_BOTTOM_PERCENT + scrim.height * interpolatedAmount
+ )
}
}
}
@@ -97,12 +96,17 @@
scrim.interpolatedRevealAmount = interpolatedAmount
scrim.startColorAlpha =
- getPercentPastThreshold(1 - interpolatedAmount,
- threshold = 1 - START_COLOR_REVEAL_PERCENTAGE)
+ getPercentPastThreshold(
+ 1 - interpolatedAmount,
+ threshold = 1 - START_COLOR_REVEAL_PERCENTAGE
+ )
scrim.revealGradientEndColorAlpha =
- 1f - getPercentPastThreshold(interpolatedAmount,
- threshold = REVEAL_GRADIENT_END_COLOR_ALPHA_START_PERCENTAGE)
+ 1f -
+ getPercentPastThreshold(
+ interpolatedAmount,
+ threshold = REVEAL_GRADIENT_END_COLOR_ALPHA_START_PERCENTAGE
+ )
// Start changing gradient bounds later to avoid harsh gradient in the beginning
val gradientBoundsAmount = lerp(GRADIENT_START_BOUNDS_PERCENTAGE, 1.0f, interpolatedAmount)
@@ -179,7 +183,7 @@
*/
private val OFF_SCREEN_START_AMOUNT = 0.05f
- private val WIDTH_INCREASE_MULTIPLIER = 1.25f
+ private val INCREASE_MULTIPLIER = 1.25f
override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {
val interpolatedAmount = Interpolators.FAST_OUT_SLOW_IN_REVERSE.getInterpolation(amount)
@@ -188,15 +192,36 @@
with(scrim) {
revealGradientEndColorAlpha = 1f - fadeAmount
interpolatedRevealAmount = interpolatedAmount
- setRevealGradientBounds(
+ @Rotation val rotation = RotationUtils.getRotation(scrim.getContext())
+ if (rotation == RotationUtils.ROTATION_NONE) {
+ setRevealGradientBounds(
width * (1f + OFF_SCREEN_START_AMOUNT) -
- width * WIDTH_INCREASE_MULTIPLIER * interpolatedAmount,
- powerButtonY -
- height * interpolatedAmount,
+ width * INCREASE_MULTIPLIER * interpolatedAmount,
+ powerButtonY - height * interpolatedAmount,
width * (1f + OFF_SCREEN_START_AMOUNT) +
- width * WIDTH_INCREASE_MULTIPLIER * interpolatedAmount,
- powerButtonY +
- height * interpolatedAmount)
+ width * INCREASE_MULTIPLIER * interpolatedAmount,
+ powerButtonY + height * interpolatedAmount
+ )
+ } else if (rotation == RotationUtils.ROTATION_LANDSCAPE) {
+ setRevealGradientBounds(
+ powerButtonY - width * interpolatedAmount,
+ (-height * OFF_SCREEN_START_AMOUNT) -
+ height * INCREASE_MULTIPLIER * interpolatedAmount,
+ powerButtonY + width * interpolatedAmount,
+ (-height * OFF_SCREEN_START_AMOUNT) +
+ height * INCREASE_MULTIPLIER * interpolatedAmount
+ )
+ } else {
+ // RotationUtils.ROTATION_SEASCAPE
+ setRevealGradientBounds(
+ (width - powerButtonY) - width * interpolatedAmount,
+ height * (1f + OFF_SCREEN_START_AMOUNT) -
+ height * INCREASE_MULTIPLIER * interpolatedAmount,
+ (width - powerButtonY) + width * interpolatedAmount,
+ height * (1f + OFF_SCREEN_START_AMOUNT) +
+ height * INCREASE_MULTIPLIER * interpolatedAmount
+ )
+ }
}
}
}
@@ -208,9 +233,7 @@
*/
class LightRevealScrim(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
- /**
- * Listener that is called if the scrim's opaqueness changes
- */
+ /** Listener that is called if the scrim's opaqueness changes */
lateinit var isScrimOpaqueChangedListener: Consumer<Boolean>
/**
@@ -224,8 +247,11 @@
revealEffect.setRevealAmountOnScrim(value, this)
updateScrimOpaque()
- Trace.traceCounter(Trace.TRACE_TAG_APP, "light_reveal_amount",
- (field * 100).toInt())
+ Trace.traceCounter(
+ Trace.TRACE_TAG_APP,
+ "light_reveal_amount",
+ (field * 100).toInt()
+ )
invalidate()
}
}
@@ -250,10 +276,10 @@
/**
* Alpha of the fill that can be used in the beginning of the animation to hide the content.
- * Normally the gradient bounds are animated from small size so the content is not visible,
- * but if the start gradient bounds allow to see some content this could be used to make the
- * reveal smoother. It can help to add fade in effect in the beginning of the animation.
- * The color of the fill is determined by [revealGradientEndColor].
+ * Normally the gradient bounds are animated from small size so the content is not visible, but
+ * if the start gradient bounds allow to see some content this could be used to make the reveal
+ * smoother. It can help to add fade in effect in the beginning of the animation. The color of
+ * the fill is determined by [revealGradientEndColor].
*
* 0 - no fill and content is visible, 1 - the content is covered with the start color
*/
@@ -281,9 +307,7 @@
}
}
- /**
- * Is the scrim currently fully opaque
- */
+ /** Is the scrim currently fully opaque */
var isScrimOpaque = false
private set(value) {
if (field != value) {
@@ -318,16 +342,22 @@
* Paint used to draw a transparent-to-white radial gradient. This will be scaled and translated
* via local matrix in [onDraw] so we never need to construct a new shader.
*/
- private val gradientPaint = Paint().apply {
- shader = RadialGradient(
- 0f, 0f, 1f,
- intArrayOf(Color.TRANSPARENT, Color.WHITE), floatArrayOf(0f, 1f),
- Shader.TileMode.CLAMP)
+ private val gradientPaint =
+ Paint().apply {
+ shader =
+ RadialGradient(
+ 0f,
+ 0f,
+ 1f,
+ intArrayOf(Color.TRANSPARENT, Color.WHITE),
+ floatArrayOf(0f, 1f),
+ Shader.TileMode.CLAMP
+ )
- // SRC_OVER ensures that we draw the semitransparent pixels over other views in the same
- // window, rather than outright replacing them.
- xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_OVER)
- }
+ // SRC_OVER ensures that we draw the semitransparent pixels over other views in the same
+ // window, rather than outright replacing them.
+ xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_OVER)
+ }
/**
* Matrix applied to [gradientPaint]'s RadialGradient shader to move the gradient to
@@ -347,8 +377,8 @@
* simply a helper method that sets [revealGradientCenter], [revealGradientWidth], and
* [revealGradientHeight] for you.
*
- * This method does not call [invalidate] - you should do so once you're done changing
- * properties.
+ * This method does not call [invalidate]
+ * - you should do so once you're done changing properties.
*/
fun setRevealGradientBounds(left: Float, top: Float, right: Float, bottom: Float) {
revealGradientWidth = right - left
@@ -359,8 +389,12 @@
}
override fun onDraw(canvas: Canvas?) {
- if (canvas == null || revealGradientWidth <= 0 || revealGradientHeight <= 0 ||
- revealAmount == 0f) {
+ if (
+ canvas == null ||
+ revealGradientWidth <= 0 ||
+ revealGradientHeight <= 0 ||
+ revealAmount == 0f
+ ) {
if (revealAmount < 1f) {
canvas?.drawColor(revealGradientEndColor)
}
@@ -383,8 +417,10 @@
}
private fun setPaintColorFilter() {
- gradientPaint.colorFilter = PorterDuffColorFilter(
- getColorWithAlpha(revealGradientEndColor, revealGradientEndColorAlpha),
- PorterDuff.Mode.MULTIPLY)
+ gradientPaint.colorFilter =
+ PorterDuffColorFilter(
+ getColorWithAlpha(revealGradientEndColor, revealGradientEndColorAlpha),
+ PorterDuff.Mode.MULTIPLY
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
index e21acb7..0b1807d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
@@ -123,9 +123,6 @@
/** Sets whether the window was collapsed by force or not. */
default void setForceWindowCollapsed(boolean force) {}
- /** Sets whether panel is expanded or not. */
- default void setPanelExpanded(boolean isExpanded) {}
-
/** Gets whether the panel is expanded or not. */
default boolean getPanelExpanded() {
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 408293c..815b86e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -805,7 +805,7 @@
iconState.hidden = isAppearing
|| (view instanceof ExpandableNotificationRow
&& ((ExpandableNotificationRow) view).isLowPriority()
- && mShelfIcons.hasMaxNumDot())
+ && mShelfIcons.areIconsOverflowing())
|| (transitionAmount == 0.0f && !iconState.isAnimating(icon))
|| row.isAboveShelf()
|| row.showingPulsing()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index fdec745..58ce447 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -53,6 +53,7 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
+import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.policy.CallbackController;
@@ -152,13 +153,18 @@
private Interpolator mDozeInterpolator = Interpolators.FAST_OUT_SLOW_IN;
@Inject
- public StatusBarStateControllerImpl(UiEventLogger uiEventLogger, DumpManager dumpManager,
- InteractionJankMonitor interactionJankMonitor) {
+ public StatusBarStateControllerImpl(
+ UiEventLogger uiEventLogger,
+ DumpManager dumpManager,
+ InteractionJankMonitor interactionJankMonitor,
+ ShadeExpansionStateManager shadeExpansionStateManager
+ ) {
mUiEventLogger = uiEventLogger;
mInteractionJankMonitor = interactionJankMonitor;
for (int i = 0; i < HISTORY_SIZE; i++) {
mHistoricalRecords[i] = new HistoricalState();
}
+ shadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged);
dumpManager.registerDumpable(this);
}
@@ -262,21 +268,6 @@
}
@Override
- public boolean setPanelExpanded(boolean expanded) {
- if (mIsExpanded == expanded) {
- return false;
- }
- mIsExpanded = expanded;
- String tag = getClass().getSimpleName() + "#setIsExpanded";
- DejankUtils.startDetectingBlockingIpcs(tag);
- for (RankedListener rl : new ArrayList<>(mListeners)) {
- rl.mListener.onExpandedChanged(mIsExpanded);
- }
- DejankUtils.stopDetectingBlockingIpcs(tag);
- return true;
- }
-
- @Override
public float getInterpolatedDozeAmount() {
return mDozeInterpolator.getInterpolation(mDozeAmount);
}
@@ -324,6 +315,18 @@
}
}
+ private void onShadeExpansionFullyChanged(Boolean isExpanded) {
+ if (mIsExpanded != isExpanded) {
+ mIsExpanded = isExpanded;
+ String tag = getClass().getSimpleName() + "#setIsExpanded";
+ DejankUtils.startDetectingBlockingIpcs(tag);
+ for (RankedListener rl : new ArrayList<>(mListeners)) {
+ rl.mListener.onExpandedChanged(mIsExpanded);
+ }
+ DejankUtils.stopDetectingBlockingIpcs(tag);
+ }
+ }
+
private void startDozeAnimation() {
if (mDozeAmount == 0f || mDozeAmount == 1f) {
mDozeInterpolator = mIsDozing
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
index 1189107..5a392a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
@@ -109,13 +109,6 @@
void setAndInstrumentDozeAmount(View view, float dozeAmount, boolean animated);
/**
- * Update the expanded state from {@link CentralSurfaces}'s perspective
- * @param expanded are we expanded?
- * @return {@code true} if the state changed, else {@code false}
- */
- boolean setPanelExpanded(boolean expanded);
-
- /**
* Sets whether to leave status bar open when hiding keyguard
*/
void setLeaveOpenOnKeyguardHide(boolean leaveOpen);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
index ec221b7..c523d22 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
@@ -15,9 +15,7 @@
*/
package com.android.systemui.statusbar.connectivity;
-import static com.android.settingslib.mobile.MobileMappings.getDefaultIcons;
-import static com.android.settingslib.mobile.MobileMappings.getIconKey;
-import static com.android.settingslib.mobile.MobileMappings.mapIconSets;
+import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
import android.content.Context;
import android.content.Intent;
@@ -46,6 +44,7 @@
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.SignalStrengthUtil;
import com.android.systemui.R;
+import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy;
import com.android.systemui.util.CarrierConfigTracker;
import java.io.PrintWriter;
@@ -63,6 +62,7 @@
private final TelephonyManager mPhone;
private final CarrierConfigTracker mCarrierConfigTracker;
private final SubscriptionDefaults mDefaults;
+ private final MobileMappingsProxy mMobileMappingsProxy;
private final String mNetworkNameDefault;
private final String mNetworkNameSeparator;
private final ContentObserver mObserver;
@@ -121,6 +121,7 @@
TelephonyManager phone,
CallbackHandler callbackHandler,
NetworkControllerImpl networkController,
+ MobileMappingsProxy mobileMappingsProxy,
SubscriptionInfo info,
SubscriptionDefaults defaults,
Looper receiverLooper,
@@ -135,13 +136,14 @@
mPhone = phone;
mDefaults = defaults;
mSubscriptionInfo = info;
+ mMobileMappingsProxy = mobileMappingsProxy;
mNetworkNameSeparator = getTextIfExists(
R.string.status_bar_network_name_separator).toString();
mNetworkNameDefault = getTextIfExists(
com.android.internal.R.string.lockscreen_carrier_default).toString();
- mNetworkToIconLookup = mapIconSets(mConfig);
- mDefaultIcons = getDefaultIcons(mConfig);
+ mNetworkToIconLookup = mMobileMappingsProxy.mapIconSets(mConfig);
+ mDefaultIcons = mMobileMappingsProxy.getDefaultIcons(mConfig);
String networkName = info.getCarrierName() != null ? info.getCarrierName().toString()
: mNetworkNameDefault;
@@ -161,8 +163,8 @@
void setConfiguration(Config config) {
mConfig = config;
updateInflateSignalStrength();
- mNetworkToIconLookup = mapIconSets(mConfig);
- mDefaultIcons = getDefaultIcons(mConfig);
+ mNetworkToIconLookup = mMobileMappingsProxy.mapIconSets(mConfig);
+ mDefaultIcons = mMobileMappingsProxy.getDefaultIcons(mConfig);
updateTelephony();
}
@@ -271,8 +273,9 @@
dataContentDescription = mContext.getString(R.string.data_connection_no_internet);
}
- final QsInfo qsInfo = getQsInfo(contentDescription, icons.dataType);
- final SbInfo sbInfo = getSbInfo(contentDescription, icons.dataType);
+ int iconId = mCurrentState.getNetworkTypeIcon(mContext);
+ final QsInfo qsInfo = getQsInfo(contentDescription, iconId);
+ final SbInfo sbInfo = getSbInfo(contentDescription, iconId);
MobileDataIndicators mobileDataIndicators = new MobileDataIndicators(
sbInfo.icon,
@@ -373,6 +376,10 @@
} else if (action.equals(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
updateDataSim();
notifyListenersIfNecessary();
+ } else if (action.equals(TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED)) {
+ int carrierId = intent.getIntExtra(
+ TelephonyManager.EXTRA_CARRIER_ID, UNKNOWN_CARRIER_ID);
+ mCurrentState.setCarrierId(carrierId);
}
}
@@ -477,7 +484,8 @@
mCurrentState.level = getSignalLevel(mCurrentState.signalStrength);
}
- String iconKey = getIconKey(mCurrentState.telephonyDisplayInfo);
+ mCurrentState.setCarrierId(mPhone.getSimCarrierId());
+ String iconKey = mMobileMappingsProxy.getIconKey(mCurrentState.telephonyDisplayInfo);
if (mNetworkToIconLookup.get(iconKey) != null) {
mCurrentState.iconGroup = mNetworkToIconLookup.get(iconKey);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt
index 7938179..a323454 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt
@@ -22,6 +22,7 @@
import com.android.settingslib.mobile.MobileMappings
import com.android.settingslib.mobile.MobileStatusTracker
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
import com.android.systemui.util.CarrierConfigTracker
import javax.inject.Inject
@@ -33,6 +34,7 @@
val context: Context,
val callbackHandler: CallbackHandler,
val carrierConfigTracker: CarrierConfigTracker,
+ val mobileMappings: MobileMappingsProxy,
) {
fun createMobileSignalController(
config: MobileMappings.Config,
@@ -56,6 +58,7 @@
phone,
callbackHandler,
networkController,
+ mobileMappings,
subscriptionInfo,
subscriptionDefaults,
receiverLooper,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt
index f20d206..1fb6a98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt
@@ -16,10 +16,14 @@
package com.android.systemui.statusbar.connectivity
+import android.annotation.DrawableRes
+import android.content.Context
import android.telephony.ServiceState
import android.telephony.SignalStrength
import android.telephony.TelephonyDisplayInfo
import android.telephony.TelephonyManager
+import com.android.internal.annotations.VisibleForTesting
+import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.settingslib.Utils
import com.android.settingslib.mobile.MobileStatusTracker.MobileStatus
import com.android.settingslib.mobile.TelephonyIcons
@@ -41,7 +45,7 @@
@JvmField var roaming: Boolean = false,
@JvmField var dataState: Int = TelephonyManager.DATA_DISCONNECTED,
// Tracks the on/off state of the defaultDataSubscription
- @JvmField var defaultDataOff: Boolean = false
+ @JvmField var defaultDataOff: Boolean = false,
) : ConnectivityState() {
@JvmField var telephonyDisplayInfo = TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN,
@@ -49,6 +53,11 @@
@JvmField var serviceState: ServiceState? = null
@JvmField var signalStrength: SignalStrength? = null
+ var carrierId = TelephonyManager.UNKNOWN_CARRIER_ID
+
+ @VisibleForTesting
+ var networkTypeResIdCache: NetworkTypeResIdCache = NetworkTypeResIdCache()
+
/** @return true if this state is disabled or not default data */
val isDataDisabledOrNotDefault: Boolean
get() = (iconGroup === TelephonyIcons.DATA_DISABLED ||
@@ -125,6 +134,21 @@
return serviceState != null && serviceState!!.roaming
}
+ /**
+ *
+ * Load the (potentially customized) icon resource id for the current network type. Note that
+ * this operation caches the result. Note that reading the [MobileIconGroup.dataType] field
+ * directly will not yield correct results in cases where the carrierId has an associated
+ * override. This is the preferred method for getting the network type indicator.
+ *
+ * @return a drawable res id appropriate for the current (carrierId, networkType) pair
+ */
+ @DrawableRes
+ fun getNetworkTypeIcon(context: Context): Int {
+ val icon = (iconGroup as MobileIconGroup)
+ return networkTypeResIdCache.get(icon, carrierId, context)
+ }
+
fun setFromMobileStatus(mobileStatus: MobileStatus) {
activityIn = mobileStatus.activityIn
activityOut = mobileStatus.activityOut
@@ -140,6 +164,7 @@
super.toString(builder)
builder.append(',')
builder.append("dataSim=$dataSim,")
+ builder.append("carrierId=$carrierId")
builder.append("networkName=$networkName,")
builder.append("networkNameData=$networkNameData,")
builder.append("dataConnected=$dataConnected,")
@@ -157,6 +182,8 @@
builder.append("voiceServiceState=${getVoiceServiceState()},")
builder.append("isInService=${isInService()},")
+ builder.append("networkTypeIconCache=$networkTypeResIdCache")
+
builder.append("serviceState=${serviceState?.minLog() ?: "(null)"},")
builder.append("signalStrength=${signalStrength?.minLog() ?: "(null)"},")
builder.append("displayInfo=$telephonyDisplayInfo")
@@ -164,6 +191,7 @@
override fun tableColumns(): List<String> {
val columns = listOf("dataSim",
+ "carrierId",
"networkName",
"networkNameData",
"dataConnected",
@@ -178,6 +206,7 @@
"showQuickSettingsRatIcon",
"voiceServiceState",
"isInService",
+ "networkTypeIconCache",
"serviceState",
"signalStrength",
"displayInfo")
@@ -187,6 +216,7 @@
override fun tableData(): List<String> {
val columns = listOf(dataSim,
+ carrierId,
networkName,
networkNameData,
dataConnected,
@@ -201,6 +231,7 @@
showQuickSettingsRatIcon(),
getVoiceServiceState(),
isInService(),
+ networkTypeResIdCache,
serviceState?.minLog() ?: "(null)",
signalStrength?.minLog() ?: "(null)",
telephonyDisplayInfo).map { it.toString() }
@@ -217,6 +248,7 @@
if (networkName != other.networkName) return false
if (networkNameData != other.networkNameData) return false
+ if (carrierId != other.carrierId) return false
if (dataSim != other.dataSim) return false
if (dataConnected != other.dataConnected) return false
if (isEmergency != other.isEmergency) return false
@@ -238,6 +270,7 @@
var result = super.hashCode()
result = 31 * result + (networkName?.hashCode() ?: 0)
result = 31 * result + (networkNameData?.hashCode() ?: 0)
+ result = 31 * result + (carrierId.hashCode())
result = 31 * result + dataSim.hashCode()
result = 31 * result + dataConnected.hashCode()
result = 31 * result + isEmergency.hashCode()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
index 450b757..73d6483 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -22,6 +22,7 @@
import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT;
import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_NONE;
import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import android.annotation.Nullable;
import android.content.BroadcastReceiver;
@@ -138,7 +139,7 @@
private final MobileSignalControllerFactory mMobileFactory;
private TelephonyCallback.ActiveDataSubscriptionIdListener mPhoneStateListener;
- private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ private int mActiveMobileDataSubscription = INVALID_SUBSCRIPTION_ID;
// Subcontrollers.
@VisibleForTesting
@@ -502,6 +503,7 @@
filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
filter.addAction(TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
filter.addAction(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED);
+ filter.addAction(TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED);
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mReceiverHandler);
mListening = true;
@@ -792,6 +794,20 @@
mConfig = Config.readConfig(mContext);
mReceiverHandler.post(this::handleConfigurationChanged);
break;
+
+ case TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED: {
+ // Notify the relevant MobileSignalController of the change
+ int subId = intent.getIntExtra(
+ TelephonyManager.EXTRA_SUBSCRIPTION_ID,
+ INVALID_SUBSCRIPTION_ID
+ );
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
+ if (mMobileSignalControllers.indexOfKey(subId) >= 0) {
+ mMobileSignalControllers.get(subId).handleBroadcast(intent);
+ }
+ }
+ }
+ break;
case Intent.ACTION_SIM_STATE_CHANGED:
// Avoid rebroadcast because SysUI is direct boot aware.
if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
@@ -819,7 +835,7 @@
break;
default:
int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ INVALID_SUBSCRIPTION_ID);
if (SubscriptionManager.isValidSubscriptionId(subId)) {
if (mMobileSignalControllers.indexOfKey(subId) >= 0) {
mMobileSignalControllers.get(subId).handleBroadcast(intent);
@@ -1335,6 +1351,9 @@
String slotString = args.getString("slot");
int slot = TextUtils.isEmpty(slotString) ? 0 : Integer.parseInt(slotString);
slot = MathUtils.constrain(slot, 0, 8);
+ String carrierIdString = args.getString("carrierid");
+ int carrierId = TextUtils.isEmpty(carrierIdString) ? 0
+ : Integer.parseInt(carrierIdString);
// Ensure we have enough sim slots
List<SubscriptionInfo> subs = new ArrayList<>();
while (mMobileSignalControllers.size() <= slot) {
@@ -1346,6 +1365,9 @@
}
// Hack to index linearly for easy use.
MobileSignalController controller = mMobileSignalControllers.valueAt(slot);
+ if (carrierId != 0) {
+ controller.getState().setCarrierId(carrierId);
+ }
controller.getState().dataSim = datatype != null;
controller.getState().isDefault = datatype != null;
controller.getState().dataConnected = datatype != null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkTypeResIdCache.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkTypeResIdCache.kt
new file mode 100644
index 0000000..9be7ee9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkTypeResIdCache.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.connectivity
+
+import android.annotation.DrawableRes
+import android.content.Context
+import com.android.settingslib.SignalIcon.MobileIconGroup
+import com.android.settingslib.mobile.MobileIconCarrierIdOverrides
+import com.android.settingslib.mobile.MobileIconCarrierIdOverridesImpl
+
+/**
+ * Cache for network type resource IDs.
+ *
+ * The default framework behavior is to have a statically defined icon per network type. See
+ * [MobileIconGroup] for the standard mapping.
+ *
+ * For the case of carrierId-defined overrides, we want to check [MobileIconCarrierIdOverrides] for
+ * an existing icon override, and cache the result of the operation
+ */
+class NetworkTypeResIdCache(
+ private val overrides: MobileIconCarrierIdOverrides = MobileIconCarrierIdOverridesImpl()
+) {
+ @DrawableRes private var cachedResId: Int = 0
+ private var lastCarrierId: Int? = null
+ private var lastIconGroup: MobileIconGroup? = null
+ private var isOverridden: Boolean = false
+
+ @DrawableRes
+ fun get(iconGroup: MobileIconGroup, carrierId: Int, context: Context): Int {
+ if (lastCarrierId != carrierId || lastIconGroup != iconGroup) {
+ lastCarrierId = carrierId
+ lastIconGroup = iconGroup
+
+ val maybeOverride = calculateOverriddenIcon(iconGroup, carrierId, context)
+ if (maybeOverride > 0) {
+ cachedResId = maybeOverride
+ isOverridden = true
+ } else {
+ cachedResId = iconGroup.dataType
+ isOverridden = false
+ }
+ }
+
+ return cachedResId
+ }
+
+ override fun toString(): String {
+ return "networkTypeResIdCache={id=$cachedResId, isOverridden=$isOverridden}"
+ }
+
+ @DrawableRes
+ private fun calculateOverriddenIcon(
+ iconGroup: MobileIconGroup,
+ carrierId: Int,
+ context: Context,
+ ): Int {
+ val name = iconGroup.name
+ if (!overrides.carrierIdEntryExists(carrierId)) {
+ return 0
+ }
+
+ return overrides.getOverrideFor(carrierId, name, context.resources)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProvider.kt
index a02dd34..42b874f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProvider.kt
@@ -37,6 +37,13 @@
* own [Configuration] and track resources based on the full set of available mcc-mnc combinations.
*
* (for future reference: b/240555502 is the initiating bug for this)
+ *
+ * NOTE: MCC/MNC qualifiers are not sufficient to fully describe a network type icon qualified by
+ * network type + carrier ID. This class exists to keep the legacy behavior of using the MCC/MNC
+ * resource qualifiers working, but if a carrier-specific icon is requested, then the override
+ * provided by [MobileIconCarrierIdOverrides] will take precedence.
+ *
+ * TODO(b/258503704): consider removing this class in favor of the `carrierId` overrides
*/
@SysUISingleton
class MobileContextProvider
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index eacb18e..14d0d7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -300,7 +300,7 @@
@Override
public boolean isShowingAlternateAuthOnUnlock() {
- return statusBarKeyguardViewManager.get().shouldShowAltAuth();
+ return statusBarKeyguardViewManager.get().canShowAlternateBouncer();
}
};
return new DialogLaunchAnimator(callback, interactionJankMonitor);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index ff63891..df2de56 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -34,6 +34,7 @@
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeEventsModule;
+import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
@@ -160,6 +161,7 @@
NotificationVisibilityProvider visibilityProvider,
NotifPipeline notifPipeline,
StatusBarStateController statusBarStateController,
+ ShadeExpansionStateManager shadeExpansionStateManager,
NotificationLogger.ExpansionStateLogger expansionStateLogger,
NotificationPanelLogger notificationPanelLogger) {
return new NotificationLogger(
@@ -169,6 +171,7 @@
visibilityProvider,
notifPipeline,
statusBarStateController,
+ shadeExpansionStateManager,
expansionStateLogger,
notificationPanelLogger);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 6391877..58f59be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -36,6 +36,7 @@
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
+import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
@@ -92,25 +93,6 @@
private Boolean mPanelExpanded = null; // Use null to indicate state is not yet known
private boolean mLogging = false;
- protected final OnChildLocationsChangedListener mNotificationLocationsChangedListener =
- new OnChildLocationsChangedListener() {
- @Override
- public void onChildLocationsChanged() {
- if (mHandler.hasCallbacks(mVisibilityReporter)) {
- // Visibilities will be reported when the existing
- // callback is executed.
- return;
- }
- // Calculate when we're allowed to run the visibility
- // reporter. Note that this timestamp might already have
- // passed. That's OK, the callback will just be executed
- // ASAP.
- long nextReportUptimeMs =
- mLastVisibilityReportUptimeMs + VISIBILITY_REPORT_MIN_DELAY_MS;
- mHandler.postAtTime(mVisibilityReporter, nextReportUptimeMs);
- }
- };
-
// Tracks notifications currently visible in mNotificationStackScroller and
// emits visibility events via NoMan on changes.
protected Runnable mVisibilityReporter = new Runnable() {
@@ -219,6 +201,7 @@
NotificationVisibilityProvider visibilityProvider,
NotifPipeline notifPipeline,
StatusBarStateController statusBarStateController,
+ ShadeExpansionStateManager shadeExpansionStateManager,
ExpansionStateLogger expansionStateLogger,
NotificationPanelLogger notificationPanelLogger) {
mNotificationListener = notificationListener;
@@ -232,6 +215,7 @@
mNotificationPanelLogger = notificationPanelLogger;
// Not expected to be destroyed, don't need to unsubscribe
statusBarStateController.addCallback(this);
+ shadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged);
registerNewPipelineListener();
}
@@ -278,14 +262,14 @@
if (DEBUG) {
Log.i(TAG, "startNotificationLogging");
}
- mListContainer.setChildLocationsChangedListener(mNotificationLocationsChangedListener);
+ mListContainer.setChildLocationsChangedListener(this::onChildLocationsChanged);
// Some transitions like mVisibleToUser=false -> mVisibleToUser=true don't
// cause the scroller to emit child location events. Hence generate
// one ourselves to guarantee that we're reporting visible
// notifications.
// (Note that in cases where the scroller does emit events, this
// additional event doesn't break anything.)
- mNotificationLocationsChangedListener.onChildLocationsChanged();
+ onChildLocationsChanged();
}
}
@@ -411,21 +395,6 @@
}
/**
- * Called by CentralSurfaces to notify the logger that the panel expansion has changed.
- * The panel may be showing any of the normal notification panel, the AOD, or the bouncer.
- * @param isExpanded True if the panel is expanded.
- */
- public void onPanelExpandedChanged(boolean isExpanded) {
- if (DEBUG) {
- Log.i(TAG, "onPanelExpandedChanged: new=" + isExpanded);
- }
- mPanelExpanded = isExpanded;
- synchronized (mDozingLock) {
- maybeUpdateLoggingStatus();
- }
- }
-
- /**
* Called when the notification is expanded / collapsed.
*/
public void onExpansionChanged(String key, boolean isUserAction, boolean isExpanded) {
@@ -434,6 +403,36 @@
}
@VisibleForTesting
+ void onShadeExpansionFullyChanged(Boolean isExpanded) {
+ // mPanelExpanded is initialized as null
+ if (mPanelExpanded == null || !mPanelExpanded.equals(isExpanded)) {
+ if (DEBUG) {
+ Log.i(TAG, "onPanelExpandedChanged: new=" + isExpanded);
+ }
+ mPanelExpanded = isExpanded;
+ synchronized (mDozingLock) {
+ maybeUpdateLoggingStatus();
+ }
+ }
+ }
+
+ @VisibleForTesting
+ void onChildLocationsChanged() {
+ if (mHandler.hasCallbacks(mVisibilityReporter)) {
+ // Visibilities will be reported when the existing
+ // callback is executed.
+ return;
+ }
+ // Calculate when we're allowed to run the visibility
+ // reporter. Note that this timestamp might already have
+ // passed. That's OK, the callback will just be executed
+ // ASAP.
+ long nextReportUptimeMs =
+ mLastVisibilityReportUptimeMs + VISIBILITY_REPORT_MIN_DELAY_MS;
+ mHandler.postAtTime(mVisibilityReporter, nextReportUptimeMs);
+ }
+
+ @VisibleForTesting
public void setVisibilityReporter(Runnable visibilityReporter) {
mVisibilityReporter = visibilityReporter;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 9e7717c..b93e150 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -135,6 +135,8 @@
private static final String TAG = "ExpandableNotifRow";
private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean DEBUG_ONMEASURE =
+ Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
private static final int COLORED_DIVIDER_ALPHA = 0x7B;
private static final int MENU_VIEW_INDEX = 0;
@@ -1341,21 +1343,6 @@
return mOnKeyguard;
}
- public void removeAllChildren() {
- List<ExpandableNotificationRow> notificationChildren =
- mChildrenContainer.getAttachedChildren();
- ArrayList<ExpandableNotificationRow> clonedList = new ArrayList<>(notificationChildren);
- for (int i = 0; i < clonedList.size(); i++) {
- ExpandableNotificationRow row = clonedList.get(i);
- if (row.keepInParent()) {
- continue;
- }
- mChildrenContainer.removeNotification(row);
- row.setIsChildInGroup(false, null);
- }
- onAttachedChildrenCountChanged();
- }
-
@Override
public void dismiss(boolean refocusOnDismiss) {
super.dismiss(refocusOnDismiss);
@@ -1526,7 +1513,7 @@
l.setAlpha(alpha);
}
if (mChildrenContainer != null) {
- mChildrenContainer.setAlpha(alpha);
+ mChildrenContainer.setContentAlpha(alpha);
}
}
@@ -1739,6 +1726,11 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Trace.beginSection(appendTraceStyleTag("ExpNotRow#onMeasure"));
+ if (DEBUG_ONMEASURE) {
+ Log.d(TAG, "onMeasure("
+ + "widthMeasureSpec=" + MeasureSpec.toString(widthMeasureSpec) + ", "
+ + "heightMeasureSpec=" + MeasureSpec.toString(heightMeasureSpec) + ")");
+ }
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Trace.endSection();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index b2628e4..6f4d6d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -719,7 +719,7 @@
*/
public boolean isBouncerInTransit() {
return mStatusBarKeyguardViewManager != null
- && mStatusBarKeyguardViewManager.isBouncerInTransit();
+ && mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 0554fb5..d43ca823 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -25,6 +25,7 @@
import android.graphics.Path;
import android.graphics.Path.Direction;
import android.graphics.drawable.ColorDrawable;
+import android.os.Trace;
import android.service.notification.StatusBarNotification;
import android.util.AttributeSet;
import android.util.Log;
@@ -219,6 +220,7 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ Trace.beginSection("NotificationChildrenContainer#onMeasure");
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
@@ -267,6 +269,7 @@
}
setMeasuredDimension(width, height);
+ Trace.endSection();
}
@Override
@@ -495,6 +498,20 @@
}
/**
+ * Sets the alpha on the content, while leaving the background of the container itself as is.
+ *
+ * @param alpha alpha value to apply to the content
+ */
+ public void setContentAlpha(float alpha) {
+ for (int i = 0; i < mNotificationHeader.getChildCount(); i++) {
+ mNotificationHeader.getChildAt(i).setAlpha(alpha);
+ }
+ for (ExpandableNotificationRow child : getAttachedChildren()) {
+ child.setContentAlpha(alpha);
+ }
+ }
+
+ /**
* To be called any time the rows have been updated
*/
public void updateExpansionStates() {
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 2c3330e..41dbf1d 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
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.stack;
+import static android.os.Trace.TRACE_TAG_ALWAYS;
+
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_SHADE_CLEAR_ALL;
import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SILENT;
@@ -44,6 +46,7 @@
import android.graphics.Path;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.Trace;
import android.provider.Settings;
import android.util.AttributeSet;
import android.util.IndentingPrintWriter;
@@ -1074,6 +1077,12 @@
@Override
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ Trace.beginSection("NotificationStackScrollLayout#onMeasure");
+ if (SPEW) {
+ Log.d(TAG, "onMeasure("
+ + "widthMeasureSpec=" + MeasureSpec.toString(widthMeasureSpec) + ", "
+ + "heightMeasureSpec=" + MeasureSpec.toString(heightMeasureSpec) + ")");
+ }
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
@@ -1090,6 +1099,13 @@
for (int i = 0; i < size; i++) {
measureChild(getChildAt(i), childWidthSpec, childHeightSpec);
}
+ Trace.endSection();
+ }
+
+ @Override
+ public void requestLayout() {
+ Trace.instant(TRACE_TAG_ALWAYS, "NotificationStackScrollLayout#requestLayout");
+ super.requestLayout();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 2f7d344..9dcbe20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -462,7 +462,7 @@
break;
case MODE_SHOW_BOUNCER:
Trace.beginSection("MODE_SHOW_BOUNCER");
- mKeyguardViewController.showBouncer(true);
+ mKeyguardViewController.showPrimaryBouncer(true);
Trace.endSection();
break;
case MODE_WAKE_AND_UNLOCK_FROM_DREAM:
@@ -542,7 +542,7 @@
return MODE_WAKE_AND_UNLOCK_FROM_DREAM;
}
if (mKeyguardStateController.isShowing()) {
- if (mKeyguardViewController.bouncerIsOrWillBeShowing() && unlockingAllowed) {
+ if (mKeyguardViewController.primaryBouncerIsOrWillBeShowing() && unlockingAllowed) {
return MODE_DISMISS_BOUNCER;
} else if (unlockingAllowed) {
return MODE_UNLOCK_COLLAPSING;
@@ -585,7 +585,7 @@
return MODE_UNLOCK_COLLAPSING;
}
if (mKeyguardStateController.isShowing()) {
- if ((mKeyguardViewController.bouncerIsOrWillBeShowing()
+ if ((mKeyguardViewController.primaryBouncerIsOrWillBeShowing()
|| mKeyguardBypassController.getAltBouncerShowing()) && unlockingAllowed) {
return MODE_DISMISS_BOUNCER;
} else if (unlockingAllowed && (bypass || mAuthController.isUdfpsFingerDown())) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 1961e1d..1ab9be7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -258,8 +258,6 @@
void onKeyguardViewManagerStatesUpdated();
- void setPanelExpanded(boolean isExpanded);
-
ViewGroup getNotificationScrollLayout();
boolean isPulsing();
@@ -456,7 +454,11 @@
void setTransitionToFullShadeProgress(float transitionToFullShadeProgress);
- void setBouncerHiddenFraction(float expansion);
+ /**
+ * Sets the amount of progress to the bouncer being fully hidden/visible. 1 means the bouncer
+ * is fully hidden, while 0 means the bouncer is visible.
+ */
+ void setPrimaryBouncerHiddenFraction(float expansion);
@VisibleForTesting
void updateScrimController();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index 41f0520..f3482f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -55,6 +55,7 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.qs.QSPanelController;
+import com.android.systemui.shade.CameraLauncher;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.CommandQueue;
@@ -71,6 +72,8 @@
import javax.inject.Inject;
+import dagger.Lazy;
+
/** */
@CentralSurfacesComponent.CentralSurfacesScope
public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callbacks {
@@ -99,6 +102,7 @@
private final boolean mVibrateOnOpening;
private final VibrationEffect mCameraLaunchGestureVibrationEffect;
private final SystemBarAttributesListener mSystemBarAttributesListener;
+ private final Lazy<CameraLauncher> mCameraLauncherLazy;
private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
@@ -128,8 +132,8 @@
Optional<Vibrator> vibratorOptional,
DisableFlagsLogger disableFlagsLogger,
@DisplayId int displayId,
- SystemBarAttributesListener systemBarAttributesListener) {
-
+ SystemBarAttributesListener systemBarAttributesListener,
+ Lazy<CameraLauncher> cameraLauncherLazy) {
mCentralSurfaces = centralSurfaces;
mContext = context;
mShadeController = shadeController;
@@ -152,6 +156,7 @@
mVibratorOptional = vibratorOptional;
mDisableFlagsLogger = disableFlagsLogger;
mDisplayId = displayId;
+ mCameraLauncherLazy = cameraLauncherLazy;
mVibrateOnOpening = resources.getBoolean(R.bool.config_vibrateOnIconAnimation);
mCameraLaunchGestureVibrationEffect = getCameraGestureVibrationEffect(
@@ -346,7 +351,8 @@
mCentralSurfaces.setLaunchCameraOnFinishedGoingToSleep(true);
return;
}
- if (!mNotificationPanelViewController.canCameraGestureBeLaunched()) {
+ if (!mCameraLauncherLazy.get().canCameraGestureBeLaunched(
+ mNotificationPanelViewController.getBarState())) {
if (CentralSurfaces.DEBUG_CAMERA_LIFT) {
Slog.d(CentralSurfaces.TAG, "Can't launch camera right now");
}
@@ -383,7 +389,8 @@
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
mStatusBarKeyguardViewManager.reset(true /* hide */);
}
- mNotificationPanelViewController.launchCamera(source);
+ mCameraLauncherLazy.get().launchCamera(source,
+ mNotificationPanelViewController.isFullyCollapsed());
mCentralSurfaces.updateScrimController();
} else {
// We need to defer the camera launch until the screen comes on, since otherwise
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 6bb5272..334f1af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -175,6 +175,7 @@
import com.android.systemui.ripple.RippleShader.RippleShape;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.settings.brightness.BrightnessSliderController;
+import com.android.systemui.shade.CameraLauncher;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.NotificationShadeWindowViewController;
@@ -468,6 +469,7 @@
private final PluginManager mPluginManager;
private final ShadeController mShadeController;
private final InitController mInitController;
+ private final Lazy<CameraLauncher> mCameraLauncherLazy;
private final PluginDependencyProvider mPluginDependencyProvider;
private final KeyguardDismissUtil mKeyguardDismissUtil;
@@ -600,6 +602,7 @@
private Runnable mLaunchTransitionEndRunnable;
private Runnable mLaunchTransitionCancelRunnable;
+ private boolean mLaunchingAffordance;
private boolean mLaunchCameraWhenFinishedWaking;
private boolean mLaunchCameraOnFinishedGoingToSleep;
private boolean mLaunchEmergencyActionWhenFinishedWaking;
@@ -744,7 +747,8 @@
InteractionJankMonitor jankMonitor,
DeviceStateManager deviceStateManager,
WiredChargingRippleController wiredChargingRippleController,
- IDreamManager dreamManager) {
+ IDreamManager dreamManager,
+ Lazy<CameraLauncher> cameraLauncherLazy) {
mContext = context;
mNotificationsController = notificationsController;
mFragmentService = fragmentService;
@@ -821,6 +825,7 @@
mMessageRouter = messageRouter;
mWallpaperManager = wallpaperManager;
mJankMonitor = jankMonitor;
+ mCameraLauncherLazy = cameraLauncherLazy;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
mStartingSurfaceOptional = startingSurfaceOptional;
@@ -831,6 +836,7 @@
mScreenOffAnimationController = screenOffAnimationController;
mShadeExpansionStateManager.addExpansionListener(this::onPanelExpansionChanged);
+ mShadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged);
mBubbleExpandListener = (isExpanding, key) ->
mContext.getMainExecutor().execute(this::updateScrimController);
@@ -1359,6 +1365,7 @@
private void onPanelExpansionChanged(ShadeExpansionChangeEvent event) {
float fraction = event.getFraction();
boolean tracking = event.getTracking();
+ boolean isExpanded = event.getExpanded();
dispatchPanelExpansionForKeyguardDismiss(fraction, tracking);
if (fraction == 0 || fraction == 1) {
@@ -1371,6 +1378,23 @@
}
}
+ @VisibleForTesting
+ void onShadeExpansionFullyChanged(Boolean isExpanded) {
+ if (mPanelExpanded != isExpanded) {
+ mPanelExpanded = isExpanded;
+ if (isExpanded && mStatusBarStateController.getState() != StatusBarState.KEYGUARD) {
+ if (DEBUG) {
+ Log.v(TAG, "clearing notification effects from Height");
+ }
+ clearNotificationEffects();
+ }
+
+ if (!isExpanded) {
+ mRemoteInputManager.onPanelCollapsed();
+ }
+ }
+ }
+
@NonNull
@Override
public Lifecycle getLifecycle() {
@@ -1775,27 +1799,6 @@
}
@Override
- public void setPanelExpanded(boolean isExpanded) {
- if (mPanelExpanded != isExpanded) {
- mNotificationLogger.onPanelExpandedChanged(isExpanded);
- }
- mPanelExpanded = isExpanded;
- mStatusBarHideIconsForBouncerManager.setPanelExpandedAndTriggerUpdate(isExpanded);
- mNotificationShadeWindowController.setPanelExpanded(isExpanded);
- mStatusBarStateController.setPanelExpanded(isExpanded);
- if (isExpanded && mStatusBarStateController.getState() != StatusBarState.KEYGUARD) {
- if (DEBUG) {
- Log.v(TAG, "clearing notification effects from Height");
- }
- clearNotificationEffects();
- }
-
- if (!isExpanded) {
- mRemoteInputManager.onPanelCollapsed();
- }
- }
-
- @Override
public ViewGroup getNotificationScrollLayout() {
return mStackScroller;
}
@@ -2247,13 +2250,6 @@
}
pw.println(" Panels: ");
- if (mNotificationPanelViewController != null) {
- pw.println(" mNotificationPanel="
- + mNotificationPanelViewController.getView() + " params="
- + mNotificationPanelViewController.getView().getLayoutParams().debug(""));
- pw.print (" ");
- mNotificationPanelViewController.dump(pw, args);
- }
pw.println(" mStackScroller: " + mStackScroller + " (dump moved)");
pw.println(" Theme:");
String nightMode = mUiModeManager == null ? "null" : mUiModeManager.getNightMode() + "";
@@ -2549,12 +2545,6 @@
// ordering.
mMainExecutor.execute(mShadeController::runPostCollapseRunnables);
}
- } else if (mNotificationPanelViewController.isLaunchTransitionFinished()) {
- // We are not dismissing the shade, but the launch transition is already
- // finished,
- // so nobody will call readyForKeyguardDone anymore. Post it such that
- // keyguardDonePending gets called first.
- mMainExecutor.execute(mStatusBarKeyguardViewManager::readyForKeyguardDone);
}
return deferred;
}
@@ -2964,7 +2954,7 @@
private void onLaunchTransitionFadingEnded() {
mNotificationPanelViewController.resetAlpha();
- mNotificationPanelViewController.onAffordanceLaunchEnded();
+ mCameraLauncherLazy.get().setLaunchingAffordance(false);
releaseGestureWakeLock();
runLaunchTransitionEndRunnable();
mKeyguardStateController.setLaunchTransitionFadingAway(false);
@@ -3034,7 +3024,7 @@
private void onLaunchTransitionTimeout() {
Log.w(TAG, "Launch transition: Timeout!");
- mNotificationPanelViewController.onAffordanceLaunchEnded();
+ mCameraLauncherLazy.get().setLaunchingAffordance(false);
releaseGestureWakeLock();
mNotificationPanelViewController.resetViews(false /* animate */);
}
@@ -3087,7 +3077,7 @@
}
mMessageRouter.cancelMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
releaseGestureWakeLock();
- mNotificationPanelViewController.onAffordanceLaunchEnded();
+ mCameraLauncherLazy.get().setLaunchingAffordance(false);
mNotificationPanelViewController.resetAlpha();
mNotificationPanelViewController.resetTranslation();
mNotificationPanelViewController.resetViewGroupFade();
@@ -3245,7 +3235,7 @@
@Override
public void endAffordanceLaunch() {
releaseGestureWakeLock();
- mNotificationPanelViewController.onAffordanceLaunchEnded();
+ mCameraLauncherLazy.get().setLaunchingAffordance(false);
}
/**
@@ -3308,9 +3298,9 @@
// lock screen where users can use the UDFPS affordance to enter the device
mStatusBarKeyguardViewManager.reset(true);
} else if (mState == StatusBarState.KEYGUARD
- && !mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()
+ && !mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()
&& isKeyguardSecure()) {
- mStatusBarKeyguardViewManager.showGenericBouncer(true /* scrimmed */);
+ mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */);
}
}
}
@@ -3518,7 +3508,7 @@
final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() {
@Override
public void onFinishedGoingToSleep() {
- mNotificationPanelViewController.onAffordanceLaunchEnded();
+ mCameraLauncherLazy.get().setLaunchingAffordance(false);
releaseGestureWakeLock();
mLaunchCameraWhenFinishedWaking = false;
mDeviceInteractive = false;
@@ -3619,7 +3609,8 @@
.updateSensitivenessForOccludedWakeup();
}
if (mLaunchCameraWhenFinishedWaking) {
- mNotificationPanelViewController.launchCamera(mLastCameraLaunchSource);
+ mCameraLauncherLazy.get().launchCamera(mLastCameraLaunchSource,
+ mNotificationPanelViewController.isFullyCollapsed());
mLaunchCameraWhenFinishedWaking = false;
}
if (mLaunchEmergencyActionWhenFinishedWaking) {
@@ -3792,7 +3783,7 @@
* is fully hidden, while 0 means the bouncer is visible.
*/
@Override
- public void setBouncerHiddenFraction(float expansion) {
+ public void setPrimaryBouncerHiddenFraction(float expansion) {
mScrimController.setBouncerHiddenFraction(expansion);
}
@@ -3810,11 +3801,10 @@
mScrimController.setExpansionAffectsAlpha(!unlocking);
- boolean launchingAffordanceWithPreview =
- mNotificationPanelViewController.isLaunchingAffordanceWithPreview();
+ boolean launchingAffordanceWithPreview = mLaunchingAffordance;
mScrimController.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview);
- if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) {
+ if (mStatusBarKeyguardViewManager.isShowingAlternateBouncer()) {
if (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
|| mTransitionToFullShadeProgress > 0f) {
mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
@@ -3825,7 +3815,7 @@
// Bouncer needs the front scrim when it's on top of an activity,
// tapping on a notification, editing QS or being dismissed by
// FLAG_DISMISS_KEYGUARD_ACTIVITY.
- ScrimState state = mStatusBarKeyguardViewManager.bouncerNeedsScrimming()
+ ScrimState state = mStatusBarKeyguardViewManager.primaryBouncerNeedsScrimming()
? ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER;
mScrimController.transitionTo(state);
} else if (launchingAffordanceWithPreview) {
@@ -4134,7 +4124,7 @@
*/
@Override
public boolean isBouncerShowingScrimmed() {
- return isBouncerShowing() && mStatusBarKeyguardViewManager.bouncerNeedsScrimming();
+ return isBouncerShowing() && mStatusBarKeyguardViewManager.primaryBouncerNeedsScrimming();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 103e4f6..3743fff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -34,6 +34,7 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
+import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.provider.OnReorderingAllowedListener;
@@ -111,7 +112,8 @@
ConfigurationController configurationController,
@Main Handler handler,
AccessibilityManagerWrapper accessibilityManagerWrapper,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger,
+ ShadeExpansionStateManager shadeExpansionStateManager) {
super(context, logger, handler, accessibilityManagerWrapper, uiEventLogger);
Resources resources = mContext.getResources();
mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time);
@@ -132,6 +134,8 @@
updateResources();
}
});
+
+ shadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged);
}
public void setAnimationStateHandler(AnimationStateHandler handler) {
@@ -221,13 +225,7 @@
mTrackingHeadsUp = trackingHeadsUp;
}
- /**
- * Notify that the status bar panel gets expanded or collapsed.
- *
- * @param isExpanded True to notify expanded, false to notify collapsed.
- * TODO(b/237811427) replace with a listener
- */
- public void setIsPanelExpanded(boolean isExpanded) {
+ private void onShadeExpansionFullyChanged(Boolean isExpanded) {
if (isExpanded != mIsExpanded) {
mIsExpanded = isExpanded;
if (isExpanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
index 4897c52..78b28d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
@@ -23,6 +23,8 @@
import android.view.ViewPropertyAnimator
import android.view.WindowInsets
import android.widget.FrameLayout
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.LockIconViewController
import com.android.systemui.R
import com.android.systemui.keyguard.ui.binder.KeyguardBottomAreaViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardBottomAreaViewBinder.bind
@@ -51,13 +53,20 @@
private var ambientIndicationArea: View? = null
private lateinit var binding: KeyguardBottomAreaViewBinder.Binding
+ private lateinit var lockIconViewController: LockIconViewController
/** Initializes the view. */
fun init(
viewModel: KeyguardBottomAreaViewModel,
falsingManager: FalsingManager,
+ lockIconViewController: LockIconViewController,
) {
- binding = bind(this, viewModel, falsingManager)
+ binding = bind(
+ this,
+ viewModel,
+ falsingManager,
+ )
+ this.lockIconViewController = lockIconViewController
}
/**
@@ -114,4 +123,29 @@
}
return insets
}
+
+ override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
+ super.onLayout(changed, left, top, right, bottom)
+ findViewById<View>(R.id.ambient_indication_container)?.let {
+ val (ambientLeft, ambientTop) = it.locationOnScreen
+ if (binding.shouldConstrainToTopOfLockIcon()) {
+ //make top of ambient indication view the bottom of the lock icon
+ it.layout(
+ ambientLeft,
+ lockIconViewController.bottom.toInt(),
+ right - ambientLeft,
+ ambientTop + it.measuredHeight
+ )
+ } else {
+ //make bottom of ambient indication view the top of the lock icon
+ val lockLocationTop = lockIconViewController.top
+ it.layout(
+ ambientLeft,
+ lockLocationTop.toInt() - it.measuredHeight,
+ right - ambientLeft,
+ lockLocationTop.toInt()
+ )
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index a28fca1..aa0757e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -55,13 +55,13 @@
import javax.inject.Inject;
/**
- * A class which manages the bouncer on the lockscreen.
+ * A class which manages the primary (pin/pattern/password) bouncer on the lockscreen.
* @deprecated Use KeyguardBouncerRepository
*/
@Deprecated
public class KeyguardBouncer {
- private static final String TAG = "KeyguardBouncer";
+ private static final String TAG = "PrimaryKeyguardBouncer";
static final long BOUNCER_FACE_DELAY = 1200;
public static final float ALPHA_EXPANSION_THRESHOLD = 0.95f;
/**
@@ -78,7 +78,7 @@
private final FalsingCollector mFalsingCollector;
private final DismissCallbackRegistry mDismissCallbackRegistry;
private final Handler mHandler;
- private final List<BouncerExpansionCallback> mExpansionCallbacks = new ArrayList<>();
+ private final List<PrimaryBouncerExpansionCallback> mExpansionCallbacks = new ArrayList<>();
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final KeyguardStateController mKeyguardStateController;
private final KeyguardSecurityModel mKeyguardSecurityModel;
@@ -126,7 +126,7 @@
private KeyguardBouncer(Context context, ViewMediatorCallback callback,
ViewGroup container,
DismissCallbackRegistry dismissCallbackRegistry, FalsingCollector falsingCollector,
- BouncerExpansionCallback expansionCallback,
+ PrimaryBouncerExpansionCallback expansionCallback,
KeyguardStateController keyguardStateController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
KeyguardBypassController keyguardBypassController, @Main Handler handler,
@@ -279,10 +279,7 @@
* @see #onFullyShown()
*/
private void onFullyHidden() {
- cancelShowRunnable();
- setVisibility(View.INVISIBLE);
- mFalsingCollector.onBouncerHidden();
- DejankUtils.postAfterTraversal(mResetRunnable);
+
}
private void setVisibility(@View.Visibility int visibility) {
@@ -459,7 +456,13 @@
onFullyShown();
dispatchFullyShown();
} else if (fraction == EXPANSION_HIDDEN && oldExpansion != EXPANSION_HIDDEN) {
- onFullyHidden();
+ DejankUtils.postAfterTraversal(mResetRunnable);
+ /*
+ * There are cases where #hide() was not invoked, such as when
+ * NotificationPanelViewController controls the hide animation. Make sure the state gets
+ * updated by calling #hide() directly.
+ */
+ hide(false /* destroyView */);
dispatchFullyHidden();
} else if (fraction != EXPANSION_VISIBLE && oldExpansion == EXPANSION_VISIBLE) {
dispatchStartingToHide();
@@ -571,37 +574,37 @@
}
private void dispatchFullyShown() {
- for (BouncerExpansionCallback callback : mExpansionCallbacks) {
+ for (PrimaryBouncerExpansionCallback callback : mExpansionCallbacks) {
callback.onFullyShown();
}
}
private void dispatchStartingToHide() {
- for (BouncerExpansionCallback callback : mExpansionCallbacks) {
+ for (PrimaryBouncerExpansionCallback callback : mExpansionCallbacks) {
callback.onStartingToHide();
}
}
private void dispatchStartingToShow() {
- for (BouncerExpansionCallback callback : mExpansionCallbacks) {
+ for (PrimaryBouncerExpansionCallback callback : mExpansionCallbacks) {
callback.onStartingToShow();
}
}
private void dispatchFullyHidden() {
- for (BouncerExpansionCallback callback : mExpansionCallbacks) {
+ for (PrimaryBouncerExpansionCallback callback : mExpansionCallbacks) {
callback.onFullyHidden();
}
}
private void dispatchExpansionChanged() {
- for (BouncerExpansionCallback callback : mExpansionCallbacks) {
+ for (PrimaryBouncerExpansionCallback callback : mExpansionCallbacks) {
callback.onExpansionChanged(mExpansion);
}
}
private void dispatchVisibilityChanged() {
- for (BouncerExpansionCallback callback : mExpansionCallbacks) {
+ for (PrimaryBouncerExpansionCallback callback : mExpansionCallbacks) {
callback.onVisibilityChanged(mContainer.getVisibility() == View.VISIBLE);
}
}
@@ -647,7 +650,7 @@
/**
* Adds a callback to listen to bouncer expansion updates.
*/
- public void addBouncerExpansionCallback(BouncerExpansionCallback callback) {
+ public void addBouncerExpansionCallback(PrimaryBouncerExpansionCallback callback) {
if (!mExpansionCallbacks.contains(callback)) {
mExpansionCallbacks.add(callback);
}
@@ -657,11 +660,14 @@
* Removes a previously added callback. If the callback was never added, this methood
* does nothing.
*/
- public void removeBouncerExpansionCallback(BouncerExpansionCallback callback) {
+ public void removeBouncerExpansionCallback(PrimaryBouncerExpansionCallback callback) {
mExpansionCallbacks.remove(callback);
}
- public interface BouncerExpansionCallback {
+ /**
+ * Callback updated when the primary bouncer's show and hide states change.
+ */
+ public interface PrimaryBouncerExpansionCallback {
/**
* Invoked when the bouncer expansion reaches {@link KeyguardBouncer#EXPANSION_VISIBLE}.
* This is NOT called each time the bouncer is shown, but rather only when the fully
@@ -745,7 +751,7 @@
* Construct a KeyguardBouncer that will exist in the given container.
*/
public KeyguardBouncer create(ViewGroup container,
- BouncerExpansionCallback expansionCallback) {
+ PrimaryBouncerExpansionCallback expansionCallback) {
return new KeyguardBouncer(mContext, mCallback, container,
mDismissCallbackRegistry, mFalsingCollector, expansionCallback,
mKeyguardStateController, mKeyguardUpdateMonitor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 18877f9..7a49a49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -26,6 +26,7 @@
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.os.Trace;
import android.util.AttributeSet;
import android.util.Pair;
import android.util.TypedValue;
@@ -527,4 +528,11 @@
mClipRect.set(0, mTopClipping, getWidth(), getHeight());
setClipBounds(mClipRect);
}
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ Trace.beginSection("KeyguardStatusBarView#onMeasure");
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ Trace.endSection();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index c189ace..4ee2de1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -141,7 +141,6 @@
/* Maximum number of icons in short shelf on lockscreen when also showing overflow dot. */
public static final int MAX_ICONS_ON_LOCKSCREEN = 3;
public static final int MAX_STATIC_ICONS = 4;
- private static final int MAX_DOTS = 1;
private boolean mIsStaticLayout = true;
private final HashMap<View, IconState> mIconStates = new HashMap<>();
@@ -166,8 +165,7 @@
private IconState mLastVisibleIconState;
private IconState mFirstVisibleIconState;
private float mVisualOverflowStart;
- // Keep track of overflow in range [0, 3]
- private int mNumDots;
+ private boolean mIsShowingOverflowDot;
private StatusBarIconView mIsolatedIcon;
private Rect mIsolatedIconLocation;
private int[] mAbsolutePosition = new int[2];
@@ -387,8 +385,8 @@
}
}
- public boolean hasMaxNumDot() {
- return mNumDots >= MAX_DOTS;
+ public boolean areIconsOverflowing() {
+ return mIsShowingOverflowDot;
}
private boolean areAnimationsEnabled(StatusBarIconView icon) {
@@ -494,7 +492,7 @@
: 1f;
translationX += iconState.iconAppearAmount * view.getWidth() * drawingScale;
}
- mNumDots = 0;
+ mIsShowingOverflowDot = false;
if (firstOverflowIndex != -1) {
translationX = mVisualOverflowStart;
for (int i = firstOverflowIndex; i < childCount; i++) {
@@ -502,15 +500,14 @@
IconState iconState = mIconStates.get(view);
int dotWidth = mStaticDotDiameter + mDotPadding;
iconState.setXTranslation(translationX);
- if (mNumDots < MAX_DOTS) {
- if (mNumDots == 0 && iconState.iconAppearAmount < 0.8f) {
+ if (!mIsShowingOverflowDot) {
+ if (iconState.iconAppearAmount < 0.8f) {
iconState.visibleState = StatusBarIconView.STATE_ICON;
} else {
iconState.visibleState = StatusBarIconView.STATE_DOT;
- mNumDots++;
+ mIsShowingOverflowDot = true;
}
- translationX += (mNumDots == MAX_DOTS ? MAX_DOTS * dotWidth : dotWidth)
- * iconState.iconAppearAmount;
+ translationX += dotWidth * iconState.iconAppearAmount;
mLastVisibleIconState = iconState;
} else {
iconState.visibleState = StatusBarIconView.STATE_HIDDEN;
@@ -618,10 +615,6 @@
return Math.min(getWidth(), translation);
}
- private float getMaxOverflowStart() {
- return getLayoutEnd() - mIconSize;
- }
-
public void setChangingViewPositions(boolean changingViewPositions) {
mChangingViewPositions = changingViewPositions;
}
@@ -645,25 +638,6 @@
mSpeedBumpIndex = speedBumpIndex;
}
- public boolean hasOverflow() {
- return mNumDots > 0;
- }
-
- // Give some extra room for btw notifications if we can
- public int getNoOverflowExtraPadding() {
- if (mNumDots != 0) {
- return 0;
- }
-
- int collapsedPadding = mIconSize;
-
- if (collapsedPadding + getFinalTranslationX() > getWidth()) {
- collapsedPadding = getWidth() - getFinalTranslationX();
- }
-
- return collapsedPadding;
- }
-
public int getIconSize() {
return mIconSize;
}
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 cf3a48c..d54a863 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -820,15 +820,7 @@
mNotificationsAlpha = KEYGUARD_SCRIM_ALPHA;
}
} else if (mState == ScrimState.AUTH_SCRIMMED_SHADE) {
- float behindFraction = getInterpolatedFraction();
- behindFraction = (float) Math.pow(behindFraction, 0.8f);
-
- mBehindAlpha = behindFraction * mDefaultScrimAlpha;
- mNotificationsAlpha = mBehindAlpha;
- if (mClipsQsScrim) {
- mBehindAlpha = 1;
- mBehindTint = Color.BLACK;
- }
+ mNotificationsAlpha = (float) Math.pow(getInterpolatedFraction(), 0.8f);
} else if (mState == ScrimState.KEYGUARD || mState == ScrimState.SHADE_LOCKED
|| mState == ScrimState.PULSING) {
Pair<Integer, Float> result = calculateBackStateForState(mState);
@@ -903,7 +895,7 @@
float stateBehind = mClipsQsScrim ? state.getNotifAlpha() : state.getBehindAlpha();
float behindAlpha;
- int behindTint;
+ int behindTint = state.getBehindTint();
if (mDarkenWhileDragging) {
behindAlpha = MathUtils.lerp(mDefaultScrimAlpha, stateBehind,
interpolatedFract);
@@ -911,17 +903,19 @@
behindAlpha = MathUtils.lerp(0 /* start */, stateBehind,
interpolatedFract);
}
- if (mClipsQsScrim) {
- behindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getNotifTint(),
+ if (mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()) {
+ if (mClipsQsScrim) {
+ behindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getNotifTint(),
state.getNotifTint(), interpolatedFract);
- } else {
- behindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getBehindTint(),
+ } else {
+ behindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getBehindTint(),
state.getBehindTint(), interpolatedFract);
+ }
}
if (mQsExpansion > 0) {
behindAlpha = MathUtils.lerp(behindAlpha, mDefaultScrimAlpha, mQsExpansion);
float tintProgress = mQsExpansion;
- if (mStatusBarKeyguardViewManager.isBouncerInTransit()) {
+ if (mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()) {
// this is case of - on lockscreen - going from expanded QS to bouncer.
// Because mQsExpansion is already interpolated and transition between tints
// is too slow, we want to speed it up and make it more aligned to bouncer
@@ -1104,7 +1098,7 @@
}
private float getInterpolatedFraction() {
- if (mStatusBarKeyguardViewManager.isBouncerInTransit()) {
+ if (mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()) {
return BouncerPanelExpansionCalculator
.aboutToShowBouncerProgress(mPanelExpansionFraction);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index b447f0d..52430d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -90,11 +90,14 @@
AUTH_SCRIMMED_SHADE {
@Override
public void prepare(ScrimState previousState) {
- // notif & behind scrim alpha values are determined by ScrimController#applyState
+ // notif scrim alpha values are determined by ScrimController#applyState
// based on the shade expansion
mFrontTint = Color.BLACK;
mFrontAlpha = .66f;
+
+ mBehindTint = Color.BLACK;
+ mBehindAlpha = 1f;
}
},
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt
index 5113191..4d9de09 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt
@@ -5,6 +5,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
+import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -24,10 +25,11 @@
*/
@SysUISingleton
class StatusBarHideIconsForBouncerManager @Inject constructor(
- private val commandQueue: CommandQueue,
- @Main private val mainExecutor: DelayableExecutor,
- statusBarWindowStateController: StatusBarWindowStateController,
- dumpManager: DumpManager
+ private val commandQueue: CommandQueue,
+ @Main private val mainExecutor: DelayableExecutor,
+ statusBarWindowStateController: StatusBarWindowStateController,
+ shadeExpansionStateManager: ShadeExpansionStateManager,
+ dumpManager: DumpManager
) : Dumpable {
// State variables set by external classes.
private var panelExpanded: Boolean = false
@@ -47,6 +49,12 @@
statusBarWindowStateController.addListener {
state -> setStatusBarStateAndTriggerUpdate(state)
}
+ shadeExpansionStateManager.addFullExpansionListener { isExpanded ->
+ if (panelExpanded != isExpanded) {
+ panelExpanded = isExpanded
+ updateHideIconsForBouncer(animate = false)
+ }
+ }
}
/** Returns true if the status bar icons should be hidden in the bouncer. */
@@ -63,11 +71,6 @@
this.displayId = displayId
}
- fun setPanelExpandedAndTriggerUpdate(panelExpanded: Boolean) {
- this.panelExpanded = panelExpanded
- updateHideIconsForBouncer(animate = false)
- }
-
fun setIsOccludedAndTriggerUpdate(isOccluded: Boolean) {
this.isOccluded = isOccluded
updateHideIconsForBouncer(animate = false)
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 93b6437..9e075e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -60,8 +60,8 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.data.BouncerView;
-import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor;
-import com.android.systemui.keyguard.domain.interactor.BouncerInteractor;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -78,7 +78,7 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
-import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback;
+import com.android.systemui.statusbar.phone.KeyguardBouncer.PrimaryBouncerExpansionCallback;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.unfold.FoldAodAnimationController;
@@ -135,62 +135,64 @@
@Nullable
private final FoldAodAnimationController mFoldAodAnimationController;
private KeyguardMessageAreaController<AuthKeyguardMessageArea> mKeyguardMessageAreaController;
- private final BouncerCallbackInteractor mBouncerCallbackInteractor;
- private final BouncerInteractor mBouncerInteractor;
- private final BouncerView mBouncerView;
+ private final PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor;
+ private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
+ private final BouncerView mPrimaryBouncerView;
private final Lazy<com.android.systemui.shade.ShadeController> mShadeController;
- private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() {
- private boolean mBouncerAnimating;
+ private final PrimaryBouncerExpansionCallback mExpansionCallback =
+ new PrimaryBouncerExpansionCallback() {
+ private boolean mPrimaryBouncerAnimating;
- @Override
- public void onFullyShown() {
- mBouncerAnimating = false;
- updateStates();
- }
-
- @Override
- public void onStartingToHide() {
- mBouncerAnimating = true;
- updateStates();
- }
-
- @Override
- public void onStartingToShow() {
- mBouncerAnimating = true;
- updateStates();
- }
-
- @Override
- public void onFullyHidden() {
- mBouncerAnimating = false;
- }
-
- @Override
- public void onExpansionChanged(float expansion) {
- if (mBouncerAnimating) {
- mCentralSurfaces.setBouncerHiddenFraction(expansion);
- }
- }
-
- @Override
- public void onVisibilityChanged(boolean isVisible) {
- mCentralSurfaces
- .setBouncerShowingOverDream(
- isVisible && mDreamOverlayStateController.isOverlayActive());
-
- if (!isVisible) {
- mCentralSurfaces.setBouncerHiddenFraction(KeyguardBouncer.EXPANSION_HIDDEN);
+ @Override
+ public void onFullyShown() {
+ mPrimaryBouncerAnimating = false;
+ updateStates();
}
- /* Register predictive back callback when keyguard becomes visible, and unregister
- when it's hidden. */
- if (isVisible) {
- registerBackCallback();
- } else {
- unregisterBackCallback();
+ @Override
+ public void onStartingToHide() {
+ mPrimaryBouncerAnimating = true;
+ updateStates();
}
- }
+
+ @Override
+ public void onStartingToShow() {
+ mPrimaryBouncerAnimating = true;
+ updateStates();
+ }
+
+ @Override
+ public void onFullyHidden() {
+ mPrimaryBouncerAnimating = false;
+ updateStates();
+ }
+
+ @Override
+ public void onExpansionChanged(float expansion) {
+ if (mPrimaryBouncerAnimating) {
+ mCentralSurfaces.setPrimaryBouncerHiddenFraction(expansion);
+ }
+ }
+
+ @Override
+ public void onVisibilityChanged(boolean isVisible) {
+ mCentralSurfaces.setBouncerShowingOverDream(
+ isVisible && mDreamOverlayStateController.isOverlayActive());
+
+ if (!isVisible) {
+ mCentralSurfaces.setPrimaryBouncerHiddenFraction(
+ KeyguardBouncer.EXPANSION_HIDDEN);
+ }
+
+ /* Register predictive back callback when keyguard becomes visible, and unregister
+ when it's hidden. */
+ if (isVisible) {
+ registerBackCallback();
+ } else {
+ unregisterBackCallback();
+ }
+ }
};
private final OnBackInvokedCallback mOnBackInvokedCallback = () -> {
@@ -223,7 +225,7 @@
private View mNotificationContainer;
- @Nullable protected KeyguardBouncer mBouncer;
+ @Nullable protected KeyguardBouncer mPrimaryBouncer;
protected boolean mRemoteInputActive;
private boolean mGlobalActionsVisible = false;
private boolean mLastGlobalActionsVisible = false;
@@ -236,8 +238,8 @@
protected boolean mFirstUpdate = true;
protected boolean mLastShowing;
protected boolean mLastOccluded;
- private boolean mLastBouncerShowing;
- private boolean mLastBouncerIsOrWillBeShowing;
+ private boolean mLastPrimaryBouncerShowing;
+ private boolean mLastPrimaryBouncerIsOrWillBeShowing;
private boolean mLastBouncerDismissible;
protected boolean mLastRemoteInputActive;
private boolean mLastDozing;
@@ -265,7 +267,7 @@
private final LatencyTracker mLatencyTracker;
private final KeyguardSecurityModel mKeyguardSecurityModel;
private KeyguardBypassController mBypassController;
- @Nullable private AlternateAuthInterceptor mAlternateAuthInterceptor;
+ @Nullable private AlternateBouncer mAlternateBouncer;
private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
new KeyguardUpdateMonitorCallback() {
@@ -300,9 +302,9 @@
LatencyTracker latencyTracker,
KeyguardSecurityModel keyguardSecurityModel,
FeatureFlags featureFlags,
- BouncerCallbackInteractor bouncerCallbackInteractor,
- BouncerInteractor bouncerInteractor,
- BouncerView bouncerView) {
+ PrimaryBouncerCallbackInteractor primaryBouncerCallbackInteractor,
+ PrimaryBouncerInteractor primaryBouncerInteractor,
+ BouncerView primaryBouncerView) {
mContext = context;
mViewMediatorCallback = callback;
mLockPatternUtils = lockPatternUtils;
@@ -320,9 +322,9 @@
mShadeController = shadeController;
mLatencyTracker = latencyTracker;
mKeyguardSecurityModel = keyguardSecurityModel;
- mBouncerCallbackInteractor = bouncerCallbackInteractor;
- mBouncerInteractor = bouncerInteractor;
- mBouncerView = bouncerView;
+ mPrimaryBouncerCallbackInteractor = primaryBouncerCallbackInteractor;
+ mPrimaryBouncerInteractor = primaryBouncerInteractor;
+ mPrimaryBouncerView = primaryBouncerView;
mFoldAodAnimationController = sysUIUnfoldComponent
.map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null);
mIsModernBouncerEnabled = featureFlags.isEnabled(Flags.MODERN_BOUNCER);
@@ -340,9 +342,9 @@
ViewGroup container = mCentralSurfaces.getBouncerContainer();
if (mIsModernBouncerEnabled) {
- mBouncerCallbackInteractor.addBouncerExpansionCallback(mExpansionCallback);
+ mPrimaryBouncerCallbackInteractor.addBouncerExpansionCallback(mExpansionCallback);
} else {
- mBouncer = mKeyguardBouncerFactory.create(container, mExpansionCallback);
+ mPrimaryBouncer = mKeyguardBouncerFactory.create(container, mExpansionCallback);
}
mNotificationPanelViewController = notificationPanelViewController;
if (shadeExpansionStateManager != null) {
@@ -361,20 +363,20 @@
* Sets the given alt auth interceptor to null if it's the current auth interceptor. Else,
* does nothing.
*/
- public void removeAlternateAuthInterceptor(@NonNull AlternateAuthInterceptor authInterceptor) {
- if (Objects.equals(mAlternateAuthInterceptor, authInterceptor)) {
- mAlternateAuthInterceptor = null;
- resetAlternateAuth(true);
+ public void removeAlternateAuthInterceptor(@NonNull AlternateBouncer authInterceptor) {
+ if (Objects.equals(mAlternateBouncer, authInterceptor)) {
+ mAlternateBouncer = null;
+ hideAlternateBouncer(true);
}
}
/**
* Sets a new alt auth interceptor.
*/
- public void setAlternateAuthInterceptor(@NonNull AlternateAuthInterceptor authInterceptor) {
- if (!Objects.equals(mAlternateAuthInterceptor, authInterceptor)) {
- mAlternateAuthInterceptor = authInterceptor;
- resetAlternateAuth(false);
+ public void setAlternateBouncer(@NonNull AlternateBouncer authInterceptor) {
+ if (!Objects.equals(mAlternateBouncer, authInterceptor)) {
+ mAlternateBouncer = authInterceptor;
+ hideAlternateBouncer(false);
}
}
@@ -458,49 +460,48 @@
if (mDozing && !mPulsing) {
return;
} else if (mNotificationPanelViewController.isUnlockHintRunning()) {
- if (mBouncer != null) {
- mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
} else {
- mBouncerInteractor.setPanelExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
+ mPrimaryBouncerInteractor.setPanelExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
}
} else if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED) {
// Don't expand to the bouncer. Instead transition back to the lock screen (see
// CentralSurfaces#showBouncerOrLockScreenIfKeyguard)
return;
- } else if (bouncerNeedsScrimming()) {
- if (mBouncer != null) {
- mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
+ } else if (needsFullscreenBouncer()) {
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
} else {
- mBouncerInteractor.setPanelExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
+ mPrimaryBouncerInteractor.setPanelExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
}
} else if (mKeyguardStateController.isShowing() && !hideBouncerOverDream) {
if (!isWakeAndUnlocking()
&& !(mBiometricUnlockController.getMode() == MODE_DISMISS_BOUNCER)
- && !mNotificationPanelViewController.isLaunchTransitionFinished()
&& !isUnlockCollapsing()) {
- if (mBouncer != null) {
- mBouncer.setExpansion(fraction);
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.setExpansion(fraction);
} else {
- mBouncerInteractor.setPanelExpansion(fraction);
+ mPrimaryBouncerInteractor.setPanelExpansion(fraction);
}
}
if (fraction != KeyguardBouncer.EXPANSION_HIDDEN && tracking
&& !mKeyguardStateController.canDismissLockScreen()
- && !bouncerIsShowing()
+ && !primaryBouncerIsShowing()
&& !bouncerIsAnimatingAway()) {
- if (mBouncer != null) {
- mBouncer.show(false /* resetSecuritySelection */, false /* scrimmed */);
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.show(false /* resetSecuritySelection */, false /* scrimmed */);
} else {
- mBouncerInteractor.show(/* isScrimmed= */false);
+ mPrimaryBouncerInteractor.show(/* isScrimmed= */false);
}
}
- } else if (!mKeyguardStateController.isShowing() && isBouncerInTransit()) {
+ } else if (!mKeyguardStateController.isShowing() && isPrimaryBouncerInTransit()) {
// Keyguard is not visible anymore, but expansion animation was still running.
// We need to hide the bouncer, otherwise it will be stuck in transit.
- if (mBouncer != null) {
- mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
} else {
- mBouncerInteractor.setPanelExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
+ mPrimaryBouncerInteractor.setPanelExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
}
} else if (mPulsing && fraction == KeyguardBouncer.EXPANSION_VISIBLE) {
// Panel expanded while pulsing but didn't translate the bouncer (because we are
@@ -544,17 +545,17 @@
if (needsFullscreenBouncer() && !mDozing) {
// The keyguard might be showing (already). So we need to hide it.
mCentralSurfaces.hideKeyguard();
- if (mBouncer != null) {
- mBouncer.show(true /* resetSecuritySelection */);
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.show(true /* resetSecuritySelection */);
} else {
- mBouncerInteractor.show(true);
+ mPrimaryBouncerInteractor.show(true);
}
} else {
mCentralSurfaces.showKeyguard();
if (hideBouncerWhenShowing) {
hideBouncer(false /* destroyView */);
- if (mBouncer != null) {
- mBouncer.prepare();
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.prepare();
}
}
}
@@ -562,23 +563,25 @@
}
/**
- * If applicable, shows the alternate authentication bouncer. Else, shows the input
- * (pin/password/pattern) bouncer.
- * @param scrimmed true when the input bouncer should show scrimmed, false when the user will be
- * dragging it and translation should be deferred {@see KeyguardBouncer#show(boolean, boolean)}
+ *
+ * If possible, shows the alternate bouncer. Else, shows the primary (pin/pattern/password)
+ * bouncer.
+ * @param scrimmed true when the primary bouncer should show scrimmed,
+ * false when the user will be dragging it and translation should be deferred
+ * {@see KeyguardBouncer#show(boolean, boolean)}
*/
- public void showGenericBouncer(boolean scrimmed) {
- if (shouldShowAltAuth()) {
- updateAlternateAuthShowing(mAlternateAuthInterceptor.showAlternateAuthBouncer());
+ public void showBouncer(boolean scrimmed) {
+ if (canShowAlternateBouncer()) {
+ updateAlternateBouncerShowing(mAlternateBouncer.showAlternateBouncer());
return;
}
- showBouncer(scrimmed);
+ showPrimaryBouncer(scrimmed);
}
- /** Whether we should show the alternate authentication instead of the traditional bouncer. */
- public boolean shouldShowAltAuth() {
- return mAlternateAuthInterceptor != null
+ /** Whether we can show the alternate bouncer instead of the primary bouncer. */
+ public boolean canShowAlternateBouncer() {
+ return mAlternateBouncer != null
&& mKeyguardUpdateManager.isUnlockingWithBiometricAllowed(true);
}
@@ -587,10 +590,10 @@
*/
@VisibleForTesting
void hideBouncer(boolean destroyView) {
- if (mBouncer != null) {
- mBouncer.hide(destroyView);
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.hide(destroyView);
} else {
- mBouncerInteractor.hide();
+ mPrimaryBouncerInteractor.hide();
}
if (mKeyguardStateController.isShowing()) {
// If we were showing the bouncer and then aborting, we need to also clear out any
@@ -601,19 +604,19 @@
}
/**
- * Shows the keyguard input bouncer - the password challenge on the lock screen
+ * Shows the primary bouncer - the pin/pattern/password challenge on the lock screen.
*
* @param scrimmed true when the bouncer should show scrimmed, false when the user will be
* dragging it and translation should be deferred {@see KeyguardBouncer#show(boolean, boolean)}
*/
- public void showBouncer(boolean scrimmed) {
- resetAlternateAuth(false);
+ public void showPrimaryBouncer(boolean scrimmed) {
+ hideAlternateBouncer(false);
if (mKeyguardStateController.isShowing() && !isBouncerShowing()) {
- if (mBouncer != null) {
- mBouncer.show(false /* resetSecuritySelection */, scrimmed);
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.show(false /* resetSecuritySelection */, scrimmed);
} else {
- mBouncerInteractor.show(scrimmed);
+ mPrimaryBouncerInteractor.show(scrimmed);
}
}
updateStates();
@@ -645,42 +648,41 @@
// If there is an an alternate auth interceptor (like the UDFPS), show that one
// instead of the bouncer.
- if (shouldShowAltAuth()) {
+ if (canShowAlternateBouncer()) {
if (!afterKeyguardGone) {
- if (mBouncer != null) {
- mBouncer.setDismissAction(mAfterKeyguardGoneAction,
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.setDismissAction(mAfterKeyguardGoneAction,
mKeyguardGoneCancelAction);
} else {
- mBouncerInteractor.setDismissAction(mAfterKeyguardGoneAction,
+ mPrimaryBouncerInteractor.setDismissAction(mAfterKeyguardGoneAction,
mKeyguardGoneCancelAction);
}
mAfterKeyguardGoneAction = null;
mKeyguardGoneCancelAction = null;
}
- updateAlternateAuthShowing(
- mAlternateAuthInterceptor.showAlternateAuthBouncer());
+ updateAlternateBouncerShowing(mAlternateBouncer.showAlternateBouncer());
return;
}
if (afterKeyguardGone) {
// we'll handle the dismiss action after keyguard is gone, so just show the
// bouncer
- if (mBouncer != null) {
- mBouncer.show(false /* resetSecuritySelection */);
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.show(false /* resetSecuritySelection */);
} else {
- mBouncerInteractor.show(/* isScrimmed= */true);
+ mPrimaryBouncerInteractor.show(/* isScrimmed= */true);
}
} else {
// after authentication success, run dismiss action with the option to defer
// hiding the keyguard based on the return value of the OnDismissAction
- if (mBouncer != null) {
- mBouncer.showWithDismissAction(mAfterKeyguardGoneAction,
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.showWithDismissAction(mAfterKeyguardGoneAction,
mKeyguardGoneCancelAction);
} else {
- mBouncerInteractor.setDismissAction(
+ mPrimaryBouncerInteractor.setDismissAction(
mAfterKeyguardGoneAction, mKeyguardGoneCancelAction);
- mBouncerInteractor.show(/* isScrimmed= */true);
+ mPrimaryBouncerInteractor.show(/* isScrimmed= */true);
}
// bouncer will handle the dismiss action, so we no longer need to track it here
mAfterKeyguardGoneAction = null;
@@ -725,28 +727,28 @@
} else {
showBouncerOrKeyguard(hideBouncerWhenShowing);
}
- resetAlternateAuth(false);
+ hideAlternateBouncer(false);
mKeyguardUpdateManager.sendKeyguardReset();
updateStates();
}
}
@Override
- public void resetAlternateAuth(boolean forceUpdateScrim) {
- final boolean updateScrim = (mAlternateAuthInterceptor != null
- && mAlternateAuthInterceptor.hideAlternateAuthBouncer())
+ public void hideAlternateBouncer(boolean forceUpdateScrim) {
+ final boolean updateScrim = (mAlternateBouncer != null
+ && mAlternateBouncer.hideAlternateBouncer())
|| forceUpdateScrim;
- updateAlternateAuthShowing(updateScrim);
+ updateAlternateBouncerShowing(updateScrim);
}
- private void updateAlternateAuthShowing(boolean updateScrim) {
- final boolean isShowingAltAuth = isShowingAlternateAuth();
+ private void updateAlternateBouncerShowing(boolean updateScrim) {
+ final boolean isShowingAlternateBouncer = isShowingAlternateBouncer();
if (mKeyguardMessageAreaController != null) {
- mKeyguardMessageAreaController.setIsVisible(isShowingAltAuth);
+ mKeyguardMessageAreaController.setIsVisible(isShowingAlternateBouncer);
mKeyguardMessageAreaController.setMessage("");
}
- mBypassController.setAltBouncerShowing(isShowingAltAuth);
- mKeyguardUpdateManager.setUdfpsBouncerShowing(isShowingAltAuth);
+ mBypassController.setAltBouncerShowing(isShowingAlternateBouncer);
+ mKeyguardUpdateManager.setUdfpsBouncerShowing(isShowingAlternateBouncer);
if (updateScrim) {
mCentralSurfaces.updateScrimController();
@@ -783,10 +785,10 @@
@Override
public void onFinishedGoingToSleep() {
- if (mBouncer != null) {
- mBouncer.onScreenTurnedOff();
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.onScreenTurnedOff();
} else {
- mBouncerInteractor.onScreenTurnedOff();
+ mPrimaryBouncerInteractor.onScreenTurnedOff();
}
}
@@ -845,21 +847,6 @@
if (isShowing && isOccluding) {
SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED,
SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__OCCLUDED);
- if (mNotificationPanelViewController.isLaunchTransitionFinished()) {
- final Runnable endRunnable = new Runnable() {
- @Override
- public void run() {
- mNotificationShadeWindowController.setKeyguardOccluded(isOccluded);
- reset(true /* hideBouncerWhenShowing */);
- }
- };
- mCentralSurfaces.fadeKeyguardAfterLaunchTransition(
- null /* beforeFading */,
- endRunnable,
- endRunnable);
- return;
- }
-
if (mCentralSurfaces.isLaunchingActivityOverLockscreen()) {
// When isLaunchingActivityOverLockscreen() is true, we know for sure that the post
// collapse runnables will be run.
@@ -886,18 +873,18 @@
// by a FLAG_DISMISS_KEYGUARD_ACTIVITY.
reset(isOccluding /* hideBouncerWhenShowing*/);
}
- if (animate && !isOccluded && isShowing && !bouncerIsShowing()) {
+ if (animate && !isOccluded && isShowing && !primaryBouncerIsShowing()) {
mCentralSurfaces.animateKeyguardUnoccluding();
}
}
@Override
public void startPreHideAnimation(Runnable finishRunnable) {
- if (bouncerIsShowing()) {
- if (mBouncer != null) {
- mBouncer.startPreHideAnimation(finishRunnable);
+ if (primaryBouncerIsShowing()) {
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.startPreHideAnimation(finishRunnable);
} else {
- mBouncerInteractor.startDisappearAnimation(finishRunnable);
+ mPrimaryBouncerInteractor.startDisappearAnimation(finishRunnable);
}
mNotificationPanelViewController.startBouncerPreHideAnimation();
@@ -931,8 +918,7 @@
long uptimeMillis = SystemClock.uptimeMillis();
long delay = Math.max(0, startTime + HIDE_TIMING_CORRECTION_MS - uptimeMillis);
- if (mNotificationPanelViewController.isLaunchTransitionFinished()
- || mKeyguardStateController.isFlingingToDismissKeyguard()) {
+ if (mKeyguardStateController.isFlingingToDismissKeyguard()) {
final boolean wasFlingingToDismissKeyguard =
mKeyguardStateController.isFlingingToDismissKeyguard();
mCentralSurfaces.fadeKeyguardAfterLaunchTransition(new Runnable() {
@@ -1011,13 +997,13 @@
updateResources();
return;
}
- boolean wasShowing = bouncerIsShowing();
- boolean wasScrimmed = bouncerIsScrimmed();
+ boolean wasShowing = primaryBouncerIsShowing();
+ boolean wasScrimmed = primaryBouncerIsScrimmed();
hideBouncer(true /* destroyView */);
- mBouncer.prepare();
+ mPrimaryBouncer.prepare();
- if (wasShowing) showBouncer(wasScrimmed);
+ if (wasShowing) showPrimaryBouncer(wasScrimmed);
}
public void onKeyguardFadedAway() {
@@ -1061,8 +1047,8 @@
* WARNING: This method might cause Binder calls.
*/
public boolean isSecure() {
- if (mBouncer != null) {
- return mBouncer.isSecure();
+ if (mPrimaryBouncer != null) {
+ return mPrimaryBouncer.isSecure();
}
return mKeyguardSecurityModel.getSecurityMode(
@@ -1076,7 +1062,7 @@
* @return whether a back press can be handled right now.
*/
public boolean canHandleBackPressed() {
- return bouncerIsShowing();
+ return primaryBouncerIsShowing();
}
/**
@@ -1089,7 +1075,7 @@
mCentralSurfaces.endAffordanceLaunch();
// The second condition is for SIM card locked bouncer
- if (bouncerIsScrimmed() && !needsFullscreenBouncer()) {
+ if (primaryBouncerIsScrimmed() && !needsFullscreenBouncer()) {
hideBouncer(false);
updateStates();
} else {
@@ -1110,27 +1096,27 @@
@Override
public boolean isBouncerShowing() {
- return bouncerIsShowing() || isShowingAlternateAuth();
+ return primaryBouncerIsShowing() || isShowingAlternateBouncer();
}
@Override
- public boolean bouncerIsOrWillBeShowing() {
- return isBouncerShowing() || isBouncerInTransit();
+ public boolean primaryBouncerIsOrWillBeShowing() {
+ return isBouncerShowing() || isPrimaryBouncerInTransit();
}
public boolean isFullscreenBouncer() {
- if (mBouncerView.getDelegate() != null) {
- return mBouncerView.getDelegate().isFullScreenBouncer();
+ if (mPrimaryBouncerView.getDelegate() != null) {
+ return mPrimaryBouncerView.getDelegate().isFullScreenBouncer();
}
- return mBouncer != null && mBouncer.isFullscreenBouncer();
+ return mPrimaryBouncer != null && mPrimaryBouncer.isFullscreenBouncer();
}
/**
* Clear out any potential actions that were saved to run when the device is unlocked
*/
public void cancelPostAuthActions() {
- if (bouncerIsOrWillBeShowing()) {
- return; // allow bouncer to trigger saved actions
+ if (primaryBouncerIsOrWillBeShowing()) {
+ return; // allow the primary bouncer to trigger saved actions
}
mAfterKeyguardGoneAction = null;
mDismissActionWillAnimateOnKeyguard = false;
@@ -1169,25 +1155,25 @@
}
boolean showing = mKeyguardStateController.isShowing();
boolean occluded = mKeyguardStateController.isOccluded();
- boolean bouncerShowing = bouncerIsShowing();
- boolean bouncerIsOrWillBeShowing = bouncerIsOrWillBeShowing();
- boolean bouncerDismissible = !isFullscreenBouncer();
+ boolean primaryBouncerShowing = primaryBouncerIsShowing();
+ boolean primaryBouncerIsOrWillBeShowing = primaryBouncerIsOrWillBeShowing();
+ boolean primaryBouncerDismissible = !isFullscreenBouncer();
boolean remoteInputActive = mRemoteInputActive;
- if ((bouncerDismissible || !showing || remoteInputActive) !=
- (mLastBouncerDismissible || !mLastShowing || mLastRemoteInputActive)
+ if ((primaryBouncerDismissible || !showing || remoteInputActive)
+ != (mLastBouncerDismissible || !mLastShowing || mLastRemoteInputActive)
|| mFirstUpdate) {
- if (bouncerDismissible || !showing || remoteInputActive) {
- if (mBouncer != null) {
- mBouncer.setBackButtonEnabled(true);
+ if (primaryBouncerDismissible || !showing || remoteInputActive) {
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.setBackButtonEnabled(true);
} else {
- mBouncerInteractor.setBackButtonEnabled(true);
+ mPrimaryBouncerInteractor.setBackButtonEnabled(true);
}
} else {
- if (mBouncer != null) {
- mBouncer.setBackButtonEnabled(false);
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.setBackButtonEnabled(false);
} else {
- mBouncerInteractor.setBackButtonEnabled(false);
+ mPrimaryBouncerInteractor.setBackButtonEnabled(false);
}
}
}
@@ -1198,23 +1184,26 @@
updateNavigationBarVisibility(navBarVisible);
}
- if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) {
- mNotificationShadeWindowController.setBouncerShowing(bouncerShowing);
- mCentralSurfaces.setBouncerShowing(bouncerShowing);
+ boolean isPrimaryBouncerShowingChanged =
+ primaryBouncerShowing != mLastPrimaryBouncerShowing;
+ mLastPrimaryBouncerShowing = primaryBouncerShowing;
+
+ if (isPrimaryBouncerShowingChanged || mFirstUpdate) {
+ mNotificationShadeWindowController.setBouncerShowing(primaryBouncerShowing);
+ mCentralSurfaces.setBouncerShowing(primaryBouncerShowing);
}
- if (bouncerIsOrWillBeShowing != mLastBouncerIsOrWillBeShowing || mFirstUpdate
- || bouncerShowing != mLastBouncerShowing) {
- mKeyguardUpdateManager.sendKeyguardBouncerChanged(bouncerIsOrWillBeShowing,
- bouncerShowing);
+ if (primaryBouncerIsOrWillBeShowing != mLastPrimaryBouncerIsOrWillBeShowing || mFirstUpdate
+ || isPrimaryBouncerShowingChanged) {
+ mKeyguardUpdateManager.sendPrimaryBouncerChanged(primaryBouncerIsOrWillBeShowing,
+ primaryBouncerShowing);
}
mFirstUpdate = false;
mLastShowing = showing;
mLastGlobalActionsVisible = mGlobalActionsVisible;
mLastOccluded = occluded;
- mLastBouncerShowing = bouncerShowing;
- mLastBouncerIsOrWillBeShowing = bouncerIsOrWillBeShowing;
- mLastBouncerDismissible = bouncerDismissible;
+ mLastPrimaryBouncerIsOrWillBeShowing = primaryBouncerIsOrWillBeShowing;
+ mLastBouncerDismissible = primaryBouncerDismissible;
mLastRemoteInputActive = remoteInputActive;
mLastDozing = mDozing;
mLastPulsing = mPulsing;
@@ -1258,7 +1247,7 @@
|| mPulsing && !mIsDocked)
&& mGesturalNav;
return (!keyguardVisible && !hideWhileDozing && !mScreenOffAnimationPlaying
- || bouncerIsShowing()
+ || primaryBouncerIsShowing()
|| mRemoteInputActive
|| keyguardWithGestureNav
|| mGlobalActionsVisible);
@@ -1274,32 +1263,32 @@
&& !mLastScreenOffAnimationPlaying || mLastPulsing && !mLastIsDocked)
&& mLastGesturalNav;
return (!keyguardShowing && !hideWhileDozing && !mLastScreenOffAnimationPlaying
- || mLastBouncerShowing || mLastRemoteInputActive || keyguardWithGestureNav
+ || mLastPrimaryBouncerShowing || mLastRemoteInputActive || keyguardWithGestureNav
|| mLastGlobalActionsVisible);
}
public boolean shouldDismissOnMenuPressed() {
- if (mBouncerView.getDelegate() != null) {
- return mBouncerView.getDelegate().shouldDismissOnMenuPressed();
+ if (mPrimaryBouncerView.getDelegate() != null) {
+ return mPrimaryBouncerView.getDelegate().shouldDismissOnMenuPressed();
}
- return mBouncer != null && mBouncer.shouldDismissOnMenuPressed();
+ return mPrimaryBouncer != null && mPrimaryBouncer.shouldDismissOnMenuPressed();
}
public boolean interceptMediaKey(KeyEvent event) {
- if (mBouncerView.getDelegate() != null) {
- return mBouncerView.getDelegate().interceptMediaKey(event);
+ if (mPrimaryBouncerView.getDelegate() != null) {
+ return mPrimaryBouncerView.getDelegate().interceptMediaKey(event);
}
- return mBouncer != null && mBouncer.interceptMediaKey(event);
+ return mPrimaryBouncer != null && mPrimaryBouncer.interceptMediaKey(event);
}
/**
* @return true if the pre IME back event should be handled
*/
public boolean dispatchBackKeyEventPreIme() {
- if (mBouncerView.getDelegate() != null) {
- return mBouncerView.getDelegate().dispatchBackKeyEventPreIme();
+ if (mPrimaryBouncerView.getDelegate() != null) {
+ return mPrimaryBouncerView.getDelegate().dispatchBackKeyEventPreIme();
}
- return mBouncer != null && mBouncer.dispatchBackKeyEventPreIme();
+ return mPrimaryBouncer != null && mPrimaryBouncer.dispatchBackKeyEventPreIme();
}
public void readyForKeyguardDone() {
@@ -1308,7 +1297,7 @@
@Override
public boolean shouldDisableWindowAnimationsForUnlock() {
- return mNotificationPanelViewController.isLaunchTransitionFinished();
+ return false;
}
@Override
@@ -1345,29 +1334,29 @@
* fingerprint.
*/
public void notifyKeyguardAuthenticated(boolean strongAuth) {
- if (mBouncer != null) {
- mBouncer.notifyKeyguardAuthenticated(strongAuth);
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.notifyKeyguardAuthenticated(strongAuth);
} else {
- mBouncerInteractor.notifyKeyguardAuthenticated(strongAuth);
+ mPrimaryBouncerInteractor.notifyKeyguardAuthenticated(strongAuth);
}
- if (mAlternateAuthInterceptor != null && isShowingAlternateAuth()) {
- resetAlternateAuth(false);
+ if (mAlternateBouncer != null && isShowingAlternateBouncer()) {
+ hideAlternateBouncer(false);
executeAfterKeyguardGoneAction();
}
}
/** Display security message to relevant KeyguardMessageArea. */
public void setKeyguardMessage(String message, ColorStateList colorState) {
- if (isShowingAlternateAuth()) {
+ if (isShowingAlternateBouncer()) {
if (mKeyguardMessageAreaController != null) {
mKeyguardMessageAreaController.setMessage(message);
}
} else {
- if (mBouncer != null) {
- mBouncer.showMessage(message, colorState);
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.showMessage(message, colorState);
} else {
- mBouncerInteractor.showMessage(message, colorState);
+ mPrimaryBouncerInteractor.showMessage(message, colorState);
}
}
}
@@ -1406,12 +1395,15 @@
}
}
- public boolean bouncerNeedsScrimming() {
+ /**
+ * Whether the primary bouncer requires scrimming.
+ */
+ public boolean primaryBouncerNeedsScrimming() {
// When a dream overlay is active, scrimming will cause any expansion to immediately expand.
return (mKeyguardStateController.isOccluded()
&& !mDreamOverlayStateController.isOverlayActive())
- || bouncerWillDismissWithAction()
- || (bouncerIsShowing() && bouncerIsScrimmed())
+ || primaryBouncerWillDismissWithAction()
+ || (primaryBouncerIsShowing() && primaryBouncerIsScrimmed())
|| isFullscreenBouncer();
}
@@ -1421,10 +1413,10 @@
* configuration.
*/
public void updateResources() {
- if (mBouncer != null) {
- mBouncer.updateResources();
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.updateResources();
} else {
- mBouncerInteractor.updateResources();
+ mPrimaryBouncerInteractor.updateResources();
}
}
@@ -1436,19 +1428,20 @@
pw.println(" mAfterKeyguardGoneRunnables: " + mAfterKeyguardGoneRunnables);
pw.println(" mPendingWakeupAction: " + mPendingWakeupAction);
pw.println(" isBouncerShowing(): " + isBouncerShowing());
- pw.println(" bouncerIsOrWillBeShowing(): " + bouncerIsOrWillBeShowing());
+ pw.println(" bouncerIsOrWillBeShowing(): " + primaryBouncerIsOrWillBeShowing());
pw.println(" Registered KeyguardViewManagerCallbacks:");
for (KeyguardViewManagerCallback callback : mCallbacks) {
pw.println(" " + callback);
}
- if (mBouncer != null) {
- mBouncer.dump(pw);
+ if (mPrimaryBouncer != null) {
+ pw.println("PrimaryBouncer:");
+ mPrimaryBouncer.dump(pw);
}
- if (mAlternateAuthInterceptor != null) {
- pw.println("AltAuthInterceptor: ");
- mAlternateAuthInterceptor.dump(pw);
+ if (mAlternateBouncer != null) {
+ pw.println("AlternateBouncer:");
+ mAlternateBouncer.dump(pw);
}
}
@@ -1496,13 +1489,12 @@
}
@Nullable
- public KeyguardBouncer getBouncer() {
- return mBouncer;
+ public KeyguardBouncer getPrimaryBouncer() {
+ return mPrimaryBouncer;
}
- public boolean isShowingAlternateAuth() {
- return mAlternateAuthInterceptor != null
- && mAlternateAuthInterceptor.isShowingAlternateAuthBouncer();
+ public boolean isShowingAlternateBouncer() {
+ return mAlternateBouncer != null && mAlternateBouncer.isShowingAlternateBouncer();
}
/**
@@ -1516,10 +1508,10 @@
/** Update keyguard position based on a tapped X coordinate. */
public void updateKeyguardPosition(float x) {
- if (mBouncer != null) {
- mBouncer.updateKeyguardPosition(x);
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.updateKeyguardPosition(x);
} else {
- mBouncerInteractor.setKeyguardPosition(x);
+ mPrimaryBouncerInteractor.setKeyguardPosition(x);
}
}
@@ -1551,41 +1543,41 @@
*/
public void requestFp(boolean request, int udfpsColor) {
mKeyguardUpdateManager.requestFingerprintAuthOnOccludingApp(request);
- if (mAlternateAuthInterceptor != null) {
- mAlternateAuthInterceptor.requestUdfps(request, udfpsColor);
+ if (mAlternateBouncer != null) {
+ mAlternateBouncer.requestUdfps(request, udfpsColor);
}
}
/**
* Returns if bouncer expansion is between 0 and 1 non-inclusive.
*/
- public boolean isBouncerInTransit() {
- if (mBouncer != null) {
- return mBouncer.inTransit();
+ public boolean isPrimaryBouncerInTransit() {
+ if (mPrimaryBouncer != null) {
+ return mPrimaryBouncer.inTransit();
} else {
- return mBouncerInteractor.isInTransit();
+ return mPrimaryBouncerInteractor.isInTransit();
}
}
/**
* Returns if bouncer is showing
*/
- public boolean bouncerIsShowing() {
- if (mBouncer != null) {
- return mBouncer.isShowing();
+ public boolean primaryBouncerIsShowing() {
+ if (mPrimaryBouncer != null) {
+ return mPrimaryBouncer.isShowing();
} else {
- return mBouncerInteractor.isFullyShowing();
+ return mPrimaryBouncerInteractor.isFullyShowing();
}
}
/**
* Returns if bouncer is scrimmed
*/
- public boolean bouncerIsScrimmed() {
- if (mBouncer != null) {
- return mBouncer.isScrimmed();
+ public boolean primaryBouncerIsScrimmed() {
+ if (mPrimaryBouncer != null) {
+ return mPrimaryBouncer.isScrimmed();
} else {
- return mBouncerInteractor.isScrimmed();
+ return mPrimaryBouncerInteractor.isScrimmed();
}
}
@@ -1593,10 +1585,10 @@
* Returns if bouncer is animating away
*/
public boolean bouncerIsAnimatingAway() {
- if (mBouncer != null) {
- return mBouncer.isAnimatingAway();
+ if (mPrimaryBouncer != null) {
+ return mPrimaryBouncer.isAnimatingAway();
} else {
- return mBouncerInteractor.isAnimatingAway();
+ return mPrimaryBouncerInteractor.isAnimatingAway();
}
}
@@ -1604,11 +1596,11 @@
/**
* Returns if bouncer will dismiss with action
*/
- public boolean bouncerWillDismissWithAction() {
- if (mBouncer != null) {
- return mBouncer.willDismissWithAction();
+ public boolean primaryBouncerWillDismissWithAction() {
+ if (mPrimaryBouncer != null) {
+ return mPrimaryBouncer.willDismissWithAction();
} else {
- return mBouncerInteractor.willDismissWithAction();
+ return mPrimaryBouncerInteractor.willDismissWithAction();
}
}
@@ -1623,26 +1615,26 @@
}
/**
- * Delegate used to send show/reset events to an alternate authentication method instead of the
- * regular pin/pattern/password bouncer.
+ * Delegate used to send show and hide events to an alternate authentication method instead of
+ * the regular pin/pattern/password bouncer.
*/
- public interface AlternateAuthInterceptor {
+ public interface AlternateBouncer {
/**
* Show alternate authentication bouncer.
* @return whether alternate auth method was newly shown
*/
- boolean showAlternateAuthBouncer();
+ boolean showAlternateBouncer();
/**
* Hide alternate authentication bouncer
* @return whether the alternate auth method was newly hidden
*/
- boolean hideAlternateAuthBouncer();
+ boolean hideAlternateBouncer();
/**
* @return true if the alternate auth bouncer is showing
*/
- boolean isShowingAlternateAuthBouncer();
+ boolean isShowingAlternateBouncer();
/**
* Use when an app occluding the keyguard would like to give the user ability to
@@ -1654,7 +1646,7 @@
void requestUdfps(boolean requestUdfps, int color);
/**
- * print information for the alternate auth interceptor registered
+ * print information for the alternate bouncer registered
*/
void dump(PrintWriter pw);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 70af77e..8a49850 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -133,7 +133,7 @@
if (!row.isPinned()) {
mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
}
- mStatusBarKeyguardViewManager.showGenericBouncer(true /* scrimmed */);
+ mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */);
mPendingRemoteInputView = clicked;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
index d9c0293..2a039da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
@@ -34,6 +34,7 @@
import com.android.systemui.R;
import com.android.systemui.ScreenDecorations;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -68,12 +69,15 @@
private int mDisplayCutoutTouchableRegionSize;
private int mStatusBarHeight;
+ private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener;
+
@Inject
public StatusBarTouchableRegionManager(
Context context,
NotificationShadeWindowController notificationShadeWindowController,
ConfigurationController configurationController,
HeadsUpManagerPhone headsUpManager,
+ ShadeExpansionStateManager shadeExpansionStateManager,
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController
) {
mContext = context;
@@ -101,17 +105,7 @@
updateTouchableRegion();
}
});
- mHeadsUpManager.addHeadsUpPhoneListener(
- new HeadsUpManagerPhone.OnHeadsUpPhoneListenerChange() {
- @Override
- public void onHeadsUpGoingAwayStateChanged(boolean headsUpGoingAway) {
- if (!headsUpGoingAway) {
- updateTouchableRegionAfterLayout();
- } else {
- updateTouchableRegion();
- }
- }
- });
+ mHeadsUpManager.addHeadsUpPhoneListener(this::onHeadsUpGoingAwayStateChanged);
mNotificationShadeWindowController = notificationShadeWindowController;
mNotificationShadeWindowController.setForcePluginOpenListener((forceOpen) -> {
@@ -119,6 +113,9 @@
});
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
+ shadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged);
+
+ mOnComputeInternalInsetsListener = this::onComputeInternalInsets;
}
protected void setup(
@@ -136,17 +133,11 @@
pw.println(mTouchableRegion);
}
- /**
- * Notify that the status bar panel gets expanded or collapsed.
- *
- * @param isExpanded True to notify expanded, false to notify collapsed.
- * TODO(b/237811427) replace with a listener
- */
- public void setPanelExpanded(boolean isExpanded) {
+ private void onShadeExpansionFullyChanged(Boolean isExpanded) {
if (isExpanded != mIsStatusBarExpanded) {
mIsStatusBarExpanded = isExpanded;
if (isExpanded) {
- // make sure our state is sane
+ // make sure our state is sensible
mForceCollapsedUntilLayout = false;
}
updateTouchableRegion();
@@ -260,18 +251,22 @@
|| mUnlockedScreenOffAnimationController.isAnimationPlaying();
}
- private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener =
- new OnComputeInternalInsetsListener() {
- @Override
- public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
- if (shouldMakeEntireScreenTouchable()) {
- return;
- }
-
- // Update touch insets to include any area needed for touching features that live in
- // the status bar (ie: heads up notifications)
- info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
- info.touchableRegion.set(calculateTouchableRegion());
+ private void onHeadsUpGoingAwayStateChanged(boolean headsUpGoingAway) {
+ if (!headsUpGoingAway) {
+ updateTouchableRegionAfterLayout();
+ } else {
+ updateTouchableRegion();
}
- };
+ }
+
+ private void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
+ if (shouldMakeEntireScreenTouchable()) {
+ return;
+ }
+
+ // Update touch insets to include any area needed for touching features that live in
+ // the status bar (ie: heads up notifications)
+ info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ info.touchableRegion.set(calculateTouchableRegion());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManager.java
index e7d9221..678c2d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManager.java
@@ -87,7 +87,7 @@
private void updateDialogListeners() {
if (shouldHideAffordance()) {
- mKeyguardViewController.resetAlternateAuth(true);
+ mKeyguardViewController.hideAlternateBouncer(true);
}
for (Listener listener : mListeners) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/MobileMappings.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/MobileMappings.kt
index 60bd038..501467f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/MobileMappings.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/MobileMappings.kt
@@ -32,6 +32,7 @@
interface MobileMappingsProxy {
fun mapIconSets(config: Config): Map<String, MobileIconGroup>
fun getDefaultIcons(config: Config): MobileIconGroup
+ fun getIconKey(displayInfo: TelephonyDisplayInfo): String
fun toIconKey(@NetworkType networkType: Int): String
fun toIconKeyOverride(@NetworkType networkType: Int): String
}
@@ -44,6 +45,9 @@
override fun getDefaultIcons(config: Config): MobileIconGroup =
MobileMappings.getDefaultIcons(config)
+ override fun getIconKey(displayInfo: TelephonyDisplayInfo): String =
+ MobileMappings.getIconKey(displayInfo)
+
override fun toIconKey(@NetworkType networkType: Int): String =
MobileMappings.toIconKey(networkType)
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
index 637fac0..a9d05d1 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
@@ -22,7 +22,6 @@
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.os.PowerManager
-import android.os.SystemClock
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -35,6 +34,7 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.wakelock.WakeLock
/**
* A generic controller that can temporarily display a new view in a new window.
@@ -54,6 +54,7 @@
private val configurationController: ConfigurationController,
private val powerManager: PowerManager,
@LayoutRes private val viewLayoutRes: Int,
+ private val wakeLockBuilder: WakeLock.Builder,
) : CoreStartable {
/**
* Window layout params that will be used as a starting point for the [windowLayoutParams] of
@@ -84,6 +85,22 @@
private var cancelViewTimeout: Runnable? = null
/**
+ * A wakelock that is acquired when view is displayed and screen off,
+ * then released when view is removed.
+ */
+ private var wakeLock: WakeLock? = null
+
+ /** A string that keeps track of wakelock reason once it is acquired till it gets released */
+ private var wakeReasonAcquired: String? = null
+
+ /**
+ * A stack of pairs of device id and temporary view info. This is used when there may be
+ * multiple devices in range, and we want to always display the chip for the most recently
+ * active device.
+ */
+ internal val activeViews: ArrayDeque<Pair<String, T>> = ArrayDeque()
+
+ /**
* Displays the view with the provided [newInfo].
*
* This method handles inflating and attaching the view, then delegates to [updateView] to
@@ -92,6 +109,12 @@
fun displayView(newInfo: T) {
val currentDisplayInfo = displayInfo
+ // Update our list of active devices by removing it if necessary, then adding back at the
+ // front of the list
+ val id = newInfo.id
+ val position = findAndRemoveFromActiveViewsList(id)
+ activeViews.addFirst(Pair(id, newInfo))
+
if (currentDisplayInfo != null &&
currentDisplayInfo.info.windowTitle == newInfo.windowTitle) {
// We're already displaying information in the correctly-titled window, so we just need
@@ -103,23 +126,37 @@
// We're already displaying information but that information is under a different
// window title. So, we need to remove the old window with the old title and add a
// new window with the new title.
- removeView(removalReason = "New info has new window title: ${newInfo.windowTitle}")
+ removeView(
+ id,
+ removalReason = "New info has new window title: ${newInfo.windowTitle}"
+ )
}
// At this point, we're guaranteed to no longer be displaying a view.
// So, set up all our callbacks and inflate the view.
configurationController.addCallback(displayScaleListener)
- // Wake the screen if necessary so the user will see the view. (Per b/239426653, we want
- // the view to show over the dream state, so we should only wake up if the screen is
- // completely off.)
- if (!powerManager.isScreenOn) {
- powerManager.wakeUp(
- SystemClock.uptimeMillis(),
- PowerManager.WAKE_REASON_APPLICATION,
- "com.android.systemui:${newInfo.wakeReason}",
- )
+
+ wakeLock = if (!powerManager.isScreenOn) {
+ // If the screen is off, fully wake it so the user can see the view.
+ wakeLockBuilder
+ .setTag(newInfo.windowTitle)
+ .setLevelsAndFlags(
+ PowerManager.FULL_WAKE_LOCK or
+ PowerManager.ACQUIRE_CAUSES_WAKEUP
+ )
+ .build()
+ } else {
+ // Per b/239426653, we want the view to show over the dream state.
+ // If the screen is on, using screen bright level will leave screen on the dream
+ // state but ensure the screen will not go off before wake lock is released.
+ wakeLockBuilder
+ .setTag(newInfo.windowTitle)
+ .setLevelsAndFlags(PowerManager.SCREEN_BRIGHT_WAKE_LOCK)
+ .build()
}
- logger.logViewAddition(newInfo.windowTitle)
+ wakeLock?.acquire(newInfo.wakeReason)
+ wakeReasonAcquired = newInfo.wakeReason
+ logger.logViewAddition(id, newInfo.windowTitle)
inflateAndUpdateView(newInfo)
}
@@ -130,9 +167,13 @@
// include it just to be safe.
FLAG_CONTENT_ICONS or FLAG_CONTENT_TEXT or FLAG_CONTENT_CONTROLS
)
- cancelViewTimeout?.run()
+
+ // Only cancel timeout of the most recent view displayed, as it will be reset.
+ if (position == 0) {
+ cancelViewTimeout?.run()
+ }
cancelViewTimeout = mainExecutor.executeDelayed(
- { removeView(REMOVAL_REASON_TIMEOUT) },
+ { removeView(id, REMOVAL_REASON_TIMEOUT) },
timeout.toLong()
)
}
@@ -155,6 +196,7 @@
it.copyFrom(windowLayoutParams)
it.title = newInfo.windowTitle
}
+ newView.keepScreenOn = true
windowManager.addView(newView, paramsWithTitle)
animateViewIn(newView)
}
@@ -174,25 +216,67 @@
}
/**
- * Hides the view.
+ * Hides the view given its [id].
*
+ * @param id the id of the device responsible of displaying the temp view.
* @param removalReason a short string describing why the view was removed (timeout, state
* change, etc.)
*/
- fun removeView(removalReason: String) {
+ fun removeView(id: String, removalReason: String) {
val currentDisplayInfo = displayInfo ?: return
- val currentView = currentDisplayInfo.view
- animateViewOut(currentView) { windowManager.removeView(currentView) }
+ val removalPosition = findAndRemoveFromActiveViewsList(id)
+ if (removalPosition == null) {
+ logger.logViewRemovalIgnored(id, "view not found in the list")
+ return
+ }
+ if (removalPosition != 0) {
+ logger.logViewRemovalIgnored(id, "most recent view is being displayed.")
+ return
+ }
+ logger.logViewRemoval(id, removalReason)
- logger.logViewRemoval(removalReason)
+ val newViewToDisplay = if (activeViews.isEmpty()) {
+ null
+ } else {
+ activeViews[0].second
+ }
+
+ val currentView = currentDisplayInfo.view
+ animateViewOut(currentView) {
+ windowManager.removeView(currentView)
+ wakeLock?.release(wakeReasonAcquired)
+ }
+
configurationController.removeCallback(displayScaleListener)
// Re-set to null immediately (instead as part of the animation end runnable) so
- // that if a new view event comes in while this view is animating out, we still display the
- // new view appropriately.
+ // that if a new view event comes in while this view is animating out, we still display
+ // the new view appropriately.
displayInfo = null
// No need to time the view out since it's already gone
cancelViewTimeout?.run()
+
+ if (newViewToDisplay != null) {
+ mainExecutor.executeDelayed({ displayView(newViewToDisplay)}, DISPLAY_VIEW_DELAY)
+ }
+ }
+
+ /**
+ * Finds and removes the active view with the given [id] from the stack, or null if there is no
+ * active view with that ID
+ *
+ * @param id that temporary view belonged to.
+ *
+ * @return index of the view in the stack , otherwise null.
+ */
+ private fun findAndRemoveFromActiveViewsList(id: String): Int? {
+ for (i in 0 until activeViews.size) {
+ if (activeViews[i].first == id) {
+ activeViews.removeAt(i)
+ return i
+ }
+ }
+ return null
}
/**
@@ -233,6 +317,7 @@
}
private const val REMOVAL_REASON_TIMEOUT = "TIMEOUT"
+const val DISPLAY_VIEW_DELAY = 50L
private data class IconInfo(
val iconName: String,
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt
index cbb5002..df83960 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt
@@ -37,6 +37,11 @@
* disappears.
*/
open val timeoutMs: Int = DEFAULT_TIMEOUT_MILLIS
+
+ /**
+ * The id of the temporary view.
+ */
+ abstract val id: String
}
const val DEFAULT_TIMEOUT_MILLIS = 10000
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt
index 428a104..133a384 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt
@@ -24,13 +24,42 @@
internal val buffer: LogBuffer,
internal val tag: String,
) {
- /** Logs that we added the view in a window titled [windowTitle]. */
- fun logViewAddition(windowTitle: String) {
- buffer.log(tag, LogLevel.DEBUG, { str1 = windowTitle }, { "View added. window=$str1" })
+ /** Logs that we added the view with the given [id] in a window titled [windowTitle]. */
+ fun logViewAddition(id: String, windowTitle: String) {
+ buffer.log(
+ tag,
+ LogLevel.DEBUG,
+ {
+ str1 = windowTitle
+ str2 = id
+ },
+ { "View added. window=$str1 id=$str2" }
+ )
}
- /** Logs that we removed the chip for the given [reason]. */
- fun logViewRemoval(reason: String) {
- buffer.log(tag, LogLevel.DEBUG, { str1 = reason }, { "View removed due to: $str1" })
+ /** Logs that we removed the view with the given [id] for the given [reason]. */
+ fun logViewRemoval(id: String, reason: String) {
+ buffer.log(
+ tag,
+ LogLevel.DEBUG,
+ {
+ str1 = reason
+ str2 = id
+ },
+ { "View with id=$str2 is removed due to: $str1" }
+ )
+ }
+
+ /** Logs that we ignored removal of the view with the given [id]. */
+ fun logViewRemovalIgnored(id: String, reason: String) {
+ buffer.log(
+ tag,
+ LogLevel.DEBUG,
+ {
+ str1 = reason
+ str2 = id
+ },
+ { "Removal of view with id=$str2 is ignored because $str1" }
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
index 87b6e8d..44e5ce8 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
@@ -44,6 +44,7 @@
import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.view.ViewUtil
+import com.android.systemui.util.wakelock.WakeLock
import javax.inject.Inject
/**
@@ -75,6 +76,7 @@
private val falsingCollector: FalsingCollector,
private val viewUtil: ViewUtil,
private val vibratorHelper: VibratorHelper,
+ wakeLockBuilder: WakeLock.Builder,
) : TemporaryViewDisplayController<ChipbarInfo, ChipbarLogger>(
context,
logger,
@@ -84,6 +86,7 @@
configurationController,
powerManager,
R.layout.chipbar,
+ wakeLockBuilder,
) {
private lateinit var parent: ChipbarRootView
@@ -92,8 +95,6 @@
gravity = Gravity.TOP.or(Gravity.CENTER_HORIZONTAL)
}
- override fun start() {}
-
override fun updateView(
newInfo: ChipbarInfo,
currentView: ViewGroup
@@ -192,6 +193,8 @@
)
}
+ override fun start() {}
+
override fun getTouchableRegion(view: View, outRect: Rect) {
viewUtil.setRectToViewWindowLocation(view, outRect)
}
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
index 6237365..b92e0ec 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
@@ -40,6 +40,7 @@
override val windowTitle: String,
override val wakeReason: String,
override val timeoutMs: Int,
+ override val id: String,
) : TemporaryViewInfo()
/** The possible items to display at the end of the chipbar. */
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
index 61eadeb..5ea4399 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
@@ -48,6 +48,7 @@
import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeControllerImpl;
+import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -159,7 +160,8 @@
ConfigurationController configurationController,
@Main Handler handler,
AccessibilityManagerWrapper accessibilityManagerWrapper,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger,
+ ShadeExpansionStateManager shadeExpansionStateManager) {
return new HeadsUpManagerPhone(
context,
headsUpManagerLogger,
@@ -170,7 +172,8 @@
configurationController,
handler,
accessibilityManagerWrapper,
- uiEventLogger
+ uiEventLogger,
+ shadeExpansionStateManager
);
}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index bf70673..3507cb7 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -381,7 +381,7 @@
// Don't annoy when user dismissed in past. (But make sure the disk is adoptable; we
// used to allow snoozing non-adoptable disks too.)
- if (rec.isSnoozed() && disk.isAdoptable()) {
+ if (rec == null || (rec.isSnoozed() && disk.isAdoptable())) {
return null;
}
if (disk.isAdoptable() && !rec.isInited() && rec.getType() != VolumeInfo.TYPE_PUBLIC
diff --git a/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java b/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
index f017126..b56c403 100644
--- a/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
@@ -25,6 +25,8 @@
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;
+import android.window.OnBackInvokedCallback;
+import android.window.OnBackInvokedDispatcher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -59,6 +61,7 @@
private final ActivityStarter mActivityStarter;
private Dialog mSetupUserDialog;
+ private final OnBackInvokedCallback mBackCallback = this::onBackInvoked;
@Inject
public CreateUserActivity(UserCreator userCreator,
@@ -82,6 +85,10 @@
mSetupUserDialog = createDialog();
mSetupUserDialog.show();
+
+ getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+ mBackCallback);
}
@Override
@@ -125,10 +132,20 @@
@Override
public void onBackPressed() {
- super.onBackPressed();
+ onBackInvoked();
+ }
+
+ private void onBackInvoked() {
if (mSetupUserDialog != null) {
mSetupUserDialog.dismiss();
}
+ finish();
+ }
+
+ @Override
+ protected void onDestroy() {
+ getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(mBackCallback);
+ super.onDestroy();
}
private void addUserNow(String userName, Drawable userIcon) {
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
index 6a23260..ffaf524 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -250,6 +250,10 @@
override fun onUserChanged(newUser: Int, userContext: Context) {
send()
}
+
+ override fun onProfilesChanged(profiles: List<UserInfo>) {
+ send()
+ }
}
tracker.addCallback(callback, mainDispatcher.asExecutor())
diff --git a/packages/SystemUI/src/com/android/systemui/util/BrightnessProgressDrawable.kt b/packages/SystemUI/src/com/android/systemui/util/BrightnessProgressDrawable.kt
new file mode 100644
index 0000000..12a0c03
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/BrightnessProgressDrawable.kt
@@ -0,0 +1,106 @@
+/*
+ * 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.util
+
+import android.content.pm.ActivityInfo
+import android.content.res.Resources
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.DrawableWrapper
+import android.graphics.drawable.InsetDrawable
+
+/**
+ * [DrawableWrapper] to use in the progress of brightness slider.
+ *
+ * This drawable is used to change the bounds of the enclosed drawable depending on the level to
+ * simulate a sliding progress, instead of using clipping or scaling. That way, the shape of the
+ * edges is maintained.
+ *
+ * Meant to be used with a rounded ends background, it will also prevent deformation when the slider
+ * is meant to be smaller than the rounded corner. The background should have rounded corners that
+ * are half of the height.
+ *
+ * This class also assumes that a "thumb" icon exists within the end's edge of the progress
+ * drawable, and the slider's width, when interacted on, if offset by half the size of the thumb
+ * icon which puts the icon directly underneath the user's finger.
+ */
+class BrightnessProgressDrawable @JvmOverloads constructor(drawable: Drawable? = null) :
+ InsetDrawable(drawable, 0) {
+
+ companion object {
+ private const val MAX_LEVEL = 10000 // Taken from Drawable
+ }
+
+ override fun onLayoutDirectionChanged(layoutDirection: Int): Boolean {
+ onLevelChange(level)
+ return super.onLayoutDirectionChanged(layoutDirection)
+ }
+
+ override fun onBoundsChange(bounds: Rect) {
+ super.onBoundsChange(bounds)
+ onLevelChange(level)
+ }
+
+ override fun onLevelChange(level: Int): Boolean {
+ val db = drawable?.bounds!!
+
+ // The thumb offset shifts the sun icon directly under the user's thumb
+ val thumbOffset = bounds.height() / 2
+ val width = bounds.width() * level / MAX_LEVEL + thumbOffset
+
+ // On 0, the width is bounds.height (a circle), and on MAX_LEVEL, the width is bounds.width
+ drawable?.setBounds(
+ bounds.left,
+ db.top,
+ width.coerceAtMost(bounds.width()).coerceAtLeast(bounds.height()),
+ db.bottom
+ )
+ return super.onLevelChange(level)
+ }
+
+ override fun getConstantState(): ConstantState {
+ // This should not be null as it was created with a state in the constructor.
+ return RoundedCornerState(super.getConstantState()!!)
+ }
+
+ override fun getChangingConfigurations(): Int {
+ return super.getChangingConfigurations() or ActivityInfo.CONFIG_DENSITY
+ }
+
+ override fun canApplyTheme(): Boolean {
+ return (drawable?.canApplyTheme() ?: false) || super.canApplyTheme()
+ }
+
+ private class RoundedCornerState(private val wrappedState: ConstantState) : ConstantState() {
+ override fun newDrawable(): Drawable {
+ return newDrawable(null, null)
+ }
+
+ override fun newDrawable(res: Resources?, theme: Resources.Theme?): Drawable {
+ val wrapper = wrappedState.newDrawable(res, theme) as DrawableWrapper
+ return BrightnessProgressDrawable(wrapper.drawable)
+ }
+
+ override fun getChangingConfigurations(): Int {
+ return wrappedState.changingConfigurations
+ }
+
+ override fun canApplyTheme(): Boolean {
+ return true
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt b/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt
index 99eb03b..1059d6c 100644
--- a/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt
@@ -33,11 +33,6 @@
* Meant to be used with a rounded ends background, it will also prevent deformation when the slider
* is meant to be smaller than the rounded corner. The background should have rounded corners that
* are half of the height.
- *
- * This class also assumes that a "thumb" icon exists within the end's edge of the progress
- * drawable, and the slider's width, when interacted on, if offset by half the size of the thumb
- * icon which puts the icon directly underneath the user's finger.
- *
*/
class RoundedCornerProgressDrawable @JvmOverloads constructor(
drawable: Drawable? = null
@@ -59,16 +54,9 @@
override fun onLevelChange(level: Int): Boolean {
val db = drawable?.bounds!!
-
- // The thumb offset shifts the sun icon directly under the user's thumb
- val thumbOffset = bounds.height() / 2
- val width = bounds.width() * level / MAX_LEVEL + thumbOffset
-
// On 0, the width is bounds.height (a circle), and on MAX_LEVEL, the width is bounds.width
- drawable?.setBounds(
- bounds.left, db.top,
- width.coerceAtMost(bounds.width()).coerceAtLeast(bounds.height()), db.bottom
- )
+ val width = bounds.height() + (bounds.width() - bounds.height()) * level / MAX_LEVEL
+ drawable?.setBounds(bounds.left, db.top, bounds.left + width, db.bottom)
return super.onLevelChange(level)
}
@@ -103,4 +91,4 @@
return true
}
}
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java b/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java
index 8d77c4a..f320d07 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java
+++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java
@@ -38,6 +38,11 @@
long DEFAULT_MAX_TIMEOUT = 20000;
/**
+ * Default wake-lock levels and flags.
+ */
+ int DEFAULT_LEVELS_AND_FLAGS = PowerManager.PARTIAL_WAKE_LOCK;
+
+ /**
* @param why A tag that will be saved for sysui dumps.
* @see android.os.PowerManager.WakeLock#acquire()
**/
@@ -60,13 +65,21 @@
* Creates a {@link WakeLock} that has a default release timeout.
* @see android.os.PowerManager.WakeLock#acquire(long) */
static WakeLock createPartial(Context context, String tag, long maxTimeout) {
- return wrap(createPartialInner(context, tag), maxTimeout);
+ return wrap(createWakeLockInner(context, tag, DEFAULT_LEVELS_AND_FLAGS), maxTimeout);
+ }
+
+ /**
+ * Creates a {@link WakeLock} that has a default release timeout and flags.
+ */
+ static WakeLock createWakeLock(Context context, String tag, int flags, long maxTimeout) {
+ return wrap(createWakeLockInner(context, tag, flags), maxTimeout);
}
@VisibleForTesting
- static PowerManager.WakeLock createPartialInner(Context context, String tag) {
+ static PowerManager.WakeLock createWakeLockInner(
+ Context context, String tag, int levelsAndFlags) {
return context.getSystemService(PowerManager.class)
- .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, tag);
+ .newWakeLock(levelsAndFlags, tag);
}
static Runnable wrapImpl(WakeLock w, Runnable r) {
@@ -131,6 +144,7 @@
class Builder {
private final Context mContext;
private String mTag;
+ private int mLevelsAndFlags = DEFAULT_LEVELS_AND_FLAGS;
private long mMaxTimeout = DEFAULT_MAX_TIMEOUT;
@Inject
@@ -143,13 +157,18 @@
return this;
}
+ public Builder setLevelsAndFlags(int levelsAndFlags) {
+ this.mLevelsAndFlags = levelsAndFlags;
+ return this;
+ }
+
public Builder setMaxTimeout(long maxTimeout) {
this.mMaxTimeout = maxTimeout;
return this;
}
public WakeLock build() {
- return WakeLock.createPartial(mContext, mTag, mMaxTimeout);
+ return WakeLock.createWakeLock(mContext, mTag, mLevelsAndFlags, mMaxTimeout);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 903aba1..2c64fe1 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -1230,6 +1230,9 @@
effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
break;
case RINGER_MODE_VIBRATE:
+ // Feedback handled by onStateChange, for feedback both when user toggles
+ // directly in volume dialog, or drags slider to a value of 0 in settings.
+ break;
default:
effect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
}
@@ -1630,9 +1633,8 @@
&& mState.ringerModeInternal != -1
&& mState.ringerModeInternal != state.ringerModeInternal
&& state.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE) {
- mController.vibrate(VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK));
+ mController.vibrate(VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK));
}
-
mState = state;
mDynamic.clear();
// add any new dynamic rows
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index 4e77514..a4384d5 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -25,6 +25,7 @@
import static android.service.notification.NotificationStats.DISMISSAL_BUBBLE;
import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
+import static com.android.systemui.flags.Flags.WM_BUBBLE_BAR;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
@@ -51,6 +52,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.model.SysUiState;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shared.system.QuickStepContract;
@@ -129,6 +131,7 @@
CommonNotifCollection notifCollection,
NotifPipeline notifPipeline,
SysUiState sysUiState,
+ FeatureFlags featureFlags,
Executor sysuiMainExecutor) {
if (bubblesOptional.isPresent()) {
return new BubblesManager(context,
@@ -146,6 +149,7 @@
notifCollection,
notifPipeline,
sysUiState,
+ featureFlags,
sysuiMainExecutor);
} else {
return null;
@@ -168,6 +172,7 @@
CommonNotifCollection notifCollection,
NotifPipeline notifPipeline,
SysUiState sysUiState,
+ FeatureFlags featureFlags,
Executor sysuiMainExecutor) {
mContext = context;
mBubbles = bubbles;
@@ -352,6 +357,7 @@
});
}
};
+ mBubbles.setBubbleBarEnabled(featureFlags.isEnabled(WM_BUBBLE_BAR));
mBubbles.setSysuiProxy(mSysuiProxy);
}
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index c55ee61..4891339 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -93,6 +93,21 @@
android:excludeFromRecents="true"
/>
+ <activity android:name="com.android.systemui.controls.management.ControlsEditingActivityTest$TestableControlsEditingActivity"
+ android:exported="false"
+ android:excludeFromRecents="true"
+ />
+
+ <activity android:name="com.android.systemui.controls.management.ControlsFavoritingActivityTest$TestableControlsFavoritingActivity"
+ android:exported="false"
+ android:excludeFromRecents="true"
+ />
+
+ <activity android:name="com.android.systemui.controls.management.ControlsProviderSelectorActivityTest$TestableControlsProviderSelectorActivity"
+ android:exported="false"
+ android:excludeFromRecents="true"
+ />
+
<activity android:name="com.android.systemui.screenshot.ScrollViewActivity"
android:exported="false" />
@@ -106,6 +121,12 @@
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true" />
+ <activity android:name=".user.CreateUserActivityTest$CreateUserActivityTestable"
+ android:exported="false"
+ android:theme="@style/Theme.SystemUI.Dialog.Alert"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true" />
+
<provider
android:name="androidx.startup.InitializationProvider"
tools:replace="android:authorities"
@@ -119,6 +140,12 @@
tools:replace="android:authorities"
tools:node="remove" />
+ <provider android:name="com.android.systemui.keyguard.KeyguardQuickAffordanceProvider"
+ android:authorities="com.android.systemui.test.keyguard.quickaffordance.disabled"
+ android:enabled="false"
+ tools:replace="android:authorities"
+ tools:node="remove" />
+
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.android.systemui.test.fileprovider"
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
index 5d2b0ca..8290084 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
@@ -16,8 +16,11 @@
package com.android.keyguard;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -90,4 +93,11 @@
mMessageAreaController.setIsVisible(true);
verify(mKeyguardMessageArea).setIsVisible(true);
}
+
+ @Test
+ public void testGetMessage() {
+ String msg = "abc";
+ when(mKeyguardMessageArea.getText()).thenReturn(msg);
+ assertThat(mMessageAreaController.getMessage()).isEqualTo(msg);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
index b369098..ffd95f4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -31,6 +31,7 @@
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
+import org.mockito.Mockito.`when`
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -118,4 +119,14 @@
keyguardPasswordViewController.startAppearAnimation()
verify(mKeyguardMessageAreaController).setMessage(R.string.keyguard_enter_your_password)
}
+
+ @Test
+ fun startAppearAnimation_withExistingMessage() {
+ `when`(mKeyguardMessageAreaController.message).thenReturn("Unlock to continue.")
+ keyguardPasswordViewController.startAppearAnimation()
+ verify(
+ mKeyguardMessageAreaController,
+ never()
+ ).setMessage(R.string.keyguard_enter_your_password)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index 9eff704..b3d1c8f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -33,6 +33,7 @@
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
+import org.mockito.Mockito.never
import org.mockito.MockitoAnnotations
@SmallTest
@@ -112,4 +113,14 @@
mKeyguardPatternViewController.startAppearAnimation()
verify(mKeyguardMessageAreaController).setMessage(R.string.keyguard_enter_your_pattern)
}
+
+ @Test
+ fun startAppearAnimation_withExistingMessage() {
+ `when`(mKeyguardMessageAreaController.message).thenReturn("Unlock to continue.")
+ mKeyguardPatternViewController.startAppearAnimation()
+ verify(
+ mKeyguardMessageAreaController,
+ never()
+ ).setMessage(R.string.keyguard_enter_your_password)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index d9efdea..8bcfe6f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -100,4 +100,12 @@
pinViewController.startAppearAnimation()
verify(keyguardMessageAreaController).setMessage(R.string.keyguard_enter_your_pin)
}
+
+ @Test
+ fun startAppearAnimation_withExistingMessage() {
+ Mockito.`when`(keyguardMessageAreaController.message).thenReturn("Unlock to continue.")
+ pinViewController.startAppearAnimation()
+ verify(keyguardMessageAreaController, Mockito.never())
+ .setMessage(R.string.keyguard_enter_your_password)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index 0a2b3d8..4d58b09 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -54,7 +54,8 @@
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.biometrics.SidefpsController;
+import com.android.systemui.biometrics.SideFpsController;
+import com.android.systemui.biometrics.SideFpsUiRequestSource;
import com.android.systemui.classifier.FalsingA11yDelegate;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
@@ -141,7 +142,7 @@
@Mock
private KeyguardViewController mKeyguardViewController;
@Mock
- private SidefpsController mSidefpsController;
+ private SideFpsController mSideFpsController;
@Mock
private KeyguardPasswordViewController mKeyguardPasswordViewControllerMock;
@Mock
@@ -189,7 +190,7 @@
mKeyguardStateController, mKeyguardSecurityViewFlipperController,
mConfigurationController, mFalsingCollector, mFalsingManager,
mUserSwitcherController, mFeatureFlags, mGlobalSettings,
- mSessionTracker, Optional.of(mSidefpsController), mFalsingA11yDelegate).create(
+ mSessionTracker, Optional.of(mSideFpsController), mFalsingA11yDelegate).create(
mSecurityCallback);
}
@@ -345,48 +346,48 @@
@Test
public void onBouncerVisibilityChanged_allConditionsGood_sideFpsHintShown() {
setupConditionsToEnableSideFpsHint();
- reset(mSidefpsController);
+ reset(mSideFpsController);
mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
- verify(mSidefpsController).show();
- verify(mSidefpsController, never()).hide();
+ verify(mSideFpsController).show(SideFpsUiRequestSource.PRIMARY_BOUNCER);
+ verify(mSideFpsController, never()).hide(any());
}
@Test
public void onBouncerVisibilityChanged_fpsSensorNotRunning_sideFpsHintHidden() {
setupConditionsToEnableSideFpsHint();
setFingerprintDetectionRunning(false);
- reset(mSidefpsController);
+ reset(mSideFpsController);
mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
- verify(mSidefpsController).hide();
- verify(mSidefpsController, never()).show();
+ verify(mSideFpsController).hide(SideFpsUiRequestSource.PRIMARY_BOUNCER);
+ verify(mSideFpsController, never()).show(any());
}
@Test
public void onBouncerVisibilityChanged_withoutSidedSecurity_sideFpsHintHidden() {
setupConditionsToEnableSideFpsHint();
setSideFpsHintEnabledFromResources(false);
- reset(mSidefpsController);
+ reset(mSideFpsController);
mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
- verify(mSidefpsController).hide();
- verify(mSidefpsController, never()).show();
+ verify(mSideFpsController).hide(SideFpsUiRequestSource.PRIMARY_BOUNCER);
+ verify(mSideFpsController, never()).show(any());
}
@Test
public void onBouncerVisibilityChanged_needsStrongAuth_sideFpsHintHidden() {
setupConditionsToEnableSideFpsHint();
setNeedsStrongAuth(true);
- reset(mSidefpsController);
+ reset(mSideFpsController);
mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
- verify(mSidefpsController).hide();
- verify(mSidefpsController, never()).show();
+ verify(mSideFpsController).hide(SideFpsUiRequestSource.PRIMARY_BOUNCER);
+ verify(mSideFpsController, never()).show(any());
}
@Test
@@ -394,13 +395,13 @@
setupGetSecurityView();
setupConditionsToEnableSideFpsHint();
mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
- verify(mSidefpsController, atLeastOnce()).show();
- reset(mSidefpsController);
+ verify(mSideFpsController, atLeastOnce()).show(SideFpsUiRequestSource.PRIMARY_BOUNCER);
+ reset(mSideFpsController);
mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.INVISIBLE);
- verify(mSidefpsController).hide();
- verify(mSidefpsController, never()).show();
+ verify(mSideFpsController).hide(SideFpsUiRequestSource.PRIMARY_BOUNCER);
+ verify(mSideFpsController, never()).show(any());
}
@Test
@@ -408,13 +409,13 @@
setupGetSecurityView();
setupConditionsToEnableSideFpsHint();
mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
- verify(mSidefpsController, atLeastOnce()).show();
- reset(mSidefpsController);
+ verify(mSideFpsController, atLeastOnce()).show(SideFpsUiRequestSource.PRIMARY_BOUNCER);
+ reset(mSideFpsController);
mKeyguardSecurityContainerController.onStartingToHide();
- verify(mSidefpsController).hide();
- verify(mSidefpsController, never()).show();
+ verify(mSideFpsController).hide(SideFpsUiRequestSource.PRIMARY_BOUNCER);
+ verify(mSideFpsController, never()).show(any());
}
@Test
@@ -422,13 +423,13 @@
setupGetSecurityView();
setupConditionsToEnableSideFpsHint();
mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
- verify(mSidefpsController, atLeastOnce()).show();
- reset(mSidefpsController);
+ verify(mSideFpsController, atLeastOnce()).show(SideFpsUiRequestSource.PRIMARY_BOUNCER);
+ reset(mSideFpsController);
mKeyguardSecurityContainerController.onPause();
- verify(mSidefpsController).hide();
- verify(mSidefpsController, never()).show();
+ verify(mSideFpsController).hide(SideFpsUiRequestSource.PRIMARY_BOUNCER);
+ verify(mSideFpsController, never()).show(any());
}
@Test
@@ -436,12 +437,12 @@
setupGetSecurityView();
setupConditionsToEnableSideFpsHint();
mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
- reset(mSidefpsController);
+ reset(mSideFpsController);
mKeyguardSecurityContainerController.onResume(0);
- verify(mSidefpsController).show();
- verify(mSidefpsController, never()).hide();
+ verify(mSideFpsController).show(SideFpsUiRequestSource.PRIMARY_BOUNCER);
+ verify(mSideFpsController, never()).hide(any());
}
@Test
@@ -450,12 +451,12 @@
setupConditionsToEnableSideFpsHint();
setSideFpsHintEnabledFromResources(false);
mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
- reset(mSidefpsController);
+ reset(mSideFpsController);
mKeyguardSecurityContainerController.onResume(0);
- verify(mSidefpsController).hide();
- verify(mSidefpsController, never()).show();
+ verify(mSideFpsController).hide(SideFpsUiRequestSource.PRIMARY_BOUNCER);
+ verify(mSideFpsController, never()).show(any());
}
@Test
@@ -547,6 +548,22 @@
verify(mKeyguardPasswordViewControllerMock, never()).showMessage(null, null);
}
+ @Test
+ public void onDensityorFontScaleChanged() {
+ ArgumentCaptor<ConfigurationController.ConfigurationListener>
+ configurationListenerArgumentCaptor = ArgumentCaptor.forClass(
+ ConfigurationController.ConfigurationListener.class);
+ mKeyguardSecurityContainerController.onViewAttached();
+ verify(mConfigurationController).addCallback(configurationListenerArgumentCaptor.capture());
+ configurationListenerArgumentCaptor.getValue().onDensityOrFontScaleChanged();
+
+ verify(mView).onDensityOrFontScaleChanged();
+ verify(mKeyguardSecurityViewFlipperController).onDensityOrFontScaleChanged();
+ verify(mKeyguardSecurityViewFlipperController).getSecurityView(any(SecurityMode.class),
+ any(KeyguardSecurityCallback.class));
+ }
+
+
private KeyguardSecurityContainer.SwipeListener getRegisteredSwipeListener() {
mKeyguardSecurityContainerController.onViewAttached();
verify(mView).setSwipeListener(mSwipeListenerArgumentCaptor.capture());
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index 1bd14e5..36ed669 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -262,9 +262,12 @@
ConstraintSet.Constraint userSwitcherConstraint =
getViewConstraint(R.id.keyguard_bouncer_user_switcher);
- assertThat(viewFlipperConstraint.layout.topToTop).isEqualTo(PARENT_ID);
+ assertThat(viewFlipperConstraint.layout.topToBottom).isEqualTo(
+ R.id.keyguard_bouncer_user_switcher);
assertThat(viewFlipperConstraint.layout.bottomToBottom).isEqualTo(PARENT_ID);
assertThat(userSwitcherConstraint.layout.topToTop).isEqualTo(PARENT_ID);
+ assertThat(userSwitcherConstraint.layout.bottomToTop).isEqualTo(
+ mSecurityViewFlipper.getId());
assertThat(userSwitcherConstraint.layout.topMargin).isEqualTo(
getContext().getResources().getDimensionPixelSize(
R.dimen.bouncer_user_switcher_y_trans));
@@ -308,6 +311,17 @@
}
@Test
+ public void testOnDensityOrFontScaleChanged() {
+ setupUserSwitcher();
+ View oldUserSwitcher = mKeyguardSecurityContainer.findViewById(
+ R.id.keyguard_bouncer_user_switcher);
+ mKeyguardSecurityContainer.onDensityOrFontScaleChanged();
+ View newUserSwitcher = mKeyguardSecurityContainer.findViewById(
+ R.id.keyguard_bouncer_user_switcher);
+ assertThat(oldUserSwitcher).isNotEqualTo(newUserSwitcher);
+ }
+
+ @Test
public void testTouchesAreRecognizedAsBeingOnTheOtherSideOfSecurity() {
setupUserSwitcher();
setViewWidth(VIEW_WIDTH);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
index 9296d3d..fd02ac9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
@@ -106,4 +106,10 @@
}
}
}
+
+ @Test
+ public void onDensityOrFontScaleChanged() {
+ mKeyguardSecurityViewFlipperController.onDensityOrFontScaleChanged();
+ verify(mView).removeAllViews();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 5718835..27094c0 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -629,7 +629,7 @@
@Test
public void testNoStartAuthenticate_whenAboutToShowBouncer() {
- mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(
+ mKeyguardUpdateMonitor.sendPrimaryBouncerChanged(
/* bouncerIsOrWillBeShowing */ true, /* bouncerFullyShown */ false);
verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
@@ -1313,7 +1313,10 @@
Arrays.asList("Unlocked by wearable"));
// THEN the showTrustGrantedMessage should be called with the first message
- verify(mTestCallback).showTrustGrantedMessage("Unlocked by wearable");
+ verify(mTestCallback).onTrustGrantedWithFlags(
+ eq(0),
+ eq(KeyguardUpdateMonitor.getCurrentUser()),
+ eq("Unlocked by wearable"));
}
@Test
@@ -1620,7 +1623,7 @@
}
@Test
- public void testShouldListenForFace_whenFaceIsLockedOut_returnsFalse()
+ public void testShouldListenForFace_whenFaceIsLockedOut_returnsTrue()
throws RemoteException {
// Preconditions for face auth to run
keyguardNotGoingAway();
@@ -1637,7 +1640,9 @@
faceAuthLockedOut();
mTestableLooper.processAllMessages();
- assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ // This is needed beccause we want to show face locked out error message whenever face auth
+ // is supposed to run.
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
}
@Test
@@ -1849,7 +1854,7 @@
}
private void setKeyguardBouncerVisibility(boolean isVisible) {
- mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(isVisible, isVisible);
+ mKeyguardUpdateMonitor.sendPrimaryBouncerChanged(isVisible, isVisible);
mTestableLooper.processAllMessages();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ChooserSelectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/ChooserSelectorTest.kt
index 6b1ef38..81d0034 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ChooserSelectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/ChooserSelectorTest.kt
@@ -3,6 +3,7 @@
import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageManager
+import android.content.pm.UserInfo
import android.content.res.Resources
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
@@ -11,9 +12,11 @@
import com.android.systemui.flags.FlagListenable
import com.android.systemui.flags.Flags
import com.android.systemui.flags.UnreleasedFlag
+import com.android.systemui.settings.UserTracker
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.kotlinArgumentCaptor
+import com.android.systemui.util.mockito.whenever
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel
@@ -26,9 +29,9 @@
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.never
+import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@@ -44,6 +47,8 @@
private lateinit var chooserSelector: ChooserSelector
@Mock private lateinit var mockContext: Context
+ @Mock private lateinit var mockProfileContext: Context
+ @Mock private lateinit var mockUserTracker: UserTracker
@Mock private lateinit var mockPackageManager: PackageManager
@Mock private lateinit var mockResources: Resources
@Mock private lateinit var mockFeatureFlags: FeatureFlags
@@ -52,12 +57,20 @@
fun setup() {
MockitoAnnotations.initMocks(this)
- `when`(mockContext.packageManager).thenReturn(mockPackageManager)
- `when`(mockContext.resources).thenReturn(mockResources)
- `when`(mockResources.getString(anyInt())).thenReturn(
+ whenever(mockContext.createContextAsUser(any(), anyInt())).thenReturn(mockProfileContext)
+ whenever(mockContext.resources).thenReturn(mockResources)
+ whenever(mockProfileContext.packageManager).thenReturn(mockPackageManager)
+ whenever(mockResources.getString(anyInt())).thenReturn(
ComponentName("TestPackage", "TestClass").flattenToString())
+ whenever(mockUserTracker.userProfiles).thenReturn(listOf(UserInfo(), UserInfo()))
- chooserSelector = ChooserSelector(mockContext, mockFeatureFlags, testScope, testDispatcher)
+ chooserSelector = ChooserSelector(
+ mockContext,
+ mockUserTracker,
+ mockFeatureFlags,
+ testScope,
+ testDispatcher,
+ )
}
@After
@@ -74,7 +87,9 @@
// Assert
verify(mockFeatureFlags).addListener(
- eq<Flag<*>>(Flags.CHOOSER_UNBUNDLED), flagListener.capture())
+ eq<Flag<*>>(Flags.CHOOSER_UNBUNDLED),
+ flagListener.capture(),
+ )
verify(mockFeatureFlags, never()).removeListener(any())
// Act
@@ -87,86 +102,102 @@
@Test
fun initialize_enablesUnbundledChooser_whenFlagEnabled() {
// Arrange
- `when`(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(true)
+ whenever(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(true)
// Act
chooserSelector.start()
// Assert
- verify(mockPackageManager).setComponentEnabledSetting(
+ verify(mockPackageManager, times(2)).setComponentEnabledSetting(
eq(ComponentName("TestPackage", "TestClass")),
eq(PackageManager.COMPONENT_ENABLED_STATE_ENABLED),
- anyInt())
+ anyInt(),
+ )
}
@Test
fun initialize_disablesUnbundledChooser_whenFlagDisabled() {
// Arrange
- `when`(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
+ whenever(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
// Act
chooserSelector.start()
// Assert
- verify(mockPackageManager).setComponentEnabledSetting(
+ verify(mockPackageManager, times(2)).setComponentEnabledSetting(
eq(ComponentName("TestPackage", "TestClass")),
eq(PackageManager.COMPONENT_ENABLED_STATE_DISABLED),
- anyInt())
+ anyInt(),
+ )
}
@Test
fun enablesUnbundledChooser_whenFlagBecomesEnabled() {
// Arrange
- `when`(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
+ whenever(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
chooserSelector.start()
verify(mockFeatureFlags).addListener(
- eq<Flag<*>>(Flags.CHOOSER_UNBUNDLED), flagListener.capture())
+ eq<Flag<*>>(Flags.CHOOSER_UNBUNDLED),
+ flagListener.capture(),
+ )
verify(mockPackageManager, never()).setComponentEnabledSetting(
- any(), eq(PackageManager.COMPONENT_ENABLED_STATE_ENABLED), anyInt())
+ any(),
+ eq(PackageManager.COMPONENT_ENABLED_STATE_ENABLED),
+ anyInt(),
+ )
// Act
- `when`(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(true)
+ whenever(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(true)
flagListener.value.onFlagChanged(TestFlagEvent(Flags.CHOOSER_UNBUNDLED.id))
// Assert
- verify(mockPackageManager).setComponentEnabledSetting(
+ verify(mockPackageManager, times(2)).setComponentEnabledSetting(
eq(ComponentName("TestPackage", "TestClass")),
eq(PackageManager.COMPONENT_ENABLED_STATE_ENABLED),
- anyInt())
+ anyInt(),
+ )
}
@Test
fun disablesUnbundledChooser_whenFlagBecomesDisabled() {
// Arrange
- `when`(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(true)
+ whenever(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(true)
chooserSelector.start()
verify(mockFeatureFlags).addListener(
- eq<Flag<*>>(Flags.CHOOSER_UNBUNDLED), flagListener.capture())
+ eq<Flag<*>>(Flags.CHOOSER_UNBUNDLED),
+ flagListener.capture(),
+ )
verify(mockPackageManager, never()).setComponentEnabledSetting(
- any(), eq(PackageManager.COMPONENT_ENABLED_STATE_DISABLED), anyInt())
+ any(),
+ eq(PackageManager.COMPONENT_ENABLED_STATE_DISABLED),
+ anyInt(),
+ )
// Act
- `when`(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
+ whenever(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
flagListener.value.onFlagChanged(TestFlagEvent(Flags.CHOOSER_UNBUNDLED.id))
// Assert
- verify(mockPackageManager).setComponentEnabledSetting(
+ verify(mockPackageManager, times(2)).setComponentEnabledSetting(
eq(ComponentName("TestPackage", "TestClass")),
eq(PackageManager.COMPONENT_ENABLED_STATE_DISABLED),
- anyInt())
+ anyInt(),
+ )
}
@Test
fun doesNothing_whenAnotherFlagChanges() {
// Arrange
- `when`(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
+ whenever(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
chooserSelector.start()
verify(mockFeatureFlags).addListener(
- eq<Flag<*>>(Flags.CHOOSER_UNBUNDLED), flagListener.capture())
+ eq<Flag<*>>(Flags.CHOOSER_UNBUNDLED),
+ flagListener.capture(),
+ )
clearInvocations(mockPackageManager)
// Act
- `when`(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
+ whenever(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
flagListener.value.onFlagChanged(TestFlagEvent(Flags.CHOOSER_UNBUNDLED.id + 1))
// Assert
diff --git a/packages/SystemUI/tests/src/com/android/systemui/DisplayCutoutBaseViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/DisplayCutoutBaseViewTest.kt
index a4e0825..5886206 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/DisplayCutoutBaseViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/DisplayCutoutBaseViewTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui
+import android.content.Context
import android.graphics.Canvas
import android.graphics.Insets
import android.graphics.Path
@@ -48,6 +49,7 @@
@Mock private lateinit var mockCanvas: Canvas
@Mock private lateinit var mockRootView: View
@Mock private lateinit var mockDisplay: Display
+ @Mock private lateinit var mockContext: Context
private lateinit var cutoutBaseView: DisplayCutoutBaseView
private val cutout: DisplayCutout = DisplayCutout.Builder()
@@ -168,7 +170,9 @@
R.bool.config_fillMainBuiltInDisplayCutout, fillCutout)
cutoutBaseView = spy(DisplayCutoutBaseView(mContext))
- whenever(cutoutBaseView.display).thenReturn(mockDisplay)
+
+ whenever(cutoutBaseView.context).thenReturn(mockContext)
+ whenever(mockContext.display).thenReturn(mockDisplay)
whenever(mockDisplay.uniqueId).thenReturn("mockDisplayUniqueId")
whenever(cutoutBaseView.rootView).thenReturn(mockRootView)
whenever(cutoutBaseView.getPhysicalPixelDisplaySizeRatio()).thenReturn(1f)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt
index 054650b..8207fa6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui
+import android.content.Context
import android.graphics.Insets
import android.graphics.PixelFormat
import android.graphics.Rect
@@ -44,6 +45,7 @@
@Mock private lateinit var mockDisplay: Display
@Mock private lateinit var mockRootView: View
+ @Mock private lateinit var mockContext: Context
private val displayWidth = 100
private val displayHeight = 200
@@ -75,7 +77,8 @@
decorHwcLayer = Mockito.spy(ScreenDecorHwcLayer(mContext, decorationSupport))
whenever(decorHwcLayer.width).thenReturn(displayWidth)
whenever(decorHwcLayer.height).thenReturn(displayHeight)
- whenever(decorHwcLayer.display).thenReturn(mockDisplay)
+ whenever(decorHwcLayer.context).thenReturn(mockContext)
+ whenever(mockContext.display).thenReturn(mockDisplay)
whenever(decorHwcLayer.rootView).thenReturn(mockRootView)
whenever(mockRootView.left).thenReturn(0)
whenever(mockRootView.top).thenReturn(0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 45b8ce1..12c2bbf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -175,6 +175,25 @@
}
@Test
+ fun testFocusLossAfterRotating() {
+ val container = initializeFingerprintContainer()
+ waitForIdleSync()
+
+ val requestID = authContainer?.requestId ?: 0L
+
+ verify(callback).onDialogAnimatedIn(requestID)
+ container.onOrientationChanged()
+ container.onWindowFocusChanged(false)
+ waitForIdleSync()
+
+ verify(callback, never()).onDismissed(
+ eq(AuthDialogCallback.DISMISSED_USER_CANCELED),
+ eq<ByteArray?>(null), /* credentialAttestation */
+ eq(requestID)
+ )
+ }
+
+ @Test
fun testDismissesOnFocusLoss_hidesKeyboardWhenVisible() {
val container = initializeFingerprintContainer(
authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 4dd46ed..40b2cdf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -152,7 +152,7 @@
@Mock
private UdfpsController mUdfpsController;
@Mock
- private SidefpsController mSidefpsController;
+ private SideFpsController mSideFpsController;
@Mock
private DisplayManager mDisplayManager;
@Mock
@@ -255,7 +255,7 @@
mAuthController = new TestableAuthController(mContextSpy, mExecution, mCommandQueue,
mActivityTaskManager, mWindowManager, mFingerprintManager, mFaceManager,
- () -> mUdfpsController, () -> mSidefpsController, mStatusBarStateController,
+ () -> mUdfpsController, () -> mSideFpsController, mStatusBarStateController,
mVibratorHelper);
mAuthController.start();
@@ -288,7 +288,7 @@
// This test requires an uninitialized AuthController.
AuthController authController = new TestableAuthController(mContextSpy, mExecution,
mCommandQueue, mActivityTaskManager, mWindowManager, mFingerprintManager,
- mFaceManager, () -> mUdfpsController, () -> mSidefpsController,
+ mFaceManager, () -> mUdfpsController, () -> mSideFpsController,
mStatusBarStateController, mVibratorHelper);
authController.start();
@@ -318,7 +318,7 @@
// This test requires an uninitialized AuthController.
AuthController authController = new TestableAuthController(mContextSpy, mExecution,
mCommandQueue, mActivityTaskManager, mWindowManager, mFingerprintManager,
- mFaceManager, () -> mUdfpsController, () -> mSidefpsController,
+ mFaceManager, () -> mUdfpsController, () -> mSideFpsController,
mStatusBarStateController, mVibratorHelper);
authController.start();
@@ -1009,7 +1009,7 @@
FingerprintManager fingerprintManager,
FaceManager faceManager,
Provider<UdfpsController> udfpsControllerFactory,
- Provider<SidefpsController> sidefpsControllerFactory,
+ Provider<SideFpsController> sidefpsControllerFactory,
StatusBarStateController statusBarStateController,
VibratorHelper vibratorHelper) {
super(context, execution, commandQueue, activityTaskManager, windowManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
new file mode 100644
index 0000000..e7d5632
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
@@ -0,0 +1,472 @@
+/*
+ * 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.biometrics
+
+import android.animation.Animator
+import android.app.ActivityManager
+import android.app.ActivityTaskManager
+import android.content.ComponentName
+import android.graphics.Insets
+import android.graphics.Rect
+import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
+import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS
+import android.hardware.biometrics.BiometricOverlayConstants.REASON_UNKNOWN
+import android.hardware.biometrics.SensorLocationInternal
+import android.hardware.biometrics.SensorProperties
+import android.hardware.display.DisplayManager
+import android.hardware.display.DisplayManagerGlobal
+import android.hardware.fingerprint.FingerprintManager
+import android.hardware.fingerprint.FingerprintSensorProperties
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
+import android.hardware.fingerprint.ISidefpsController
+import android.os.Handler
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.Display
+import android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS
+import android.view.DisplayInfo
+import android.view.LayoutInflater
+import android.view.Surface
+import android.view.View
+import android.view.ViewPropertyAnimator
+import android.view.WindowInsets
+import android.view.WindowManager
+import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
+import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
+import android.view.WindowMetrics
+import androidx.test.filters.SmallTest
+import com.airbnb.lottie.LottieAnimationView
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.recents.OverviewProxyService
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyFloat
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.anyLong
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenEver
+import org.mockito.junit.MockitoJUnit
+
+private const val DISPLAY_ID = 2
+private const val SENSOR_ID = 1
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class SideFpsControllerTest : SysuiTestCase() {
+
+ @JvmField @Rule var rule = MockitoJUnit.rule()
+
+ @Mock lateinit var layoutInflater: LayoutInflater
+ @Mock lateinit var fingerprintManager: FingerprintManager
+ @Mock lateinit var windowManager: WindowManager
+ @Mock lateinit var activityTaskManager: ActivityTaskManager
+ @Mock lateinit var sideFpsView: View
+ @Mock lateinit var displayManager: DisplayManager
+ @Mock lateinit var overviewProxyService: OverviewProxyService
+ @Mock lateinit var handler: Handler
+ @Mock lateinit var dumpManager: DumpManager
+ @Captor lateinit var overlayCaptor: ArgumentCaptor<View>
+ @Captor lateinit var overlayViewParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams>
+
+ private val executor = FakeExecutor(FakeSystemClock())
+ private lateinit var overlayController: ISidefpsController
+ private lateinit var sideFpsController: SideFpsController
+
+ enum class DeviceConfig {
+ X_ALIGNED,
+ Y_ALIGNED_UNFOLDED,
+ Y_ALIGNED_FOLDED
+ }
+
+ private lateinit var deviceConfig: DeviceConfig
+ private lateinit var indicatorBounds: Rect
+ private lateinit var displayBounds: Rect
+ private lateinit var sensorLocation: SensorLocationInternal
+ private var displayWidth: Int = 0
+ private var displayHeight: Int = 0
+ private var boundsWidth: Int = 0
+ private var boundsHeight: Int = 0
+
+ @Before
+ fun setup() {
+ context.addMockSystemService(DisplayManager::class.java, displayManager)
+ context.addMockSystemService(WindowManager::class.java, windowManager)
+
+ whenEver(layoutInflater.inflate(R.layout.sidefps_view, null, false)).thenReturn(sideFpsView)
+ whenEver(sideFpsView.findViewById<LottieAnimationView>(eq(R.id.sidefps_animation)))
+ .thenReturn(mock(LottieAnimationView::class.java))
+ with(mock(ViewPropertyAnimator::class.java)) {
+ whenEver(sideFpsView.animate()).thenReturn(this)
+ whenEver(alpha(anyFloat())).thenReturn(this)
+ whenEver(setStartDelay(anyLong())).thenReturn(this)
+ whenEver(setDuration(anyLong())).thenReturn(this)
+ whenEver(setListener(any())).thenAnswer {
+ (it.arguments[0] as Animator.AnimatorListener).onAnimationEnd(
+ mock(Animator::class.java)
+ )
+ this
+ }
+ }
+ }
+
+ private fun testWithDisplay(
+ deviceConfig: DeviceConfig = DeviceConfig.X_ALIGNED,
+ initInfo: DisplayInfo.() -> Unit = {},
+ windowInsets: WindowInsets = insetsForSmallNavbar(),
+ block: () -> Unit
+ ) {
+ this.deviceConfig = deviceConfig
+
+ when (deviceConfig) {
+ DeviceConfig.X_ALIGNED -> {
+ displayWidth = 2560
+ displayHeight = 1600
+ sensorLocation = SensorLocationInternal("", 2325, 0, 0)
+ boundsWidth = 160
+ boundsHeight = 84
+ }
+ DeviceConfig.Y_ALIGNED_UNFOLDED -> {
+ displayWidth = 2208
+ displayHeight = 1840
+ sensorLocation = SensorLocationInternal("", 0, 510, 0)
+ boundsWidth = 110
+ boundsHeight = 210
+ }
+ DeviceConfig.Y_ALIGNED_FOLDED -> {
+ displayWidth = 1080
+ displayHeight = 2100
+ sensorLocation = SensorLocationInternal("", 0, 590, 0)
+ boundsWidth = 110
+ boundsHeight = 210
+ }
+ }
+ indicatorBounds = Rect(0, 0, boundsWidth, boundsHeight)
+ displayBounds = Rect(0, 0, displayWidth, displayHeight)
+ var locations = listOf(sensorLocation)
+
+ whenEver(fingerprintManager.sensorPropertiesInternal)
+ .thenReturn(
+ listOf(
+ FingerprintSensorPropertiesInternal(
+ SENSOR_ID,
+ SensorProperties.STRENGTH_STRONG,
+ 5 /* maxEnrollmentsPerUser */,
+ listOf() /* componentInfo */,
+ FingerprintSensorProperties.TYPE_POWER_BUTTON,
+ true /* halControlsIllumination */,
+ true /* resetLockoutRequiresHardwareAuthToken */,
+ locations
+ )
+ )
+ )
+
+ val displayInfo = DisplayInfo()
+ displayInfo.initInfo()
+ val dmGlobal = mock(DisplayManagerGlobal::class.java)
+ val display = Display(dmGlobal, DISPLAY_ID, displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS)
+ whenEver(dmGlobal.getDisplayInfo(eq(DISPLAY_ID))).thenReturn(displayInfo)
+ whenEver(windowManager.defaultDisplay).thenReturn(display)
+ whenEver(windowManager.maximumWindowMetrics)
+ .thenReturn(WindowMetrics(displayBounds, WindowInsets.CONSUMED))
+ whenEver(windowManager.currentWindowMetrics)
+ .thenReturn(WindowMetrics(displayBounds, windowInsets))
+
+ sideFpsController =
+ SideFpsController(
+ context.createDisplayContext(display),
+ layoutInflater,
+ fingerprintManager,
+ windowManager,
+ activityTaskManager,
+ overviewProxyService,
+ displayManager,
+ executor,
+ handler,
+ dumpManager
+ )
+
+ overlayController =
+ ArgumentCaptor.forClass(ISidefpsController::class.java)
+ .apply { verify(fingerprintManager).setSidefpsController(capture()) }
+ .value
+
+ block()
+ }
+
+ @Test
+ fun testSubscribesToOrientationChangesWhenShowingOverlay() = testWithDisplay {
+ overlayController.show(SENSOR_ID, REASON_UNKNOWN)
+ executor.runAllReady()
+
+ verify(displayManager).registerDisplayListener(any(), eq(handler), anyLong())
+
+ overlayController.hide(SENSOR_ID)
+ executor.runAllReady()
+ verify(displayManager).unregisterDisplayListener(any())
+ }
+
+ @Test
+ fun testShowsAndHides() = testWithDisplay {
+ overlayController.show(SENSOR_ID, REASON_UNKNOWN)
+ executor.runAllReady()
+
+ verify(windowManager).addView(overlayCaptor.capture(), any())
+
+ reset(windowManager)
+ overlayController.hide(SENSOR_ID)
+ executor.runAllReady()
+
+ verify(windowManager, never()).addView(any(), any())
+ verify(windowManager).removeView(eq(overlayCaptor.value))
+ }
+
+ @Test
+ fun testShowsOnce() = testWithDisplay {
+ repeat(5) {
+ overlayController.show(SENSOR_ID, REASON_UNKNOWN)
+ executor.runAllReady()
+ }
+
+ verify(windowManager).addView(any(), any())
+ verify(windowManager, never()).removeView(any())
+ }
+
+ @Test
+ fun testHidesOnce() = testWithDisplay {
+ overlayController.show(SENSOR_ID, REASON_UNKNOWN)
+ executor.runAllReady()
+
+ repeat(5) {
+ overlayController.hide(SENSOR_ID)
+ executor.runAllReady()
+ }
+
+ verify(windowManager).addView(any(), any())
+ verify(windowManager).removeView(any())
+ }
+
+ @Test fun testIgnoredForKeyguard() = testWithDisplay { testIgnoredFor(REASON_AUTH_KEYGUARD) }
+
+ @Test
+ fun testShowsForMostSettings() = testWithDisplay {
+ whenEver(activityTaskManager.getTasks(anyInt())).thenReturn(listOf(fpEnrollTask()))
+ testIgnoredFor(REASON_AUTH_SETTINGS, ignored = false)
+ }
+
+ @Test
+ fun testIgnoredForVerySpecificSettings() = testWithDisplay {
+ whenEver(activityTaskManager.getTasks(anyInt())).thenReturn(listOf(fpSettingsTask()))
+ testIgnoredFor(REASON_AUTH_SETTINGS)
+ }
+
+ private fun testIgnoredFor(reason: Int, ignored: Boolean = true) {
+ overlayController.show(SENSOR_ID, reason)
+ executor.runAllReady()
+
+ verify(windowManager, if (ignored) never() else times(1)).addView(any(), any())
+ }
+
+ @Test
+ fun showsWithTaskbar() =
+ testWithDisplay(deviceConfig = DeviceConfig.X_ALIGNED, { rotation = Surface.ROTATION_0 }) {
+ hidesWithTaskbar(visible = true)
+ }
+
+ @Test
+ fun showsWithTaskbarOnY() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED,
+ { rotation = Surface.ROTATION_0 }
+ ) { hidesWithTaskbar(visible = true) }
+
+ @Test
+ fun showsWithTaskbar90() =
+ testWithDisplay(deviceConfig = DeviceConfig.X_ALIGNED, { rotation = Surface.ROTATION_90 }) {
+ hidesWithTaskbar(visible = true)
+ }
+
+ @Test
+ fun showsWithTaskbar90OnY() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED,
+ { rotation = Surface.ROTATION_90 }
+ ) { hidesWithTaskbar(visible = true) }
+
+ @Test
+ fun showsWithTaskbar180() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.X_ALIGNED,
+ { rotation = Surface.ROTATION_180 }
+ ) { hidesWithTaskbar(visible = true) }
+
+ @Test
+ fun showsWithTaskbar270OnY() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED,
+ { rotation = Surface.ROTATION_270 }
+ ) { hidesWithTaskbar(visible = true) }
+
+ @Test
+ fun showsWithTaskbarCollapsedDown() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.X_ALIGNED,
+ { rotation = Surface.ROTATION_270 },
+ windowInsets = insetsForSmallNavbar()
+ ) { hidesWithTaskbar(visible = true) }
+
+ @Test
+ fun showsWithTaskbarCollapsedDownOnY() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED,
+ { rotation = Surface.ROTATION_180 },
+ windowInsets = insetsForSmallNavbar()
+ ) { hidesWithTaskbar(visible = true) }
+
+ @Test
+ fun hidesWithTaskbarDown() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.X_ALIGNED,
+ { rotation = Surface.ROTATION_180 },
+ windowInsets = insetsForLargeNavbar()
+ ) { hidesWithTaskbar(visible = false) }
+
+ @Test
+ fun hidesWithTaskbarDownOnY() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED,
+ { rotation = Surface.ROTATION_270 },
+ windowInsets = insetsForLargeNavbar()
+ ) { hidesWithTaskbar(visible = true) }
+
+ private fun hidesWithTaskbar(visible: Boolean) {
+ overlayController.show(SENSOR_ID, REASON_UNKNOWN)
+ executor.runAllReady()
+
+ sideFpsController.overviewProxyListener.onTaskbarStatusUpdated(visible, false)
+ executor.runAllReady()
+
+ verify(windowManager).addView(any(), any())
+ verify(windowManager, never()).removeView(any())
+ verify(sideFpsView).visibility = if (visible) View.VISIBLE else View.GONE
+ }
+
+ @Test
+ fun testIndicatorPlacementForXAlignedSensor() =
+ testWithDisplay(deviceConfig = DeviceConfig.X_ALIGNED) {
+ overlayController.show(SENSOR_ID, REASON_UNKNOWN)
+ sideFpsController.overlayOffsets = sensorLocation
+ sideFpsController.updateOverlayParams(windowManager.defaultDisplay, indicatorBounds)
+ executor.runAllReady()
+
+ verify(windowManager).updateViewLayout(any(), overlayViewParamsCaptor.capture())
+
+ assertThat(overlayViewParamsCaptor.value.x).isEqualTo(sensorLocation.sensorLocationX)
+ assertThat(overlayViewParamsCaptor.value.y).isEqualTo(0)
+ }
+
+ @Test
+ fun testIndicatorPlacementForYAlignedSensor() =
+ testWithDisplay(deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED) {
+ sideFpsController.overlayOffsets = sensorLocation
+ sideFpsController.updateOverlayParams(windowManager.defaultDisplay, indicatorBounds)
+ overlayController.show(SENSOR_ID, REASON_UNKNOWN)
+ executor.runAllReady()
+
+ verify(windowManager).updateViewLayout(any(), overlayViewParamsCaptor.capture())
+ assertThat(overlayViewParamsCaptor.value.x).isEqualTo(displayWidth - boundsWidth)
+ assertThat(overlayViewParamsCaptor.value.y).isEqualTo(sensorLocation.sensorLocationY)
+ }
+
+ @Test
+ fun hasSideFpsSensor_withSensorProps_returnsTrue() = testWithDisplay {
+ // By default all those tests assume the side fps sensor is available.
+
+ assertThat(fingerprintManager.hasSideFpsSensor()).isTrue()
+ }
+
+ @Test
+ fun hasSideFpsSensor_withoutSensorProps_returnsFalse() {
+ whenEver(fingerprintManager.sensorPropertiesInternal).thenReturn(null)
+
+ assertThat(fingerprintManager.hasSideFpsSensor()).isFalse()
+ }
+
+ @Test
+ fun testLayoutParams_hasNoMoveAnimationWindowFlag() =
+ testWithDisplay(deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED) {
+ sideFpsController.overlayOffsets = sensorLocation
+ sideFpsController.updateOverlayParams(windowManager.defaultDisplay, indicatorBounds)
+ overlayController.show(SENSOR_ID, REASON_UNKNOWN)
+ executor.runAllReady()
+
+ verify(windowManager).updateViewLayout(any(), overlayViewParamsCaptor.capture())
+
+ val lpFlags = overlayViewParamsCaptor.value.privateFlags
+
+ assertThat((lpFlags and PRIVATE_FLAG_NO_MOVE_ANIMATION) != 0).isTrue()
+ }
+
+ @Test
+ fun testLayoutParams_hasTrustedOverlayWindowFlag() =
+ testWithDisplay(deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED) {
+ sideFpsController.overlayOffsets = sensorLocation
+ sideFpsController.updateOverlayParams(windowManager.defaultDisplay, indicatorBounds)
+ overlayController.show(SENSOR_ID, REASON_UNKNOWN)
+ executor.runAllReady()
+
+ verify(windowManager).updateViewLayout(any(), overlayViewParamsCaptor.capture())
+
+ val lpFlags = overlayViewParamsCaptor.value.privateFlags
+
+ assertThat((lpFlags and PRIVATE_FLAG_TRUSTED_OVERLAY) != 0).isTrue()
+ }
+}
+
+private fun insetsForSmallNavbar() = insetsWithBottom(60)
+
+private fun insetsForLargeNavbar() = insetsWithBottom(100)
+
+private fun insetsWithBottom(bottom: Int) =
+ WindowInsets.Builder()
+ .setInsets(WindowInsets.Type.navigationBars(), Insets.of(0, 0, 0, bottom))
+ .build()
+
+private fun fpEnrollTask() = settingsTask(".biometrics.fingerprint.FingerprintEnrollEnrolling")
+
+private fun fpSettingsTask() = settingsTask(".biometrics.fingerprint.FingerprintSettings")
+
+private fun settingsTask(cls: String) =
+ ActivityManager.RunningTaskInfo().apply {
+ topActivity = ComponentName.createRelative("com.android.settings", cls)
+ }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
deleted file mode 100644
index 8d969d0..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
+++ /dev/null
@@ -1,493 +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.biometrics
-
-import android.animation.Animator
-import android.app.ActivityManager
-import android.app.ActivityTaskManager
-import android.content.ComponentName
-import android.graphics.Insets
-import android.graphics.Rect
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_UNKNOWN
-import android.hardware.biometrics.SensorLocationInternal
-import android.hardware.biometrics.SensorProperties
-import android.hardware.display.DisplayManager
-import android.hardware.display.DisplayManagerGlobal
-import android.hardware.fingerprint.FingerprintManager
-import android.hardware.fingerprint.FingerprintSensorProperties
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
-import android.hardware.fingerprint.ISidefpsController
-import android.os.Handler
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.view.Display
-import android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS
-import android.view.DisplayInfo
-import android.view.LayoutInflater
-import android.view.Surface
-import android.view.View
-import android.view.ViewPropertyAnimator
-import android.view.WindowInsets
-import android.view.WindowManager
-import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
-import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
-import android.view.WindowMetrics
-import androidx.test.filters.SmallTest
-import com.airbnb.lottie.LottieAnimationView
-import com.android.systemui.R
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.recents.OverviewProxyService
-import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.time.FakeSystemClock
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Captor
-import org.mockito.Mock
-import org.mockito.Mockito.any
-import org.mockito.Mockito.anyFloat
-import org.mockito.Mockito.anyInt
-import org.mockito.Mockito.anyLong
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.reset
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenEver
-import org.mockito.junit.MockitoJUnit
-
-private const val DISPLAY_ID = 2
-private const val SENSOR_ID = 1
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper
-class SidefpsControllerTest : SysuiTestCase() {
-
- @JvmField @Rule
- var rule = MockitoJUnit.rule()
-
- @Mock
- lateinit var layoutInflater: LayoutInflater
- @Mock
- lateinit var fingerprintManager: FingerprintManager
- @Mock
- lateinit var windowManager: WindowManager
- @Mock
- lateinit var activityTaskManager: ActivityTaskManager
- @Mock
- lateinit var sidefpsView: View
- @Mock
- lateinit var displayManager: DisplayManager
- @Mock
- lateinit var overviewProxyService: OverviewProxyService
- @Mock
- lateinit var handler: Handler
- @Captor
- lateinit var overlayCaptor: ArgumentCaptor<View>
- @Captor
- lateinit var overlayViewParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams>
-
- private val executor = FakeExecutor(FakeSystemClock())
- private lateinit var overlayController: ISidefpsController
- private lateinit var sideFpsController: SidefpsController
-
- enum class DeviceConfig { X_ALIGNED, Y_ALIGNED_UNFOLDED, Y_ALIGNED_FOLDED }
-
- private lateinit var deviceConfig: DeviceConfig
- private lateinit var indicatorBounds: Rect
- private lateinit var displayBounds: Rect
- private lateinit var sensorLocation: SensorLocationInternal
- private var displayWidth: Int = 0
- private var displayHeight: Int = 0
- private var boundsWidth: Int = 0
- private var boundsHeight: Int = 0
-
- @Before
- fun setup() {
- context.addMockSystemService(DisplayManager::class.java, displayManager)
- context.addMockSystemService(WindowManager::class.java, windowManager)
-
- whenEver(layoutInflater.inflate(R.layout.sidefps_view, null, false)).thenReturn(sidefpsView)
- whenEver(sidefpsView.findViewById<LottieAnimationView>(eq(R.id.sidefps_animation)))
- .thenReturn(mock(LottieAnimationView::class.java))
- with(mock(ViewPropertyAnimator::class.java)) {
- whenEver(sidefpsView.animate()).thenReturn(this)
- whenEver(alpha(anyFloat())).thenReturn(this)
- whenEver(setStartDelay(anyLong())).thenReturn(this)
- whenEver(setDuration(anyLong())).thenReturn(this)
- whenEver(setListener(any())).thenAnswer {
- (it.arguments[0] as Animator.AnimatorListener)
- .onAnimationEnd(mock(Animator::class.java))
- this
- }
- }
- }
-
- private fun testWithDisplay(
- deviceConfig: DeviceConfig = DeviceConfig.X_ALIGNED,
- initInfo: DisplayInfo.() -> Unit = {},
- windowInsets: WindowInsets = insetsForSmallNavbar(),
- block: () -> Unit
- ) {
- this.deviceConfig = deviceConfig
-
- when (deviceConfig) {
- DeviceConfig.X_ALIGNED -> {
- displayWidth = 2560
- displayHeight = 1600
- sensorLocation = SensorLocationInternal("", 2325, 0, 0)
- boundsWidth = 160
- boundsHeight = 84
- }
- DeviceConfig.Y_ALIGNED_UNFOLDED -> {
- displayWidth = 2208
- displayHeight = 1840
- sensorLocation = SensorLocationInternal("", 0, 510, 0)
- boundsWidth = 110
- boundsHeight = 210
- }
- DeviceConfig.Y_ALIGNED_FOLDED -> {
- displayWidth = 1080
- displayHeight = 2100
- sensorLocation = SensorLocationInternal("", 0, 590, 0)
- boundsWidth = 110
- boundsHeight = 210
- }
- }
- indicatorBounds = Rect(0, 0, boundsWidth, boundsHeight)
- displayBounds = Rect(0, 0, displayWidth, displayHeight)
- var locations = listOf(sensorLocation)
-
- whenEver(fingerprintManager.sensorPropertiesInternal).thenReturn(
- listOf(
- FingerprintSensorPropertiesInternal(
- SENSOR_ID,
- SensorProperties.STRENGTH_STRONG,
- 5 /* maxEnrollmentsPerUser */,
- listOf() /* componentInfo */,
- FingerprintSensorProperties.TYPE_POWER_BUTTON,
- true /* halControlsIllumination */,
- true /* resetLockoutRequiresHardwareAuthToken */,
- locations
- )
- )
- )
-
- val displayInfo = DisplayInfo()
- displayInfo.initInfo()
- val dmGlobal = mock(DisplayManagerGlobal::class.java)
- val display = Display(dmGlobal, DISPLAY_ID, displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS)
- whenEver(dmGlobal.getDisplayInfo(eq(DISPLAY_ID))).thenReturn(displayInfo)
- whenEver(windowManager.defaultDisplay).thenReturn(display)
- whenEver(windowManager.maximumWindowMetrics).thenReturn(
- WindowMetrics(displayBounds, WindowInsets.CONSUMED)
- )
- whenEver(windowManager.currentWindowMetrics).thenReturn(
- WindowMetrics(displayBounds, windowInsets)
- )
-
- sideFpsController = SidefpsController(
- context.createDisplayContext(display), layoutInflater, fingerprintManager,
- windowManager, activityTaskManager, overviewProxyService, displayManager, executor,
- handler
- )
-
- overlayController = ArgumentCaptor.forClass(ISidefpsController::class.java).apply {
- verify(fingerprintManager).setSidefpsController(capture())
- }.value
-
- block()
- }
-
- @Test
- fun testSubscribesToOrientationChangesWhenShowingOverlay() = testWithDisplay {
- overlayController.show(SENSOR_ID, REASON_UNKNOWN)
- executor.runAllReady()
-
- verify(displayManager).registerDisplayListener(any(), eq(handler), anyLong())
-
- overlayController.hide(SENSOR_ID)
- executor.runAllReady()
- verify(displayManager).unregisterDisplayListener(any())
- }
-
- @Test
- fun testShowsAndHides() = testWithDisplay {
- overlayController.show(SENSOR_ID, REASON_UNKNOWN)
- executor.runAllReady()
-
- verify(windowManager).addView(overlayCaptor.capture(), any())
-
- reset(windowManager)
- overlayController.hide(SENSOR_ID)
- executor.runAllReady()
-
- verify(windowManager, never()).addView(any(), any())
- verify(windowManager).removeView(eq(overlayCaptor.value))
- }
-
- @Test
- fun testShowsOnce() = testWithDisplay {
- repeat(5) {
- overlayController.show(SENSOR_ID, REASON_UNKNOWN)
- executor.runAllReady()
- }
-
- verify(windowManager).addView(any(), any())
- verify(windowManager, never()).removeView(any())
- }
-
- @Test
- fun testHidesOnce() = testWithDisplay {
- overlayController.show(SENSOR_ID, REASON_UNKNOWN)
- executor.runAllReady()
-
- repeat(5) {
- overlayController.hide(SENSOR_ID)
- executor.runAllReady()
- }
-
- verify(windowManager).addView(any(), any())
- verify(windowManager).removeView(any())
- }
-
- @Test
- fun testIgnoredForKeyguard() = testWithDisplay {
- testIgnoredFor(REASON_AUTH_KEYGUARD)
- }
-
- @Test
- fun testShowsForMostSettings() = testWithDisplay {
- whenEver(activityTaskManager.getTasks(anyInt())).thenReturn(listOf(fpEnrollTask()))
- testIgnoredFor(REASON_AUTH_SETTINGS, ignored = false)
- }
-
- @Test
- fun testIgnoredForVerySpecificSettings() = testWithDisplay {
- whenEver(activityTaskManager.getTasks(anyInt())).thenReturn(listOf(fpSettingsTask()))
- testIgnoredFor(REASON_AUTH_SETTINGS)
- }
-
- private fun testIgnoredFor(reason: Int, ignored: Boolean = true) {
- overlayController.show(SENSOR_ID, reason)
- executor.runAllReady()
-
- verify(windowManager, if (ignored) never() else times(1)).addView(any(), any())
- }
-
- @Test
- fun showsWithTaskbar() = testWithDisplay(
- deviceConfig = DeviceConfig.X_ALIGNED,
- { rotation = Surface.ROTATION_0 }
- ) {
- hidesWithTaskbar(visible = true)
- }
-
- @Test
- fun showsWithTaskbarOnY() = testWithDisplay(
- deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED,
- { rotation = Surface.ROTATION_0 }
- ) {
- hidesWithTaskbar(visible = true)
- }
-
- @Test
- fun showsWithTaskbar90() = testWithDisplay(
- deviceConfig = DeviceConfig.X_ALIGNED,
- { rotation = Surface.ROTATION_90 }
- ) {
- hidesWithTaskbar(visible = true)
- }
-
- @Test
- fun showsWithTaskbar90OnY() = testWithDisplay(
- deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED,
- { rotation = Surface.ROTATION_90 }
- ) {
- hidesWithTaskbar(visible = true)
- }
-
- @Test
- fun showsWithTaskbar180() = testWithDisplay(
- deviceConfig = DeviceConfig.X_ALIGNED,
- { rotation = Surface.ROTATION_180 }
- ) {
- hidesWithTaskbar(visible = true)
- }
-
- @Test
- fun showsWithTaskbar270OnY() = testWithDisplay(
- deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED,
- { rotation = Surface.ROTATION_270 }
- ) {
- hidesWithTaskbar(visible = true)
- }
-
- @Test
- fun showsWithTaskbarCollapsedDown() = testWithDisplay(
- deviceConfig = DeviceConfig.X_ALIGNED,
- { rotation = Surface.ROTATION_270 },
- windowInsets = insetsForSmallNavbar()
- ) {
- hidesWithTaskbar(visible = true)
- }
-
- @Test
- fun showsWithTaskbarCollapsedDownOnY() = testWithDisplay(
- deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED,
- { rotation = Surface.ROTATION_180 },
- windowInsets = insetsForSmallNavbar()
- ) {
- hidesWithTaskbar(visible = true)
- }
-
- @Test
- fun hidesWithTaskbarDown() = testWithDisplay(
- deviceConfig = DeviceConfig.X_ALIGNED,
- { rotation = Surface.ROTATION_180 },
- windowInsets = insetsForLargeNavbar()
- ) {
- hidesWithTaskbar(visible = false)
- }
-
- @Test
- fun hidesWithTaskbarDownOnY() = testWithDisplay(
- deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED,
- { rotation = Surface.ROTATION_270 },
- windowInsets = insetsForLargeNavbar()
- ) {
- hidesWithTaskbar(visible = true)
- }
-
- private fun hidesWithTaskbar(visible: Boolean) {
- overlayController.show(SENSOR_ID, REASON_UNKNOWN)
- executor.runAllReady()
-
- sideFpsController.overviewProxyListener.onTaskbarStatusUpdated(visible, false)
- executor.runAllReady()
-
- verify(windowManager).addView(any(), any())
- verify(windowManager, never()).removeView(any())
- verify(sidefpsView).visibility = if (visible) View.VISIBLE else View.GONE
- }
-
- @Test
- fun testIndicatorPlacementForXAlignedSensor() = testWithDisplay(
- deviceConfig = DeviceConfig.X_ALIGNED
- ) {
- overlayController.show(SENSOR_ID, REASON_UNKNOWN)
- sideFpsController.overlayOffsets = sensorLocation
- sideFpsController.updateOverlayParams(
- windowManager.defaultDisplay,
- indicatorBounds
- )
- executor.runAllReady()
-
- verify(windowManager).updateViewLayout(any(), overlayViewParamsCaptor.capture())
-
- assertThat(overlayViewParamsCaptor.value.x).isEqualTo(sensorLocation.sensorLocationX)
- assertThat(overlayViewParamsCaptor.value.y).isEqualTo(0)
- }
-
- @Test
- fun testIndicatorPlacementForYAlignedSensor() = testWithDisplay(
- deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED
- ) {
- sideFpsController.overlayOffsets = sensorLocation
- sideFpsController.updateOverlayParams(
- windowManager.defaultDisplay,
- indicatorBounds
- )
- overlayController.show(SENSOR_ID, REASON_UNKNOWN)
- executor.runAllReady()
-
- verify(windowManager).updateViewLayout(any(), overlayViewParamsCaptor.capture())
- assertThat(overlayViewParamsCaptor.value.x).isEqualTo(displayWidth - boundsWidth)
- assertThat(overlayViewParamsCaptor.value.y).isEqualTo(sensorLocation.sensorLocationY)
- }
-
- @Test
- fun hasSideFpsSensor_withSensorProps_returnsTrue() = testWithDisplay {
- // By default all those tests assume the side fps sensor is available.
-
- assertThat(fingerprintManager.hasSideFpsSensor()).isTrue()
- }
-
- @Test
- fun hasSideFpsSensor_withoutSensorProps_returnsFalse() {
- whenEver(fingerprintManager.sensorPropertiesInternal).thenReturn(null)
-
- assertThat(fingerprintManager.hasSideFpsSensor()).isFalse()
- }
-
- @Test
- fun testLayoutParams_hasNoMoveAnimationWindowFlag() = testWithDisplay(
- deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED
- ) {
- sideFpsController.overlayOffsets = sensorLocation
- sideFpsController.updateOverlayParams(
- windowManager.defaultDisplay,
- indicatorBounds
- )
- overlayController.show(SENSOR_ID, REASON_UNKNOWN)
- executor.runAllReady()
-
- verify(windowManager).updateViewLayout(any(), overlayViewParamsCaptor.capture())
-
- val lpFlags = overlayViewParamsCaptor.value.privateFlags
-
- assertThat((lpFlags and PRIVATE_FLAG_NO_MOVE_ANIMATION) != 0).isTrue()
- }
-
- @Test
- fun testLayoutParams_hasTrustedOverlayWindowFlag() = testWithDisplay(
- deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED
- ) {
- sideFpsController.overlayOffsets = sensorLocation
- sideFpsController.updateOverlayParams(
- windowManager.defaultDisplay,
- indicatorBounds
- )
- overlayController.show(SENSOR_ID, REASON_UNKNOWN)
- executor.runAllReady()
-
- verify(windowManager).updateViewLayout(any(), overlayViewParamsCaptor.capture())
-
- val lpFlags = overlayViewParamsCaptor.value.privateFlags
-
- assertThat((lpFlags and PRIVATE_FLAG_TRUSTED_OVERLAY) != 0).isTrue()
- }
-}
-
-private fun insetsForSmallNavbar() = insetsWithBottom(60)
-private fun insetsForLargeNavbar() = insetsWithBottom(100)
-private fun insetsWithBottom(bottom: Int) = WindowInsets.Builder()
- .setInsets(WindowInsets.Type.navigationBars(), Insets.of(0, 0, 0, bottom))
- .build()
-
-private fun fpEnrollTask() = settingsTask(".biometrics.fingerprint.FingerprintEnrollEnrolling")
-private fun fpSettingsTask() = settingsTask(".biometrics.fingerprint.FingerprintSettings")
-private fun settingsTask(cls: String) = ActivityManager.RunningTaskInfo().apply {
- topActivity = ComponentName.createRelative("com.android.settings", cls)
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index 6ec5a6c..4b459c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -43,7 +43,7 @@
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.keyguard.domain.interactor.BouncerInteractor
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -106,7 +106,7 @@
@Mock private lateinit var udfpsEnrollView: UdfpsEnrollView
@Mock private lateinit var activityLaunchAnimator: ActivityLaunchAnimator
@Mock private lateinit var featureFlags: FeatureFlags
- @Mock private lateinit var bouncerInteractor: BouncerInteractor
+ @Mock private lateinit var mPrimaryBouncerInteractor: PrimaryBouncerInteractor
@Captor private lateinit var layoutParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams>
private val onTouch = { _: View, _: MotionEvent, _: Boolean -> true }
@@ -141,7 +141,7 @@
configurationController, systemClock, keyguardStateController,
unlockedScreenOffAnimationController, udfpsDisplayMode, REQUEST_ID, reason,
controllerCallback, onTouch, activityLaunchAnimator, featureFlags,
- bouncerInteractor, isDebuggable
+ mPrimaryBouncerInteractor, isDebuggable
)
block()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index aa14a40..acdafe3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -73,7 +73,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.keyguard.domain.interactor.BouncerInteractor;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeExpansionStateManager;
@@ -114,10 +114,8 @@
@Rule
public MockitoRule rule = MockitoJUnit.rule();
-
// Unit under test
private UdfpsController mUdfpsController;
-
// Dependencies
private FakeExecutor mBiometricsExecutor;
@Mock
@@ -171,7 +169,6 @@
private UdfpsDisplayMode mUdfpsDisplayMode;
@Mock
private FeatureFlags mFeatureFlags;
-
// Stuff for configuring mocks
@Mock
private UdfpsView mUdfpsView;
@@ -192,7 +189,7 @@
@Mock
private AlternateUdfpsTouchProvider mAlternateTouchProvider;
@Mock
- private BouncerInteractor mBouncerInteractor;
+ private PrimaryBouncerInteractor mPrimaryBouncerInteractor;
// Capture listeners so that they can be used to send events
@Captor
@@ -249,54 +246,42 @@
FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC,
true /* resetLockoutRequiresHardwareAuthToken */);
- List<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
- props.add(mOpticalProps);
- props.add(mUltrasonicProps);
- when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props);
-
mFgExecutor = new FakeExecutor(new FakeSystemClock());
// Create a fake background executor.
mBiometricsExecutor = new FakeExecutor(new FakeSystemClock());
- mUdfpsController = new UdfpsController(
- mContext,
- execution,
- mLayoutInflater,
- mFingerprintManager,
- mWindowManager,
- mStatusBarStateController,
- mFgExecutor,
- new ShadeExpansionStateManager(),
- mStatusBarKeyguardViewManager,
- mDumpManager,
- mKeyguardUpdateMonitor,
- mFeatureFlags,
- mFalsingManager,
- mPowerManager,
- mAccessibilityManager,
- mLockscreenShadeTransitionController,
- mScreenLifecycle,
- mVibrator,
- mUdfpsHapticsSimulator,
- mUdfpsShell,
- mKeyguardStateController,
- mDisplayManager,
- mHandler,
- mConfigurationController,
- mSystemClock,
- mUnlockedScreenOffAnimationController,
- mSystemUIDialogManager,
- mLatencyTracker,
- mActivityLaunchAnimator,
- Optional.of(mAlternateTouchProvider),
- mBiometricsExecutor,
- mBouncerInteractor);
+ initUdfpsController(true /* hasAlternateTouchProvider */);
+ }
+
+ private void initUdfpsController(boolean hasAlternateTouchProvider) {
+ initUdfpsController(mOpticalProps, hasAlternateTouchProvider);
+ }
+
+ private void initUdfpsController(FingerprintSensorPropertiesInternal sensorProps,
+ boolean hasAlternateTouchProvider) {
+ reset(mFingerprintManager);
+ reset(mScreenLifecycle);
+
+ final Optional<AlternateUdfpsTouchProvider> alternateTouchProvider =
+ hasAlternateTouchProvider ? Optional.of(mAlternateTouchProvider) : Optional.empty();
+
+ mUdfpsController = new UdfpsController(mContext, new FakeExecution(), mLayoutInflater,
+ mFingerprintManager, mWindowManager, mStatusBarStateController, mFgExecutor,
+ new ShadeExpansionStateManager(), mStatusBarKeyguardViewManager, mDumpManager,
+ mKeyguardUpdateMonitor, mFeatureFlags, mFalsingManager, mPowerManager,
+ mAccessibilityManager, mLockscreenShadeTransitionController, mScreenLifecycle,
+ mVibrator, mUdfpsHapticsSimulator, mUdfpsShell, mKeyguardStateController,
+ mDisplayManager, mHandler, mConfigurationController, mSystemClock,
+ mUnlockedScreenOffAnimationController, mSystemUIDialogManager, mLatencyTracker,
+ mActivityLaunchAnimator, alternateTouchProvider, mBiometricsExecutor,
+ mPrimaryBouncerInteractor);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
mScreenObserver = mScreenObserverCaptor.getValue();
- mUdfpsController.updateOverlayParams(mOpticalProps, new UdfpsOverlayParams());
+
+ mUdfpsController.updateOverlayParams(sensorProps, new UdfpsOverlayParams());
mUdfpsController.setUdfpsDisplayMode(mUdfpsDisplayMode);
}
@@ -333,8 +318,7 @@
}
@Test
- public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice()
- throws RemoteException {
+ public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice() throws RemoteException {
onActionMoveTouch_whenCanDismissLockScreen_entersDevice(false /* stale */);
}
@@ -405,14 +389,14 @@
// GIVEN overlay was showing and the udfps bouncer is showing
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- when(mStatusBarKeyguardViewManager.isShowingAlternateAuth()).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.isShowingAlternateBouncer()).thenReturn(true);
// WHEN the overlay is hidden
mOverlayController.hideUdfpsOverlay(mOpticalProps.sensorId);
mFgExecutor.runAllReady();
// THEN the udfps bouncer is reset
- verify(mStatusBarKeyguardViewManager).resetAlternateAuth(eq(true));
+ verify(mStatusBarKeyguardViewManager).hideAlternateBouncer(eq(true));
}
@Test
@@ -521,8 +505,37 @@
new MotionEvent.PointerCoords[]{pc}, 0, 0, 1f, 1f, 0, 0, 0, 0);
}
+ private static class TestParams {
+ public final FingerprintSensorPropertiesInternal sensorProps;
+ public final boolean hasAlternateTouchProvider;
+
+ TestParams(FingerprintSensorPropertiesInternal sensorProps,
+ boolean hasAlternateTouchProvider) {
+ this.sensorProps = sensorProps;
+ this.hasAlternateTouchProvider = hasAlternateTouchProvider;
+ }
+ }
+
+ private void runWithAllParams(ThrowingConsumer<TestParams> testParamsConsumer) {
+ for (FingerprintSensorPropertiesInternal sensorProps : List.of(mOpticalProps,
+ mUltrasonicProps)) {
+ for (boolean hasAlternateTouchProvider : new boolean[]{false, true}) {
+ initUdfpsController(sensorProps, hasAlternateTouchProvider);
+ testParamsConsumer.accept(new TestParams(sensorProps, hasAlternateTouchProvider));
+ }
+ }
+ }
+
@Test
- public void onTouch_propagatesTouchInNativeOrientationAndResolution() throws RemoteException {
+ public void onTouch_propagatesTouchInNativeOrientationAndResolution() {
+ runWithAllParams(
+ this::onTouch_propagatesTouchInNativeOrientationAndResolutionParameterized);
+ }
+
+ private void onTouch_propagatesTouchInNativeOrientationAndResolutionParameterized(
+ TestParams testParams) throws RemoteException {
+ reset(mUdfpsView);
+
final Rect sensorBounds = new Rect(1000, 1900, 1080, 1920); // Bottom right corner.
final int displayWidth = 1080;
final int displayHeight = 1920;
@@ -541,13 +554,13 @@
when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
// Show the overlay.
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
BiometricOverlayConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
// Test ROTATION_0
- mUdfpsController.updateOverlayParams(mOpticalProps,
+ mUdfpsController.updateOverlayParams(testParams.sensorProps,
new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
scaleFactor, Surface.ROTATION_0));
MotionEvent event = obtainMotionEvent(ACTION_DOWN, displayWidth, displayHeight, touchMinor,
@@ -559,12 +572,19 @@
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
mBiometricsExecutor.runAllReady();
event.recycle();
- verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
- eq(expectedY), eq(expectedMinor), eq(expectedMajor));
+ if (testParams.hasAlternateTouchProvider) {
+ verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
+ eq(expectedY), eq(expectedMinor), eq(expectedMajor));
+ } else {
+ verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
+ eq(testParams.sensorProps.sensorId), eq(expectedX), eq(expectedY),
+ eq(expectedMinor), eq(expectedMajor));
+ }
// Test ROTATION_90
reset(mAlternateTouchProvider);
- mUdfpsController.updateOverlayParams(mOpticalProps,
+ reset(mFingerprintManager);
+ mUdfpsController.updateOverlayParams(testParams.sensorProps,
new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
scaleFactor, Surface.ROTATION_90));
event = obtainMotionEvent(ACTION_DOWN, displayHeight, 0, touchMinor, touchMajor);
@@ -575,12 +595,19 @@
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
mBiometricsExecutor.runAllReady();
event.recycle();
- verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
- eq(expectedY), eq(expectedMinor), eq(expectedMajor));
+ if (testParams.hasAlternateTouchProvider) {
+ verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
+ eq(expectedY), eq(expectedMinor), eq(expectedMajor));
+ } else {
+ verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
+ eq(testParams.sensorProps.sensorId), eq(expectedX), eq(expectedY),
+ eq(expectedMinor), eq(expectedMajor));
+ }
// Test ROTATION_270
reset(mAlternateTouchProvider);
- mUdfpsController.updateOverlayParams(mOpticalProps,
+ reset(mFingerprintManager);
+ mUdfpsController.updateOverlayParams(testParams.sensorProps,
new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
scaleFactor, Surface.ROTATION_270));
event = obtainMotionEvent(ACTION_DOWN, 0, displayWidth, touchMinor, touchMajor);
@@ -591,12 +618,19 @@
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
mBiometricsExecutor.runAllReady();
event.recycle();
- verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
- eq(expectedY), eq(expectedMinor), eq(expectedMajor));
+ if (testParams.hasAlternateTouchProvider) {
+ verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
+ eq(expectedY), eq(expectedMinor), eq(expectedMajor));
+ } else {
+ verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
+ eq(testParams.sensorProps.sensorId), eq(expectedX), eq(expectedY),
+ eq(expectedMinor), eq(expectedMajor));
+ }
// Test ROTATION_180
reset(mAlternateTouchProvider);
- mUdfpsController.updateOverlayParams(mOpticalProps,
+ reset(mFingerprintManager);
+ mUdfpsController.updateOverlayParams(testParams.sensorProps,
new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
scaleFactor, Surface.ROTATION_180));
// ROTATION_180 is not supported. It should be treated like ROTATION_0.
@@ -608,26 +642,22 @@
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
mBiometricsExecutor.runAllReady();
event.recycle();
- verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
- eq(expectedY), eq(expectedMinor), eq(expectedMajor));
- }
-
- private void runForAllUdfpsTypes(
- ThrowingConsumer<FingerprintSensorPropertiesInternal> sensorPropsConsumer) {
- for (FingerprintSensorPropertiesInternal sensorProps : List.of(mOpticalProps,
- mUltrasonicProps)) {
- mUdfpsController.updateOverlayParams(sensorProps, new UdfpsOverlayParams());
- sensorPropsConsumer.accept(sensorProps);
+ if (testParams.hasAlternateTouchProvider) {
+ verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
+ eq(expectedY), eq(expectedMinor), eq(expectedMajor));
+ } else {
+ verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
+ eq(testParams.sensorProps.sensorId), eq(expectedX), eq(expectedY),
+ eq(expectedMinor), eq(expectedMajor));
}
}
@Test
public void fingerDown() {
- runForAllUdfpsTypes(this::fingerDownForSensor);
+ runWithAllParams(this::fingerDownParameterized);
}
- private void fingerDownForSensor(FingerprintSensorPropertiesInternal sensorProps)
- throws RemoteException {
+ private void fingerDownParameterized(TestParams testParams) throws RemoteException {
reset(mUdfpsView, mAlternateTouchProvider, mFingerprintManager, mLatencyTracker,
mKeyguardUpdateMonitor);
@@ -637,7 +667,7 @@
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
// GIVEN that the overlay is showing
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, sensorProps.sensorId,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
@@ -655,14 +685,22 @@
mFgExecutor.runAllReady();
- // THEN FingerprintManager is notified about onPointerDown
- verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(0), eq(0), eq(0f),
- eq(0f));
- verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(), anyInt(),
- anyFloat(), anyFloat());
+ // THEN the touch provider is notified about onPointerDown.
+ if (testParams.hasAlternateTouchProvider) {
+ verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(0), eq(0), eq(0f),
+ eq(0f));
+ verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(),
+ anyInt(), anyFloat(), anyFloat());
+ verify(mKeyguardUpdateMonitor).onUdfpsPointerDown(eq((int) TEST_REQUEST_ID));
+ } else {
+ verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
+ eq(testParams.sensorProps.sensorId), eq(0), eq(0), eq(0f), eq(0f));
+ verify(mAlternateTouchProvider, never()).onPointerDown(anyInt(), anyInt(), anyInt(),
+ anyFloat(), anyFloat());
+ }
// AND display configuration begins
- if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
verify(mLatencyTracker).onActionStart(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
verify(mUdfpsView).configureDisplay(mOnDisplayConfiguredCaptor.capture());
} else {
@@ -671,16 +709,27 @@
verify(mUdfpsView, never()).configureDisplay(any());
}
verify(mLatencyTracker, never()).onActionEnd(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
- verify(mKeyguardUpdateMonitor).onUdfpsPointerDown(eq((int) TEST_REQUEST_ID));
- if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
// AND onDisplayConfigured notifies FingerprintManager about onUiReady
mOnDisplayConfiguredCaptor.getValue().run();
mBiometricsExecutor.runAllReady();
- InOrder inOrder = inOrder(mAlternateTouchProvider, mLatencyTracker);
- inOrder.verify(mAlternateTouchProvider).onUiReady();
- inOrder.verify(mLatencyTracker).onActionEnd(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
+ if (testParams.hasAlternateTouchProvider) {
+ InOrder inOrder = inOrder(mAlternateTouchProvider, mLatencyTracker);
+ inOrder.verify(mAlternateTouchProvider).onUiReady();
+ inOrder.verify(mLatencyTracker).onActionEnd(
+ eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
+ verify(mFingerprintManager, never()).onUiReady(anyLong(), anyInt());
+ } else {
+ InOrder inOrder = inOrder(mFingerprintManager, mLatencyTracker);
+ inOrder.verify(mFingerprintManager).onUiReady(eq(TEST_REQUEST_ID),
+ eq(testParams.sensorProps.sensorId));
+ inOrder.verify(mLatencyTracker).onActionEnd(
+ eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
+ verify(mAlternateTouchProvider, never()).onUiReady();
+ }
} else {
+ verify(mFingerprintManager, never()).onUiReady(anyLong(), anyInt());
verify(mAlternateTouchProvider, never()).onUiReady();
verify(mLatencyTracker, never()).onActionEnd(
eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
@@ -689,24 +738,23 @@
@Test
public void aodInterrupt() {
- runForAllUdfpsTypes(this::aodInterruptForSensor);
+ runWithAllParams(this::aodInterruptParameterized);
}
- private void aodInterruptForSensor(FingerprintSensorPropertiesInternal sensorProps)
- throws RemoteException {
+ private void aodInterruptParameterized(TestParams testParams) throws RemoteException {
mUdfpsController.cancelAodInterrupt();
reset(mUdfpsView, mAlternateTouchProvider, mFingerprintManager, mKeyguardUpdateMonitor);
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
// GIVEN that the overlay is showing and screen is on and fp is running
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, sensorProps.sensorId,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
// WHEN fingerprint is requested because of AOD interrupt
mUdfpsController.onAodInterrupt(0, 0, 2f, 3f);
mFgExecutor.runAllReady();
- if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
// THEN display configuration begins
// AND onDisplayConfigured notifies FingerprintManager about onUiReady
verify(mUdfpsView).configureDisplay(mOnDisplayConfiguredCaptor.capture());
@@ -715,29 +763,37 @@
verify(mUdfpsView, never()).configureDisplay(mOnDisplayConfiguredCaptor.capture());
}
mBiometricsExecutor.runAllReady();
- verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID),
- eq(0), eq(0), eq(3f) /* minor */, eq(2f) /* major */);
- verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(), anyInt(),
- anyFloat(), anyFloat());
- verify(mKeyguardUpdateMonitor).onUdfpsPointerDown(eq((int) TEST_REQUEST_ID));
+
+ if (testParams.hasAlternateTouchProvider) {
+ verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(0), eq(0),
+ eq(3f) /* minor */, eq(2f) /* major */);
+ verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(),
+ anyInt(), anyFloat(), anyFloat());
+ verify(mKeyguardUpdateMonitor).onUdfpsPointerDown(eq((int) TEST_REQUEST_ID));
+ } else {
+ verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
+ eq(testParams.sensorProps.sensorId), eq(0), eq(0), eq(3f) /* minor */,
+ eq(2f) /* major */);
+ verify(mAlternateTouchProvider, never()).onPointerDown(anyLong(), anyInt(), anyInt(),
+ anyFloat(), anyFloat());
+ }
}
@Test
public void cancelAodInterrupt() {
- runForAllUdfpsTypes(this::cancelAodInterruptForSensor);
+ runWithAllParams(this::cancelAodInterruptParameterized);
}
- private void cancelAodInterruptForSensor(FingerprintSensorPropertiesInternal sensorProps)
- throws RemoteException {
+ private void cancelAodInterruptParameterized(TestParams testParams) throws RemoteException {
reset(mUdfpsView);
// GIVEN AOD interrupt
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, sensorProps.sensorId,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
- if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
// WHEN it is cancelled
mUdfpsController.cancelAodInterrupt();
@@ -754,21 +810,20 @@
@Test
public void aodInterruptTimeout() {
- runForAllUdfpsTypes(this::aodInterruptTimeoutForSensor);
+ runWithAllParams(this::aodInterruptTimeoutParameterized);
}
- private void aodInterruptTimeoutForSensor(FingerprintSensorPropertiesInternal sensorProps)
- throws RemoteException {
+ private void aodInterruptTimeoutParameterized(TestParams testParams) throws RemoteException {
reset(mUdfpsView);
// GIVEN AOD interrupt
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, sensorProps.sensorId,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
mFgExecutor.runAllReady();
- if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
} else {
when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
@@ -776,7 +831,7 @@
// WHEN it times out
mFgExecutor.advanceClockToNext();
mFgExecutor.runAllReady();
- if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
// THEN the display is unconfigured.
verify(mUdfpsView).unconfigureDisplay();
} else {
@@ -787,23 +842,23 @@
@Test
public void aodInterruptCancelTimeoutActionOnFingerUp() {
- runForAllUdfpsTypes(this::aodInterruptCancelTimeoutActionOnFingerUpForSensor);
+ runWithAllParams(this::aodInterruptCancelTimeoutActionOnFingerUpParameterized);
}
- private void aodInterruptCancelTimeoutActionOnFingerUpForSensor(
- FingerprintSensorPropertiesInternal sensorProps) throws RemoteException {
+ private void aodInterruptCancelTimeoutActionOnFingerUpParameterized(TestParams testParams)
+ throws RemoteException {
reset(mUdfpsView);
when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
// GIVEN AOD interrupt
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, sensorProps.sensorId,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
mFgExecutor.runAllReady();
- if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
// Configure UdfpsView to accept the ACTION_UP event
when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
} else {
@@ -833,7 +888,7 @@
moveEvent.recycle();
mFgExecutor.runAllReady();
- if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
// Configure UdfpsView to accept the finger up event
when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
} else {
@@ -844,7 +899,7 @@
mFgExecutor.advanceClockToNext();
mFgExecutor.runAllReady();
- if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
// THEN the display should be unconfigured once. If the timeout action is not
// cancelled, the display would be unconfigured twice which would cause two
// FP attempts.
@@ -856,23 +911,23 @@
@Test
public void aodInterruptCancelTimeoutActionOnAcquired() {
- runForAllUdfpsTypes(this::aodInterruptCancelTimeoutActionOnAcquiredForSensor);
+ runWithAllParams(this::aodInterruptCancelTimeoutActionOnAcquiredParameterized);
}
- private void aodInterruptCancelTimeoutActionOnAcquiredForSensor(
- FingerprintSensorPropertiesInternal sensorProps) throws RemoteException {
+ private void aodInterruptCancelTimeoutActionOnAcquiredParameterized(TestParams testParams)
+ throws RemoteException {
reset(mUdfpsView);
when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
// GIVEN AOD interrupt
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, sensorProps.sensorId,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
mFgExecutor.runAllReady();
- if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
// Configure UdfpsView to accept the acquired event
when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
} else {
@@ -880,7 +935,7 @@
}
// WHEN acquired is received
- mOverlayController.onAcquired(sensorProps.sensorId,
+ mOverlayController.onAcquired(testParams.sensorProps.sensorId,
BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD);
// Configure UdfpsView to accept the ACTION_DOWN event
@@ -900,7 +955,7 @@
moveEvent.recycle();
mFgExecutor.runAllReady();
- if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
// Configure UdfpsView to accept the finger up event
when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
} else {
@@ -911,7 +966,7 @@
mFgExecutor.advanceClockToNext();
mFgExecutor.runAllReady();
- if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
// THEN the display should be unconfigured once. If the timeout action is not
// cancelled, the display would be unconfigured twice which would cause two
// FP attempts.
@@ -923,15 +978,14 @@
@Test
public void aodInterruptScreenOff() {
- runForAllUdfpsTypes(this::aodInterruptScreenOffForSensor);
+ runWithAllParams(this::aodInterruptScreenOffParameterized);
}
- private void aodInterruptScreenOffForSensor(FingerprintSensorPropertiesInternal sensorProps)
- throws RemoteException {
+ private void aodInterruptScreenOffParameterized(TestParams testParams) throws RemoteException {
reset(mUdfpsView);
// GIVEN screen off
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, sensorProps.sensorId,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOff();
mFgExecutor.runAllReady();
@@ -945,17 +999,16 @@
@Test
public void aodInterrupt_fingerprintNotRunning() {
- runForAllUdfpsTypes(this::aodInterrupt_fingerprintNotRunningForSensor);
+ runWithAllParams(this::aodInterrupt_fingerprintNotRunningParameterized);
}
- private void aodInterrupt_fingerprintNotRunningForSensor(
- FingerprintSensorPropertiesInternal sensorProps) throws RemoteException {
+ private void aodInterrupt_fingerprintNotRunningParameterized(TestParams testParams)
+ throws RemoteException {
reset(mUdfpsView);
// GIVEN showing overlay
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, sensorProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD,
- mUdfpsOverlayControllerCallback);
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java
index e5c7a42..75629f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java
@@ -30,7 +30,7 @@
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.keyguard.domain.interactor.BouncerInteractor;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.shade.ShadeExpansionListener;
@@ -72,7 +72,7 @@
protected @Mock UdfpsController mUdfpsController;
protected @Mock ActivityLaunchAnimator mActivityLaunchAnimator;
protected @Mock KeyguardBouncer mBouncer;
- protected @Mock BouncerInteractor mBouncerInteractor;
+ protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor;
protected FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
protected FakeSystemClock mSystemClock = new FakeSystemClock();
@@ -86,9 +86,9 @@
private @Captor ArgumentCaptor<ShadeExpansionListener> mExpansionListenerCaptor;
protected List<ShadeExpansionListener> mExpansionListeners;
- private @Captor ArgumentCaptor<StatusBarKeyguardViewManager.AlternateAuthInterceptor>
- mAltAuthInterceptorCaptor;
- protected StatusBarKeyguardViewManager.AlternateAuthInterceptor mAltAuthInterceptor;
+ private @Captor ArgumentCaptor<StatusBarKeyguardViewManager.AlternateBouncer>
+ mAlternateBouncerCaptor;
+ protected StatusBarKeyguardViewManager.AlternateBouncer mAlternateBouncer;
private @Captor ArgumentCaptor<KeyguardStateController.Callback>
mKeyguardStateControllerCallbackCaptor;
@@ -131,9 +131,9 @@
}
protected void captureAltAuthInterceptor() {
- verify(mStatusBarKeyguardViewManager).setAlternateAuthInterceptor(
- mAltAuthInterceptorCaptor.capture());
- mAltAuthInterceptor = mAltAuthInterceptorCaptor.getValue();
+ verify(mStatusBarKeyguardViewManager).setAlternateBouncer(
+ mAlternateBouncerCaptor.capture());
+ mAlternateBouncer = mAlternateBouncerCaptor.getValue();
}
protected void captureKeyguardStateControllerCallback() {
@@ -149,7 +149,7 @@
protected UdfpsKeyguardViewController createUdfpsKeyguardViewController(
boolean useModernBouncer) {
mFeatureFlags.set(Flags.MODERN_BOUNCER, useModernBouncer);
- when(mStatusBarKeyguardViewManager.getBouncer()).thenReturn(
+ when(mStatusBarKeyguardViewManager.getPrimaryBouncer()).thenReturn(
useModernBouncer ? null : mBouncer);
return new UdfpsKeyguardViewController(
mView,
@@ -167,6 +167,6 @@
mUdfpsController,
mActivityLaunchAnimator,
mFeatureFlags,
- mBouncerInteractor);
+ mPrimaryBouncerInteractor);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index 55b6194..16728b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -46,9 +46,9 @@
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
public class UdfpsKeyguardViewControllerTest extends UdfpsKeyguardViewControllerBaseTest {
- private @Captor ArgumentCaptor<KeyguardBouncer.BouncerExpansionCallback>
+ private @Captor ArgumentCaptor<KeyguardBouncer.PrimaryBouncerExpansionCallback>
mBouncerExpansionCallbackCaptor;
- private KeyguardBouncer.BouncerExpansionCallback mBouncerExpansionCallback;
+ private KeyguardBouncer.PrimaryBouncerExpansionCallback mBouncerExpansionCallback;
@Override
public UdfpsKeyguardViewController createUdfpsKeyguardViewController() {
@@ -63,7 +63,7 @@
captureBouncerExpansionCallback();
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
- when(mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()).thenReturn(true);
mBouncerExpansionCallback.onVisibilityChanged(true);
assertTrue(mController.shouldPauseAuth());
@@ -239,13 +239,13 @@
sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED);
assertTrue(mController.shouldPauseAuth());
- mAltAuthInterceptor.showAlternateAuthBouncer(); // force show
+ mAlternateBouncer.showAlternateBouncer(); // force show
assertFalse(mController.shouldPauseAuth());
- assertTrue(mAltAuthInterceptor.isShowingAlternateAuthBouncer());
+ assertTrue(mAlternateBouncer.isShowingAlternateBouncer());
- mAltAuthInterceptor.hideAlternateAuthBouncer(); // stop force show
+ mAlternateBouncer.hideAlternateBouncer(); // stop force show
assertTrue(mController.shouldPauseAuth());
- assertFalse(mAltAuthInterceptor.isShowingAlternateAuthBouncer());
+ assertFalse(mAlternateBouncer.isShowingAlternateBouncer());
}
@Test
@@ -258,7 +258,7 @@
mController.onViewDetached();
// THEN remove alternate auth interceptor
- verify(mStatusBarKeyguardViewManager).removeAlternateAuthInterceptor(mAltAuthInterceptor);
+ verify(mStatusBarKeyguardViewManager).removeAlternateAuthInterceptor(mAlternateBouncer);
}
@Test
@@ -268,14 +268,15 @@
captureAltAuthInterceptor();
// GIVEN udfps bouncer isn't showing
- mAltAuthInterceptor.hideAlternateAuthBouncer();
+ mAlternateBouncer.hideAlternateBouncer();
// WHEN touch is observed outside the view
mController.onTouchOutsideView();
// THEN bouncer / alt auth methods are never called
+ verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean());
verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
- verify(mStatusBarKeyguardViewManager, never()).resetAlternateAuth(anyBoolean());
+ verify(mStatusBarKeyguardViewManager, never()).hideAlternateBouncer(anyBoolean());
}
@Test
@@ -285,32 +286,33 @@
captureAltAuthInterceptor();
// GIVEN udfps bouncer is showing
- mAltAuthInterceptor.showAlternateAuthBouncer();
+ mAlternateBouncer.showAlternateBouncer();
// WHEN touch is observed outside the view 200ms later (just within threshold)
mSystemClock.advanceTime(200);
mController.onTouchOutsideView();
// THEN bouncer / alt auth methods are never called because not enough time has passed
+ verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean());
verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
- verify(mStatusBarKeyguardViewManager, never()).resetAlternateAuth(anyBoolean());
+ verify(mStatusBarKeyguardViewManager, never()).hideAlternateBouncer(anyBoolean());
}
@Test
- public void testShowingUdfpsBouncerOnTouchOutsideAboveThreshold_showInputBouncer() {
+ public void testShowingUdfpsBouncerOnTouchOutsideAboveThreshold_showPrimaryBouncer() {
// GIVEN view is attached
mController.onViewAttached();
captureAltAuthInterceptor();
// GIVEN udfps bouncer is showing
- mAltAuthInterceptor.showAlternateAuthBouncer();
+ mAlternateBouncer.showAlternateBouncer();
// WHEN touch is observed outside the view 205ms later
mSystemClock.advanceTime(205);
mController.onTouchOutsideView();
// THEN show the bouncer
- verify(mStatusBarKeyguardViewManager).showBouncer(eq(true));
+ verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(eq(true));
}
@Test
@@ -341,7 +343,7 @@
when(mResourceContext.getString(anyInt())).thenReturn("test string");
// WHEN status bar expansion is 0 but udfps bouncer is requested
- mAltAuthInterceptor.showAlternateAuthBouncer();
+ mAlternateBouncer.showAlternateBouncer();
// THEN alpha is 255
verify(mView).setUnpausedAlpha(255);
@@ -372,7 +374,7 @@
captureKeyguardStateControllerCallback();
captureAltAuthInterceptor();
updateStatusBarExpansion(1f, true);
- mAltAuthInterceptor.showAlternateAuthBouncer();
+ mAlternateBouncer.showAlternateBouncer();
reset(mView);
// WHEN we're transitioning to the full shade
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt
index 7b19768..68e744e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt
@@ -25,8 +25,8 @@
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.BouncerView
import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
-import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor
-import com.android.systemui.keyguard.domain.interactor.BouncerInteractor
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.KeyguardBouncer
import com.android.systemui.statusbar.phone.KeyguardBypassController
@@ -59,14 +59,14 @@
}
override fun createUdfpsKeyguardViewController(): UdfpsKeyguardViewController? {
- mBouncerInteractor =
- BouncerInteractor(
+ mPrimaryBouncerInteractor =
+ PrimaryBouncerInteractor(
keyguardBouncerRepository,
mock(BouncerView::class.java),
mock(Handler::class.java),
mKeyguardStateController,
mock(KeyguardSecurityModel::class.java),
- mock(BouncerCallbackInteractor::class.java),
+ mock(PrimaryBouncerCallbackInteractor::class.java),
mock(FalsingCollector::class.java),
mock(DismissCallbackRegistry::class.java),
mock(KeyguardBypassController::class.java),
@@ -86,7 +86,7 @@
// WHEN the bouncer expansion is VISIBLE
val job = mController.listenForBouncerExpansion(this)
- keyguardBouncerRepository.setVisible(true)
+ keyguardBouncerRepository.setPrimaryVisible(true)
keyguardBouncerRepository.setPanelExpansion(KeyguardBouncer.EXPANSION_VISIBLE)
yield()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
index 6bc7308..f8579ff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
@@ -39,6 +39,8 @@
import com.android.internal.logging.testing.FakeMetricsLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingDataProvider.GestureFinalizedListener;
+import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -66,6 +68,8 @@
@Mock
private SingleTapClassifier mSingleTapClassfier;
@Mock
+ private LongTapClassifier mLongTapClassifier;
+ @Mock
private DoubleTapClassifier mDoubleTapClassifier;
@Mock
private FalsingClassifier mClassifierA;
@@ -80,6 +84,7 @@
private AccessibilityManager mAccessibilityManager;
private final FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+ private final FakeFeatureFlags mFakeFeatureFlags = new FakeFeatureFlags();
private final FalsingClassifier.Result mFalsedResult =
FalsingClassifier.Result.falsed(1, getClass().getSimpleName(), "");
@@ -94,6 +99,7 @@
when(mClassifierB.classifyGesture(anyInt(), anyDouble(), anyDouble()))
.thenReturn(mPassedResult);
when(mSingleTapClassfier.isTap(any(List.class), anyDouble())).thenReturn(mPassedResult);
+ when(mLongTapClassifier.isTap(any(List.class), anyDouble())).thenReturn(mFalsedResult);
when(mDoubleTapClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble()))
.thenReturn(mPassedResult);
mClassifiers.add(mClassifierA);
@@ -101,9 +107,9 @@
when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(mMotionEventList);
when(mKeyguardStateController.isShowing()).thenReturn(true);
mBrightLineFalsingManager = new BrightLineFalsingManager(mFalsingDataProvider,
- mMetricsLogger, mClassifiers, mSingleTapClassfier, mDoubleTapClassifier,
- mHistoryTracker, mKeyguardStateController, mAccessibilityManager,
- false);
+ mMetricsLogger, mClassifiers, mSingleTapClassfier, mLongTapClassifier,
+ mDoubleTapClassifier, mHistoryTracker, mKeyguardStateController,
+ mAccessibilityManager, false, mFakeFeatureFlags);
ArgumentCaptor<GestureFinalizedListener> gestureCompleteListenerCaptor =
@@ -113,6 +119,7 @@
gestureCompleteListenerCaptor.capture());
mGestureFinalizedListener = gestureCompleteListenerCaptor.getValue();
+ mFakeFeatureFlags.set(Flags.FALSING_FOR_LONG_TAPS, true);
}
@Test
@@ -212,7 +219,7 @@
}
@Test
- public void testIsFalseTap_EmptyRecentEvents() {
+ public void testIsFalseSingleTap_EmptyRecentEvents() {
// Ensure we look at prior events if recent events has already been emptied.
when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(new ArrayList<>());
when(mFalsingDataProvider.getPriorMotionEvents()).thenReturn(mMotionEventList);
@@ -223,7 +230,7 @@
@Test
- public void testIsFalseTap_RobustCheck_NoFaceAuth() {
+ public void testIsFalseSingleTap_RobustCheck_NoFaceAuth() {
when(mSingleTapClassfier.isTap(mMotionEventList, 0)).thenReturn(mPassedResult);
when(mDoubleTapClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble()))
.thenReturn(mFalsedResult);
@@ -233,13 +240,50 @@
}
@Test
- public void testIsFalseTap_RobustCheck_FaceAuth() {
+ public void testIsFalseSingleTap_RobustCheck_FaceAuth() {
when(mSingleTapClassfier.isTap(mMotionEventList, 0)).thenReturn(mPassedResult);
when(mFalsingDataProvider.isJustUnlockedWithFace()).thenReturn(true);
assertThat(mBrightLineFalsingManager.isFalseTap(NO_PENALTY)).isFalse();
}
@Test
+ public void testIsFalseLongTap_EmptyRecentEvents() {
+ // Ensure we look at prior events if recent events has already been emptied.
+ when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(new ArrayList<>());
+ when(mFalsingDataProvider.getPriorMotionEvents()).thenReturn(mMotionEventList);
+
+ mBrightLineFalsingManager.isFalseLongTap(0);
+ verify(mLongTapClassifier).isTap(mMotionEventList, 0);
+ }
+
+ @Test
+ public void testIsFalseLongTap_FalseLongTap_NotFlagged() {
+ mFakeFeatureFlags.set(Flags.FALSING_FOR_LONG_TAPS, false);
+ when(mLongTapClassifier.isTap(mMotionEventList, 0)).thenReturn(mFalsedResult);
+ assertThat(mBrightLineFalsingManager.isFalseLongTap(NO_PENALTY)).isFalse();
+ }
+
+ @Test
+ public void testIsFalseLongTap_FalseLongTap() {
+ when(mLongTapClassifier.isTap(mMotionEventList, 0)).thenReturn(mFalsedResult);
+ assertThat(mBrightLineFalsingManager.isFalseLongTap(NO_PENALTY)).isTrue();
+ }
+
+ @Test
+ public void testIsFalseLongTap_RobustCheck_NoFaceAuth() {
+ when(mLongTapClassifier.isTap(mMotionEventList, 0)).thenReturn(mPassedResult);
+ when(mFalsingDataProvider.isJustUnlockedWithFace()).thenReturn(false);
+ assertThat(mBrightLineFalsingManager.isFalseLongTap(NO_PENALTY)).isFalse();
+ }
+
+ @Test
+ public void testIsFalseLongTap_RobustCheck_FaceAuth() {
+ when(mLongTapClassifier.isTap(mMotionEventList, 0)).thenReturn(mPassedResult);
+ when(mFalsingDataProvider.isJustUnlockedWithFace()).thenReturn(true);
+ assertThat(mBrightLineFalsingManager.isFalseLongTap(NO_PENALTY)).isFalse();
+ }
+
+ @Test
public void testIsFalseDoubleTap() {
when(mDoubleTapClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble()))
.thenReturn(mPassedResult);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
index b811aab..4281ee0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
@@ -32,6 +32,8 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.testing.FakeMetricsLogger;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -57,6 +59,8 @@
@Mock
private SingleTapClassifier mSingleTapClassifier;
@Mock
+ private LongTapClassifier mLongTapClassifier;
+ @Mock
private DoubleTapClassifier mDoubleTapClassifier;
@Mock
private FalsingClassifier mClassifierA;
@@ -71,6 +75,7 @@
private final FalsingClassifier.Result mPassedResult = FalsingClassifier.Result.passed(1);
private final FalsingClassifier.Result mFalsedResult =
FalsingClassifier.Result.falsed(1, getClass().getSimpleName(), "");
+ private final FakeFeatureFlags mFakeFeatureFlags = new FakeFeatureFlags();
@Before
public void setup() {
@@ -78,15 +83,17 @@
when(mClassifierA.classifyGesture(anyInt(), anyDouble(), anyDouble()))
.thenReturn(mFalsedResult);
when(mSingleTapClassifier.isTap(any(List.class), anyDouble())).thenReturn(mFalsedResult);
+ when(mLongTapClassifier.isTap(any(List.class), anyDouble())).thenReturn(mFalsedResult);
when(mDoubleTapClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble()))
.thenReturn(mFalsedResult);
mClassifiers.add(mClassifierA);
when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(mMotionEventList);
when(mKeyguardStateController.isShowing()).thenReturn(true);
mBrightLineFalsingManager = new BrightLineFalsingManager(mFalsingDataProvider,
- mMetricsLogger, mClassifiers, mSingleTapClassifier, mDoubleTapClassifier,
- mHistoryTracker, mKeyguardStateController, mAccessibilityManager,
- false);
+ mMetricsLogger, mClassifiers, mSingleTapClassifier, mLongTapClassifier,
+ mDoubleTapClassifier, mHistoryTracker, mKeyguardStateController,
+ mAccessibilityManager, false, mFakeFeatureFlags);
+ mFakeFeatureFlags.set(Flags.FALSING_FOR_LONG_TAPS, true);
}
@Test
@@ -105,6 +112,13 @@
@Test
+ public void testA11yDisablesLongTap() {
+ assertThat(mBrightLineFalsingManager.isFalseLongTap(1)).isTrue();
+ when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true);
+ assertThat(mBrightLineFalsingManager.isFalseLongTap(1)).isFalse();
+ }
+
+ @Test
public void testA11yDisablesDoubleTap() {
assertThat(mBrightLineFalsingManager.isFalseDoubleTap()).isTrue();
when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt
new file mode 100644
index 0000000..0b72a68
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt
@@ -0,0 +1,112 @@
+package com.android.systemui.controls.management
+
+import android.content.ComponentName
+import android.content.Intent
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.window.OnBackInvokedCallback
+import android.window.OnBackInvokedDispatcher
+import androidx.test.filters.SmallTest
+import androidx.test.rule.ActivityTestRule
+import androidx.test.runner.intercepting.SingleActivityFactory
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.controls.CustomIconCache
+import com.android.systemui.controls.controller.ControlsControllerImpl
+import com.android.systemui.controls.ui.ControlsUiController
+import java.util.concurrent.CountDownLatch
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class ControlsEditingActivityTest : SysuiTestCase() {
+ @Mock lateinit var controller: ControlsControllerImpl
+
+ @Mock lateinit var broadcastDispatcher: BroadcastDispatcher
+
+ @Mock lateinit var customIconCache: CustomIconCache
+
+ @Mock lateinit var uiController: ControlsUiController
+
+ private lateinit var controlsEditingActivity: ControlsEditingActivity_Factory
+ private var latch: CountDownLatch = CountDownLatch(1)
+
+ @Mock private lateinit var mockDispatcher: OnBackInvokedDispatcher
+ @Captor private lateinit var captureCallback: ArgumentCaptor<OnBackInvokedCallback>
+
+ @Rule
+ @JvmField
+ var activityRule =
+ ActivityTestRule(
+ object :
+ SingleActivityFactory<TestableControlsEditingActivity>(
+ TestableControlsEditingActivity::class.java
+ ) {
+ override fun create(intent: Intent?): TestableControlsEditingActivity {
+ return TestableControlsEditingActivity(
+ controller,
+ broadcastDispatcher,
+ customIconCache,
+ uiController,
+ mockDispatcher,
+ latch
+ )
+ }
+ },
+ false,
+ false
+ )
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ val intent = Intent()
+ intent.putExtra(ControlsEditingActivity.EXTRA_STRUCTURE, "TestTitle")
+ val cname = ComponentName("TestPackageName", "TestClassName")
+ intent.putExtra(Intent.EXTRA_COMPONENT_NAME, cname)
+ activityRule.launchActivity(intent)
+ }
+
+ @Test
+ fun testBackCallbackRegistrationAndUnregistration() {
+ // 1. ensure that launching the activity results in it registering a callback
+ verify(mockDispatcher)
+ .registerOnBackInvokedCallback(
+ ArgumentMatchers.eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
+ captureCallback.capture()
+ )
+ activityRule.finishActivity()
+ latch.await() // ensure activity is finished
+ // 2. ensure that when the activity is finished, it unregisters the same callback
+ verify(mockDispatcher).unregisterOnBackInvokedCallback(captureCallback.value)
+ }
+
+ public class TestableControlsEditingActivity(
+ private val controller: ControlsControllerImpl,
+ private val broadcastDispatcher: BroadcastDispatcher,
+ private val customIconCache: CustomIconCache,
+ private val uiController: ControlsUiController,
+ private val mockDispatcher: OnBackInvokedDispatcher,
+ private val latch: CountDownLatch
+ ) : ControlsEditingActivity(controller, broadcastDispatcher, customIconCache, uiController) {
+ override fun getOnBackInvokedDispatcher(): OnBackInvokedDispatcher {
+ return mockDispatcher
+ }
+
+ override fun onStop() {
+ super.onStop()
+ // ensures that test runner thread does not proceed until ui thread is done
+ latch.countDown()
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
new file mode 100644
index 0000000..4b0f7e6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
@@ -0,0 +1,122 @@
+package com.android.systemui.controls.management
+
+import android.content.Intent
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.window.OnBackInvokedCallback
+import android.window.OnBackInvokedDispatcher
+import androidx.test.filters.SmallTest
+import androidx.test.rule.ActivityTestRule
+import androidx.test.runner.intercepting.SingleActivityFactory
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.controls.controller.ControlsControllerImpl
+import com.android.systemui.controls.ui.ControlsUiController
+import com.android.systemui.dagger.qualifiers.Main
+import com.google.common.util.concurrent.MoreExecutors
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.Executor
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class ControlsFavoritingActivityTest : SysuiTestCase() {
+ @Main private val executor: Executor = MoreExecutors.directExecutor()
+
+ @Mock lateinit var controller: ControlsControllerImpl
+
+ @Mock lateinit var listingController: ControlsListingController
+
+ @Mock lateinit var broadcastDispatcher: BroadcastDispatcher
+
+ @Mock lateinit var uiController: ControlsUiController
+
+ private lateinit var controlsFavoritingActivity: ControlsFavoritingActivity_Factory
+ private var latch: CountDownLatch = CountDownLatch(1)
+
+ @Mock private lateinit var mockDispatcher: OnBackInvokedDispatcher
+ @Captor private lateinit var captureCallback: ArgumentCaptor<OnBackInvokedCallback>
+
+ @Rule
+ @JvmField
+ var activityRule =
+ ActivityTestRule(
+ object :
+ SingleActivityFactory<TestableControlsFavoritingActivity>(
+ TestableControlsFavoritingActivity::class.java
+ ) {
+ override fun create(intent: Intent?): TestableControlsFavoritingActivity {
+ return TestableControlsFavoritingActivity(
+ executor,
+ controller,
+ listingController,
+ broadcastDispatcher,
+ uiController,
+ mockDispatcher,
+ latch
+ )
+ }
+ },
+ false,
+ false
+ )
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ val intent = Intent()
+ intent.putExtra(ControlsFavoritingActivity.EXTRA_FROM_PROVIDER_SELECTOR, true)
+ activityRule.launchActivity(intent)
+ }
+
+ @Test
+ fun testBackCallbackRegistrationAndUnregistration() {
+ // 1. ensure that launching the activity results in it registering a callback
+ verify(mockDispatcher)
+ .registerOnBackInvokedCallback(
+ ArgumentMatchers.eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
+ captureCallback.capture()
+ )
+ activityRule.finishActivity()
+ latch.await() // ensure activity is finished
+ // 2. ensure that when the activity is finished, it unregisters the same callback
+ verify(mockDispatcher).unregisterOnBackInvokedCallback(captureCallback.value)
+ }
+
+ public class TestableControlsFavoritingActivity(
+ executor: Executor,
+ controller: ControlsControllerImpl,
+ listingController: ControlsListingController,
+ broadcastDispatcher: BroadcastDispatcher,
+ uiController: ControlsUiController,
+ private val mockDispatcher: OnBackInvokedDispatcher,
+ private val latch: CountDownLatch
+ ) :
+ ControlsFavoritingActivity(
+ executor,
+ controller,
+ listingController,
+ broadcastDispatcher,
+ uiController
+ ) {
+ override fun getOnBackInvokedDispatcher(): OnBackInvokedDispatcher {
+ return mockDispatcher
+ }
+
+ override fun onStop() {
+ super.onStop()
+ // ensures that test runner thread does not proceed until ui thread is done
+ latch.countDown()
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
index db41d8d..dedc723 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
@@ -16,27 +16,42 @@
package com.android.systemui.controls.management
+import android.Manifest
import android.content.ComponentName
import android.content.Context
import android.content.ContextWrapper
+import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
import android.content.pm.ServiceInfo
+import android.os.Bundle
import android.os.UserHandle
+import android.service.controls.ControlsProviderService
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.settingslib.applications.ServiceListing
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.ControlsServiceInfo
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags.USE_APP_PANELS
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argThat
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import org.junit.After
import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatcher
import org.mockito.Mock
-import org.mockito.Mockito
import org.mockito.Mockito.`when`
import org.mockito.Mockito.inOrder
import org.mockito.Mockito.mock
@@ -51,10 +66,8 @@
class ControlsListingControllerImplTest : SysuiTestCase() {
companion object {
- private const val TEST_LABEL = "TEST_LABEL"
- private const val TEST_PERMISSION = "permission"
- fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
- fun <T> any(): T = Mockito.any<T>()
+ private const val FLAGS = PackageManager.MATCH_DIRECT_BOOT_AWARE.toLong() or
+ PackageManager.MATCH_DIRECT_BOOT_UNAWARE.toLong()
}
@Mock
@@ -63,15 +76,17 @@
private lateinit var mockCallback: ControlsListingController.ControlsListingCallback
@Mock
private lateinit var mockCallbackOther: ControlsListingController.ControlsListingCallback
- @Mock
- private lateinit var serviceInfo: ServiceInfo
- @Mock
- private lateinit var serviceInfo2: ServiceInfo
@Mock(stubOnly = true)
private lateinit var userTracker: UserTracker
+ @Mock(stubOnly = true)
+ private lateinit var dumpManager: DumpManager
+ @Mock
+ private lateinit var packageManager: PackageManager
+ @Mock
+ private lateinit var featureFlags: FeatureFlags
- private var componentName = ComponentName("pkg1", "class1")
- private var componentName2 = ComponentName("pkg2", "class2")
+ private var componentName = ComponentName("pkg", "class1")
+ private var activityName = ComponentName("pkg", "activity")
private val executor = FakeExecutor(FakeSystemClock())
@@ -87,9 +102,15 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
- `when`(serviceInfo.componentName).thenReturn(componentName)
- `when`(serviceInfo2.componentName).thenReturn(componentName2)
`when`(userTracker.userId).thenReturn(user)
+ `when`(userTracker.userContext).thenReturn(context)
+ // Return disabled by default
+ `when`(packageManager.getComponentEnabledSetting(any()))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED)
+ mContext.setMockPackageManager(packageManager)
+
+ // Return true by default, we'll test the false path
+ `when`(featureFlags.isEnabled(USE_APP_PANELS)).thenReturn(true)
val wrapper = object : ContextWrapper(mContext) {
override fun createContextAsUser(user: UserHandle, flags: Int): Context {
@@ -97,7 +118,14 @@
}
}
- controller = ControlsListingControllerImpl(wrapper, executor, { mockSL }, userTracker)
+ controller = ControlsListingControllerImpl(
+ wrapper,
+ executor,
+ { mockSL },
+ userTracker,
+ dumpManager,
+ featureFlags
+ )
verify(mockSL).addCallback(capture(serviceListingCallbackCaptor))
}
@@ -123,9 +151,16 @@
Unit
}
`when`(mockServiceListing.reload()).then {
- callback?.onServicesReloaded(listOf(serviceInfo))
+ callback?.onServicesReloaded(listOf(ServiceInfo(componentName)))
}
- ControlsListingControllerImpl(mContext, exec, { mockServiceListing }, userTracker)
+ ControlsListingControllerImpl(
+ mContext,
+ exec,
+ { mockServiceListing },
+ userTracker,
+ dumpManager,
+ featureFlags
+ )
}
@Test
@@ -148,7 +183,7 @@
@Test
fun testCallbackGetsList() {
- val list = listOf(serviceInfo)
+ val list = listOf(ServiceInfo(componentName))
controller.addCallback(mockCallback)
controller.addCallback(mockCallbackOther)
@@ -188,6 +223,8 @@
@Test
fun testChangeUserSendsCorrectServiceUpdate() {
+ val serviceInfo = ServiceInfo(componentName)
+
val list = listOf(serviceInfo)
controller.addCallback(mockCallback)
@@ -223,4 +260,284 @@
verify(mockCallback).onServicesUpdated(capture(captor))
assertEquals(0, captor.value.size)
}
+
+ @Test
+ fun test_nullPanelActivity() {
+ val list = listOf(ServiceInfo(componentName))
+ serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+ executor.runAllReady()
+
+ assertNull(controller.getCurrentServices()[0].panelActivity)
+ }
+
+ @Test
+ fun testNoActivity_nullPanel() {
+ val serviceInfo = ServiceInfo(
+ componentName,
+ activityName
+ )
+
+ val list = listOf(serviceInfo)
+ serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+ executor.runAllReady()
+
+ assertNull(controller.getCurrentServices()[0].panelActivity)
+ }
+
+ @Test
+ fun testActivityWithoutPermission_nullPanel() {
+ val serviceInfo = ServiceInfo(
+ componentName,
+ activityName
+ )
+
+ setUpQueryResult(listOf(ActivityInfo(activityName)))
+
+ val list = listOf(serviceInfo)
+ serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+ executor.runAllReady()
+
+ assertNull(controller.getCurrentServices()[0].panelActivity)
+ }
+
+ @Test
+ fun testActivityPermissionNotExported_nullPanel() {
+ val serviceInfo = ServiceInfo(
+ componentName,
+ activityName
+ )
+
+ setUpQueryResult(listOf(
+ ActivityInfo(activityName, permission = Manifest.permission.BIND_CONTROLS)
+ ))
+
+ val list = listOf(serviceInfo)
+ serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+ executor.runAllReady()
+
+ assertNull(controller.getCurrentServices()[0].panelActivity)
+ }
+
+ @Test
+ fun testActivityDisabled_nullPanel() {
+ val serviceInfo = ServiceInfo(
+ componentName,
+ activityName
+ )
+
+ setUpQueryResult(listOf(
+ ActivityInfo(
+ activityName,
+ exported = true,
+ permission = Manifest.permission.BIND_CONTROLS
+ )
+ ))
+
+ val list = listOf(serviceInfo)
+ serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+ executor.runAllReady()
+
+ assertNull(controller.getCurrentServices()[0].panelActivity)
+ }
+
+ @Test
+ fun testActivityEnabled_correctPanel() {
+ val serviceInfo = ServiceInfo(
+ componentName,
+ activityName
+ )
+
+ `when`(packageManager.getComponentEnabledSetting(eq(activityName)))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED)
+
+ setUpQueryResult(listOf(
+ ActivityInfo(
+ activityName,
+ exported = true,
+ permission = Manifest.permission.BIND_CONTROLS
+ )
+ ))
+
+ val list = listOf(serviceInfo)
+ serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+ executor.runAllReady()
+
+ assertEquals(activityName, controller.getCurrentServices()[0].panelActivity)
+ }
+
+ @Test
+ fun testActivityDefaultEnabled_correctPanel() {
+ val serviceInfo = ServiceInfo(
+ componentName,
+ activityName
+ )
+
+ `when`(packageManager.getComponentEnabledSetting(eq(activityName)))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
+
+ setUpQueryResult(listOf(
+ ActivityInfo(
+ activityName,
+ enabled = true,
+ exported = true,
+ permission = Manifest.permission.BIND_CONTROLS
+ )
+ ))
+
+ val list = listOf(serviceInfo)
+ serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+ executor.runAllReady()
+
+ assertEquals(activityName, controller.getCurrentServices()[0].panelActivity)
+ }
+
+ @Test
+ fun testActivityDefaultDisabled_nullPanel() {
+ val serviceInfo = ServiceInfo(
+ componentName,
+ activityName
+ )
+
+ `when`(packageManager.getComponentEnabledSetting(eq(activityName)))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
+
+ setUpQueryResult(listOf(
+ ActivityInfo(
+ activityName,
+ enabled = false,
+ exported = true,
+ permission = Manifest.permission.BIND_CONTROLS
+ )
+ ))
+
+ val list = listOf(serviceInfo)
+ serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+ executor.runAllReady()
+
+ assertNull(controller.getCurrentServices()[0].panelActivity)
+ }
+
+ @Test
+ fun testActivityDefaultEnabled_flagDisabled_nullPanel() {
+ `when`(featureFlags.isEnabled(USE_APP_PANELS)).thenReturn(false)
+ val serviceInfo = ServiceInfo(
+ componentName,
+ activityName,
+ )
+
+ `when`(packageManager.getComponentEnabledSetting(eq(activityName)))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
+
+ setUpQueryResult(listOf(
+ ActivityInfo(
+ activityName,
+ enabled = true,
+ exported = true,
+ permission = Manifest.permission.BIND_CONTROLS
+ )
+ ))
+
+ val list = listOf(serviceInfo)
+ serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+ executor.runAllReady()
+
+ assertNull(controller.getCurrentServices()[0].panelActivity)
+ }
+
+ @Test
+ fun testActivityDifferentPackage_nullPanel() {
+ val serviceInfo = ServiceInfo(
+ componentName,
+ ComponentName("other_package", "cls")
+ )
+
+ `when`(packageManager.getComponentEnabledSetting(eq(activityName)))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
+
+ setUpQueryResult(listOf(
+ ActivityInfo(
+ activityName,
+ enabled = true,
+ exported = true,
+ permission = Manifest.permission.BIND_CONTROLS
+ )
+ ))
+
+ val list = listOf(serviceInfo)
+ serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+ executor.runAllReady()
+
+ assertNull(controller.getCurrentServices()[0].panelActivity)
+ }
+
+ private fun ServiceInfo(
+ componentName: ComponentName,
+ panelActivityComponentName: ComponentName? = null
+ ): ServiceInfo {
+ return ServiceInfo().apply {
+ packageName = componentName.packageName
+ name = componentName.className
+ panelActivityComponentName?.let {
+ metaData = Bundle().apply {
+ putString(
+ ControlsProviderService.META_DATA_PANEL_ACTIVITY,
+ it.flattenToShortString()
+ )
+ }
+ }
+ }
+ }
+
+ private fun ActivityInfo(
+ componentName: ComponentName,
+ exported: Boolean = false,
+ enabled: Boolean = true,
+ permission: String? = null
+ ): ActivityInfo {
+ return ActivityInfo().apply {
+ packageName = componentName.packageName
+ name = componentName.className
+ this.permission = permission
+ this.exported = exported
+ this.enabled = enabled
+ }
+ }
+
+ private fun setUpQueryResult(infos: List<ActivityInfo>) {
+ `when`(
+ packageManager.queryIntentActivitiesAsUser(
+ argThat(IntentMatcher(activityName)),
+ argThat(FlagsMatcher(FLAGS)),
+ eq(UserHandle.of(user))
+ )
+ ).thenReturn(infos.map {
+ ResolveInfo().apply { activityInfo = it }
+ })
+ }
+
+ private class IntentMatcher(
+ private val componentName: ComponentName
+ ) : ArgumentMatcher<Intent> {
+ override fun matches(argument: Intent?): Boolean {
+ return argument?.component == componentName
+ }
+ }
+
+ private class FlagsMatcher(
+ private val flags: Long
+ ) : ArgumentMatcher<PackageManager.ResolveInfoFlags> {
+ override fun matches(argument: PackageManager.ResolveInfoFlags?): Boolean {
+ return flags == argument?.value
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt
new file mode 100644
index 0000000..acc6222
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.management
+
+import android.content.Intent
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.window.OnBackInvokedCallback
+import android.window.OnBackInvokedDispatcher
+import androidx.test.filters.SmallTest
+import androidx.test.rule.ActivityTestRule
+import androidx.test.runner.intercepting.SingleActivityFactory
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.controls.controller.ControlsController
+import com.android.systemui.controls.ui.ControlsUiController
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.google.common.util.concurrent.MoreExecutors
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.Executor
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class ControlsProviderSelectorActivityTest : SysuiTestCase() {
+ @Main private val executor: Executor = MoreExecutors.directExecutor()
+
+ @Background private val backExecutor: Executor = MoreExecutors.directExecutor()
+
+ @Mock lateinit var listingController: ControlsListingController
+
+ @Mock lateinit var controlsController: ControlsController
+
+ @Mock lateinit var broadcastDispatcher: BroadcastDispatcher
+
+ @Mock lateinit var uiController: ControlsUiController
+
+ private lateinit var controlsProviderSelectorActivity: ControlsProviderSelectorActivity_Factory
+ private var latch: CountDownLatch = CountDownLatch(1)
+
+ @Mock private lateinit var mockDispatcher: OnBackInvokedDispatcher
+ @Captor private lateinit var captureCallback: ArgumentCaptor<OnBackInvokedCallback>
+
+ @Rule
+ @JvmField
+ var activityRule =
+ ActivityTestRule(
+ object :
+ SingleActivityFactory<TestableControlsProviderSelectorActivity>(
+ TestableControlsProviderSelectorActivity::class.java
+ ) {
+ override fun create(intent: Intent?): TestableControlsProviderSelectorActivity {
+ return TestableControlsProviderSelectorActivity(
+ executor,
+ backExecutor,
+ listingController,
+ controlsController,
+ broadcastDispatcher,
+ uiController,
+ mockDispatcher,
+ latch
+ )
+ }
+ },
+ false,
+ false
+ )
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ val intent = Intent()
+ intent.putExtra(ControlsProviderSelectorActivity.BACK_SHOULD_EXIT, true)
+ activityRule.launchActivity(intent)
+ }
+
+ @Test
+ fun testBackCallbackRegistrationAndUnregistration() {
+ // 1. ensure that launching the activity results in it registering a callback
+ verify(mockDispatcher)
+ .registerOnBackInvokedCallback(
+ ArgumentMatchers.eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
+ captureCallback.capture()
+ )
+ activityRule.finishActivity()
+ latch.await() // ensure activity is finished
+ // 2. ensure that when the activity is finished, it unregisters the same callback
+ verify(mockDispatcher).unregisterOnBackInvokedCallback(captureCallback.value)
+ }
+
+ public class TestableControlsProviderSelectorActivity(
+ executor: Executor,
+ backExecutor: Executor,
+ listingController: ControlsListingController,
+ controlsController: ControlsController,
+ broadcastDispatcher: BroadcastDispatcher,
+ uiController: ControlsUiController,
+ private val mockDispatcher: OnBackInvokedDispatcher,
+ private val latch: CountDownLatch
+ ) :
+ ControlsProviderSelectorActivity(
+ executor,
+ backExecutor,
+ listingController,
+ controlsController,
+ broadcastDispatcher,
+ uiController
+ ) {
+ override fun getOnBackInvokedDispatcher(): OnBackInvokedDispatcher {
+ return mockDispatcher
+ }
+
+ override fun onStop() {
+ super.onStop()
+ // ensures that test runner thread does not proceed until ui thread is done
+ latch.countDown()
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index c234178..517804d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -36,10 +36,10 @@
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dreams.complication.ComplicationHostViewController;
-import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor;
import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
-import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback;
+import com.android.systemui.statusbar.phone.KeyguardBouncer.PrimaryBouncerExpansionCallback;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import org.junit.Before;
@@ -90,7 +90,7 @@
ViewRootImpl mViewRoot;
@Mock
- BouncerCallbackInteractor mBouncerCallbackInteractor;
+ PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor;
@Mock
DreamOverlayAnimationsController mAnimationsController;
@@ -106,7 +106,7 @@
when(mDreamOverlayContainerView.getResources()).thenReturn(mResources);
when(mDreamOverlayContainerView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
- when(mStatusBarKeyguardViewManager.getBouncer()).thenReturn(mBouncer);
+ when(mStatusBarKeyguardViewManager.getPrimaryBouncer()).thenReturn(mBouncer);
when(mDreamOverlayContainerView.getViewRootImpl()).thenReturn(mViewRoot);
mController = new DreamOverlayContainerViewController(
@@ -121,7 +121,7 @@
MAX_BURN_IN_OFFSET,
BURN_IN_PROTECTION_UPDATE_INTERVAL,
MILLIS_UNTIL_FULL_JITTER,
- mBouncerCallbackInteractor,
+ mPrimaryBouncerCallbackInteractor,
mAnimationsController,
mStateController);
}
@@ -167,8 +167,8 @@
@Test
public void testBouncerAnimation_doesNotApply() {
- final ArgumentCaptor<BouncerExpansionCallback> bouncerExpansionCaptor =
- ArgumentCaptor.forClass(BouncerExpansionCallback.class);
+ final ArgumentCaptor<PrimaryBouncerExpansionCallback> bouncerExpansionCaptor =
+ ArgumentCaptor.forClass(PrimaryBouncerExpansionCallback.class);
mController.onViewAttached();
verify(mBouncer).addBouncerExpansionCallback(bouncerExpansionCaptor.capture());
@@ -178,8 +178,8 @@
@Test
public void testBouncerAnimation_updateBlur() {
- final ArgumentCaptor<BouncerExpansionCallback> bouncerExpansionCaptor =
- ArgumentCaptor.forClass(BouncerExpansionCallback.class);
+ final ArgumentCaptor<PrimaryBouncerExpansionCallback> bouncerExpansionCaptor =
+ ArgumentCaptor.forClass(PrimaryBouncerExpansionCallback.class);
mController.onViewAttached();
verify(mBouncer).addBouncerExpansionCallback(bouncerExpansionCaptor.capture());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
index 849ac5e..7a2ba95 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
@@ -347,21 +347,22 @@
addComplication(engine, thirdViewInfo);
- // The first added view should now be underneath the second view.
+ // The first added view should now be underneath the third view.
verifyChange(firstViewInfo, false, lp -> {
assertThat(lp.topToBottom == thirdViewInfo.view.getId()).isTrue();
assertThat(lp.endToEnd == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
assertThat(lp.topMargin).isEqualTo(margin);
});
- // The second view should be in underneath the third view.
+ // The second view should be to the start of the third view.
verifyChange(secondViewInfo, false, lp -> {
assertThat(lp.endToStart == thirdViewInfo.view.getId()).isTrue();
assertThat(lp.topToTop == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
assertThat(lp.getMarginEnd()).isEqualTo(margin);
});
- // The third view should be in at the top.
+ // The third view should be at the top end corner. No margin should be applied if not
+ // specified.
verifyChange(thirdViewInfo, true, lp -> {
assertThat(lp.topToTop == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
assertThat(lp.endToEnd == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
@@ -425,14 +426,14 @@
addComplication(engine, thirdViewInfo);
- // The first added view should now be underneath the second view.
+ // The first added view should now be underneath the third view.
verifyChange(firstViewInfo, false, lp -> {
assertThat(lp.topToBottom == thirdViewInfo.view.getId()).isTrue();
assertThat(lp.endToEnd == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
assertThat(lp.topMargin).isEqualTo(complicationMargin);
});
- // The second view should be in underneath the third view.
+ // The second view should be to the start of the third view.
verifyChange(secondViewInfo, false, lp -> {
assertThat(lp.endToStart == thirdViewInfo.view.getId()).isTrue();
assertThat(lp.topToTop == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
@@ -441,6 +442,69 @@
}
/**
+ * Ensures the root complication applies margin if specified.
+ */
+ @Test
+ public void testRootComplicationSpecifiedMargin() {
+ final int defaultMargin = 5;
+ final int complicationMargin = 10;
+ final ComplicationLayoutEngine engine =
+ new ComplicationLayoutEngine(mLayout, defaultMargin, mTouchSession, 0, 0);
+
+ final ViewInfo firstViewInfo = new ViewInfo(
+ new ComplicationLayoutParams(
+ 100,
+ 100,
+ ComplicationLayoutParams.POSITION_TOP
+ | ComplicationLayoutParams.POSITION_END,
+ ComplicationLayoutParams.DIRECTION_DOWN,
+ 0),
+ Complication.CATEGORY_STANDARD,
+ mLayout);
+
+ addComplication(engine, firstViewInfo);
+
+ final ViewInfo secondViewInfo = new ViewInfo(
+ new ComplicationLayoutParams(
+ 100,
+ 100,
+ ComplicationLayoutParams.POSITION_TOP
+ | ComplicationLayoutParams.POSITION_END,
+ ComplicationLayoutParams.DIRECTION_START,
+ 0),
+ Complication.CATEGORY_SYSTEM,
+ mLayout);
+
+ addComplication(engine, secondViewInfo);
+
+ firstViewInfo.clearInvocations();
+ secondViewInfo.clearInvocations();
+
+ final ViewInfo thirdViewInfo = new ViewInfo(
+ new ComplicationLayoutParams(
+ 100,
+ 100,
+ ComplicationLayoutParams.POSITION_TOP
+ | ComplicationLayoutParams.POSITION_END,
+ ComplicationLayoutParams.DIRECTION_START,
+ 1,
+ complicationMargin),
+ Complication.CATEGORY_SYSTEM,
+ mLayout);
+
+ addComplication(engine, thirdViewInfo);
+
+ // The third view is the root view and has specified margin, which should be applied based
+ // on its direction.
+ verifyChange(thirdViewInfo, true, lp -> {
+ assertThat(lp.getMarginStart()).isEqualTo(0);
+ assertThat(lp.getMarginEnd()).isEqualTo(complicationMargin);
+ assertThat(lp.topMargin).isEqualTo(0);
+ assertThat(lp.bottomMargin).isEqualTo(0);
+ });
+ }
+
+ /**
* Ensures layout in a particular position updates.
*/
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java
index cb7e47b..ce7561e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java
@@ -97,6 +97,31 @@
}
/**
+ * Ensures ComplicationLayoutParams correctly returns whether the complication specified margin.
+ */
+ @Test
+ public void testIsMarginSpecified() {
+ final ComplicationLayoutParams paramsNoMargin = new ComplicationLayoutParams(
+ 100,
+ 100,
+ ComplicationLayoutParams.POSITION_TOP
+ | ComplicationLayoutParams.POSITION_START,
+ ComplicationLayoutParams.DIRECTION_DOWN,
+ 0);
+ assertThat(paramsNoMargin.isMarginSpecified()).isFalse();
+
+ final ComplicationLayoutParams paramsWithMargin = new ComplicationLayoutParams(
+ 100,
+ 100,
+ ComplicationLayoutParams.POSITION_TOP
+ | ComplicationLayoutParams.POSITION_START,
+ ComplicationLayoutParams.DIRECTION_DOWN,
+ 0,
+ 20 /*margin*/);
+ assertThat(paramsWithMargin.isMarginSpecified()).isTrue();
+ }
+
+ /**
* Ensures unspecified margin uses default.
*/
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
index 318f2bc..170a70f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
@@ -20,7 +20,6 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
-import java.lang.IllegalStateException
import org.junit.Assert.fail
import org.junit.Test
import org.junit.runner.RunWith
@@ -29,12 +28,12 @@
@RunWith(AndroidTestingRunner::class)
class FakeFeatureFlagsTest : SysuiTestCase() {
- private val unreleasedFlag = UnreleasedFlag(-1000)
- private val releasedFlag = ReleasedFlag(-1001)
- private val stringFlag = StringFlag(-1002)
- private val resourceBooleanFlag = ResourceBooleanFlag(-1003, resourceId = -1)
- private val resourceStringFlag = ResourceStringFlag(-1004, resourceId = -1)
- private val sysPropBooleanFlag = SysPropBooleanFlag(-1005, name = "test")
+ private val unreleasedFlag = UnreleasedFlag(-1000, "-1000", "test")
+ private val releasedFlag = ReleasedFlag(-1001, "-1001", "test")
+ private val stringFlag = StringFlag(-1002, "-1002", "test")
+ private val resourceBooleanFlag = ResourceBooleanFlag(-1003, "-1003", "test", resourceId = -1)
+ private val resourceStringFlag = ResourceStringFlag(-1004, "-1004", "test", resourceId = -1)
+ private val sysPropBooleanFlag = SysPropBooleanFlag(-1005, "test", "test")
/**
* FakeFeatureFlags does not honor any default values. All flags which are accessed must be
@@ -47,7 +46,7 @@
assertThat(flags.isEnabled(Flags.TEAMFOOD)).isFalse()
fail("Expected an exception when accessing an unspecified flag.")
} catch (ex: IllegalStateException) {
- assertThat(ex.message).contains("TEAMFOOD")
+ assertThat(ex.message).contains("id=1")
}
try {
assertThat(flags.isEnabled(unreleasedFlag)).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
index 4b3b70e..7592cc5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
@@ -20,6 +20,7 @@
import android.content.Intent
import android.content.pm.PackageManager.NameNotFoundException
import android.content.res.Resources
+import android.content.res.Resources.NotFoundException
import android.test.suitebuilder.annotation.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.commandline.CommandRegistry
@@ -30,10 +31,6 @@
import com.android.systemui.util.mockito.withArgCaptor
import com.android.systemui.util.settings.SecureSettings
import com.google.common.truth.Truth.assertThat
-import java.io.PrintWriter
-import java.io.Serializable
-import java.io.StringWriter
-import java.util.function.Consumer
import org.junit.Assert
import org.junit.Before
import org.junit.Test
@@ -45,8 +42,12 @@
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import java.io.PrintWriter
+import java.io.Serializable
+import java.io.StringWriter
+import java.util.function.Consumer
+import org.mockito.Mockito.`when` as whenever
/**
* NOTE: This test is for the version of FeatureFlagManager in src-debug, which allows overriding
@@ -56,21 +57,32 @@
class FeatureFlagsDebugTest : SysuiTestCase() {
private lateinit var mFeatureFlagsDebug: FeatureFlagsDebug
- @Mock private lateinit var flagManager: FlagManager
- @Mock private lateinit var mockContext: Context
- @Mock private lateinit var secureSettings: SecureSettings
- @Mock private lateinit var systemProperties: SystemPropertiesHelper
- @Mock private lateinit var resources: Resources
- @Mock private lateinit var commandRegistry: CommandRegistry
- @Mock private lateinit var restarter: Restarter
+ @Mock
+ private lateinit var flagManager: FlagManager
+ @Mock
+ private lateinit var mockContext: Context
+ @Mock
+ private lateinit var secureSettings: SecureSettings
+ @Mock
+ private lateinit var systemProperties: SystemPropertiesHelper
+ @Mock
+ private lateinit var resources: Resources
+ @Mock
+ private lateinit var commandRegistry: CommandRegistry
+ @Mock
+ private lateinit var restarter: Restarter
private val flagMap = mutableMapOf<Int, Flag<*>>()
private lateinit var broadcastReceiver: BroadcastReceiver
private lateinit var clearCacheAction: Consumer<Int>
private val serverFlagReader = ServerFlagReaderFake()
private val deviceConfig = DeviceConfigProxyFake()
- private val teamfoodableFlagA = UnreleasedFlag(500, true)
- private val teamfoodableFlagB = ReleasedFlag(501, true)
+ private val teamfoodableFlagA = UnreleasedFlag(
+ 500, name = "a", namespace = "test", teamfood = true
+ )
+ private val teamfoodableFlagB = ReleasedFlag(
+ 501, name = "b", namespace = "test", teamfood = true
+ )
@Before
fun setup() {
@@ -83,7 +95,6 @@
secureSettings,
systemProperties,
resources,
- deviceConfig,
serverFlagReader,
flagMap,
restarter
@@ -91,8 +102,10 @@
mFeatureFlagsDebug.init()
verify(flagManager).onSettingsChangedAction = any()
broadcastReceiver = withArgCaptor {
- verify(mockContext).registerReceiver(capture(), any(), nullable(), nullable(),
- any())
+ verify(mockContext).registerReceiver(
+ capture(), any(), nullable(), nullable(),
+ any()
+ )
}
clearCacheAction = withArgCaptor {
verify(flagManager).clearCacheAction = capture()
@@ -106,10 +119,42 @@
whenever(flagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true)
whenever(flagManager.readFlagValue<Boolean>(eq(4), any())).thenReturn(false)
- assertThat(mFeatureFlagsDebug.isEnabled(ReleasedFlag(2))).isTrue()
- assertThat(mFeatureFlagsDebug.isEnabled(UnreleasedFlag(3))).isTrue()
- assertThat(mFeatureFlagsDebug.isEnabled(ReleasedFlag(4))).isFalse()
- assertThat(mFeatureFlagsDebug.isEnabled(UnreleasedFlag(5))).isFalse()
+ assertThat(
+ mFeatureFlagsDebug.isEnabled(
+ ReleasedFlag(
+ 2,
+ name = "2",
+ namespace = "test"
+ )
+ )
+ ).isTrue()
+ assertThat(
+ mFeatureFlagsDebug.isEnabled(
+ UnreleasedFlag(
+ 3,
+ name = "3",
+ namespace = "test"
+ )
+ )
+ ).isTrue()
+ assertThat(
+ mFeatureFlagsDebug.isEnabled(
+ ReleasedFlag(
+ 4,
+ name = "3",
+ namespace = "test"
+ )
+ )
+ ).isFalse()
+ assertThat(
+ mFeatureFlagsDebug.isEnabled(
+ UnreleasedFlag(
+ 5,
+ name = "4",
+ namespace = "test"
+ )
+ )
+ ).isFalse()
}
@Test
@@ -137,9 +182,9 @@
@Test
fun teamFoodFlag_Overridden() {
whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagA.id), any()))
- .thenReturn(true)
+ .thenReturn(true)
whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagB.id), any()))
- .thenReturn(false)
+ .thenReturn(false)
whenever(flagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(true)
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagA)).isTrue()
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagB)).isFalse()
@@ -160,17 +205,26 @@
whenever(flagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true)
whenever(flagManager.readFlagValue<Boolean>(eq(5), any())).thenReturn(false)
- assertThat(mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(1, 1001))).isFalse()
- assertThat(mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(2, 1002))).isTrue()
- assertThat(mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(3, 1003))).isTrue()
+ assertThat(
+ mFeatureFlagsDebug.isEnabled(
+ ResourceBooleanFlag(
+ 1,
+ "1",
+ "test",
+ 1001
+ )
+ )
+ ).isFalse()
+ assertThat(mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(2, "2", "test", 1002))).isTrue()
+ assertThat(mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(3, "3", "test", 1003))).isTrue()
Assert.assertThrows(NameNotFoundException::class.java) {
- mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(4, 1004))
+ mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(4, "4", "test", 1004))
}
// Test that resource is loaded (and validated) even when the setting is set.
// This prevents developers from not noticing when they reference an invalid resource.
Assert.assertThrows(NameNotFoundException::class.java) {
- mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(5, 1005))
+ mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(5, "5", "test", 1005))
}
}
@@ -183,36 +237,30 @@
return@thenAnswer it.getArgument(1)
}
- assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(1, "a"))).isFalse()
- assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(2, "b"))).isTrue()
- assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(3, "c", true))).isTrue()
- assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(4, "d", false))).isFalse()
- assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(5, "e"))).isFalse()
- }
-
- @Test
- fun readDeviceConfigBooleanFlag() {
- val namespace = "test_namespace"
- deviceConfig.setProperty(namespace, "a", "true", false)
- deviceConfig.setProperty(namespace, "b", "false", false)
- deviceConfig.setProperty(namespace, "c", null, false)
-
- assertThat(mFeatureFlagsDebug.isEnabled(DeviceConfigBooleanFlag(1, "a", namespace)))
- .isTrue()
- assertThat(mFeatureFlagsDebug.isEnabled(DeviceConfigBooleanFlag(2, "b", namespace)))
- .isFalse()
- assertThat(mFeatureFlagsDebug.isEnabled(DeviceConfigBooleanFlag(3, "c", namespace)))
- .isFalse()
+ assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(1, "a", "test"))).isFalse()
+ assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(2, "b", "test"))).isTrue()
+ assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(3, "c", "test", true))).isTrue()
+ assertThat(
+ mFeatureFlagsDebug.isEnabled(
+ SysPropBooleanFlag(
+ 4,
+ "d",
+ "test",
+ false
+ )
+ )
+ ).isFalse()
+ assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(5, "e", "test"))).isFalse()
}
@Test
fun readStringFlag() {
whenever(flagManager.readFlagValue<String>(eq(3), any())).thenReturn("foo")
whenever(flagManager.readFlagValue<String>(eq(4), any())).thenReturn("bar")
- assertThat(mFeatureFlagsDebug.getString(StringFlag(1, "biz"))).isEqualTo("biz")
- assertThat(mFeatureFlagsDebug.getString(StringFlag(2, "baz"))).isEqualTo("baz")
- assertThat(mFeatureFlagsDebug.getString(StringFlag(3, "buz"))).isEqualTo("foo")
- assertThat(mFeatureFlagsDebug.getString(StringFlag(4, "buz"))).isEqualTo("bar")
+ assertThat(mFeatureFlagsDebug.getString(StringFlag(1, "1", "test", "biz"))).isEqualTo("biz")
+ assertThat(mFeatureFlagsDebug.getString(StringFlag(2, "2", "test", "baz"))).isEqualTo("baz")
+ assertThat(mFeatureFlagsDebug.getString(StringFlag(3, "3", "test", "buz"))).isEqualTo("foo")
+ assertThat(mFeatureFlagsDebug.getString(StringFlag(4, "4", "test", "buz"))).isEqualTo("bar")
}
@Test
@@ -228,29 +276,93 @@
whenever(flagManager.readFlagValue<String>(eq(4), any())).thenReturn("override4")
whenever(flagManager.readFlagValue<String>(eq(6), any())).thenReturn("override6")
- assertThat(mFeatureFlagsDebug.getString(ResourceStringFlag(1, 1001))).isEqualTo("")
- assertThat(mFeatureFlagsDebug.getString(ResourceStringFlag(2, 1002))).isEqualTo("resource2")
- assertThat(mFeatureFlagsDebug.getString(ResourceStringFlag(3, 1003))).isEqualTo("override3")
+ assertThat(
+ mFeatureFlagsDebug.getString(
+ ResourceStringFlag(
+ 1,
+ "1",
+ "test",
+ 1001
+ )
+ )
+ ).isEqualTo("")
+ assertThat(
+ mFeatureFlagsDebug.getString(
+ ResourceStringFlag(
+ 2,
+ "2",
+ "test",
+ 1002
+ )
+ )
+ ).isEqualTo("resource2")
+ assertThat(
+ mFeatureFlagsDebug.getString(
+ ResourceStringFlag(
+ 3,
+ "3",
+ "test",
+ 1003
+ )
+ )
+ ).isEqualTo("override3")
Assert.assertThrows(NullPointerException::class.java) {
- mFeatureFlagsDebug.getString(ResourceStringFlag(4, 1004))
+ mFeatureFlagsDebug.getString(ResourceStringFlag(4, "4", "test", 1004))
}
Assert.assertThrows(NameNotFoundException::class.java) {
- mFeatureFlagsDebug.getString(ResourceStringFlag(5, 1005))
+ mFeatureFlagsDebug.getString(ResourceStringFlag(5, "5", "test", 1005))
}
// Test that resource is loaded (and validated) even when the setting is set.
// This prevents developers from not noticing when they reference an invalid resource.
Assert.assertThrows(NameNotFoundException::class.java) {
- mFeatureFlagsDebug.getString(ResourceStringFlag(6, 1005))
+ mFeatureFlagsDebug.getString(ResourceStringFlag(6, "6", "test", 1005))
+ }
+ }
+
+ @Test
+ fun readIntFlag() {
+ whenever(flagManager.readFlagValue<Int>(eq(3), any())).thenReturn(22)
+ whenever(flagManager.readFlagValue<Int>(eq(4), any())).thenReturn(48)
+ assertThat(mFeatureFlagsDebug.getInt(IntFlag(1, "1", "test", 12))).isEqualTo(12)
+ assertThat(mFeatureFlagsDebug.getInt(IntFlag(2, "2", "test", 93))).isEqualTo(93)
+ assertThat(mFeatureFlagsDebug.getInt(IntFlag(3, "3", "test", 8))).isEqualTo(22)
+ assertThat(mFeatureFlagsDebug.getInt(IntFlag(4, "4", "test", 234))).isEqualTo(48)
+ }
+
+ @Test
+ fun readResourceIntFlag() {
+ whenever(resources.getInteger(1001)).thenReturn(88)
+ whenever(resources.getInteger(1002)).thenReturn(61)
+ whenever(resources.getInteger(1003)).thenReturn(9342)
+ whenever(resources.getInteger(1004)).thenThrow(NotFoundException("unknown resource"))
+ whenever(resources.getInteger(1005)).thenThrow(NotFoundException("unknown resource"))
+ whenever(resources.getInteger(1006)).thenThrow(NotFoundException("unknown resource"))
+
+ whenever(flagManager.readFlagValue<Int>(eq(3), any())).thenReturn(20)
+ whenever(flagManager.readFlagValue<Int>(eq(4), any())).thenReturn(500)
+ whenever(flagManager.readFlagValue<Int>(eq(5), any())).thenReturn(9519)
+
+ assertThat(mFeatureFlagsDebug.getInt(ResourceIntFlag(1, "1", "test", 1001))).isEqualTo(88)
+ assertThat(mFeatureFlagsDebug.getInt(ResourceIntFlag(2, "2", "test", 1002))).isEqualTo(61)
+ assertThat(mFeatureFlagsDebug.getInt(ResourceIntFlag(3, "3", "test", 1003))).isEqualTo(20)
+
+ Assert.assertThrows(NotFoundException::class.java) {
+ mFeatureFlagsDebug.getInt(ResourceIntFlag(4, "4", "test", 1004))
+ }
+ // Test that resource is loaded (and validated) even when the setting is set.
+ // This prevents developers from not noticing when they reference an invalid resource.
+ Assert.assertThrows(NotFoundException::class.java) {
+ mFeatureFlagsDebug.getInt(ResourceIntFlag(5, "5", "test", 1005))
}
}
@Test
fun broadcastReceiver_IgnoresInvalidData() {
- addFlag(UnreleasedFlag(1))
- addFlag(ResourceBooleanFlag(2, 1002))
- addFlag(StringFlag(3, "flag3"))
- addFlag(ResourceStringFlag(4, 1004))
+ addFlag(UnreleasedFlag(1, "1", "test"))
+ addFlag(ResourceBooleanFlag(2, "2", "test", 1002))
+ addFlag(StringFlag(3, "3", "test", "flag3"))
+ addFlag(ResourceStringFlag(4, "4", "test", 1004))
broadcastReceiver.onReceive(mockContext, null)
broadcastReceiver.onReceive(mockContext, Intent())
@@ -266,7 +378,7 @@
@Test
fun intentWithId_NoValueKeyClears() {
- addFlag(UnreleasedFlag(1))
+ addFlag(UnreleasedFlag(1, name = "1", namespace = "test"))
// trying to erase an id not in the map does nothing
broadcastReceiver.onReceive(
@@ -285,10 +397,10 @@
@Test
fun setBooleanFlag() {
- addFlag(UnreleasedFlag(1))
- addFlag(UnreleasedFlag(2))
- addFlag(ResourceBooleanFlag(3, 1003))
- addFlag(ResourceBooleanFlag(4, 1004))
+ addFlag(UnreleasedFlag(1, "1", "test"))
+ addFlag(UnreleasedFlag(2, "2", "test"))
+ addFlag(ResourceBooleanFlag(3, "3", "test", 1003))
+ addFlag(ResourceBooleanFlag(4, "4", "test", 1004))
setByBroadcast(1, false)
verifyPutData(1, "{\"type\":\"boolean\",\"value\":false}")
@@ -305,8 +417,8 @@
@Test
fun setStringFlag() {
- addFlag(StringFlag(1, "flag1"))
- addFlag(ResourceStringFlag(2, 1002))
+ addFlag(StringFlag(1, "flag1", "1", "test"))
+ addFlag(ResourceStringFlag(2, "2", "test", 1002))
setByBroadcast(1, "override1")
verifyPutData(1, "{\"type\":\"string\",\"value\":\"override1\"}")
@@ -317,7 +429,7 @@
@Test
fun setFlag_ClearsCache() {
- val flag1 = addFlag(StringFlag(1, "flag1"))
+ val flag1 = addFlag(StringFlag(1, "1", "test", "flag1"))
whenever(flagManager.readFlagValue<String>(eq(1), any())).thenReturn("original")
// gets the flag & cache it
@@ -339,31 +451,31 @@
@Test
fun serverSide_Overrides_MakesFalse() {
- val flag = ReleasedFlag(100)
+ val flag = ReleasedFlag(100, "100", "test")
- serverFlagReader.setFlagValue(flag.id, false)
+ serverFlagReader.setFlagValue(flag.namespace, flag.name, false)
assertThat(mFeatureFlagsDebug.isEnabled(flag)).isFalse()
}
@Test
fun serverSide_Overrides_MakesTrue() {
- val flag = UnreleasedFlag(100)
+ val flag = UnreleasedFlag(100, name = "100", namespace = "test")
- serverFlagReader.setFlagValue(flag.id, true)
+ serverFlagReader.setFlagValue(flag.namespace, flag.name, true)
assertThat(mFeatureFlagsDebug.isEnabled(flag)).isTrue()
}
@Test
fun dumpFormat() {
- val flag1 = ReleasedFlag(1)
- val flag2 = ResourceBooleanFlag(2, 1002)
- val flag3 = UnreleasedFlag(3)
- val flag4 = StringFlag(4, "")
- val flag5 = StringFlag(5, "flag5default")
- val flag6 = ResourceStringFlag(6, 1006)
- val flag7 = ResourceStringFlag(7, 1007)
+ val flag1 = ReleasedFlag(1, "1", "test")
+ val flag2 = ResourceBooleanFlag(2, "2", "test", 1002)
+ val flag3 = UnreleasedFlag(3, "3", "test")
+ val flag4 = StringFlag(4, "4", "test", "")
+ val flag5 = StringFlag(5, "5", "test", "flag5default")
+ val flag6 = ResourceStringFlag(6, "6", "test", 1006)
+ val flag7 = ResourceStringFlag(7, "7", "test", 1007)
whenever(resources.getBoolean(1002)).thenReturn(true)
whenever(resources.getString(1006)).thenReturn("resource1006")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
index b2dd60c..d5b5a4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
@@ -25,8 +25,8 @@
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
/**
* NOTE: This test is for the version of FeatureFlagManager in src-release, which should not allow
@@ -59,7 +59,9 @@
fun testBooleanResourceFlag() {
val flagId = 213
val flagResourceId = 3
- val flag = ResourceBooleanFlag(flagId, flagResourceId)
+ val flagName = "213"
+ val flagNamespace = "test"
+ val flag = ResourceBooleanFlag(flagId, flagName, flagNamespace, flagResourceId)
whenever(mResources.getBoolean(flagResourceId)).thenReturn(true)
assertThat(mFeatureFlagsRelease.isEnabled(flag)).isTrue()
}
@@ -71,57 +73,45 @@
whenever(mResources.getString(1003)).thenReturn(null)
whenever(mResources.getString(1004)).thenAnswer { throw NameNotFoundException() }
- assertThat(mFeatureFlagsRelease.getString(ResourceStringFlag(1, 1001))).isEqualTo("")
- assertThat(mFeatureFlagsRelease.getString(ResourceStringFlag(2, 1002))).isEqualTo("res2")
+ assertThat(mFeatureFlagsRelease.getString(
+ ResourceStringFlag(1, "1", "test", 1001))).isEqualTo("")
+ assertThat(mFeatureFlagsRelease.getString(
+ ResourceStringFlag(2, "2", "test", 1002))).isEqualTo("res2")
assertThrows(NullPointerException::class.java) {
- mFeatureFlagsRelease.getString(ResourceStringFlag(3, 1003))
+ mFeatureFlagsRelease.getString(ResourceStringFlag(3, "3", "test", 1003))
}
assertThrows(NameNotFoundException::class.java) {
- mFeatureFlagsRelease.getString(ResourceStringFlag(4, 1004))
+ mFeatureFlagsRelease.getString(ResourceStringFlag(4, "4", "test", 1004))
}
}
@Test
- fun testReadDeviceConfigBooleanFlag() {
- val namespace = "test_namespace"
- deviceConfig.setProperty(namespace, "a", "true", false)
- deviceConfig.setProperty(namespace, "b", "false", false)
- deviceConfig.setProperty(namespace, "c", null, false)
-
- assertThat(mFeatureFlagsRelease.isEnabled(DeviceConfigBooleanFlag(1, "a", namespace)))
- .isTrue()
- assertThat(mFeatureFlagsRelease.isEnabled(DeviceConfigBooleanFlag(2, "b", namespace)))
- .isFalse()
- assertThat(mFeatureFlagsRelease.isEnabled(DeviceConfigBooleanFlag(3, "c", namespace)))
- .isFalse()
- }
-
- @Test
fun testSysPropBooleanFlag() {
val flagId = 213
val flagName = "sys_prop_flag"
+ val flagNamespace = "test"
val flagDefault = true
- val flag = SysPropBooleanFlag(flagId, flagName, flagDefault)
+ val flag = SysPropBooleanFlag(flagId, flagName, flagNamespace, flagDefault)
whenever(mSystemProperties.getBoolean(flagName, flagDefault)).thenReturn(flagDefault)
assertThat(mFeatureFlagsRelease.isEnabled(flag)).isEqualTo(flagDefault)
}
@Test
fun serverSide_OverridesReleased_MakesFalse() {
- val flag = ReleasedFlag(100)
+ val flag = ReleasedFlag(100, "100", "test")
- serverFlagReader.setFlagValue(flag.id, false)
+ serverFlagReader.setFlagValue(flag.namespace, flag.name, false)
assertThat(mFeatureFlagsRelease.isEnabled(flag)).isFalse()
}
@Test
fun serverSide_OverridesUnreleased_Ignored() {
- val flag = UnreleasedFlag(100)
+ val flag = UnreleasedFlag(100, "100", "test")
- serverFlagReader.setFlagValue(flag.id, true)
+ serverFlagReader.setFlagValue(flag.namespace, flag.name, true)
assertThat(mFeatureFlagsRelease.isEnabled(flag)).isFalse()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagCommandTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagCommandTest.kt
index 9628ee9..fea91c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagCommandTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagCommandTest.kt
@@ -33,8 +33,10 @@
@Mock private lateinit var featureFlags: FeatureFlagsDebug
@Mock private lateinit var pw: PrintWriter
private val flagMap = mutableMapOf<Int, Flag<*>>()
- private val flagA = UnreleasedFlag(500)
- private val flagB = ReleasedFlag(501)
+ private val flagA = UnreleasedFlag(500, "500", "test")
+ private val flagB = ReleasedFlag(501, "501", "test")
+ private val stringFlag = StringFlag(502, "502", "test", "abracadabra")
+ private val intFlag = IntFlag(503, "503", "test", 12)
private lateinit var cmd: FlagCommand
@@ -44,26 +46,59 @@
whenever(featureFlags.isEnabled(any(UnreleasedFlag::class.java))).thenReturn(false)
whenever(featureFlags.isEnabled(any(ReleasedFlag::class.java))).thenReturn(true)
+ whenever(featureFlags.getString(any(StringFlag::class.java))).thenAnswer { invocation ->
+ (invocation.getArgument(0) as StringFlag).default
+ }
+ whenever(featureFlags.getInt(any(IntFlag::class.java))).thenAnswer { invocation ->
+ (invocation.getArgument(0) as IntFlag).default
+ }
+
flagMap.put(flagA.id, flagA)
flagMap.put(flagB.id, flagB)
+ flagMap.put(stringFlag.id, stringFlag)
+ flagMap.put(intFlag.id, intFlag)
cmd = FlagCommand(featureFlags, flagMap)
}
@Test
- fun readFlagCommand() {
+ fun readBooleanFlagCommand() {
cmd.execute(pw, listOf(flagA.id.toString()))
Mockito.verify(featureFlags).isEnabled(flagA)
}
@Test
- fun setFlagCommand() {
+ fun readStringFlagCommand() {
+ cmd.execute(pw, listOf(stringFlag.id.toString()))
+ Mockito.verify(featureFlags).getString(stringFlag)
+ }
+
+ @Test
+ fun readIntFlag() {
+ cmd.execute(pw, listOf(intFlag.id.toString()))
+ Mockito.verify(featureFlags).getInt(intFlag)
+ }
+
+ @Test
+ fun setBooleanFlagCommand() {
cmd.execute(pw, listOf(flagB.id.toString(), "on"))
Mockito.verify(featureFlags).setBooleanFlagInternal(flagB, true)
}
@Test
- fun toggleFlagCommand() {
+ fun setStringFlagCommand() {
+ cmd.execute(pw, listOf(stringFlag.id.toString(), "set", "foobar"))
+ Mockito.verify(featureFlags).setStringFlagInternal(stringFlag, "foobar")
+ }
+
+ @Test
+ fun setIntFlag() {
+ cmd.execute(pw, listOf(intFlag.id.toString(), "put", "123"))
+ Mockito.verify(featureFlags).setIntFlagInternal(intFlag, 123)
+ }
+
+ @Test
+ fun toggleBooleanFlagCommand() {
cmd.execute(pw, listOf(flagB.id.toString(), "toggle"))
Mockito.verify(featureFlags).setBooleanFlagInternal(flagB, false)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
index 17324a0..fca7e96 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
@@ -64,14 +64,14 @@
verifyNoMoreInteractions(mFlagSettingsHelper)
// adding the first listener registers the observer
- mFlagManager.addListener(ReleasedFlag(1), listener1)
+ mFlagManager.addListener(ReleasedFlag(1, "1", "test"), listener1)
val observer = withArgCaptor<ContentObserver> {
verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture())
}
verifyNoMoreInteractions(mFlagSettingsHelper)
// adding another listener does nothing
- mFlagManager.addListener(ReleasedFlag(2), listener2)
+ mFlagManager.addListener(ReleasedFlag(2, "2", "test"), listener2)
verifyNoMoreInteractions(mFlagSettingsHelper)
// removing the original listener does nothing with second one still present
@@ -89,7 +89,7 @@
val listener = mock<FlagListenable.Listener>()
val clearCacheAction = mock<Consumer<Int>>()
mFlagManager.clearCacheAction = clearCacheAction
- mFlagManager.addListener(ReleasedFlag(1), listener)
+ mFlagManager.addListener(ReleasedFlag(1, "1", "test"), listener)
val observer = withArgCaptor<ContentObserver> {
verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture())
}
@@ -101,8 +101,8 @@
fun testObserverInvokesListeners() {
val listener1 = mock<FlagListenable.Listener>()
val listener10 = mock<FlagListenable.Listener>()
- mFlagManager.addListener(ReleasedFlag(1), listener1)
- mFlagManager.addListener(ReleasedFlag(10), listener10)
+ mFlagManager.addListener(ReleasedFlag(1, "1", "test"), listener1)
+ mFlagManager.addListener(ReleasedFlag(10, "10", "test"), listener10)
val observer = withArgCaptor<ContentObserver> {
verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture())
}
@@ -127,8 +127,8 @@
fun testOnlySpecificFlagListenerIsInvoked() {
val listener1 = mock<FlagListenable.Listener>()
val listener10 = mock<FlagListenable.Listener>()
- mFlagManager.addListener(ReleasedFlag(1), listener1)
- mFlagManager.addListener(ReleasedFlag(10), listener10)
+ mFlagManager.addListener(ReleasedFlag(1, "1", "test"), listener1)
+ mFlagManager.addListener(ReleasedFlag(10, "10", "test"), listener10)
mFlagManager.dispatchListenersAndMaybeRestart(1, null)
val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> {
@@ -148,8 +148,8 @@
@Test
fun testSameListenerCanBeUsedForMultipleFlags() {
val listener = mock<FlagListenable.Listener>()
- mFlagManager.addListener(ReleasedFlag(1), listener)
- mFlagManager.addListener(ReleasedFlag(10), listener)
+ mFlagManager.addListener(ReleasedFlag(1, "1", "test"), listener)
+ mFlagManager.addListener(ReleasedFlag(10, "10", "test"), listener)
mFlagManager.dispatchListenersAndMaybeRestart(1, null)
val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> {
@@ -177,7 +177,7 @@
@Test
fun testListenerCanSuppressRestart() {
val restartAction = mock<Consumer<Boolean>>()
- mFlagManager.addListener(ReleasedFlag(1)) { event ->
+ mFlagManager.addListener(ReleasedFlag(1, "1", "test")) { event ->
event.requestNoRestart()
}
mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
@@ -188,7 +188,7 @@
@Test
fun testListenerOnlySuppressesRestartForOwnFlag() {
val restartAction = mock<Consumer<Boolean>>()
- mFlagManager.addListener(ReleasedFlag(10)) { event ->
+ mFlagManager.addListener(ReleasedFlag(10, "10", "test")) { event ->
event.requestNoRestart()
}
mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
@@ -199,10 +199,10 @@
@Test
fun testRestartWhenNotAllListenersRequestSuppress() {
val restartAction = mock<Consumer<Boolean>>()
- mFlagManager.addListener(ReleasedFlag(10)) { event ->
+ mFlagManager.addListener(ReleasedFlag(10, "10", "test")) { event ->
event.requestNoRestart()
}
- mFlagManager.addListener(ReleasedFlag(10)) {
+ mFlagManager.addListener(ReleasedFlag(10, "10", "test")) {
// do not request
}
mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java
deleted file mode 100644
index 250cc48..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java
+++ /dev/null
@@ -1,119 +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.flags;
-
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import android.util.Pair;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Test;
-
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-@SmallTest
-public class FlagsTest extends SysuiTestCase {
-
- @Test
- public void testDuplicateFlagIdCheckWorks() {
- List<Pair<String, Flag<?>>> flags = collectFlags(DuplicateFlagContainer.class);
- Map<Integer, List<String>> duplicates = groupDuplicateFlags(flags);
-
- assertWithMessage(generateAssertionMessage(duplicates))
- .that(duplicates.size()).isEqualTo(2);
- }
-
- @Test
- public void testNoDuplicateFlagIds() {
- List<Pair<String, Flag<?>>> flags = collectFlags(Flags.class);
- Map<Integer, List<String>> duplicates = groupDuplicateFlags(flags);
-
- assertWithMessage(generateAssertionMessage(duplicates))
- .that(duplicates.size()).isEqualTo(0);
- }
-
- private String generateAssertionMessage(Map<Integer, List<String>> duplicates) {
- StringBuilder stringBuilder = new StringBuilder();
- stringBuilder.append("Duplicate flag keys found: {");
- for (int id : duplicates.keySet()) {
- stringBuilder
- .append(" ")
- .append(id)
- .append(": [")
- .append(String.join(", ", duplicates.get(id)))
- .append("]");
- }
- stringBuilder.append(" }");
-
- return stringBuilder.toString();
- }
-
- private List<Pair<String, Flag<?>>> collectFlags(Class<?> clz) {
- List<Pair<String, Flag<?>>> flags = new ArrayList<>();
-
- Field[] fields = clz.getFields();
-
- for (Field field : fields) {
- Class<?> t = field.getType();
- if (Flag.class.isAssignableFrom(t)) {
- try {
- flags.add(Pair.create(field.getName(), (Flag<?>) field.get(null)));
- } catch (IllegalAccessException e) {
- // no-op
- }
- }
- }
-
- return flags;
- }
-
- private Map<Integer, List<String>> groupDuplicateFlags(List<Pair<String, Flag<?>>> flags) {
- Map<Integer, List<String>> grouping = new HashMap<>();
-
- for (Pair<String, Flag<?>> flag : flags) {
- grouping.putIfAbsent(flag.second.getId(), new ArrayList<>());
- grouping.get(flag.second.getId()).add(flag.first);
- }
-
- Map<Integer, List<String>> result = new HashMap<>();
- for (Integer id : grouping.keySet()) {
- if (grouping.get(id).size() > 1) {
- result.put(id, grouping.get(id));
- }
- }
-
- return result;
- }
-
- private static class DuplicateFlagContainer {
- public static final BooleanFlag A_FLAG = new UnreleasedFlag(0);
- public static final BooleanFlag B_FLAG = new UnreleasedFlag(0);
- public static final StringFlag C_FLAG = new StringFlag(0);
-
- public static final BooleanFlag D_FLAG = new UnreleasedFlag(1);
-
- public static final DoubleFlag E_FLAG = new DoubleFlag(3);
- public static final DoubleFlag F_FLAG = new DoubleFlag(3);
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt
index 6f5f460..1633912 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt
@@ -50,7 +50,7 @@
@Test
fun testChange_alertsListener() {
- val flag = ReleasedFlag(1)
+ val flag = ReleasedFlag(1, "1", "test")
serverFlagReader.listenForChanges(listOf(flag), changeListener)
deviceConfig.setProperty(NAMESPACE, "flag_override_1", "1", false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt
new file mode 100644
index 0000000..4d66a16
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard
+
+import android.content.pm.PackageManager
+import android.content.pm.ProviderInfo
+import androidx.test.filters.SmallTest
+import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.SystemUIAppComponentFactoryBase
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.shared.keyguard.data.content.KeyguardQuickAffordanceProviderClient as Client
+import com.android.systemui.shared.keyguard.data.content.KeyguardQuickAffordanceProviderContract as Contract
+import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+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.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
+
+ @Mock private lateinit var lockPatternUtils: LockPatternUtils
+ @Mock private lateinit var keyguardStateController: KeyguardStateController
+ @Mock private lateinit var userTracker: UserTracker
+ @Mock private lateinit var activityStarter: ActivityStarter
+
+ private lateinit var underTest: KeyguardQuickAffordanceProvider
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ underTest = KeyguardQuickAffordanceProvider()
+ val quickAffordanceRepository =
+ KeyguardQuickAffordanceRepository(
+ scope = CoroutineScope(IMMEDIATE),
+ backgroundDispatcher = IMMEDIATE,
+ selectionManager = KeyguardQuickAffordanceSelectionManager(),
+ configs =
+ setOf(
+ FakeKeyguardQuickAffordanceConfig(
+ key = AFFORDANCE_1,
+ pickerIconResourceId = 1,
+ ),
+ FakeKeyguardQuickAffordanceConfig(
+ key = AFFORDANCE_2,
+ pickerIconResourceId = 2,
+ ),
+ ),
+ )
+ underTest.interactor =
+ KeyguardQuickAffordanceInteractor(
+ keyguardInteractor =
+ KeyguardInteractor(
+ repository = FakeKeyguardRepository(),
+ ),
+ registry = mock(),
+ lockPatternUtils = lockPatternUtils,
+ keyguardStateController = keyguardStateController,
+ userTracker = userTracker,
+ activityStarter = activityStarter,
+ featureFlags =
+ FakeFeatureFlags().apply {
+ set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, true)
+ },
+ repository = { quickAffordanceRepository },
+ )
+
+ underTest.attachInfoForTesting(
+ context,
+ ProviderInfo().apply { authority = Contract.AUTHORITY },
+ )
+ context.contentResolver.addProvider(Contract.AUTHORITY, underTest)
+ context.testablePermissions.setPermission(
+ Contract.PERMISSION,
+ PackageManager.PERMISSION_GRANTED,
+ )
+ }
+
+ @Test
+ fun `onAttachInfo - reportsContext`() {
+ val callback: SystemUIAppComponentFactoryBase.ContextAvailableCallback = mock()
+ underTest.setContextAvailableCallback(callback)
+
+ underTest.attachInfo(context, null)
+
+ verify(callback).onContextAvailable(context)
+ }
+
+ @Test
+ fun getType() {
+ assertThat(underTest.getType(Contract.AffordanceTable.URI))
+ .isEqualTo(
+ "vnd.android.cursor.dir/vnd." +
+ "${Contract.AUTHORITY}.${Contract.AffordanceTable.TABLE_NAME}"
+ )
+ assertThat(underTest.getType(Contract.SlotTable.URI))
+ .isEqualTo(
+ "vnd.android.cursor.dir/vnd.${Contract.AUTHORITY}.${Contract.SlotTable.TABLE_NAME}"
+ )
+ assertThat(underTest.getType(Contract.SelectionTable.URI))
+ .isEqualTo(
+ "vnd.android.cursor.dir/vnd." +
+ "${Contract.AUTHORITY}.${Contract.SelectionTable.TABLE_NAME}"
+ )
+ }
+
+ @Test
+ fun `insert and query selection`() =
+ runBlocking(IMMEDIATE) {
+ val slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START
+ val affordanceId = AFFORDANCE_2
+
+ Client.insertSelection(
+ context = context,
+ slotId = slotId,
+ affordanceId = affordanceId,
+ dispatcher = IMMEDIATE,
+ )
+
+ assertThat(
+ Client.querySelections(
+ context = context,
+ dispatcher = IMMEDIATE,
+ )
+ )
+ .isEqualTo(
+ listOf(
+ Client.Selection(
+ slotId = slotId,
+ affordanceId = affordanceId,
+ )
+ )
+ )
+ }
+
+ @Test
+ fun `query slots`() =
+ runBlocking(IMMEDIATE) {
+ assertThat(
+ Client.querySlots(
+ context = context,
+ dispatcher = IMMEDIATE,
+ )
+ )
+ .isEqualTo(
+ listOf(
+ Client.Slot(
+ id = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+ capacity = 1,
+ ),
+ Client.Slot(
+ id = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
+ capacity = 1,
+ ),
+ )
+ )
+ }
+
+ @Test
+ fun `query affordances`() =
+ runBlocking(IMMEDIATE) {
+ assertThat(
+ Client.queryAffordances(
+ context = context,
+ dispatcher = IMMEDIATE,
+ )
+ )
+ .isEqualTo(
+ listOf(
+ Client.Affordance(
+ id = AFFORDANCE_1,
+ name = AFFORDANCE_1,
+ iconResourceId = 1,
+ ),
+ Client.Affordance(
+ id = AFFORDANCE_2,
+ name = AFFORDANCE_2,
+ iconResourceId = 2,
+ ),
+ )
+ )
+ }
+
+ @Test
+ fun `delete and query selection`() =
+ runBlocking(IMMEDIATE) {
+ Client.insertSelection(
+ context = context,
+ slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+ affordanceId = AFFORDANCE_1,
+ dispatcher = IMMEDIATE,
+ )
+ Client.insertSelection(
+ context = context,
+ slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
+ affordanceId = AFFORDANCE_2,
+ dispatcher = IMMEDIATE,
+ )
+
+ Client.deleteSelection(
+ context = context,
+ slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
+ affordanceId = AFFORDANCE_2,
+ dispatcher = IMMEDIATE,
+ )
+
+ assertThat(
+ Client.querySelections(
+ context = context,
+ dispatcher = IMMEDIATE,
+ )
+ )
+ .isEqualTo(
+ listOf(
+ Client.Selection(
+ slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+ affordanceId = AFFORDANCE_1,
+ )
+ )
+ )
+ }
+
+ @Test
+ fun `delete all selections in a slot`() =
+ runBlocking(IMMEDIATE) {
+ Client.insertSelection(
+ context = context,
+ slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+ affordanceId = AFFORDANCE_1,
+ dispatcher = IMMEDIATE,
+ )
+ Client.insertSelection(
+ context = context,
+ slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
+ affordanceId = AFFORDANCE_2,
+ dispatcher = IMMEDIATE,
+ )
+
+ Client.deleteAllSelections(
+ context = context,
+ slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
+ dispatcher = IMMEDIATE,
+ )
+
+ assertThat(
+ Client.querySelections(
+ context = context,
+ dispatcher = IMMEDIATE,
+ )
+ )
+ .isEqualTo(
+ listOf(
+ Client.Selection(
+ slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+ affordanceId = AFFORDANCE_1,
+ )
+ )
+ )
+ }
+
+ companion object {
+ private val IMMEDIATE = Dispatchers.Main.immediate
+ private const val AFFORDANCE_1 = "affordance_1"
+ private const val AFFORDANCE_2 = "affordance_2"
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 2c3ddd5..b6780a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -57,6 +57,7 @@
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -105,6 +106,7 @@
private @Mock ScreenOffAnimationController mScreenOffAnimationController;
private @Mock InteractionJankMonitor mInteractionJankMonitor;
private @Mock ScreenOnCoordinator mScreenOnCoordinator;
+ private @Mock ShadeController mShadeController;
private @Mock Lazy<NotificationShadeWindowController> mNotificationShadeWindowControllerLazy;
private @Mock DreamOverlayStateController mDreamOverlayStateController;
private @Mock ActivityLaunchAnimator mActivityLaunchAnimator;
@@ -307,6 +309,7 @@
mScreenOnCoordinator,
mInteractionJankMonitor,
mDreamOverlayStateController,
+ () -> mShadeController,
mNotificationShadeWindowControllerLazy,
() -> mActivityLaunchAnimator);
mViewMediator.start();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index 53d9b87..6ba0634 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -17,6 +17,8 @@
package com.android.systemui.keyguard.data.repository
import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Position
import com.android.systemui.doze.DozeHost
@@ -48,6 +50,7 @@
@Mock private lateinit var dozeHost: DozeHost
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var biometricUnlockController: BiometricUnlockController
private lateinit var underTest: KeyguardRepositoryImpl
@@ -59,10 +62,11 @@
underTest =
KeyguardRepositoryImpl(
statusBarStateController,
- keyguardStateController,
dozeHost,
wakefulnessLifecycle,
biometricUnlockController,
+ keyguardStateController,
+ keyguardUpdateMonitor,
)
}
@@ -223,6 +227,15 @@
}
@Test
+ fun isUdfpsSupported() = runBlockingTest {
+ whenever(keyguardUpdateMonitor.isUdfpsSupported).thenReturn(true)
+ assertThat(underTest.isUdfpsSupported()).isTrue()
+
+ whenever(keyguardUpdateMonitor.isUdfpsSupported).thenReturn(false)
+ assertThat(underTest.isUdfpsSupported()).isFalse()
+ }
+
+ @Test
fun isBouncerShowing() = runBlockingTest {
whenever(keyguardStateController.isBouncerShowing).thenReturn(false)
var latest: Boolean? = null
@@ -245,6 +258,48 @@
}
@Test
+ fun isKeyguardGoingAway() = runBlockingTest {
+ whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(false)
+ var latest: Boolean? = null
+ val job = underTest.isKeyguardGoingAway.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest).isFalse()
+
+ val captor = argumentCaptor<KeyguardStateController.Callback>()
+ verify(keyguardStateController).addCallback(captor.capture())
+
+ whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(true)
+ captor.value.onKeyguardGoingAwayChanged()
+ assertThat(latest).isTrue()
+
+ whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(false)
+ captor.value.onKeyguardGoingAwayChanged()
+ assertThat(latest).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
+ fun isDreaming() = runBlockingTest {
+ whenever(keyguardUpdateMonitor.isDreaming()).thenReturn(false)
+ var latest: Boolean? = null
+ val job = underTest.isDreaming.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest).isFalse()
+
+ val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
+ verify(keyguardUpdateMonitor).registerCallback(captor.capture())
+
+ captor.value.onDreamingStateChanged(true)
+ assertThat(latest).isTrue()
+
+ captor.value.onDreamingStateChanged(false)
+ assertThat(latest).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
fun biometricUnlockState() = runBlockingTest {
val values = mutableListOf<BiometricUnlockModel>()
val job = underTest.biometricUnlockState.onEach(values::add).launchIn(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
index 27d5d0a..2b03722 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
@@ -25,8 +25,8 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Interpolators
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
-import com.android.systemui.keyguard.shared.model.KeyguardState.BOUNCER
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -38,7 +38,6 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
-import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.launchIn
@@ -91,18 +90,51 @@
}
}
- assertSteps(steps, listWithStep(BigDecimal(.1)))
+ assertSteps(steps, listWithStep(BigDecimal(.1)), AOD, LOCKSCREEN)
job.cancel()
provider.stop()
}
@Test
- fun `startTransition called during another transition fails`() {
- underTest.startTransition(TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, null))
- underTest.startTransition(TransitionInfo(OWNER_NAME, LOCKSCREEN, BOUNCER, null))
+ fun `starting second transition will cancel the first transition`() {
+ runBlocking(IMMEDIATE) {
+ val (animator, provider) = setupAnimator(this)
- assertThat(wtfHandler.failed).isTrue()
+ val steps = mutableListOf<TransitionStep>()
+ val job = underTest.transition(AOD, LOCKSCREEN).onEach { steps.add(it) }.launchIn(this)
+
+ underTest.startTransition(TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, animator))
+ // 3 yields(), alternating with the animator, results in a value 0.1, which can be
+ // canceled and tested against
+ yield()
+ yield()
+ yield()
+
+ // Now start 2nd transition, which will interrupt the first
+ val job2 = underTest.transition(LOCKSCREEN, AOD).onEach { steps.add(it) }.launchIn(this)
+ val (animator2, provider2) = setupAnimator(this)
+ underTest.startTransition(TransitionInfo(OWNER_NAME, LOCKSCREEN, AOD, animator2))
+
+ val startTime = System.currentTimeMillis()
+ while (animator2.isRunning()) {
+ yield()
+ if (System.currentTimeMillis() - startTime > MAX_TEST_DURATION) {
+ fail("Failed test due to excessive runtime of: $MAX_TEST_DURATION")
+ }
+ }
+
+ val firstTransitionSteps = listWithStep(step = BigDecimal(.1), stop = BigDecimal(.1))
+ assertSteps(steps.subList(0, 4), firstTransitionSteps, AOD, LOCKSCREEN)
+
+ val secondTransitionSteps = listWithStep(step = BigDecimal(.1), start = BigDecimal(.9))
+ assertSteps(steps.subList(4, steps.size), secondTransitionSteps, LOCKSCREEN, AOD)
+
+ job.cancel()
+ job2.cancel()
+ provider.stop()
+ provider2.stop()
+ }
}
@Test
@@ -165,11 +197,15 @@
assertThat(wtfHandler.failed).isTrue()
}
- private fun listWithStep(step: BigDecimal): List<BigDecimal> {
+ private fun listWithStep(
+ step: BigDecimal,
+ start: BigDecimal = BigDecimal.ZERO,
+ stop: BigDecimal = BigDecimal.ONE,
+ ): List<BigDecimal> {
val steps = mutableListOf<BigDecimal>()
- var i = BigDecimal.ZERO
- while (i.compareTo(BigDecimal.ONE) <= 0) {
+ var i = start
+ while (i.compareTo(stop) <= 0) {
steps.add(i)
i = (i + step).setScale(2, RoundingMode.HALF_UP)
}
@@ -177,23 +213,43 @@
return steps
}
- private fun assertSteps(steps: List<TransitionStep>, fractions: List<BigDecimal>) {
+ private fun assertSteps(
+ steps: List<TransitionStep>,
+ fractions: List<BigDecimal>,
+ from: KeyguardState,
+ to: KeyguardState,
+ ) {
assertThat(steps[0])
- .isEqualTo(TransitionStep(AOD, LOCKSCREEN, 0f, TransitionState.STARTED, OWNER_NAME))
+ .isEqualTo(
+ TransitionStep(
+ from,
+ to,
+ fractions[0].toFloat(),
+ TransitionState.STARTED,
+ OWNER_NAME
+ )
+ )
fractions.forEachIndexed { index, fraction ->
assertThat(steps[index + 1])
.isEqualTo(
TransitionStep(
- AOD,
- LOCKSCREEN,
+ from,
+ to,
fraction.toFloat(),
TransitionState.RUNNING,
OWNER_NAME
)
)
}
+ val lastValue = fractions[fractions.size - 1].toFloat()
+ val status =
+ if (lastValue < 1f) {
+ TransitionState.CANCELED
+ } else {
+ TransitionState.FINISHED
+ }
assertThat(steps[steps.size - 1])
- .isEqualTo(TransitionStep(AOD, LOCKSCREEN, 1f, TransitionState.FINISHED, OWNER_NAME))
+ .isEqualTo(TransitionStep(from, to, lastValue, status, OWNER_NAME))
assertThat(wtfHandler.failed).isFalse()
}
@@ -230,7 +286,7 @@
scope.launch {
frames.collect {
// Delay is required for AnimationHandler to properly register a callback
- delay(1)
+ yield()
val (frameNumber, callback) = it
callback?.doFrame(frameNumber)
}
@@ -243,7 +299,7 @@
}
override fun postFrameCallback(cb: FrameCallback) {
- frames.value = Pair(++frameCount, cb)
+ frames.value = Pair(frameCount++, cb)
}
override fun postCommitCallback(runnable: Runnable) {}
override fun getFrameTime() = frameCount
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractorTest.kt
deleted file mode 100644
index 3a61c57..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractorTest.kt
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard.domain.interactor
-
-import android.view.View
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.phone.KeyguardBouncer
-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.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(JUnit4::class)
-class BouncerCallbackInteractorTest : SysuiTestCase() {
- private val bouncerCallbackInteractor = BouncerCallbackInteractor()
- @Mock private lateinit var bouncerExpansionCallback: KeyguardBouncer.BouncerExpansionCallback
- @Mock private lateinit var keyguardResetCallback: KeyguardBouncer.KeyguardResetCallback
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
- bouncerCallbackInteractor.addBouncerExpansionCallback(bouncerExpansionCallback)
- bouncerCallbackInteractor.addKeyguardResetCallback(keyguardResetCallback)
- }
-
- @Test
- fun testOnFullyShown() {
- bouncerCallbackInteractor.dispatchFullyShown()
- verify(bouncerExpansionCallback).onFullyShown()
- }
-
- @Test
- fun testOnFullyHidden() {
- bouncerCallbackInteractor.dispatchFullyHidden()
- verify(bouncerExpansionCallback).onFullyHidden()
- }
-
- @Test
- fun testOnExpansionChanged() {
- bouncerCallbackInteractor.dispatchExpansionChanged(5f)
- verify(bouncerExpansionCallback).onExpansionChanged(5f)
- }
-
- @Test
- fun testOnVisibilityChanged() {
- bouncerCallbackInteractor.dispatchVisibilityChanged(View.INVISIBLE)
- verify(bouncerExpansionCallback).onVisibilityChanged(false)
- }
-
- @Test
- fun testOnStartingToHide() {
- bouncerCallbackInteractor.dispatchStartingToHide()
- verify(bouncerExpansionCallback).onStartingToHide()
- }
-
- @Test
- fun testOnStartingToShow() {
- bouncerCallbackInteractor.dispatchStartingToShow()
- verify(bouncerExpansionCallback).onStartingToShow()
- }
-
- @Test
- fun testOnKeyguardReset() {
- bouncerCallbackInteractor.dispatchReset()
- verify(keyguardResetCallback).onKeyguardReset()
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt
new file mode 100644
index 0000000..db9c4e7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.phone.KeyguardBouncer
+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.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class PrimaryBouncerCallbackInteractorTest : SysuiTestCase() {
+ private val mPrimaryBouncerCallbackInteractor = PrimaryBouncerCallbackInteractor()
+ @Mock
+ private lateinit var mPrimaryBouncerExpansionCallback:
+ KeyguardBouncer.PrimaryBouncerExpansionCallback
+ @Mock private lateinit var keyguardResetCallback: KeyguardBouncer.KeyguardResetCallback
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ mPrimaryBouncerCallbackInteractor.addBouncerExpansionCallback(
+ mPrimaryBouncerExpansionCallback
+ )
+ mPrimaryBouncerCallbackInteractor.addKeyguardResetCallback(keyguardResetCallback)
+ }
+
+ @Test
+ fun testOnFullyShown() {
+ mPrimaryBouncerCallbackInteractor.dispatchFullyShown()
+ verify(mPrimaryBouncerExpansionCallback).onFullyShown()
+ }
+
+ @Test
+ fun testOnFullyHidden() {
+ mPrimaryBouncerCallbackInteractor.dispatchFullyHidden()
+ verify(mPrimaryBouncerExpansionCallback).onFullyHidden()
+ }
+
+ @Test
+ fun testOnExpansionChanged() {
+ mPrimaryBouncerCallbackInteractor.dispatchExpansionChanged(5f)
+ verify(mPrimaryBouncerExpansionCallback).onExpansionChanged(5f)
+ }
+
+ @Test
+ fun testOnVisibilityChanged() {
+ mPrimaryBouncerCallbackInteractor.dispatchVisibilityChanged(View.INVISIBLE)
+ verify(mPrimaryBouncerExpansionCallback).onVisibilityChanged(false)
+ }
+
+ @Test
+ fun testOnStartingToHide() {
+ mPrimaryBouncerCallbackInteractor.dispatchStartingToHide()
+ verify(mPrimaryBouncerExpansionCallback).onStartingToHide()
+ }
+
+ @Test
+ fun testOnStartingToShow() {
+ mPrimaryBouncerCallbackInteractor.dispatchStartingToShow()
+ verify(mPrimaryBouncerExpansionCallback).onStartingToShow()
+ }
+
+ @Test
+ fun testOnKeyguardReset() {
+ mPrimaryBouncerCallbackInteractor.dispatchReset()
+ verify(keyguardResetCallback).onKeyguardReset()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt
similarity index 60%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractorTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt
index 5743b2f..3269f5a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt
@@ -53,59 +53,58 @@
@SmallTest
@RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidTestingRunner::class)
-class BouncerInteractorTest : SysuiTestCase() {
+class PrimaryBouncerInteractorTest : SysuiTestCase() {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private lateinit var repository: KeyguardBouncerRepository
@Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var bouncerView: BouncerView
@Mock private lateinit var bouncerViewDelegate: BouncerViewDelegate
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var keyguardSecurityModel: KeyguardSecurityModel
- @Mock private lateinit var bouncerCallbackInteractor: BouncerCallbackInteractor
+ @Mock private lateinit var mPrimaryBouncerCallbackInteractor: PrimaryBouncerCallbackInteractor
@Mock private lateinit var falsingCollector: FalsingCollector
@Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry
@Mock private lateinit var keyguardBypassController: KeyguardBypassController
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
private val mainHandler = FakeHandler(Looper.getMainLooper())
- private lateinit var bouncerInteractor: BouncerInteractor
+ private lateinit var mPrimaryBouncerInteractor: PrimaryBouncerInteractor
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
DejankUtils.setImmediate(true)
- bouncerInteractor =
- BouncerInteractor(
+ mPrimaryBouncerInteractor =
+ PrimaryBouncerInteractor(
repository,
bouncerView,
mainHandler,
keyguardStateController,
keyguardSecurityModel,
- bouncerCallbackInteractor,
+ mPrimaryBouncerCallbackInteractor,
falsingCollector,
dismissCallbackRegistry,
keyguardBypassController,
keyguardUpdateMonitor,
)
- `when`(repository.startingDisappearAnimation.value).thenReturn(null)
- `when`(repository.show.value).thenReturn(null)
+ `when`(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null)
+ `when`(repository.primaryBouncerShow.value).thenReturn(null)
`when`(bouncerView.delegate).thenReturn(bouncerViewDelegate)
}
@Test
fun testShow_isScrimmed() {
- bouncerInteractor.show(true)
- verify(repository).setShowMessage(null)
+ mPrimaryBouncerInteractor.show(true)
verify(repository).setOnScreenTurnedOff(false)
verify(repository).setKeyguardAuthenticated(null)
- verify(repository).setHide(false)
- verify(repository).setStartingToHide(false)
- verify(repository).setScrimmed(true)
+ verify(repository).setPrimaryHide(false)
+ verify(repository).setPrimaryStartingToHide(false)
+ verify(repository).setPrimaryScrimmed(true)
verify(repository).setPanelExpansion(EXPANSION_VISIBLE)
- verify(repository).setShowingSoon(true)
+ verify(repository).setPrimaryShowingSoon(true)
verify(keyguardStateController).notifyBouncerShowing(true)
- verify(bouncerCallbackInteractor).dispatchStartingToShow()
- verify(repository).setVisible(true)
- verify(repository).setShow(any(KeyguardBouncerModel::class.java))
- verify(repository).setShowingSoon(false)
+ verify(mPrimaryBouncerCallbackInteractor).dispatchStartingToShow()
+ verify(repository).setPrimaryVisible(true)
+ verify(repository).setPrimaryShow(any(KeyguardBouncerModel::class.java))
+ verify(repository).setPrimaryShowingSoon(false)
}
@Test
@@ -117,60 +116,61 @@
fun testShow_keyguardIsDone() {
`when`(bouncerView.delegate?.showNextSecurityScreenOrFinish()).thenReturn(true)
verify(keyguardStateController, never()).notifyBouncerShowing(true)
- verify(bouncerCallbackInteractor, never()).dispatchStartingToShow()
+ verify(mPrimaryBouncerCallbackInteractor, never()).dispatchStartingToShow()
}
@Test
fun testHide() {
- bouncerInteractor.hide()
+ mPrimaryBouncerInteractor.hide()
verify(falsingCollector).onBouncerHidden()
verify(keyguardStateController).notifyBouncerShowing(false)
- verify(repository).setShowingSoon(false)
- verify(repository).setVisible(false)
- verify(repository).setHide(true)
- verify(repository).setShow(null)
+ verify(repository).setPrimaryShowingSoon(false)
+ verify(repository).setPrimaryVisible(false)
+ verify(repository).setPrimaryHide(true)
+ verify(repository).setPrimaryShow(null)
}
@Test
fun testExpansion() {
`when`(repository.panelExpansionAmount.value).thenReturn(0.5f)
- bouncerInteractor.setPanelExpansion(0.6f)
+ mPrimaryBouncerInteractor.setPanelExpansion(0.6f)
verify(repository).setPanelExpansion(0.6f)
- verify(bouncerCallbackInteractor).dispatchExpansionChanged(0.6f)
+ verify(mPrimaryBouncerCallbackInteractor).dispatchExpansionChanged(0.6f)
}
@Test
fun testExpansion_fullyShown() {
`when`(repository.panelExpansionAmount.value).thenReturn(0.5f)
- `when`(repository.startingDisappearAnimation.value).thenReturn(null)
- bouncerInteractor.setPanelExpansion(EXPANSION_VISIBLE)
+ `when`(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null)
+ mPrimaryBouncerInteractor.setPanelExpansion(EXPANSION_VISIBLE)
verify(falsingCollector).onBouncerShown()
- verify(bouncerCallbackInteractor).dispatchFullyShown()
+ verify(mPrimaryBouncerCallbackInteractor).dispatchFullyShown()
}
@Test
fun testExpansion_fullyHidden() {
`when`(repository.panelExpansionAmount.value).thenReturn(0.5f)
- `when`(repository.startingDisappearAnimation.value).thenReturn(null)
- bouncerInteractor.setPanelExpansion(EXPANSION_HIDDEN)
- verify(repository).setVisible(false)
- verify(repository).setShow(null)
+ `when`(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null)
+ mPrimaryBouncerInteractor.setPanelExpansion(EXPANSION_HIDDEN)
+ verify(repository).setPrimaryVisible(false)
+ verify(repository).setPrimaryShow(null)
+ verify(repository).setPrimaryHide(true)
verify(falsingCollector).onBouncerHidden()
- verify(bouncerCallbackInteractor).dispatchReset()
- verify(bouncerCallbackInteractor).dispatchFullyHidden()
+ verify(mPrimaryBouncerCallbackInteractor).dispatchReset()
+ verify(mPrimaryBouncerCallbackInteractor).dispatchFullyHidden()
}
@Test
fun testExpansion_startingToHide() {
`when`(repository.panelExpansionAmount.value).thenReturn(EXPANSION_VISIBLE)
- bouncerInteractor.setPanelExpansion(0.1f)
- verify(repository).setStartingToHide(true)
- verify(bouncerCallbackInteractor).dispatchStartingToHide()
+ mPrimaryBouncerInteractor.setPanelExpansion(0.1f)
+ verify(repository).setPrimaryStartingToHide(true)
+ verify(mPrimaryBouncerCallbackInteractor).dispatchStartingToHide()
}
@Test
fun testShowMessage() {
- bouncerInteractor.showMessage("abc", null)
+ mPrimaryBouncerInteractor.showMessage("abc", null)
verify(repository).setShowMessage(BouncerShowMessageModel("abc", null))
}
@@ -178,100 +178,100 @@
fun testDismissAction() {
val onDismissAction = mock(ActivityStarter.OnDismissAction::class.java)
val cancelAction = mock(Runnable::class.java)
- bouncerInteractor.setDismissAction(onDismissAction, cancelAction)
+ mPrimaryBouncerInteractor.setDismissAction(onDismissAction, cancelAction)
verify(bouncerViewDelegate).setDismissAction(onDismissAction, cancelAction)
}
@Test
fun testUpdateResources() {
- bouncerInteractor.updateResources()
+ mPrimaryBouncerInteractor.updateResources()
verify(repository).setResourceUpdateRequests(true)
}
@Test
fun testNotifyKeyguardAuthenticated() {
- bouncerInteractor.notifyKeyguardAuthenticated(true)
+ mPrimaryBouncerInteractor.notifyKeyguardAuthenticated(true)
verify(repository).setKeyguardAuthenticated(true)
}
@Test
fun testOnScreenTurnedOff() {
- bouncerInteractor.onScreenTurnedOff()
+ mPrimaryBouncerInteractor.onScreenTurnedOff()
verify(repository).setOnScreenTurnedOff(true)
}
@Test
fun testSetKeyguardPosition() {
- bouncerInteractor.setKeyguardPosition(0f)
+ mPrimaryBouncerInteractor.setKeyguardPosition(0f)
verify(repository).setKeyguardPosition(0f)
}
@Test
fun testNotifyKeyguardAuthenticatedHandled() {
- bouncerInteractor.notifyKeyguardAuthenticatedHandled()
+ mPrimaryBouncerInteractor.notifyKeyguardAuthenticatedHandled()
verify(repository).setKeyguardAuthenticated(null)
}
@Test
fun testNotifyUpdatedResources() {
- bouncerInteractor.notifyUpdatedResources()
+ mPrimaryBouncerInteractor.notifyUpdatedResources()
verify(repository).setResourceUpdateRequests(false)
}
@Test
fun testSetBackButtonEnabled() {
- bouncerInteractor.setBackButtonEnabled(true)
+ mPrimaryBouncerInteractor.setBackButtonEnabled(true)
verify(repository).setIsBackButtonEnabled(true)
}
@Test
fun testStartDisappearAnimation() {
val runnable = mock(Runnable::class.java)
- bouncerInteractor.startDisappearAnimation(runnable)
- verify(repository).setStartDisappearAnimation(any(Runnable::class.java))
+ mPrimaryBouncerInteractor.startDisappearAnimation(runnable)
+ verify(repository).setPrimaryStartDisappearAnimation(any(Runnable::class.java))
}
@Test
fun testIsFullShowing() {
- `when`(repository.isVisible.value).thenReturn(true)
+ `when`(repository.primaryBouncerVisible.value).thenReturn(true)
`when`(repository.panelExpansionAmount.value).thenReturn(EXPANSION_VISIBLE)
- `when`(repository.startingDisappearAnimation.value).thenReturn(null)
- assertThat(bouncerInteractor.isFullyShowing()).isTrue()
- `when`(repository.isVisible.value).thenReturn(false)
- assertThat(bouncerInteractor.isFullyShowing()).isFalse()
+ `when`(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null)
+ assertThat(mPrimaryBouncerInteractor.isFullyShowing()).isTrue()
+ `when`(repository.primaryBouncerVisible.value).thenReturn(false)
+ assertThat(mPrimaryBouncerInteractor.isFullyShowing()).isFalse()
}
@Test
fun testIsScrimmed() {
- `when`(repository.isScrimmed.value).thenReturn(true)
- assertThat(bouncerInteractor.isScrimmed()).isTrue()
- `when`(repository.isScrimmed.value).thenReturn(false)
- assertThat(bouncerInteractor.isScrimmed()).isFalse()
+ `when`(repository.primaryBouncerScrimmed.value).thenReturn(true)
+ assertThat(mPrimaryBouncerInteractor.isScrimmed()).isTrue()
+ `when`(repository.primaryBouncerScrimmed.value).thenReturn(false)
+ assertThat(mPrimaryBouncerInteractor.isScrimmed()).isFalse()
}
@Test
fun testIsInTransit() {
- `when`(repository.showingSoon.value).thenReturn(true)
- assertThat(bouncerInteractor.isInTransit()).isTrue()
- `when`(repository.showingSoon.value).thenReturn(false)
- assertThat(bouncerInteractor.isInTransit()).isFalse()
+ `when`(repository.primaryBouncerShowingSoon.value).thenReturn(true)
+ assertThat(mPrimaryBouncerInteractor.isInTransit()).isTrue()
+ `when`(repository.primaryBouncerShowingSoon.value).thenReturn(false)
+ assertThat(mPrimaryBouncerInteractor.isInTransit()).isFalse()
`when`(repository.panelExpansionAmount.value).thenReturn(0.5f)
- assertThat(bouncerInteractor.isInTransit()).isTrue()
+ assertThat(mPrimaryBouncerInteractor.isInTransit()).isTrue()
}
@Test
fun testIsAnimatingAway() {
- `when`(repository.startingDisappearAnimation.value).thenReturn(Runnable {})
- assertThat(bouncerInteractor.isAnimatingAway()).isTrue()
- `when`(repository.startingDisappearAnimation.value).thenReturn(null)
- assertThat(bouncerInteractor.isAnimatingAway()).isFalse()
+ `when`(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(Runnable {})
+ assertThat(mPrimaryBouncerInteractor.isAnimatingAway()).isTrue()
+ `when`(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null)
+ assertThat(mPrimaryBouncerInteractor.isAnimatingAway()).isFalse()
}
@Test
fun testWillDismissWithAction() {
`when`(bouncerViewDelegate.willDismissWithActions()).thenReturn(true)
- assertThat(bouncerInteractor.willDismissWithAction()).isTrue()
+ assertThat(mPrimaryBouncerInteractor.willDismissWithAction()).isTrue()
`when`(bouncerViewDelegate.willDismissWithActions()).thenReturn(false)
- assertThat(bouncerInteractor.willDismissWithAction()).isFalse()
+ assertThat(mPrimaryBouncerInteractor.willDismissWithAction()).isFalse()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
index c8e8943..6ca34e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
@@ -49,6 +49,7 @@
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertTrue
import org.junit.Before
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
@@ -119,6 +120,7 @@
MediaPlayerData.clear()
}
+ @Ignore("b/253229241")
@Test
fun testPlayerOrdering() {
// Test values: key, data, last active time
@@ -295,6 +297,7 @@
}
}
+ @Ignore("b/253229241")
@Test
fun testOrderWithSmartspace_prioritized() {
testPlayerOrdering()
@@ -312,6 +315,7 @@
assertTrue(MediaPlayerData.playerKeys().elementAt(2).isSsMediaRec)
}
+ @Ignore("b/253229241")
@Test
fun testOrderWithSmartspace_prioritized_updatingVisibleMediaPlayers() {
testPlayerOrdering()
@@ -328,6 +332,7 @@
assertTrue(MediaPlayerData.visiblePlayerKeys().elementAt(2).isSsMediaRec)
}
+ @Ignore("b/253229241")
@Test
fun testOrderWithSmartspace_notPrioritized() {
testPlayerOrdering()
@@ -346,6 +351,7 @@
assertTrue(MediaPlayerData.playerKeys().elementAt(idx).isSsMediaRec)
}
+ @Ignore("b/253229241")
@Test
fun testPlayingExistingMediaPlayerFromCarousel_visibleMediaPlayersNotUpdated() {
testPlayerOrdering()
@@ -382,6 +388,8 @@
MediaPlayerData.playerKeys().elementAt(0)
)
}
+
+ @Ignore("b/253229241")
@Test
fun testSwipeDismiss_logged() {
mediaCarouselController.mediaCarouselScrollHandler.dismissCallback.invoke()
@@ -389,6 +397,7 @@
verify(logger).logSwipeDismiss()
}
+ @Ignore("b/253229241")
@Test
fun testSettingsButton_logged() {
mediaCarouselController.settingsButton.callOnClick()
@@ -396,6 +405,7 @@
verify(logger).logCarouselSettings()
}
+ @Ignore("b/253229241")
@Test
fun testLocationChangeQs_logged() {
mediaCarouselController.onDesiredLocationChanged(
@@ -406,6 +416,7 @@
verify(logger).logCarouselPosition(MediaHierarchyManager.LOCATION_QS)
}
+ @Ignore("b/253229241")
@Test
fun testLocationChangeQqs_logged() {
mediaCarouselController.onDesiredLocationChanged(
@@ -416,6 +427,7 @@
verify(logger).logCarouselPosition(MediaHierarchyManager.LOCATION_QQS)
}
+ @Ignore("b/253229241")
@Test
fun testLocationChangeLockscreen_logged() {
mediaCarouselController.onDesiredLocationChanged(
@@ -426,6 +438,7 @@
verify(logger).logCarouselPosition(MediaHierarchyManager.LOCATION_LOCKSCREEN)
}
+ @Ignore("b/253229241")
@Test
fun testLocationChangeDream_logged() {
mediaCarouselController.onDesiredLocationChanged(
@@ -436,6 +449,7 @@
verify(logger).logCarouselPosition(MediaHierarchyManager.LOCATION_DREAM_OVERLAY)
}
+ @Ignore("b/253229241")
@Test
fun testRecommendationRemoved_logged() {
val packageName = "smartspace package"
@@ -449,6 +463,7 @@
verify(logger).logRecommendationRemoved(eq(packageName), eq(instanceId!!))
}
+ @Ignore("b/253229241")
@Test
fun testMediaLoaded_ScrollToActivePlayer() {
listener.value.onMediaDataLoaded(
@@ -506,6 +521,7 @@
)
}
+ @Ignore("b/253229241")
@Test
fun testMediaLoadedFromRecommendationCard_ScrollToActivePlayer() {
listener.value.onSmartspaceMediaDataLoaded(
@@ -549,6 +565,7 @@
assertEquals(playerIndex, 0)
}
+ @Ignore("b/253229241")
@Test
fun testRecommendationRemovedWhileNotVisible_updateHostVisibility() {
var result = false
@@ -560,6 +577,7 @@
assertEquals(true, result)
}
+ @Ignore("b/253229241")
@Test
fun testRecommendationRemovedWhileVisible_thenReorders_updateHostVisibility() {
var result = false
@@ -573,6 +591,7 @@
assertEquals(true, result)
}
+ @Ignore("b/253229241")
@Test
fun testGetCurrentVisibleMediaContentIntent() {
val clickIntent1 = mock(PendingIntent::class.java)
@@ -619,6 +638,7 @@
assertEquals(mediaCarouselController.getCurrentVisibleMediaContentIntent(), clickIntent2)
}
+ @Ignore("b/253229241")
@Test
fun testSetCurrentState_UpdatePageIndicatorAlphaWhenSquish() {
val delta = 0.0001F
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
index 8190156..1ad2ca9b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
@@ -1137,6 +1137,19 @@
/* ***** Guts tests for the player ***** */
@Test
+ fun player_longClick_isFalse() {
+ whenever(falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)).thenReturn(true)
+ player.attachPlayer(viewHolder)
+
+ val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java)
+ verify(viewHolder.player).onLongClickListener = captor.capture()
+
+ captor.value.onLongClick(viewHolder.player)
+ verify(mediaViewController, never()).openGuts()
+ verify(mediaViewController, never()).closeGuts()
+ }
+
+ @Test
fun player_longClickWhenGutsClosed_gutsOpens() {
player.attachPlayer(viewHolder)
player.bindPlayer(mediaData, KEY)
@@ -1316,6 +1329,20 @@
/* ***** Guts tests for the recommendations ***** */
@Test
+ fun recommendations_longClick_isFalse() {
+ whenever(falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)).thenReturn(true)
+ player.attachRecommendation(recommendationViewHolder)
+ player.bindRecommendation(smartspaceData)
+
+ val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java)
+ verify(viewHolder.player).onLongClickListener = captor.capture()
+
+ captor.value.onLongClick(viewHolder.player)
+ verify(mediaViewController, never()).openGuts()
+ verify(mediaViewController, never()).closeGuts()
+ }
+
+ @Test
fun recommendations_longClickWhenGutsClosed_gutsOpens() {
player.attachRecommendation(recommendationViewHolder)
player.bindRecommendation(smartspaceData)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java
new file mode 100644
index 0000000..771b986
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java
@@ -0,0 +1,168 @@
+/*
+ * 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.systemui.media.dialog;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+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 android.content.Intent;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.settingslib.media.MediaOutputConstants;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class MediaOutputDialogReceiverTest extends SysuiTestCase {
+
+ private MediaOutputDialogReceiver mMediaOutputDialogReceiver;
+
+ private final MediaOutputDialogFactory mMockMediaOutputDialogFactory =
+ mock(MediaOutputDialogFactory.class);
+
+ private final MediaOutputBroadcastDialogFactory mMockMediaOutputBroadcastDialogFactory =
+ mock(MediaOutputBroadcastDialogFactory.class);
+
+ @Before
+ public void setup() {
+ mMediaOutputDialogReceiver = new MediaOutputDialogReceiver(mMockMediaOutputDialogFactory,
+ mMockMediaOutputBroadcastDialogFactory);
+ }
+
+ @Test
+ public void showOutputSwitcher_ExtraPackageName_DialogFactoryCalled() {
+ Intent intent = new Intent(Intent.ACTION_SHOW_OUTPUT_SWITCHER);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, getContext().getPackageName());
+ mMediaOutputDialogReceiver.onReceive(getContext(), intent);
+
+ verify(mMockMediaOutputDialogFactory, times(1))
+ .create(getContext().getPackageName(), false, null);
+ verify(mMockMediaOutputBroadcastDialogFactory, never()).create(any(), anyBoolean(), any());
+ }
+
+ @Test
+ public void showOutputSwitcher_WrongExtraKey_DialogFactoryNotCalled() {
+ Intent intent = new Intent(Intent.ACTION_SHOW_OUTPUT_SWITCHER);
+ intent.putExtra("Wrong Package Name Key", getContext().getPackageName());
+ mMediaOutputDialogReceiver.onReceive(getContext(), intent);
+
+ verify(mMockMediaOutputDialogFactory, never()).create(any(), anyBoolean(), any());
+ verify(mMockMediaOutputBroadcastDialogFactory, never()).create(any(), anyBoolean(), any());
+ }
+
+ @Test
+ public void showOutputSwitcher_NoExtra_DialogFactoryNotCalled() {
+ Intent intent = new Intent(Intent.ACTION_SHOW_OUTPUT_SWITCHER);
+ mMediaOutputDialogReceiver.onReceive(getContext(), intent);
+
+ verify(mMockMediaOutputDialogFactory, never()).create(any(), anyBoolean(), any());
+ verify(mMockMediaOutputBroadcastDialogFactory, never()).create(any(), anyBoolean(), any());
+ }
+
+ @Test
+ public void launchMediaOutputDialog_ExtraPackageName_DialogFactoryCalled() {
+ Intent intent = new Intent(MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG);
+ intent.putExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME, getContext().getPackageName());
+ mMediaOutputDialogReceiver.onReceive(getContext(), intent);
+
+ verify(mMockMediaOutputDialogFactory, times(1))
+ .create(getContext().getPackageName(), false, null);
+ verify(mMockMediaOutputBroadcastDialogFactory, never()).create(any(), anyBoolean(), any());
+ }
+
+ @Test
+ public void launchMediaOutputDialog_WrongExtraKey_DialogFactoryNotCalled() {
+ Intent intent = new Intent(MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG);
+ intent.putExtra("Wrong Package Name Key", getContext().getPackageName());
+ mMediaOutputDialogReceiver.onReceive(getContext(), intent);
+
+ verify(mMockMediaOutputDialogFactory, never()).create(any(), anyBoolean(), any());
+ verify(mMockMediaOutputBroadcastDialogFactory, never()).create(any(), anyBoolean(), any());
+ }
+
+ @Test
+ public void launchMediaOutputDialog_NoExtra_DialogFactoryNotCalled() {
+ Intent intent = new Intent(MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG);
+ mMediaOutputDialogReceiver.onReceive(getContext(), intent);
+
+ verify(mMockMediaOutputDialogFactory, never()).create(any(), anyBoolean(), any());
+ verify(mMockMediaOutputBroadcastDialogFactory, never()).create(any(), anyBoolean(), any());
+ }
+
+ @Test
+ public void launchMediaOutputBroadcastDialog_ExtraPackageName_BroadcastDialogFactoryCalled() {
+ Intent intent = new Intent(
+ MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG);
+ intent.putExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME, getContext().getPackageName());
+ mMediaOutputDialogReceiver.onReceive(getContext(), intent);
+
+ verify(mMockMediaOutputDialogFactory, never()).create(any(), anyBoolean(), any());
+ verify(mMockMediaOutputBroadcastDialogFactory, times(1))
+ .create(getContext().getPackageName(), false, null);
+ }
+
+ @Test
+ public void launchMediaOutputBroadcastDialog_WrongExtraKey_DialogBroadcastFactoryNotCalled() {
+ Intent intent = new Intent(
+ MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG);
+ intent.putExtra("Wrong Package Name Key", getContext().getPackageName());
+ mMediaOutputDialogReceiver.onReceive(getContext(), intent);
+
+ verify(mMockMediaOutputDialogFactory, never()).create(any(), anyBoolean(), any());
+ verify(mMockMediaOutputBroadcastDialogFactory, never()).create(any(), anyBoolean(), any());
+ }
+
+ @Test
+ public void launchMediaOutputBroadcastDialog_NoExtra_BroadcastDialogFactoryNotCalled() {
+ Intent intent = new Intent(
+ MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG);
+ mMediaOutputDialogReceiver.onReceive(getContext(), intent);
+
+ verify(mMockMediaOutputDialogFactory, never()).create(any(), anyBoolean(), any());
+ verify(mMockMediaOutputBroadcastDialogFactory, never()).create(any(), anyBoolean(), any());
+ }
+
+ @Test
+ public void unKnownAction_ExtraPackageName_FactoriesNotCalled() {
+ Intent intent = new Intent("UnKnown Action");
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, getContext().getPackageName());
+ intent.putExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME, getContext().getPackageName());
+ mMediaOutputDialogReceiver.onReceive(getContext(), intent);
+
+ verify(mMockMediaOutputDialogFactory, never()).create(any(), anyBoolean(), any());
+ verify(mMockMediaOutputBroadcastDialogFactory, never()).create(any(), anyBoolean(), any());
+ }
+
+ @Test
+ public void unKnownActionAnd_NoExtra_FactoriesNotCalled() {
+ Intent intent = new Intent("UnKnown Action");
+ mMediaOutputDialogReceiver.onReceive(getContext(), intent);
+
+ verify(mMockMediaOutputDialogFactory, never()).create(any(), anyBoolean(), any());
+ verify(mMockMediaOutputBroadcastDialogFactory, never()).create(any(), anyBoolean(), any());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 8c3ae3d..885cc54 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -43,6 +43,7 @@
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.util.view.ViewUtil
+import com.android.systemui.util.wakelock.WakeLockFake
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -85,6 +86,10 @@
private lateinit var fakeAppIconDrawable: Drawable
private lateinit var uiEventLoggerFake: UiEventLoggerFake
private lateinit var receiverUiEventLogger: MediaTttReceiverUiEventLogger
+ private lateinit var fakeClock: FakeSystemClock
+ private lateinit var fakeExecutor: FakeExecutor
+ private lateinit var fakeWakeLockBuilder: WakeLockFake.Builder
+ private lateinit var fakeWakeLock: WakeLockFake
@Before
fun setUp() {
@@ -99,15 +104,22 @@
)).thenReturn(applicationInfo)
context.setMockPackageManager(packageManager)
+ fakeClock = FakeSystemClock()
+ fakeExecutor = FakeExecutor(fakeClock)
+
uiEventLoggerFake = UiEventLoggerFake()
receiverUiEventLogger = MediaTttReceiverUiEventLogger(uiEventLoggerFake)
+ fakeWakeLock = WakeLockFake()
+ fakeWakeLockBuilder = WakeLockFake.Builder(context)
+ fakeWakeLockBuilder.setWakeLock(fakeWakeLock)
+
controllerReceiver = MediaTttChipControllerReceiver(
commandQueue,
context,
logger,
windowManager,
- FakeExecutor(FakeSystemClock()),
+ fakeExecutor,
accessibilityManager,
configurationController,
powerManager,
@@ -115,6 +127,7 @@
mediaTttFlags,
receiverUiEventLogger,
viewUtil,
+ fakeWakeLockBuilder,
)
controllerReceiver.start()
@@ -141,6 +154,7 @@
mediaTttFlags,
receiverUiEventLogger,
viewUtil,
+ fakeWakeLockBuilder,
)
controllerReceiver.start()
@@ -200,6 +214,39 @@
}
@Test
+ fun commandQueueCallback_closeThenFar_wakeLockAcquiredThenReleased() {
+ commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
+ routeInfo,
+ null,
+ null
+ )
+
+ assertThat(fakeWakeLock.isHeld).isTrue()
+
+ commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
+ routeInfo,
+ null,
+ null
+ )
+
+ assertThat(fakeWakeLock.isHeld).isFalse()
+ }
+
+ @Test
+ fun commandQueueCallback_closeThenFar_wakeLockNeverAcquired() {
+ commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
+ routeInfo,
+ null,
+ null
+ )
+
+ assertThat(fakeWakeLock.isHeld).isFalse()
+ }
+
+ @Test
fun receivesNewStateFromCommandQueue_isLogged() {
commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
@@ -214,7 +261,12 @@
@Test
fun updateView_noOverrides_usesInfoFromAppIcon() {
controllerReceiver.displayView(
- ChipReceiverInfo(routeInfo, appIconDrawableOverride = null, appNameOverride = null)
+ ChipReceiverInfo(
+ routeInfo,
+ appIconDrawableOverride = null,
+ appNameOverride = null,
+ id = "id",
+ )
)
val view = getChipView()
@@ -227,7 +279,12 @@
val drawableOverride = context.getDrawable(R.drawable.ic_celebration)!!
controllerReceiver.displayView(
- ChipReceiverInfo(routeInfo, drawableOverride, appNameOverride = null)
+ ChipReceiverInfo(
+ routeInfo,
+ drawableOverride,
+ appNameOverride = null,
+ id = "id",
+ )
)
val view = getChipView()
@@ -239,7 +296,12 @@
val appNameOverride = "Sweet New App"
controllerReceiver.displayView(
- ChipReceiverInfo(routeInfo, appIconDrawableOverride = null, appNameOverride)
+ ChipReceiverInfo(
+ routeInfo,
+ appIconDrawableOverride = null,
+ appNameOverride,
+ id = "id",
+ )
)
val view = getChipView()
@@ -293,7 +355,7 @@
.addFeature("feature")
.setClientPackageName(packageName)
.build()
- return ChipReceiverInfo(routeInfo, null, null)
+ return ChipReceiverInfo(routeInfo, null, null, id = "id")
}
private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
index ad19bc2..4437394 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
@@ -52,6 +52,7 @@
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.util.view.ViewUtil
+import com.android.systemui.util.wakelock.WakeLockFake
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -89,6 +90,8 @@
@Mock private lateinit var viewUtil: ViewUtil
@Mock private lateinit var windowManager: WindowManager
@Mock private lateinit var vibratorHelper: VibratorHelper
+ private lateinit var fakeWakeLockBuilder: WakeLockFake.Builder
+ private lateinit var fakeWakeLock: WakeLockFake
private lateinit var chipbarCoordinator: ChipbarCoordinator
private lateinit var commandQueueCallback: CommandQueue.Callbacks
private lateinit var fakeAppIconDrawable: Drawable
@@ -118,6 +121,10 @@
fakeClock = FakeSystemClock()
fakeExecutor = FakeExecutor(fakeClock)
+ fakeWakeLock = WakeLockFake()
+ fakeWakeLockBuilder = WakeLockFake.Builder(context)
+ fakeWakeLockBuilder.setWakeLock(fakeWakeLock)
+
uiEventLoggerFake = UiEventLoggerFake()
uiEventLogger = MediaTttSenderUiEventLogger(uiEventLoggerFake)
@@ -134,6 +141,7 @@
falsingCollector,
viewUtil,
vibratorHelper,
+ fakeWakeLockBuilder,
)
chipbarCoordinator.start()
@@ -472,6 +480,36 @@
}
@Test
+ fun commandQueueCallback_almostCloseThenFarFromReceiver_wakeLockAcquiredThenReleased() {
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
+ routeInfo,
+ null
+ )
+
+ assertThat(fakeWakeLock.isHeld).isTrue()
+
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
+ routeInfo,
+ null
+ )
+
+ assertThat(fakeWakeLock.isHeld).isFalse()
+ }
+
+ @Test
+ fun commandQueueCallback_FarFromReceiver_wakeLockNeverReleased() {
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
+ routeInfo,
+ null
+ )
+
+ assertThat(fakeWakeLock.isHeld).isFalse()
+ }
+
+ @Test
fun commandQueueCallback_invalidStateParam_noChipShown() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(100, routeInfo, null)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/IconLoaderLibAppIconLoaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/IconLoaderLibAppIconLoaderTest.kt
new file mode 100644
index 0000000..9b346d0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/IconLoaderLibAppIconLoaderTest.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.mediaprojection.appselector.data
+
+import android.content.ComponentName
+import android.content.pm.ActivityInfo
+import android.content.pm.PackageManager
+import android.graphics.Bitmap
+import android.graphics.drawable.Drawable
+import androidx.test.filters.SmallTest
+import com.android.launcher3.icons.BitmapInfo
+import com.android.launcher3.icons.FastBitmapDrawable
+import com.android.launcher3.icons.IconFactory
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.shared.system.PackageManagerWrapper
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class IconLoaderLibAppIconLoaderTest : SysuiTestCase() {
+
+ private val iconFactory: IconFactory = mock()
+ private val packageManagerWrapper: PackageManagerWrapper = mock()
+ private val packageManager: PackageManager = mock()
+ private val dispatcher = Dispatchers.Unconfined
+
+ private val appIconLoader =
+ IconLoaderLibAppIconLoader(
+ backgroundDispatcher = dispatcher,
+ context = context,
+ packageManagerWrapper = packageManagerWrapper,
+ packageManager = packageManager,
+ iconFactoryProvider = { iconFactory }
+ )
+
+ @Test
+ fun loadIcon_loadsIconUsingTheSameUserId() {
+ val icon = createIcon()
+ val component = ComponentName("com.test", "TestApplication")
+ givenIcon(component, userId = 123, icon = icon)
+
+ val loadedIcon = runBlocking { appIconLoader.loadIcon(userId = 123, component = component) }
+
+ assertThat(loadedIcon).isEqualTo(icon)
+ }
+
+ private fun givenIcon(component: ComponentName, userId: Int, icon: FastBitmapDrawable) {
+ val activityInfo = mock<ActivityInfo>()
+ whenever(packageManagerWrapper.getActivityInfo(component, userId)).thenReturn(activityInfo)
+ val rawIcon = mock<Drawable>()
+ whenever(activityInfo.loadIcon(packageManager)).thenReturn(rawIcon)
+
+ val bitmapInfo = mock<BitmapInfo>()
+ whenever(iconFactory.createBadgedIconBitmap(eq(rawIcon), any())).thenReturn(bitmapInfo)
+ whenever(bitmapInfo.newIcon(context)).thenReturn(icon)
+ }
+
+ private fun createIcon(): FastBitmapDrawable =
+ FastBitmapDrawable(Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888))
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index f20c6a2..9758842 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -25,8 +25,8 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.notetask.NoteTaskIntentResolver.Companion.NOTES_ACTION
import com.android.systemui.util.mockito.whenever
-import com.android.wm.shell.floating.FloatingTasks
-import java.util.*
+import com.android.wm.shell.bubbles.Bubbles
+import java.util.Optional
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -49,8 +49,8 @@
@Mock lateinit var context: Context
@Mock lateinit var noteTaskIntentResolver: NoteTaskIntentResolver
- @Mock lateinit var floatingTasks: FloatingTasks
- @Mock lateinit var optionalFloatingTasks: Optional<FloatingTasks>
+ @Mock lateinit var bubbles: Bubbles
+ @Mock lateinit var optionalBubbles: Optional<Bubbles>
@Mock lateinit var keyguardManager: KeyguardManager
@Mock lateinit var optionalKeyguardManager: Optional<KeyguardManager>
@Mock lateinit var optionalUserManager: Optional<UserManager>
@@ -61,7 +61,7 @@
MockitoAnnotations.initMocks(this)
whenever(noteTaskIntentResolver.resolveIntent()).thenReturn(notesIntent)
- whenever(optionalFloatingTasks.orElse(null)).thenReturn(floatingTasks)
+ whenever(optionalBubbles.orElse(null)).thenReturn(bubbles)
whenever(optionalKeyguardManager.orElse(null)).thenReturn(keyguardManager)
whenever(optionalUserManager.orElse(null)).thenReturn(userManager)
whenever(userManager.isUserUnlocked).thenReturn(true)
@@ -71,7 +71,7 @@
return NoteTaskController(
context = context,
intentResolver = noteTaskIntentResolver,
- optionalFloatingTasks = optionalFloatingTasks,
+ optionalBubbles = optionalBubbles,
optionalKeyguardManager = optionalKeyguardManager,
optionalUserManager = optionalUserManager,
isEnabled = isEnabled,
@@ -85,16 +85,16 @@
createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
verify(context).startActivity(notesIntent)
- verify(floatingTasks, never()).showOrSetStashed(notesIntent)
+ verify(bubbles, never()).showAppBubble(notesIntent)
}
@Test
- fun handleSystemKey_keyguardIsUnlocked_shouldStartFloatingTask() {
+ fun handleSystemKey_keyguardIsUnlocked_shouldStartBubbles() {
whenever(keyguardManager.isKeyguardLocked).thenReturn(false)
createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
- verify(floatingTasks).showOrSetStashed(notesIntent)
+ verify(bubbles).showAppBubble(notesIntent)
verify(context, never()).startActivity(notesIntent)
}
@@ -103,17 +103,17 @@
createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_UNKNOWN)
verify(context, never()).startActivity(notesIntent)
- verify(floatingTasks, never()).showOrSetStashed(notesIntent)
+ verify(bubbles, never()).showAppBubble(notesIntent)
}
@Test
- fun handleSystemKey_floatingTasksIsNull_shouldDoNothing() {
- whenever(optionalFloatingTasks.orElse(null)).thenReturn(null)
+ fun handleSystemKey_bubblesIsNull_shouldDoNothing() {
+ whenever(optionalBubbles.orElse(null)).thenReturn(null)
createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
verify(context, never()).startActivity(notesIntent)
- verify(floatingTasks, never()).showOrSetStashed(notesIntent)
+ verify(bubbles, never()).showAppBubble(notesIntent)
}
@Test
@@ -123,7 +123,7 @@
createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
verify(context, never()).startActivity(notesIntent)
- verify(floatingTasks, never()).showOrSetStashed(notesIntent)
+ verify(bubbles, never()).showAppBubble(notesIntent)
}
@Test
@@ -133,7 +133,7 @@
createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
verify(context, never()).startActivity(notesIntent)
- verify(floatingTasks, never()).showOrSetStashed(notesIntent)
+ verify(bubbles, never()).showAppBubble(notesIntent)
}
@Test
@@ -143,7 +143,7 @@
createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
verify(context, never()).startActivity(notesIntent)
- verify(floatingTasks, never()).showOrSetStashed(notesIntent)
+ verify(bubbles, never()).showAppBubble(notesIntent)
}
@Test
@@ -151,7 +151,7 @@
createNoteTaskController(isEnabled = false).handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
verify(context, never()).startActivity(notesIntent)
- verify(floatingTasks, never()).showOrSetStashed(notesIntent)
+ verify(bubbles, never()).showAppBubble(notesIntent)
}
@Test
@@ -161,6 +161,6 @@
createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
verify(context, never()).startActivity(notesIntent)
- verify(floatingTasks, never()).showOrSetStashed(notesIntent)
+ verify(bubbles, never()).showAppBubble(notesIntent)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
index f344c8d..334089c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
@@ -21,8 +21,8 @@
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
-import com.android.wm.shell.floating.FloatingTasks
-import java.util.*
+import com.android.wm.shell.bubbles.Bubbles
+import java.util.Optional
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -43,20 +43,20 @@
internal class NoteTaskInitializerTest : SysuiTestCase() {
@Mock lateinit var commandQueue: CommandQueue
- @Mock lateinit var floatingTasks: FloatingTasks
- @Mock lateinit var optionalFloatingTasks: Optional<FloatingTasks>
+ @Mock lateinit var bubbles: Bubbles
+ @Mock lateinit var optionalBubbles: Optional<Bubbles>
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- whenever(optionalFloatingTasks.isPresent).thenReturn(true)
- whenever(optionalFloatingTasks.orElse(null)).thenReturn(floatingTasks)
+ whenever(optionalBubbles.isPresent).thenReturn(true)
+ whenever(optionalBubbles.orElse(null)).thenReturn(bubbles)
}
private fun createNoteTaskInitializer(isEnabled: Boolean = true): NoteTaskInitializer {
return NoteTaskInitializer(
- optionalFloatingTasks = optionalFloatingTasks,
+ optionalBubbles = optionalBubbles,
lazyNoteTaskController = mock(),
commandQueue = commandQueue,
isEnabled = isEnabled,
@@ -78,8 +78,8 @@
}
@Test
- fun initialize_floatingTasksNotPresent_shouldDoNothing() {
- whenever(optionalFloatingTasks.isPresent).thenReturn(false)
+ fun initialize_bubblesNotPresent_shouldDoNothing() {
+ whenever(optionalBubbles.isPresent).thenReturn(false)
createNoteTaskInitializer().initialize()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
index 4c72406..3620233 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
@@ -19,6 +19,7 @@
import com.android.systemui.privacy.PrivacyItemController
import com.android.systemui.privacy.logging.PrivacyLogger
import com.android.systemui.statusbar.phone.StatusIconContainer
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
@@ -66,6 +67,8 @@
private lateinit var broadcastDispatcher: BroadcastDispatcher
@Mock
private lateinit var safetyCenterManager: SafetyCenterManager
+ @Mock
+ private lateinit var deviceProvisionedController: DeviceProvisionedController
private val uiExecutor = FakeExecutor(FakeSystemClock())
private val backgroundExecutor = FakeExecutor(FakeSystemClock())
@@ -80,6 +83,7 @@
whenever(privacyChip.context).thenReturn(context)
whenever(privacyChip.resources).thenReturn(context.resources)
whenever(privacyChip.isAttachedToWindow).thenReturn(true)
+ whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
cameraSlotName = context.getString(com.android.internal.R.string.status_bar_camera)
microphoneSlotName = context.getString(com.android.internal.R.string.status_bar_microphone)
@@ -98,7 +102,8 @@
activityStarter,
appOpsController,
broadcastDispatcher,
- safetyCenterManager
+ safetyCenterManager,
+ deviceProvisionedController
)
backgroundExecutor.runAllReady()
@@ -199,6 +204,18 @@
)
}
+ @Test
+ fun testNoDialogWhenDeviceNotProvisioned() {
+ whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(false)
+ controller.onParentVisible()
+
+ val captor = argumentCaptor<View.OnClickListener>()
+ verify(privacyChip).setOnClickListener(capture(captor))
+
+ captor.value.onClick(privacyChip)
+ verify(privacyDialogController, never()).showDialog(any(Context::class.java))
+ }
+
private fun setPrivacyController(micCamera: Boolean, location: Boolean) {
whenever(privacyItemController.micCameraAvailable).thenReturn(micCamera)
whenever(privacyItemController.locationAvailable).thenReturn(location)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index cd7a949..72e022e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -439,6 +439,17 @@
verify(mQSPanelController).setExpanded(false);
}
+ @Test
+ public void startsListeningAfterStateChangeToExpanded_inSplitShade() {
+ QSFragment fragment = resumeAndGetFragment();
+ enableSplitShade();
+ fragment.setQsVisible(true);
+ clearInvocations(mQSPanelController);
+
+ fragment.setExpanded(true);
+ verify(mQSPanelController).setListening(true, true);
+ }
+
@Override
protected Fragment instantiate(Context context, String className, Bundle arguments) {
MockitoAnnotations.initMocks(this);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
index 3c867ab..9f28708 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
@@ -64,7 +64,7 @@
whenever(brightnessSliderFactory.create(any(), any())).thenReturn(brightnessSlider)
whenever(brightnessControllerFactory.create(any())).thenReturn(brightnessController)
whenever(qsPanel.resources).thenReturn(mContext.orCreateTestableResources.resources)
- whenever(statusBarKeyguardViewManager.isBouncerInTransit()).thenReturn(false)
+ whenever(statusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false)
whenever(qsPanel.setListening(anyBoolean())).then {
whenever(qsPanel.isListening).thenReturn(it.getArgument(0))
}
@@ -116,9 +116,9 @@
@Test
fun testIsBouncerInTransit() {
- whenever(statusBarKeyguardViewManager.isBouncerInTransit()).thenReturn(true)
+ whenever(statusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(true)
assertThat(controller.isBouncerInTransit()).isEqualTo(true)
- whenever(statusBarKeyguardViewManager.isBouncerInTransit()).thenReturn(false)
+ whenever(statusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false)
assertThat(controller.isBouncerInTransit()).isEqualTo(false)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
index b067ee7..f55d262 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
@@ -40,6 +40,8 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mockito;
@RunWith(AndroidTestingRunner.class)
@UiThreadTest
@@ -138,7 +140,7 @@
}
@Test
- public void testIconNotAnimatedWhenAllowAnimationsFalse() {
+ public void testIconStartedAndStoppedWhenAllowAnimationsFalse() {
ImageView iv = new ImageView(mContext);
AnimatedVectorDrawable d = mock(AnimatedVectorDrawable.class);
State s = new State();
@@ -148,7 +150,9 @@
mIconView.updateIcon(iv, s, false);
- verify(d, never()).start();
+ InOrder inOrder = Mockito.inOrder(d);
+ inOrder.verify(d).start();
+ inOrder.verify(d).stop();
}
private static Drawable.ConstantState fakeConstantState(Drawable otherDrawable) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java
index b652aee..cac90a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java
@@ -119,6 +119,6 @@
when(mController.isEnabledForQuickSettings()).thenReturn(true);
QSTile.State state = new QSTile.State();
mTile.handleUpdateState(state, null);
- assertEquals(state.state, Tile.STATE_ACTIVE);
+ assertEquals(state.state, Tile.STATE_INACTIVE);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
new file mode 100644
index 0000000..0aa3621
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenrecord
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.View
+import android.widget.Spinner
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.settings.UserContextProvider
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class ScreenRecordPermissionDialogTest : SysuiTestCase() {
+
+ @Mock private lateinit var starter: ActivityStarter
+ @Mock private lateinit var controller: RecordingController
+ @Mock private lateinit var userContextProvider: UserContextProvider
+ @Mock private lateinit var flags: FeatureFlags
+ @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
+ @Mock private lateinit var onStartRecordingClicked: Runnable
+
+ private lateinit var dialog: ScreenRecordPermissionDialog
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ dialog =
+ ScreenRecordPermissionDialog(
+ context,
+ controller,
+ starter,
+ dialogLaunchAnimator,
+ userContextProvider,
+ onStartRecordingClicked
+ )
+ dialog.onCreate(null)
+ whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(true)
+ }
+
+ @After
+ fun teardown() {
+ if (::dialog.isInitialized) {
+ dialog.dismiss()
+ }
+ }
+
+ @Test
+ fun testShowDialog_partialScreenSharingEnabled_optionsSpinnerIsVisible() {
+ dialog.show()
+
+ val visibility = dialog.requireViewById<Spinner>(R.id.screen_share_mode_spinner).visibility
+ assertThat(visibility).isEqualTo(View.VISIBLE)
+ }
+
+ @Test
+ fun testShowDialog_singleAppSelected_showTapsIsGone() {
+ dialog.show()
+ onSpinnerItemSelected(SINGLE_APP)
+
+ val visibility = dialog.requireViewById<View>(R.id.show_taps).visibility
+ assertThat(visibility).isEqualTo(View.GONE)
+ }
+
+ @Test
+ fun testShowDialog_entireScreenSelected_showTapsIsVisible() {
+ dialog.show()
+ onSpinnerItemSelected(ENTIRE_SCREEN)
+
+ val visibility = dialog.requireViewById<View>(R.id.show_taps).visibility
+ assertThat(visibility).isEqualTo(View.VISIBLE)
+ }
+
+ private fun onSpinnerItemSelected(position: Int) {
+ val spinner = dialog.requireViewById<Spinner>(R.id.screen_share_mode_spinner)
+ spinner.onItemSelectedListener.onItemSelected(spinner, mock(), position, /* id= */ 0)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
index bd4b94e..52462c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -311,6 +311,37 @@
}
@Test
+ fun testCallbackCalledOnUserInfoChanged() {
+ tracker.initialize(0)
+ val callback = TestCallback()
+ tracker.addCallback(callback, executor)
+ val profileID = tracker.userId + 10
+
+ `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
+ val id = invocation.getArgument<Int>(0)
+ val info = UserInfo(id, "", UserInfo.FLAG_FULL)
+ val infoProfile = UserInfo(
+ id + 10,
+ "",
+ "",
+ UserInfo.FLAG_MANAGED_PROFILE,
+ UserManager.USER_TYPE_PROFILE_MANAGED
+ )
+ infoProfile.profileGroupId = id
+ listOf(info, infoProfile)
+ }
+
+ val intent = Intent(Intent.ACTION_USER_INFO_CHANGED)
+ .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+
+ tracker.onReceive(context, intent)
+
+ assertThat(callback.calledOnUserChanged).isEqualTo(0)
+ assertThat(callback.calledOnProfilesChanged).isEqualTo(1)
+ assertThat(callback.lastUserProfiles.map { it.id }).containsExactly(0, profileID)
+ }
+
+ @Test
fun testCallbackRemoved() {
tracker.initialize(0)
val newID = 5
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index c98c1f2..7d2251e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -94,7 +94,6 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
-import com.android.systemui.camera.CameraGestureHelper;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.doze.DozeLog;
@@ -308,7 +307,7 @@
MockitoAnnotations.initMocks(this);
SystemClock systemClock = new FakeSystemClock();
mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager,
- mInteractionJankMonitor);
+ mInteractionJankMonitor, mShadeExpansionStateManager);
KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext);
keyguardStatusView.setId(R.id.keyguard_status_view);
@@ -379,7 +378,7 @@
mDumpManager,
mock(HeadsUpManagerPhone.class),
new StatusBarStateControllerImpl(new UiEventLoggerFake(), mDumpManager,
- mInteractionJankMonitor),
+ mInteractionJankMonitor, mShadeExpansionStateManager),
mKeyguardBypassController,
mDozeParameters,
mScreenOffAnimationController);
@@ -492,9 +491,9 @@
mUnlockedScreenOffAnimationController,
mShadeTransitionController,
systemClock,
- mock(CameraGestureHelper.class),
mKeyguardBottomAreaViewModel,
- mKeyguardBottomAreaInteractor);
+ mKeyguardBottomAreaInteractor,
+ mDumpManager);
mNotificationPanelViewController.initDependencies(
mCentralSurfaces,
() -> {},
@@ -854,7 +853,7 @@
AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD.getId(),
null);
- verify(mStatusBarKeyguardViewManager).showBouncer(true);
+ verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(true);
}
@Test
@@ -864,7 +863,7 @@
AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP.getId(),
null);
- verify(mStatusBarKeyguardViewManager).showBouncer(true);
+ verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(true);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 95cf9d6..d7d17b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -239,9 +239,9 @@
@Test
public void setPanelExpanded_notFocusable_altFocusable_whenPanelIsOpen() {
- mNotificationShadeWindowController.setPanelExpanded(true);
+ mNotificationShadeWindowController.onShadeExpansionFullyChanged(true);
clearInvocations(mWindowManager);
- mNotificationShadeWindowController.setPanelExpanded(true);
+ mNotificationShadeWindowController.onShadeExpansionFullyChanged(true);
verifyNoMoreInteractions(mWindowManager);
mNotificationShadeWindowController.setNotificationShadeFocusable(true);
@@ -313,7 +313,7 @@
verifyNoMoreInteractions(mWindowManager);
clearInvocations(mWindowManager);
- mNotificationShadeWindowController.batchApplyWindowLayoutParams(()-> {
+ mNotificationShadeWindowController.batchApplyWindowLayoutParams(() -> {
mNotificationShadeWindowController.setForceDozeBrightness(false);
verify(mWindowManager, never()).updateViewLayout(any(), any());
});
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
index a4a7995..a6c80ab6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
@@ -152,7 +152,7 @@
// WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
when(mStatusBarStateController.isDozing()).thenReturn(false);
- when(mStatusBarKeyguardViewManager.isShowingAlternateAuth()).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.isShowingAlternateBouncer()).thenReturn(true);
when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false);
// THEN we should intercept touch
@@ -165,7 +165,7 @@
// WHEN not showing alt auth, not dozing, drag down helper doesn't want to intercept
when(mStatusBarStateController.isDozing()).thenReturn(false);
- when(mStatusBarKeyguardViewManager.isShowingAlternateAuth()).thenReturn(false);
+ when(mStatusBarKeyguardViewManager.isShowingAlternateBouncer()).thenReturn(false);
when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false);
// THEN we shouldn't intercept touch
@@ -178,7 +178,7 @@
// WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
when(mStatusBarStateController.isDozing()).thenReturn(false);
- when(mStatusBarKeyguardViewManager.isShowingAlternateAuth()).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.isShowingAlternateBouncer()).thenReturn(true);
when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false);
// THEN we should handle the touch
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index 70cbc64..28bd26a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -33,6 +33,7 @@
import com.android.systemui.util.mockito.eq
import junit.framework.Assert.assertEquals
import junit.framework.Assert.fail
+import org.json.JSONException
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -238,4 +239,52 @@
pluginListener.onPluginDisconnected(plugin2)
assertEquals(1, changeCallCount)
}
+
+ @Test
+ fun jsonDeserialization_gotExpectedObject() {
+ val expected = ClockRegistry.ClockSetting("ID", 500)
+ val actual = ClockRegistry.ClockSetting.deserialize("""{
+ "clockId":"ID",
+ "_applied_timestamp":500
+ }""")
+ assertEquals(expected, actual)
+ }
+
+ @Test
+ fun jsonDeserialization_noTimestamp_gotExpectedObject() {
+ val expected = ClockRegistry.ClockSetting("ID", null)
+ val actual = ClockRegistry.ClockSetting.deserialize("{\"clockId\":\"ID\"}")
+ assertEquals(expected, actual)
+ }
+
+ @Test
+ fun jsonDeserialization_nullTimestamp_gotExpectedObject() {
+ val expected = ClockRegistry.ClockSetting("ID", null)
+ val actual = ClockRegistry.ClockSetting.deserialize("""{
+ "clockId":"ID",
+ "_applied_timestamp":null
+ }""")
+ assertEquals(expected, actual)
+ }
+
+ @Test(expected = JSONException::class)
+ fun jsonDeserialization_noId_threwException() {
+ val expected = ClockRegistry.ClockSetting("ID", 500)
+ val actual = ClockRegistry.ClockSetting.deserialize("{\"_applied_timestamp\":500}")
+ assertEquals(expected, actual)
+ }
+
+ @Test
+ fun jsonSerialization_gotExpectedString() {
+ val expected = "{\"clockId\":\"ID\",\"_applied_timestamp\":500}"
+ val actual = ClockRegistry.ClockSetting.serialize( ClockRegistry.ClockSetting("ID", 500))
+ assertEquals(expected, actual)
+ }
+
+ @Test
+ fun jsonSerialization_noTimestamp_gotExpectedString() {
+ val expected = "{\"clockId\":\"ID\"}"
+ val actual = ClockRegistry.ClockSetting.serialize( ClockRegistry.ClockSetting("ID", null))
+ assertEquals(expected, actual)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index ec5d089..fe4da47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -20,6 +20,7 @@
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK;
+import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT;
import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_TIMEOUT;
import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED;
@@ -54,6 +55,7 @@
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.Instrumentation;
@@ -88,6 +90,7 @@
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FaceHelpMessageDeferral;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dock.DockManager;
@@ -173,6 +176,8 @@
private FaceHelpMessageDeferral mFaceHelpMessageDeferral;
@Mock
private ScreenLifecycle mScreenLifecycle;
+ @Mock
+ private AuthController mAuthController;
@Captor
private ArgumentCaptor<DockManager.AlignmentStateListener> mAlignmentListener;
@Captor
@@ -263,8 +268,9 @@
mWakeLockBuilder,
mKeyguardStateController, mStatusBarStateController, mKeyguardUpdateMonitor,
mDockManager, mBroadcastDispatcher, mDevicePolicyManager, mIBatteryStats,
- mUserManager, mExecutor, mExecutor, mFalsingManager, mLockPatternUtils,
- mScreenLifecycle, mKeyguardBypassController, mAccessibilityManager,
+ mUserManager, mExecutor, mExecutor, mFalsingManager,
+ mAuthController, mLockPatternUtils, mScreenLifecycle,
+ mKeyguardBypassController, mAccessibilityManager,
mFaceHelpMessageDeferral);
mController.init();
mController.setIndicationArea(mIndicationArea);
@@ -1061,7 +1067,7 @@
// GIVEN a trust granted message but trust isn't granted
final String trustGrantedMsg = "testing trust granted message";
- mController.getKeyguardCallback().showTrustGrantedMessage(trustGrantedMsg);
+ mController.getKeyguardCallback().onTrustGrantedWithFlags(0, 0, trustGrantedMsg);
verifyHideIndication(INDICATION_TYPE_TRUST);
@@ -1085,7 +1091,7 @@
// WHEN the showTrustGranted method is called
final String trustGrantedMsg = "testing trust granted message";
- mController.getKeyguardCallback().showTrustGrantedMessage(trustGrantedMsg);
+ mController.getKeyguardCallback().onTrustGrantedWithFlags(0, 0, trustGrantedMsg);
// THEN verify the trust granted message shows
verifyIndicationMessage(
@@ -1102,7 +1108,7 @@
when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
// WHEN the showTrustGranted method is called with a null message
- mController.getKeyguardCallback().showTrustGrantedMessage(null);
+ mController.getKeyguardCallback().onTrustGrantedWithFlags(0, 0, null);
// THEN verify the default trust granted message shows
verifyIndicationMessage(
@@ -1119,7 +1125,7 @@
when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
// WHEN the showTrustGranted method is called with an EMPTY string
- mController.getKeyguardCallback().showTrustGrantedMessage("");
+ mController.getKeyguardCallback().onTrustGrantedWithFlags(0, 0, "");
// THEN verify NO trust message is shown
verifyNoMessage(INDICATION_TYPE_TRUST);
@@ -1374,6 +1380,125 @@
}
+ @Test
+ public void onBiometricError_faceLockedOutFirstTime_showsThePassedInMessage() {
+ createController();
+ onFaceLockoutError("first lockout");
+
+ verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE, "first lockout");
+ }
+
+ @Test
+ public void onBiometricError_faceLockedOutFirstTimeAndFpAllowed_showsTheFpFollowupMessage() {
+ createController();
+ fingerprintUnlockIsPossible();
+ onFaceLockoutError("first lockout");
+
+ verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
+ mContext.getString(R.string.keyguard_suggest_fingerprint));
+ }
+
+ @Test
+ public void onBiometricError_faceLockedOutFirstTimeAndFpNotAllowed_showsDefaultFollowup() {
+ createController();
+ fingerprintUnlockIsNotPossible();
+ onFaceLockoutError("first lockout");
+
+ verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
+ mContext.getString(R.string.keyguard_unlock));
+ }
+
+ @Test
+ public void onBiometricError_faceLockedOutSecondTimeInSession_showsUnavailableMessage() {
+ createController();
+ onFaceLockoutError("first lockout");
+ clearInvocations(mRotateTextViewController);
+
+ onFaceLockoutError("second lockout");
+
+ verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE,
+ mContext.getString(R.string.keyguard_face_unlock_unavailable));
+ }
+
+ @Test
+ public void onBiometricError_faceLockedOutSecondTimeOnBouncer_showsUnavailableMessage() {
+ createController();
+ onFaceLockoutError("first lockout");
+ clearInvocations(mRotateTextViewController);
+ when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
+
+ onFaceLockoutError("second lockout");
+
+ verify(mStatusBarKeyguardViewManager)
+ .setKeyguardMessage(
+ eq(mContext.getString(R.string.keyguard_face_unlock_unavailable)),
+ any());
+ }
+
+ @Test
+ public void onBiometricError_faceLockedOutSecondTimeButUdfpsActive_showsNoMessage() {
+ createController();
+ onFaceLockoutError("first lockout");
+ clearInvocations(mRotateTextViewController);
+
+ when(mAuthController.isUdfpsFingerDown()).thenReturn(true);
+ onFaceLockoutError("second lockout");
+
+ verifyNoMoreInteractions(mRotateTextViewController);
+ }
+
+ @Test
+ public void onBiometricError_faceLockedOutAgainAndFpAllowed_showsTheFpFollowupMessage() {
+ createController();
+ fingerprintUnlockIsPossible();
+ onFaceLockoutError("first lockout");
+ clearInvocations(mRotateTextViewController);
+
+ onFaceLockoutError("second lockout");
+
+ verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
+ mContext.getString(R.string.keyguard_suggest_fingerprint));
+ }
+
+ @Test
+ public void onBiometricError_faceLockedOutAgainAndFpNotAllowed_showsDefaultFollowup() {
+ createController();
+ fingerprintUnlockIsNotPossible();
+ onFaceLockoutError("first lockout");
+ clearInvocations(mRotateTextViewController);
+
+ onFaceLockoutError("second lockout");
+
+ verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
+ mContext.getString(R.string.keyguard_unlock));
+ }
+
+ @Test
+ public void onBiometricError_whenFaceLockoutReset_onLockOutError_showsPassedInMessage() {
+ createController();
+ onFaceLockoutError("first lockout");
+ clearInvocations(mRotateTextViewController);
+ when(mKeyguardUpdateMonitor.isFaceLockedOut()).thenReturn(false);
+ mKeyguardUpdateMonitorCallback.onLockedOutStateChanged(BiometricSourceType.FACE);
+
+ onFaceLockoutError("second lockout");
+
+ verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE, "second lockout");
+ }
+
+ @Test
+ public void onBiometricError_whenFaceIsLocked_onMultipleLockOutErrors_showUnavailableMessage() {
+ createController();
+ onFaceLockoutError("first lockout");
+ clearInvocations(mRotateTextViewController);
+ when(mKeyguardUpdateMonitor.isFaceLockedOut()).thenReturn(true);
+ mKeyguardUpdateMonitorCallback.onLockedOutStateChanged(BiometricSourceType.FACE);
+
+ onFaceLockoutError("second lockout");
+
+ verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE,
+ mContext.getString(R.string.keyguard_face_unlock_unavailable));
+ }
private void sendUpdateDisclosureBroadcast() {
mBroadcastReceiver.onReceive(mContext, new Intent());
@@ -1422,4 +1547,33 @@
anyObject(), anyBoolean());
}
}
+
+ private void verifyIndicationShown(int indicationType, String message) {
+ verify(mRotateTextViewController)
+ .updateIndication(eq(indicationType),
+ mKeyguardIndicationCaptor.capture(),
+ eq(true));
+ assertThat(mKeyguardIndicationCaptor.getValue().getMessage().toString())
+ .isEqualTo(message);
+ }
+
+ private void fingerprintUnlockIsNotPossible() {
+ setupFingerprintUnlockPossible(false);
+ }
+
+ private void fingerprintUnlockIsPossible() {
+ setupFingerprintUnlockPossible(true);
+ }
+
+ private void setupFingerprintUnlockPossible(boolean possible) {
+ when(mKeyguardUpdateMonitor
+ .getCachedIsUnlockWithFingerprintPossible(KeyguardUpdateMonitor.getCurrentUser()))
+ .thenReturn(possible);
+ }
+
+ private void onFaceLockoutError(String errMsg) {
+ mKeyguardUpdateMonitorCallback.onBiometricError(FACE_ERROR_LOCKOUT_PERMANENT,
+ errMsg,
+ BiometricSourceType.FACE);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index 1d8e5de..5124eb9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -25,6 +25,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.shade.ShadeExpansionStateManager
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -48,6 +49,7 @@
@Mock lateinit var interactionJankMonitor: InteractionJankMonitor
@Mock private lateinit var mockDarkAnimator: ObjectAnimator
+ @Mock private lateinit var shadeExpansionStateManager: ShadeExpansionStateManager
private lateinit var controller: StatusBarStateControllerImpl
private lateinit var uiEventLogger: UiEventLoggerFake
@@ -62,7 +64,7 @@
controller = object : StatusBarStateControllerImpl(
uiEventLogger,
mock(DumpManager::class.java),
- interactionJankMonitor
+ interactionJankMonitor, shadeExpansionStateManager
) {
override fun createDarkAnimator(): ObjectAnimator { return mockDarkAnimator }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileIconCarrierIdOverridesFake.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileIconCarrierIdOverridesFake.kt
new file mode 100644
index 0000000..62b4e7b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileIconCarrierIdOverridesFake.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.connectivity
+
+import android.content.res.Resources
+import com.android.settingslib.mobile.MobileIconCarrierIdOverrides
+
+typealias CarrierId = Int
+
+typealias NetworkType = String
+
+typealias ResId = Int
+
+class MobileIconCarrierIdOverridesFake : MobileIconCarrierIdOverrides {
+ /** Backing for [carrierIdEntryExists] */
+ var overriddenIds = mutableSetOf<Int>()
+
+ /** Backing for [getOverrideFor]. Map should be Map< CarrierId < NetworkType, ResId>> */
+ var overridesByCarrierId = mutableMapOf<CarrierId, Map<NetworkType, ResId>>()
+
+ override fun getOverrideFor(
+ carrierId: CarrierId,
+ networkType: NetworkType,
+ resources: Resources
+ ): ResId {
+ if (!overriddenIds.contains(carrierId)) return 0
+
+ return overridesByCarrierId[carrierId]?.get(networkType) ?: 0
+ }
+
+ override fun carrierIdEntryExists(carrierId: Int): Boolean {
+ return overriddenIds.contains(carrierId)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileStateTest.java
deleted file mode 100644
index 7ddfde3..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileStateTest.java
+++ /dev/null
@@ -1,137 +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.statusbar.connectivity;
-
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
-
-import com.android.settingslib.mobile.TelephonyIcons;
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class MobileStateTest extends SysuiTestCase {
-
- private final MobileState mState = new MobileState();
-
- @Before
- public void setUp() {
- }
-
- @Test
- public void testIsDataDisabledOrNotDefault_dataDisabled() {
- mState.iconGroup = TelephonyIcons.DATA_DISABLED;
- mState.userSetup = true;
-
- assertTrue(mState.isDataDisabledOrNotDefault());
- }
-
- @Test
- public void testIsDataDisabledOrNotDefault_notDefaultData() {
- mState.iconGroup = TelephonyIcons.NOT_DEFAULT_DATA;
- mState.userSetup = true;
-
- assertTrue(mState.isDataDisabledOrNotDefault());
- }
-
- @Test
- public void testIsDataDisabledOrNotDefault_notDisabled() {
- mState.iconGroup = TelephonyIcons.G;
- mState.userSetup = true;
-
- assertFalse(mState.isDataDisabledOrNotDefault());
- }
-
- @Test
- public void testHasActivityIn_noData_noActivity() {
- mState.dataConnected = false;
- mState.carrierNetworkChangeMode = false;
- mState.activityIn = false;
-
- assertFalse(mState.hasActivityIn());
- }
-
- @Test
- public void testHasActivityIn_noData_activityIn() {
- mState.dataConnected = false;
- mState.carrierNetworkChangeMode = false;
- mState.activityIn = true;
-
- assertFalse(mState.hasActivityIn());
- }
-
- @Test
- public void testHasActivityIn_dataConnected_activityIn() {
- mState.dataConnected = true;
- mState.carrierNetworkChangeMode = false;
- mState.activityIn = true;
-
- assertTrue(mState.hasActivityIn());
- }
-
- @Test
- public void testHasActivityIn_carrierNetworkChange() {
- mState.dataConnected = true;
- mState.carrierNetworkChangeMode = true;
- mState.activityIn = true;
-
- assertFalse(mState.hasActivityIn());
- }
-
- @Test
- public void testHasActivityOut_noData_noActivity() {
- mState.dataConnected = false;
- mState.carrierNetworkChangeMode = false;
- mState.activityOut = false;
-
- assertFalse(mState.hasActivityOut());
- }
-
- @Test
- public void testHasActivityOut_noData_activityOut() {
- mState.dataConnected = false;
- mState.carrierNetworkChangeMode = false;
- mState.activityOut = true;
-
- assertFalse(mState.hasActivityOut());
- }
-
- @Test
- public void testHasActivityOut_dataConnected_activityOut() {
- mState.dataConnected = true;
- mState.carrierNetworkChangeMode = false;
- mState.activityOut = true;
-
- assertTrue(mState.hasActivityOut());
- }
-
- @Test
- public void testHasActivityOut_carrierNetworkChange() {
- mState.dataConnected = true;
- mState.carrierNetworkChangeMode = true;
- mState.activityOut = true;
-
- assertFalse(mState.hasActivityOut());
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileStateTest.kt
new file mode 100644
index 0000000..a226ded
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileStateTest.kt
@@ -0,0 +1,119 @@
+/*
+ * 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.connectivity
+
+import android.test.suitebuilder.annotation.SmallTest
+import android.testing.AndroidTestingRunner
+import com.android.settingslib.mobile.TelephonyIcons
+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
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class MobileStateTest : SysuiTestCase() {
+
+ private val state = MobileState()
+ @Before fun setUp() {}
+
+ @Test
+ fun testIsDataDisabledOrNotDefault_dataDisabled() {
+ state.iconGroup = TelephonyIcons.DATA_DISABLED
+ state.userSetup = true
+ assertTrue(state.isDataDisabledOrNotDefault)
+ }
+
+ @Test
+ fun testIsDataDisabledOrNotDefault_notDefaultData() {
+ state.iconGroup = TelephonyIcons.NOT_DEFAULT_DATA
+ state.userSetup = true
+ assertTrue(state.isDataDisabledOrNotDefault)
+ }
+
+ @Test
+ fun testIsDataDisabledOrNotDefault_notDisabled() {
+ state.iconGroup = TelephonyIcons.G
+ state.userSetup = true
+ assertFalse(state.isDataDisabledOrNotDefault)
+ }
+
+ @Test
+ fun testHasActivityIn_noData_noActivity() {
+ state.dataConnected = false
+ state.carrierNetworkChangeMode = false
+ state.activityIn = false
+ assertFalse(state.hasActivityIn())
+ }
+
+ @Test
+ fun testHasActivityIn_noData_activityIn() {
+ state.dataConnected = false
+ state.carrierNetworkChangeMode = false
+ state.activityIn = true
+ assertFalse(state.hasActivityIn())
+ }
+
+ @Test
+ fun testHasActivityIn_dataConnected_activityIn() {
+ state.dataConnected = true
+ state.carrierNetworkChangeMode = false
+ state.activityIn = true
+ assertTrue(state.hasActivityIn())
+ }
+
+ @Test
+ fun testHasActivityIn_carrierNetworkChange() {
+ state.dataConnected = true
+ state.carrierNetworkChangeMode = true
+ state.activityIn = true
+ assertFalse(state.hasActivityIn())
+ }
+
+ @Test
+ fun testHasActivityOut_noData_noActivity() {
+ state.dataConnected = false
+ state.carrierNetworkChangeMode = false
+ state.activityOut = false
+ assertFalse(state.hasActivityOut())
+ }
+
+ @Test
+ fun testHasActivityOut_noData_activityOut() {
+ state.dataConnected = false
+ state.carrierNetworkChangeMode = false
+ state.activityOut = true
+ assertFalse(state.hasActivityOut())
+ }
+
+ @Test
+ fun testHasActivityOut_dataConnected_activityOut() {
+ state.dataConnected = true
+ state.carrierNetworkChangeMode = false
+ state.activityOut = true
+ assertTrue(state.hasActivityOut())
+ }
+
+ @Test
+ fun testHasActivityOut_carrierNetworkChange() {
+ state.dataConnected = true
+ state.carrierNetworkChangeMode = true
+ state.activityOut = true
+ assertFalse(state.hasActivityOut())
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
index 9c65fac..9c870b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
@@ -71,6 +71,7 @@
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.log.LogBuffer;
+import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.telephony.TelephonyListenerManager;
@@ -125,6 +126,8 @@
protected CarrierConfigTracker mCarrierConfigTracker;
protected FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
protected Handler mMainHandler;
+ // Use a real mobile mappings object since lots of tests rely on it
+ protected FakeMobileMappingsProxy mMobileMappingsProxy = new FakeMobileMappingsProxy();
protected WifiStatusTrackerFactory mWifiStatusTrackerFactory;
protected MobileSignalControllerFactory mMobileFactory;
@@ -219,10 +222,13 @@
mWifiStatusTrackerFactory = new WifiStatusTrackerFactory(
mContext, mMockWm, mMockNsm, mMockCm, mMainHandler);
+ // Most of these tests rely on the actual MobileMappings behavior
+ mMobileMappingsProxy.setUseRealImpl(true);
mMobileFactory = new MobileSignalControllerFactory(
mContext,
mCallbackHandler,
- mCarrierConfigTracker
+ mCarrierConfigTracker,
+ mMobileMappingsProxy
);
mNetworkController = new NetworkControllerImpl(mContext,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
index 4bed4a1..1d11226 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
@@ -18,12 +18,21 @@
import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
+import static android.telephony.TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED;
+import static android.telephony.TelephonyManager.EXTRA_CARRIER_ID;
+import static android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID;
+import static com.android.settingslib.mobile.TelephonyIcons.NR_5G_PLUS;
+
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.content.Intent;
import android.net.NetworkCapabilities;
import android.os.Handler;
import android.os.Looper;
@@ -35,6 +44,7 @@
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
+import com.android.settingslib.SignalIcon.MobileIconGroup;
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.dump.DumpManager;
@@ -45,6 +55,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.HashMap;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@@ -329,6 +341,57 @@
assertFalse(mNetworkController.isMobileDataNetworkInService());
}
+ @Test
+ public void mobileSignalController_getsCarrierId() {
+ when(mMockTm.getSimCarrierId()).thenReturn(1);
+ setupDefaultSignal();
+
+ assertEquals(1, mMobileSignalController.getState().getCarrierId());
+ }
+
+ @Test
+ public void mobileSignalController_updatesCarrierId_onChange() {
+ when(mMockTm.getSimCarrierId()).thenReturn(1);
+ setupDefaultSignal();
+
+ // Updates are sent down through this broadcast, we can send the intent directly
+ Intent intent = new Intent(ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED);
+ intent.putExtra(EXTRA_SUBSCRIPTION_ID, mSubId);
+ intent.putExtra(EXTRA_CARRIER_ID, 2);
+
+ mMobileSignalController.handleBroadcast(intent);
+
+ assertEquals(2, mMobileSignalController.getState().getCarrierId());
+ }
+
+ @Test
+ public void networkTypeIcon_hasCarrierIdOverride() {
+ int fakeCarrier = 1;
+ int fakeIconOverride = 12345;
+ int testDataNetType = 100;
+ String testDataString = "100";
+ HashMap<String, MobileIconGroup> testMap = new HashMap<>();
+ testMap.put(testDataString, NR_5G_PLUS);
+
+ // Pretend that there is an override for this icon, and this carrier ID
+ NetworkTypeResIdCache mockCache = mock(NetworkTypeResIdCache.class);
+ when(mockCache.get(eq(NR_5G_PLUS), eq(fakeCarrier), any())).thenReturn(fakeIconOverride);
+
+ // Turn off the default mobile mapping, so we can override
+ mMobileMappingsProxy.setUseRealImpl(false);
+ mMobileMappingsProxy.setIconMap(testMap);
+ // Use the mocked cache
+ mMobileSignalController.mCurrentState.setNetworkTypeResIdCache(mockCache);
+ // Rebuild the network map
+ mMobileSignalController.setConfiguration(mConfig);
+ when(mMockTm.getSimCarrierId()).thenReturn(fakeCarrier);
+
+ setupDefaultSignal();
+ updateDataConnectionState(TelephonyManager.DATA_CONNECTED, testDataNetType);
+
+ verifyDataIndicators(fakeIconOverride);
+ }
+
private void testDataActivity(int direction, boolean in, boolean out) {
updateDataActivity(direction);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkTypeResIdCacheTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkTypeResIdCacheTest.kt
new file mode 100644
index 0000000..9e73487
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkTypeResIdCacheTest.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.connectivity
+
+import android.test.suitebuilder.annotation.SmallTest
+import android.testing.AndroidTestingRunner
+import com.android.settingslib.SignalIcon.MobileIconGroup
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class NetworkTypeResIdCacheTest : SysuiTestCase() {
+ private lateinit var cache: NetworkTypeResIdCache
+ private var overrides = MobileIconCarrierIdOverridesFake()
+
+ @Before
+ fun setUp() {
+ cache = NetworkTypeResIdCache(overrides)
+ }
+
+ @Test
+ fun carrier1_noOverride_usesDefault() {
+ assertThat(cache.get(group1, CARRIER_1, context)).isEqualTo(iconDefault1)
+ }
+
+ @Test
+ fun carrier1_overridden_usesOverride() {
+ overrides.overriddenIds.add(CARRIER_1)
+ overrides.overridesByCarrierId[CARRIER_1] = mapOf(NET_TYPE_1 to iconOverride1)
+
+ assertThat(cache.get(group1, CARRIER_1, context)).isEqualTo(iconOverride1)
+ }
+
+ @Test
+ fun carrier1_override_carrier2UsesDefault() {
+ overrides.overriddenIds.add(CARRIER_1)
+ overrides.overridesByCarrierId[CARRIER_1] = mapOf(NET_TYPE_1 to iconOverride1)
+
+ assertThat(cache.get(group1, CARRIER_2, context)).isEqualTo(iconDefault1)
+ }
+
+ @Test
+ fun carrier1_overrideType1_type2UsesDefault() {
+ overrides.overriddenIds.add(CARRIER_1)
+ overrides.overridesByCarrierId[CARRIER_1] = mapOf(NET_TYPE_1 to iconOverride1)
+
+ assertThat(cache.get(group2, CARRIER_1, context)).isEqualTo(iconDefault2)
+ }
+
+ companion object {
+ // Simplified icon overrides here
+ const val CARRIER_1 = 1
+ const val CARRIER_2 = 2
+
+ const val NET_TYPE_1 = "one"
+ const val iconDefault1 = 123
+ const val iconOverride1 = 321
+ val group1 = MobileIconGroup(NET_TYPE_1, /* dataContentDesc */ 0, iconDefault1)
+
+ const val NET_TYPE_2 = "two"
+ const val iconDefault2 = 234
+
+ val group2 = MobileIconGroup(NET_TYPE_2, /* dataContentDesc*/ 0, iconDefault2)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index b2dc842..7117c23 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -41,6 +41,7 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
@@ -88,6 +89,7 @@
@Mock private NotificationVisibilityProvider mVisibilityProvider;
@Mock private NotifPipeline mNotifPipeline;
@Mock private NotificationListener mListener;
+ @Mock private ShadeExpansionStateManager mShadeExpansionStateManager;
private NotificationEntry mEntry;
private TestableNotificationLogger mLogger;
@@ -118,6 +120,7 @@
mVisibilityProvider,
mNotifPipeline,
mock(StatusBarStateControllerImpl.class),
+ mShadeExpansionStateManager,
mBarService,
mExpansionStateLogger
);
@@ -152,7 +155,7 @@
when(mListContainer.isInVisibleLocation(any())).thenReturn(true);
when(mActiveNotifEntries.getValue()).thenReturn(Lists.newArrayList(mEntry));
- mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
+ mLogger.onChildLocationsChanged();
TestableLooper.get(this).processAllMessages();
mUiBgExecutor.runAllReady();
@@ -162,7 +165,7 @@
// |mEntry| won't change visibility, so it shouldn't be reported again:
Mockito.reset(mBarService);
- mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
+ mLogger.onChildLocationsChanged();
TestableLooper.get(this).processAllMessages();
mUiBgExecutor.runAllReady();
@@ -174,7 +177,7 @@
throws Exception {
when(mListContainer.isInVisibleLocation(any())).thenReturn(true);
when(mActiveNotifEntries.getValue()).thenReturn(Lists.newArrayList(mEntry));
- mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
+ mLogger.onChildLocationsChanged();
TestableLooper.get(this).processAllMessages();
mUiBgExecutor.runAllReady();
Mockito.reset(mBarService);
@@ -189,13 +192,13 @@
}
private void setStateAsleep() {
- mLogger.onPanelExpandedChanged(true);
+ mLogger.onShadeExpansionFullyChanged(true);
mLogger.onDozingChanged(true);
mLogger.onStateChanged(StatusBarState.KEYGUARD);
}
private void setStateAwake() {
- mLogger.onPanelExpandedChanged(false);
+ mLogger.onShadeExpansionFullyChanged(false);
mLogger.onDozingChanged(false);
mLogger.onStateChanged(StatusBarState.SHADE);
}
@@ -221,7 +224,7 @@
when(mActiveNotifEntries.getValue()).thenReturn(Lists.newArrayList(mEntry));
setStateAwake();
// Now expand panel
- mLogger.onPanelExpandedChanged(true);
+ mLogger.onShadeExpansionFullyChanged(true);
assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
assertFalse(mNotificationPanelLoggerFake.get(0).isLockscreen);
assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length);
@@ -263,6 +266,7 @@
NotificationVisibilityProvider visibilityProvider,
NotifPipeline notifPipeline,
StatusBarStateControllerImpl statusBarStateController,
+ ShadeExpansionStateManager shadeExpansionStateManager,
IStatusBarService barService,
ExpansionStateLogger expansionStateLogger) {
super(
@@ -272,6 +276,7 @@
visibilityProvider,
notifPipeline,
statusBarStateController,
+ shadeExpansionStateManager,
expansionStateLogger,
mNotificationPanelLoggerFake
);
@@ -280,9 +285,5 @@
// Make this on the current thread so we can wait for it during tests.
mHandler = Handler.createAsync(Looper.myLooper());
}
-
- OnChildLocationsChangedListener getChildLocationsChangedListenerForTest() {
- return mNotificationLocationsChangedListener;
- }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 137842e..12cc114 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -232,7 +232,6 @@
@Test
public void testUserLockedResetEvenWhenNoChildren() {
mGroupRow.setUserLocked(true);
- mGroupRow.removeAllChildren();
mGroupRow.setUserLocked(false);
assertFalse("The childrencontainer should not be userlocked but is, the state "
+ "seems out of sync.", mGroupRow.getChildrenContainer().isUserLocked());
@@ -240,12 +239,11 @@
@Test
public void testReinflatedOnDensityChange() {
- mGroupRow.setUserLocked(true);
- mGroupRow.removeAllChildren();
- mGroupRow.setUserLocked(false);
NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class);
- mGroupRow.setChildrenContainer(mockContainer);
- mGroupRow.onDensityOrFontScaleChanged();
+ mNotifRow.setChildrenContainer(mockContainer);
+
+ mNotifRow.onDensityOrFontScaleChanged();
+
verify(mockContainer).reInflateViews(any(), any());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 112e759..2b189b3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -56,6 +56,7 @@
import com.android.systemui.media.controls.util.MediaFeatureFlag;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -151,7 +152,8 @@
mock(ConfigurationControllerImpl.class),
new Handler(mTestLooper.getLooper()),
mock(AccessibilityManagerWrapper.class),
- mock(UiEventLogger.class)
+ mock(UiEventLogger.class),
+ mock(ShadeExpansionStateManager.class)
);
mIconManager = new IconManager(
mock(CommonNotifCollection.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 40aec82..743e7d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -118,7 +118,7 @@
@Test
fun resetViewStates_expansionChanging_notificationBecomesTransparent() {
- whenever(mStatusBarKeyguardViewManager.isBouncerInTransit).thenReturn(false)
+ whenever(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit).thenReturn(false)
resetViewStates_expansionChanging_notificationAlphaUpdated(
expansionFraction = 0.25f,
expectedAlpha = 0.0f
@@ -127,7 +127,7 @@
@Test
fun resetViewStates_expansionChangingWhileBouncerInTransit_viewBecomesTransparent() {
- whenever(mStatusBarKeyguardViewManager.isBouncerInTransit).thenReturn(true)
+ whenever(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit).thenReturn(true)
resetViewStates_expansionChanging_notificationAlphaUpdated(
expansionFraction = 0.85f,
expectedAlpha = 0.0f
@@ -136,7 +136,7 @@
@Test
fun resetViewStates_expansionChanging_notificationAlphaUpdated() {
- whenever(mStatusBarKeyguardViewManager.isBouncerInTransit).thenReturn(false)
+ whenever(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit).thenReturn(false)
resetViewStates_expansionChanging_notificationAlphaUpdated(
expansionFraction = 0.6f,
expectedAlpha = getContentAlpha(0.6f)
@@ -145,7 +145,7 @@
@Test
fun resetViewStates_expansionChangingWhileBouncerInTransit_notificationAlphaUpdated() {
- whenever(mStatusBarKeyguardViewManager.isBouncerInTransit).thenReturn(true)
+ whenever(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit).thenReturn(true)
resetViewStates_expansionChanging_notificationAlphaUpdated(
expansionFraction = 0.95f,
expectedAlpha = aboutToShowBouncerProgress(0.95f)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index b850cf1..7246116 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -140,24 +140,24 @@
}
@Test
- public void onBiometricAuthenticated_whenFingerprintAndBiometricsDisallowed_showBouncer() {
+ public void onBiometricAuthenticated_fingerprintAndBiometricsDisallowed_showPrimaryBouncer() {
when(mUpdateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */))
.thenReturn(false);
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */);
- verify(mStatusBarKeyguardViewManager).showBouncer(anyBoolean());
+ verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean());
verify(mStatusBarKeyguardViewManager, never()).notifyKeyguardAuthenticated(anyBoolean());
assertThat(mBiometricUnlockController.getMode())
.isEqualTo(BiometricUnlockController.MODE_SHOW_BOUNCER);
}
@Test
- public void onBiometricAuthenticated_whenFingerprint_nonStrongBioDisallowed_showBouncer() {
+ public void onBiometricAuthenticated_fingerprint_nonStrongBioDisallowed_showPrimaryBouncer() {
when(mUpdateMonitor.isUnlockingWithBiometricAllowed(false /* isStrongBiometric */))
.thenReturn(false);
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
BiometricSourceType.FINGERPRINT, false /* isStrongBiometric */);
- verify(mStatusBarKeyguardViewManager).showBouncer(anyBoolean());
+ verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean());
assertThat(mBiometricUnlockController.getMode())
.isEqualTo(BiometricUnlockController.MODE_SHOW_BOUNCER);
assertThat(mBiometricUnlockController.getBiometricType())
@@ -207,7 +207,7 @@
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */);
- verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
+ verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean());
verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false));
assertThat(mBiometricUnlockController.getMode())
.isEqualTo(BiometricUnlockController.MODE_UNLOCK_COLLAPSING);
@@ -216,7 +216,7 @@
@Test
public void onBiometricAuthenticated_whenFingerprintOnBouncer_dismissBouncer() {
when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
- when(mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()).thenReturn(true);
// the value of isStrongBiometric doesn't matter here since we only care about the returned
// value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
@@ -274,7 +274,7 @@
}
@Test
- public void onBiometricAuthenticated_whenFace_andBypass_encrypted_showBouncer() {
+ public void onBiometricAuthenticated_whenFace_andBypass_encrypted_showPrimaryBouncer() {
reset(mUpdateMonitor);
when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
@@ -285,7 +285,7 @@
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
BiometricSourceType.FACE, true /* isStrongBiometric */);
- verify(mStatusBarKeyguardViewManager).showBouncer(anyBoolean());
+ verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean());
assertThat(mBiometricUnlockController.getMode())
.isEqualTo(BiometricUnlockController.MODE_SHOW_BOUNCER);
}
@@ -314,7 +314,7 @@
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
BiometricSourceType.FACE, true /* isStrongBiometric */);
- verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
+ verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean());
assertThat(mBiometricUnlockController.getMode())
.isEqualTo(BiometricUnlockController.MODE_NONE);
}
@@ -322,7 +322,7 @@
@Test
public void onBiometricAuthenticated_whenFaceOnBouncer_dismissBouncer() {
when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
- when(mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()).thenReturn(true);
// the value of isStrongBiometric doesn't matter here since we only care about the returned
// value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
@@ -342,7 +342,7 @@
when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
when(mKeyguardBypassController.onBiometricAuthenticated(any(), anyBoolean()))
.thenReturn(true);
- when(mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()).thenReturn(true);
// the value of isStrongBiometric doesn't matter here since we only care about the returned
// value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
@@ -369,23 +369,23 @@
}
@Test
- public void onUdfpsConsecutivelyFailedThreeTimes_showBouncer() {
+ public void onUdfpsConsecutivelyFailedThreeTimes_showPrimaryBouncer() {
// GIVEN UDFPS is supported
when(mUpdateMonitor.isUdfpsSupported()).thenReturn(true);
// WHEN udfps fails once - then don't show the bouncer yet
mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
- verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
+ verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean());
// WHEN udfps fails the second time - then don't show the bouncer yet
mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
- verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
+ verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean());
// WHEN udpfs fails the third time
mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
// THEN show the bouncer
- verify(mStatusBarKeyguardViewManager).showBouncer(true);
+ verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(true);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index 5ebaf69..d5bfe1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -41,6 +41,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.shade.CameraLauncher;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.CommandQueue;
@@ -60,6 +61,8 @@
import java.util.Optional;
+import dagger.Lazy;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase {
@@ -84,6 +87,7 @@
@Mock private Vibrator mVibrator;
@Mock private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
@Mock private SystemBarAttributesListener mSystemBarAttributesListener;
+ @Mock private Lazy<CameraLauncher> mCameraLauncherLazy;
CentralSurfacesCommandQueueCallbacks mSbcqCallbacks;
@@ -115,7 +119,8 @@
Optional.of(mVibrator),
new DisableFlagsLogger(),
DEFAULT_DISPLAY,
- mSystemBarAttributesListener);
+ mSystemBarAttributesListener,
+ mCameraLauncherLazy);
when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
when(mRemoteInputQuickSettingsDisabler.adjustDisableFlags(anyInt()))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 7ce3a67..41912f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -112,6 +112,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.settings.brightness.BrightnessSliderController;
+import com.android.systemui.shade.CameraLauncher;
import com.android.systemui.shade.NotificationPanelView;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.NotificationShadeWindowView;
@@ -220,6 +221,7 @@
@Mock private NotificationLockscreenUserManager mLockscreenUserManager;
@Mock private NotificationRemoteInputManager mRemoteInputManager;
@Mock private StatusBarStateControllerImpl mStatusBarStateController;
+ @Mock private ShadeExpansionStateManager mShadeExpansionStateManager;
@Mock private BatteryController mBatteryController;
@Mock private DeviceProvisionedController mDeviceProvisionedController;
@Mock private StatusBarNotificationPresenter mNotificationPresenter;
@@ -287,6 +289,8 @@
@Mock private InteractionJankMonitor mJankMonitor;
@Mock private DeviceStateManager mDeviceStateManager;
@Mock private WiredChargingRippleController mWiredChargingRippleController;
+ @Mock private Lazy<CameraLauncher> mCameraLauncherLazy;
+ @Mock private CameraLauncher mCameraLauncher;
/**
* The process of registering/unregistering a predictive back callback requires a
* ViewRootImpl, which is present IRL, but may be missing during a Mockito unit test.
@@ -339,6 +343,7 @@
mVisibilityProvider,
mock(NotifPipeline.class),
mStatusBarStateController,
+ mShadeExpansionStateManager,
mExpansionStateLogger,
new NotificationPanelLoggerFake()
);
@@ -378,6 +383,7 @@
when(mLockscreenWallpaperLazy.get()).thenReturn(mLockscreenWallpaper);
when(mBiometricUnlockControllerLazy.get()).thenReturn(mBiometricUnlockController);
+ when(mCameraLauncherLazy.get()).thenReturn(mCameraLauncher);
when(mStatusBarComponentFactory.create()).thenReturn(mCentralSurfacesComponent);
when(mCentralSurfacesComponent.getNotificationShadeWindowViewController()).thenReturn(
@@ -479,7 +485,9 @@
mActivityLaunchAnimator,
mJankMonitor,
mDeviceStateManager,
- mWiredChargingRippleController, mDreamManager) {
+ mWiredChargingRippleController,
+ mDreamManager,
+ mCameraLauncherLazy) {
@Override
protected ViewRootImpl getViewRootImpl() {
return mViewRootImpl;
@@ -891,7 +899,7 @@
mCentralSurfaces.showKeyguardImpl();
// Starting a pulse should change the scrim controller to the pulsing state
- when(mNotificationPanelViewController.isLaunchingAffordanceWithPreview()).thenReturn(true);
+ when(mCameraLauncher.isLaunchingAffordance()).thenReturn(true);
mCentralSurfaces.updateScrimController();
verify(mScrimController).transitionTo(eq(ScrimState.UNLOCKED), any());
}
@@ -927,7 +935,7 @@
mCentralSurfaces.showKeyguardImpl();
// Starting a pulse should change the scrim controller to the pulsing state
- when(mNotificationPanelViewController.isLaunchingAffordanceWithPreview()).thenReturn(false);
+ when(mCameraLauncher.isLaunchingAffordance()).thenReturn(false);
mCentralSurfaces.updateScrimController();
verify(mScrimController).transitionTo(eq(ScrimState.KEYGUARD));
}
@@ -1025,7 +1033,7 @@
@Test
public void collapseShade_callsAnimateCollapsePanels_whenExpanded() {
// GIVEN the shade is expanded
- mCentralSurfaces.setPanelExpanded(true);
+ mCentralSurfaces.onShadeExpansionFullyChanged(true);
mCentralSurfaces.setBarStateForTest(StatusBarState.SHADE);
// WHEN collapseShade is called
@@ -1038,7 +1046,7 @@
@Test
public void collapseShade_doesNotCallAnimateCollapsePanels_whenCollapsed() {
// GIVEN the shade is collapsed
- mCentralSurfaces.setPanelExpanded(false);
+ mCentralSurfaces.onShadeExpansionFullyChanged(false);
mCentralSurfaces.setBarStateForTest(StatusBarState.SHADE);
// WHEN collapseShade is called
@@ -1051,7 +1059,7 @@
@Test
public void collapseShadeForBugReport_callsAnimateCollapsePanels_whenFlagDisabled() {
// GIVEN the shade is expanded & flag enabled
- mCentralSurfaces.setPanelExpanded(true);
+ mCentralSurfaces.onShadeExpansionFullyChanged(true);
mCentralSurfaces.setBarStateForTest(StatusBarState.SHADE);
mFeatureFlags.set(Flags.LEAVE_SHADE_OPEN_FOR_BUGREPORT, false);
@@ -1065,7 +1073,7 @@
@Test
public void collapseShadeForBugReport_doesNotCallAnimateCollapsePanels_whenFlagEnabled() {
// GIVEN the shade is expanded & flag enabled
- mCentralSurfaces.setPanelExpanded(true);
+ mCentralSurfaces.onShadeExpansionFullyChanged(true);
mCentralSurfaces.setBarStateForTest(StatusBarState.SHADE);
mFeatureFlags.set(Flags.LEAVE_SHADE_OPEN_FOR_BUGREPORT, true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index e252401..780e0c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -31,6 +31,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.AlertingNotificationManagerTest;
import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -67,6 +68,7 @@
@Mock private KeyguardBypassController mBypassController;
@Mock private ConfigurationControllerImpl mConfigurationController;
@Mock private AccessibilityManagerWrapper mAccessibilityManagerWrapper;
+ @Mock private ShadeExpansionStateManager mShadeExpansionStateManager;
@Mock private UiEventLogger mUiEventLogger;
private boolean mLivesPastNormalTime;
@@ -81,7 +83,8 @@
ConfigurationController configurationController,
Handler handler,
AccessibilityManagerWrapper accessibilityManagerWrapper,
- UiEventLogger uiEventLogger
+ UiEventLogger uiEventLogger,
+ ShadeExpansionStateManager shadeExpansionStateManager
) {
super(
context,
@@ -93,7 +96,8 @@
configurationController,
handler,
accessibilityManagerWrapper,
- uiEventLogger
+ uiEventLogger,
+ shadeExpansionStateManager
);
mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
@@ -125,7 +129,8 @@
mConfigurationController,
mTestHandler,
mAccessibilityManagerWrapper,
- mUiEventLogger
+ mUiEventLogger,
+ mShadeExpansionStateManager
);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index ab209d1..d3b5418 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -58,7 +58,7 @@
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
-import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback;
+import com.android.systemui.statusbar.phone.KeyguardBouncer.PrimaryBouncerExpansionCallback;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Assert;
@@ -86,7 +86,7 @@
@Mock
private KeyguardHostViewController mKeyguardHostViewController;
@Mock
- private BouncerExpansionCallback mExpansionCallback;
+ private KeyguardBouncer.PrimaryBouncerExpansionCallback mExpansionCallback;
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
@@ -476,7 +476,8 @@
mBouncer.ensureView();
mBouncer.setExpansion(0.5f);
- final BouncerExpansionCallback callback = mock(BouncerExpansionCallback.class);
+ final PrimaryBouncerExpansionCallback callback =
+ mock(PrimaryBouncerExpansionCallback.class);
mBouncer.addBouncerExpansionCallback(callback);
mBouncer.setExpansion(EXPANSION_HIDDEN);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
index 086e5df..b80b825 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
@@ -92,7 +92,7 @@
iconContainer.calculateIconXTranslations()
assertEquals(10f, iconState.xTranslation)
- assertFalse(iconContainer.hasOverflow())
+ assertFalse(iconContainer.areIconsOverflowing())
}
@Test
@@ -121,7 +121,7 @@
assertEquals(30f, iconContainer.getIconState(iconThree).xTranslation)
assertEquals(40f, iconContainer.getIconState(iconFour).xTranslation)
- assertFalse(iconContainer.hasOverflow())
+ assertFalse(iconContainer.areIconsOverflowing())
}
@Test
@@ -150,7 +150,7 @@
assertEquals(10f, iconContainer.getIconState(iconOne).xTranslation)
assertEquals(20f, iconContainer.getIconState(iconTwo).xTranslation)
assertEquals(30f, iconContainer.getIconState(iconThree).xTranslation)
- assertTrue(iconContainer.hasOverflow())
+ assertTrue(iconContainer.areIconsOverflowing())
}
@Test
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 a5deaa4..808abc8 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
@@ -1150,7 +1150,7 @@
}
@Test
- public void testAuthScrim_notifScrimOpaque_whenShadeFullyExpanded() {
+ public void testAuthScrim_setClipQSScrimTrue_notifScrimOpaque_whenShadeFullyExpanded() {
// GIVEN device has an activity showing ('UNLOCKED' state can occur on the lock screen
// with the camera app occluding the keyguard)
mScrimController.transitionTo(ScrimState.UNLOCKED);
@@ -1176,6 +1176,34 @@
));
}
+
+ @Test
+ public void testAuthScrim_setClipQSScrimFalse_notifScrimOpaque_whenShadeFullyExpanded() {
+ // GIVEN device has an activity showing ('UNLOCKED' state can occur on the lock screen
+ // with the camera app occluding the keyguard)
+ mScrimController.transitionTo(ScrimState.UNLOCKED);
+ mScrimController.setClipsQsScrim(false);
+ mScrimController.setRawPanelExpansionFraction(1);
+ // notifications scrim alpha change require calling setQsPosition
+ mScrimController.setQsPosition(0, 300);
+ finishAnimationsImmediately();
+
+ // WHEN the user triggers the auth bouncer
+ mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
+ finishAnimationsImmediately();
+
+ assertEquals("Behind scrim should be opaque",
+ mScrimBehind.getViewAlpha(), 1, 0.0);
+ assertEquals("Notifications scrim should be opaque",
+ mNotificationsScrim.getViewAlpha(), 1, 0.0);
+
+ assertScrimTinted(Map.of(
+ mScrimInFront, true,
+ mScrimBehind, true,
+ mNotificationsScrim, false
+ ));
+ }
+
@Test
public void testAuthScrimKeyguard() {
// GIVEN device is on the keyguard
@@ -1280,7 +1308,7 @@
@Test
public void qsExpansion_BehindTint_shadeLocked_bouncerActive_usesBouncerProgress() {
- when(mStatusBarKeyguardViewManager.isBouncerInTransit()).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(true);
// clipping doesn't change tested logic but allows to assert scrims more in line with
// their expected large screen behaviour
mScrimController.setClipsQsScrim(false);
@@ -1296,7 +1324,7 @@
@Test
public void expansionNotificationAlpha_shadeLocked_bouncerActive_usesBouncerInterpolator() {
- when(mStatusBarKeyguardViewManager.isBouncerInTransit()).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(true);
mScrimController.transitionTo(SHADE_LOCKED);
@@ -1312,7 +1340,7 @@
@Test
public void expansionNotificationAlpha_shadeLocked_bouncerNotActive_usesShadeInterpolator() {
- when(mStatusBarKeyguardViewManager.isBouncerInTransit()).thenReturn(false);
+ when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false);
mScrimController.transitionTo(SHADE_LOCKED);
@@ -1327,7 +1355,7 @@
@Test
public void notificationAlpha_unnocclusionAnimating_bouncerActive_usesKeyguardNotifAlpha() {
- when(mStatusBarKeyguardViewManager.isBouncerInTransit()).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(true);
mScrimController.setClipsQsScrim(true);
mScrimController.transitionTo(ScrimState.KEYGUARD);
@@ -1349,7 +1377,7 @@
@Test
public void notificationAlpha_unnocclusionAnimating_bouncerNotActive_usesKeyguardNotifAlpha() {
- when(mStatusBarKeyguardViewManager.isBouncerInTransit()).thenReturn(false);
+ when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false);
mScrimController.transitionTo(ScrimState.KEYGUARD);
mScrimController.setUnocclusionAnimationRunning(true);
@@ -1370,7 +1398,7 @@
@Test
public void notificationAlpha_inKeyguardState_bouncerActive_usesInvertedBouncerInterpolator() {
- when(mStatusBarKeyguardViewManager.isBouncerInTransit()).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(true);
mScrimController.setClipsQsScrim(true);
mScrimController.transitionTo(ScrimState.KEYGUARD);
@@ -1390,7 +1418,7 @@
@Test
public void notificationAlpha_inKeyguardState_bouncerNotActive_usesInvertedShadeInterpolator() {
- when(mStatusBarKeyguardViewManager.isBouncerInTransit()).thenReturn(false);
+ when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false);
mScrimController.setClipsQsScrim(true);
mScrimController.transitionTo(ScrimState.KEYGUARD);
@@ -1409,6 +1437,17 @@
}
@Test
+ public void behindTint_inKeyguardState_bouncerNotActive_usesKeyguardBehindTint() {
+ when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false);
+ mScrimController.setClipsQsScrim(false);
+
+ mScrimController.transitionTo(ScrimState.KEYGUARD);
+ finishAnimationsImmediately();
+ assertThat(mScrimBehind.getTint())
+ .isEqualTo(ScrimState.KEYGUARD.getBehindTint());
+ }
+
+ @Test
public void testNotificationTransparency_followsTransitionToFullShade() {
mScrimController.transitionTo(SHADE_LOCKED);
mScrimController.setRawPanelExpansionFraction(1.0f);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 7166666..9f70565 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -56,8 +56,8 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.data.BouncerView;
import com.android.systemui.keyguard.data.BouncerViewDelegate;
-import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor;
-import com.android.systemui.keyguard.domain.interactor.BouncerInteractor;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.shade.NotificationPanelViewController;
@@ -105,8 +105,8 @@
@Mock private KeyguardBouncer.Factory mKeyguardBouncerFactory;
@Mock private KeyguardMessageAreaController.Factory mKeyguardMessageAreaFactory;
@Mock private KeyguardMessageAreaController mKeyguardMessageAreaController;
- @Mock private KeyguardBouncer mBouncer;
- @Mock private StatusBarKeyguardViewManager.AlternateAuthInterceptor mAlternateAuthInterceptor;
+ @Mock private KeyguardBouncer mPrimaryBouncer;
+ @Mock private StatusBarKeyguardViewManager.AlternateBouncer mAlternateBouncer;
@Mock private KeyguardMessageArea mKeyguardMessageArea;
@Mock private ShadeController mShadeController;
@Mock private SysUIUnfoldComponent mSysUiUnfoldComponent;
@@ -114,13 +114,13 @@
@Mock private LatencyTracker mLatencyTracker;
@Mock private FeatureFlags mFeatureFlags;
@Mock private KeyguardSecurityModel mKeyguardSecurityModel;
- @Mock private BouncerCallbackInteractor mBouncerCallbackInteractor;
- @Mock private BouncerInteractor mBouncerInteractor;
+ @Mock private PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor;
+ @Mock private PrimaryBouncerInteractor mPrimaryBouncerInteractor;
@Mock private BouncerView mBouncerView;
@Mock private BouncerViewDelegate mBouncerViewDelegate;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- private KeyguardBouncer.BouncerExpansionCallback mBouncerExpansionCallback;
+ private KeyguardBouncer.PrimaryBouncerExpansionCallback mBouncerExpansionCallback;
private FakeKeyguardStateController mKeyguardStateController =
spy(new FakeKeyguardStateController());
@@ -134,8 +134,9 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mKeyguardBouncerFactory.create(
- any(ViewGroup.class), any(KeyguardBouncer.BouncerExpansionCallback.class)))
- .thenReturn(mBouncer);
+ any(ViewGroup.class),
+ any(KeyguardBouncer.PrimaryBouncerExpansionCallback.class)))
+ .thenReturn(mPrimaryBouncer);
when(mCentralSurfaces.getBouncerContainer()).thenReturn(mContainer);
when(mContainer.findViewById(anyInt())).thenReturn(mKeyguardMessageArea);
when(mKeyguardMessageAreaFactory.create(any(KeyguardMessageArea.class)))
@@ -163,8 +164,8 @@
mLatencyTracker,
mKeyguardSecurityModel,
mFeatureFlags,
- mBouncerCallbackInteractor,
- mBouncerInteractor,
+ mPrimaryBouncerCallbackInteractor,
+ mPrimaryBouncerInteractor,
mBouncerView) {
@Override
public ViewRootImpl getViewRootImpl() {
@@ -181,8 +182,8 @@
mNotificationContainer,
mBypassController);
mStatusBarKeyguardViewManager.show(null);
- ArgumentCaptor<KeyguardBouncer.BouncerExpansionCallback> callbackArgumentCaptor =
- ArgumentCaptor.forClass(KeyguardBouncer.BouncerExpansionCallback.class);
+ ArgumentCaptor<KeyguardBouncer.PrimaryBouncerExpansionCallback> callbackArgumentCaptor =
+ ArgumentCaptor.forClass(KeyguardBouncer.PrimaryBouncerExpansionCallback.class);
verify(mKeyguardBouncerFactory).create(any(ViewGroup.class),
callbackArgumentCaptor.capture());
mBouncerExpansionCallback = callbackArgumentCaptor.getValue();
@@ -194,86 +195,86 @@
Runnable cancelAction = () -> {};
mStatusBarKeyguardViewManager.dismissWithAction(
action, cancelAction, false /* afterKeyguardGone */);
- verify(mBouncer).showWithDismissAction(eq(action), eq(cancelAction));
+ verify(mPrimaryBouncer).showWithDismissAction(eq(action), eq(cancelAction));
}
@Test
public void showBouncer_onlyWhenShowing() {
mStatusBarKeyguardViewManager.hide(0 /* startTime */, 0 /* fadeoutDuration */);
- mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */);
- verify(mBouncer, never()).show(anyBoolean(), anyBoolean());
- verify(mBouncer, never()).show(anyBoolean());
+ mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */);
+ verify(mPrimaryBouncer, never()).show(anyBoolean(), anyBoolean());
+ verify(mPrimaryBouncer, never()).show(anyBoolean());
}
@Test
public void showBouncer_notWhenBouncerAlreadyShowing() {
mStatusBarKeyguardViewManager.hide(0 /* startTime */, 0 /* fadeoutDuration */);
- when(mBouncer.isSecure()).thenReturn(true);
- mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */);
- verify(mBouncer, never()).show(anyBoolean(), anyBoolean());
- verify(mBouncer, never()).show(anyBoolean());
+ when(mPrimaryBouncer.isSecure()).thenReturn(true);
+ mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */);
+ verify(mPrimaryBouncer, never()).show(anyBoolean(), anyBoolean());
+ verify(mPrimaryBouncer, never()).show(anyBoolean());
}
@Test
public void showBouncer_showsTheBouncer() {
- mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */);
- verify(mBouncer).show(anyBoolean(), eq(true));
+ mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */);
+ verify(mPrimaryBouncer).show(anyBoolean(), eq(true));
}
@Test
- public void onPanelExpansionChanged_neverHidesScrimmedBouncer() {
- when(mBouncer.isShowing()).thenReturn(true);
- when(mBouncer.isScrimmed()).thenReturn(true);
+ public void onPanelExpansionChanged_neverHidesFullscreenBouncer() {
+ when(mPrimaryBouncer.isShowing()).thenReturn(true);
+ when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
+ KeyguardSecurityModel.SecurityMode.SimPuk);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mBouncer).setExpansion(eq(KeyguardBouncer.EXPANSION_VISIBLE));
+ verify(mPrimaryBouncer).setExpansion(eq(KeyguardBouncer.EXPANSION_VISIBLE));
+
+ reset(mPrimaryBouncer);
+ when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
+ KeyguardSecurityModel.SecurityMode.SimPin);
+ mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
+ verify(mPrimaryBouncer).setExpansion(eq(KeyguardBouncer.EXPANSION_VISIBLE));
}
@Test
public void onPanelExpansionChanged_neverShowsDuringHintAnimation() {
when(mNotificationPanelView.isUnlockHintRunning()).thenReturn(true);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mBouncer).setExpansion(eq(KeyguardBouncer.EXPANSION_HIDDEN));
+ verify(mPrimaryBouncer).setExpansion(eq(KeyguardBouncer.EXPANSION_HIDDEN));
}
@Test
public void onPanelExpansionChanged_propagatesToBouncer() {
mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mBouncer).setExpansion(eq(0.5f));
+ verify(mPrimaryBouncer).setExpansion(eq(0.5f));
}
@Test
public void onPanelExpansionChanged_hideBouncer_afterKeyguardHidden() {
mStatusBarKeyguardViewManager.hide(0, 0);
- when(mBouncer.inTransit()).thenReturn(true);
+ when(mPrimaryBouncer.inTransit()).thenReturn(true);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mBouncer).setExpansion(eq(KeyguardBouncer.EXPANSION_HIDDEN));
+ verify(mPrimaryBouncer).setExpansion(eq(KeyguardBouncer.EXPANSION_HIDDEN));
}
@Test
public void onPanelExpansionChanged_showsBouncerWhenSwiping() {
mKeyguardStateController.setCanDismissLockScreen(false);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mBouncer).show(eq(false), eq(false));
+ verify(mPrimaryBouncer).show(eq(false), eq(false));
// But not when it's already visible
- reset(mBouncer);
- when(mBouncer.isShowing()).thenReturn(true);
+ reset(mPrimaryBouncer);
+ when(mPrimaryBouncer.isShowing()).thenReturn(true);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mBouncer, never()).show(eq(false), eq(false));
+ verify(mPrimaryBouncer, never()).show(eq(false), eq(false));
// Or animating away
- reset(mBouncer);
- when(mBouncer.isAnimatingAway()).thenReturn(true);
+ reset(mPrimaryBouncer);
+ when(mPrimaryBouncer.isAnimatingAway()).thenReturn(true);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mBouncer, never()).show(eq(false), eq(false));
- }
-
- @Test
- public void onPanelExpansionChanged_neverTranslatesBouncerWhenOccluded() {
- mStatusBarKeyguardViewManager.setOccluded(true /* occluded */, false /* animate */);
- mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mBouncer, never()).setExpansion(eq(0.5f));
+ verify(mPrimaryBouncer, never()).show(eq(false), eq(false));
}
@Test
@@ -285,7 +286,7 @@
/* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
/* expanded= */ true,
/* tracking= */ false));
- verify(mBouncer, never()).setExpansion(anyFloat());
+ verify(mPrimaryBouncer, never()).setExpansion(anyFloat());
}
@Test
@@ -302,18 +303,7 @@
/* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
/* expanded= */ true,
/* tracking= */ false));
- verify(mBouncer, never()).setExpansion(anyFloat());
- }
-
- @Test
- public void onPanelExpansionChanged_neverTranslatesBouncerWhenLaunchingApp() {
- when(mNotificationPanelView.isLaunchTransitionFinished()).thenReturn(true);
- mStatusBarKeyguardViewManager.onPanelExpansionChanged(
- expansionEvent(
- /* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
- /* expanded= */ true,
- /* tracking= */ false));
- verify(mBouncer, never()).setExpansion(anyFloat());
+ verify(mPrimaryBouncer, never()).setExpansion(anyFloat());
}
@Test
@@ -324,7 +314,7 @@
/* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
/* expanded= */ true,
/* tracking= */ false));
- verify(mBouncer, never()).setExpansion(anyFloat());
+ verify(mPrimaryBouncer, never()).setExpansion(anyFloat());
}
@Test
@@ -332,7 +322,7 @@
mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */);
verify(mCentralSurfaces).animateKeyguardUnoccluding();
- when(mBouncer.isShowing()).thenReturn(true);
+ when(mPrimaryBouncer.isShowing()).thenReturn(true);
clearInvocations(mCentralSurfaces);
mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */);
verify(mCentralSurfaces, never()).animateKeyguardUnoccluding();
@@ -361,7 +351,6 @@
@Test
public void setOccluded_isInLaunchTransition_onKeyguardOccludedChangedCalled() {
- when(mNotificationPanelView.isLaunchTransitionFinished()).thenReturn(true);
mStatusBarKeyguardViewManager.show(null);
mStatusBarKeyguardViewManager.setOccluded(true /* occluded */, false /* animated */);
@@ -384,7 +373,7 @@
mStatusBarKeyguardViewManager.dismissWithAction(
action, cancelAction, true /* afterKeyguardGone */);
- when(mBouncer.isShowing()).thenReturn(false);
+ when(mPrimaryBouncer.isShowing()).thenReturn(false);
mStatusBarKeyguardViewManager.hideBouncer(true);
mStatusBarKeyguardViewManager.hide(0, 30);
verify(action, never()).onDismiss();
@@ -398,7 +387,7 @@
mStatusBarKeyguardViewManager.dismissWithAction(
action, cancelAction, true /* afterKeyguardGone */);
- when(mBouncer.isShowing()).thenReturn(false);
+ when(mPrimaryBouncer.isShowing()).thenReturn(false);
mStatusBarKeyguardViewManager.hideBouncer(true);
verify(action, never()).onDismiss();
@@ -419,9 +408,9 @@
@Test
public void testShowing_whenAlternateAuthShowing() {
- mStatusBarKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
- when(mBouncer.isShowing()).thenReturn(false);
- when(mAlternateAuthInterceptor.isShowingAlternateAuthBouncer()).thenReturn(true);
+ mStatusBarKeyguardViewManager.setAlternateBouncer(mAlternateBouncer);
+ when(mPrimaryBouncer.isShowing()).thenReturn(false);
+ when(mAlternateBouncer.isShowingAlternateBouncer()).thenReturn(true);
assertTrue(
"Is showing not accurate when alternative auth showing",
mStatusBarKeyguardViewManager.isBouncerShowing());
@@ -429,93 +418,93 @@
@Test
public void testWillBeShowing_whenAlternateAuthShowing() {
- mStatusBarKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
- when(mBouncer.isShowing()).thenReturn(false);
- when(mAlternateAuthInterceptor.isShowingAlternateAuthBouncer()).thenReturn(true);
+ mStatusBarKeyguardViewManager.setAlternateBouncer(mAlternateBouncer);
+ when(mPrimaryBouncer.isShowing()).thenReturn(false);
+ when(mAlternateBouncer.isShowingAlternateBouncer()).thenReturn(true);
assertTrue(
"Is or will be showing not accurate when alternative auth showing",
- mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing());
+ mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing());
}
@Test
- public void testHideAltAuth_onShowBouncer() {
+ public void testHideAlternateBouncer_onShowBouncer() {
// GIVEN alt auth is showing
- mStatusBarKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
- when(mBouncer.isShowing()).thenReturn(false);
- when(mAlternateAuthInterceptor.isShowingAlternateAuthBouncer()).thenReturn(true);
- reset(mAlternateAuthInterceptor);
+ mStatusBarKeyguardViewManager.setAlternateBouncer(mAlternateBouncer);
+ when(mPrimaryBouncer.isShowing()).thenReturn(false);
+ when(mAlternateBouncer.isShowingAlternateBouncer()).thenReturn(true);
+ reset(mAlternateBouncer);
// WHEN showBouncer is called
- mStatusBarKeyguardViewManager.showBouncer(true);
+ mStatusBarKeyguardViewManager.showPrimaryBouncer(true);
// THEN alt bouncer should be hidden
- verify(mAlternateAuthInterceptor).hideAlternateAuthBouncer();
+ verify(mAlternateBouncer).hideAlternateBouncer();
}
@Test
public void testBouncerIsOrWillBeShowing_whenBouncerIsInTransit() {
- when(mBouncer.isShowing()).thenReturn(false);
- when(mBouncer.inTransit()).thenReturn(true);
+ when(mPrimaryBouncer.isShowing()).thenReturn(false);
+ when(mPrimaryBouncer.inTransit()).thenReturn(true);
assertTrue(
"Is or will be showing should be true when bouncer is in transit",
- mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing());
+ mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing());
}
@Test
public void testShowAltAuth_unlockingWithBiometricNotAllowed() {
// GIVEN alt auth exists, unlocking with biometric isn't allowed
- mStatusBarKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
- when(mBouncer.isShowing()).thenReturn(false);
+ mStatusBarKeyguardViewManager.setAlternateBouncer(mAlternateBouncer);
+ when(mPrimaryBouncer.isShowing()).thenReturn(false);
when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
.thenReturn(false);
// WHEN showGenericBouncer is called
final boolean scrimmed = true;
- mStatusBarKeyguardViewManager.showGenericBouncer(scrimmed);
+ mStatusBarKeyguardViewManager.showBouncer(scrimmed);
// THEN regular bouncer is shown
- verify(mBouncer).show(anyBoolean(), eq(scrimmed));
- verify(mAlternateAuthInterceptor, never()).showAlternateAuthBouncer();
+ verify(mPrimaryBouncer).show(anyBoolean(), eq(scrimmed));
+ verify(mAlternateBouncer, never()).showAlternateBouncer();
}
@Test
- public void testShowAltAuth_unlockingWithBiometricAllowed() {
+ public void testShowAlternateBouncer_unlockingWithBiometricAllowed() {
// GIVEN alt auth exists, unlocking with biometric is allowed
- mStatusBarKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
- when(mBouncer.isShowing()).thenReturn(false);
+ mStatusBarKeyguardViewManager.setAlternateBouncer(mAlternateBouncer);
+ when(mPrimaryBouncer.isShowing()).thenReturn(false);
when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
// WHEN showGenericBouncer is called
- mStatusBarKeyguardViewManager.showGenericBouncer(true);
+ mStatusBarKeyguardViewManager.showBouncer(true);
// THEN alt auth bouncer is shown
- verify(mAlternateAuthInterceptor).showAlternateAuthBouncer();
- verify(mBouncer, never()).show(anyBoolean(), anyBoolean());
+ verify(mAlternateBouncer).showAlternateBouncer();
+ verify(mPrimaryBouncer, never()).show(anyBoolean(), anyBoolean());
}
@Test
public void testUpdateResources_delegatesToBouncer() {
mStatusBarKeyguardViewManager.updateResources();
- verify(mBouncer).updateResources();
+ verify(mPrimaryBouncer).updateResources();
}
@Test
public void updateKeyguardPosition_delegatesToBouncer() {
mStatusBarKeyguardViewManager.updateKeyguardPosition(1.0f);
- verify(mBouncer).updateKeyguardPosition(1.0f);
+ verify(mPrimaryBouncer).updateKeyguardPosition(1.0f);
}
@Test
public void testIsBouncerInTransit() {
- when(mBouncer.inTransit()).thenReturn(true);
- Truth.assertThat(mStatusBarKeyguardViewManager.isBouncerInTransit()).isTrue();
- when(mBouncer.inTransit()).thenReturn(false);
- Truth.assertThat(mStatusBarKeyguardViewManager.isBouncerInTransit()).isFalse();
- mBouncer = null;
- Truth.assertThat(mStatusBarKeyguardViewManager.isBouncerInTransit()).isFalse();
+ when(mPrimaryBouncer.inTransit()).thenReturn(true);
+ Truth.assertThat(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).isTrue();
+ when(mPrimaryBouncer.inTransit()).thenReturn(false);
+ Truth.assertThat(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).isFalse();
+ mPrimaryBouncer = null;
+ Truth.assertThat(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).isFalse();
}
private static ShadeExpansionChangeEvent expansionEvent(
@@ -546,7 +535,7 @@
eq(OnBackInvokedDispatcher.PRIORITY_OVERLAY),
mOnBackInvokedCallback.capture());
- when(mBouncer.isShowing()).thenReturn(true);
+ when(mPrimaryBouncer.isShowing()).thenReturn(true);
when(mCentralSurfaces.shouldKeyguardHideImmediately()).thenReturn(true);
/* invoke the back callback directly */
mOnBackInvokedCallback.getValue().onBackInvoked();
@@ -579,6 +568,6 @@
public void flag_off_DoesNotCallBouncerInteractor() {
when(mFeatureFlags.isEnabled(MODERN_BOUNCER)).thenReturn(false);
mStatusBarKeyguardViewManager.hideBouncer(false);
- verify(mBouncerInteractor, never()).hide();
+ verify(mPrimaryBouncerInteractor, never()).hide();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
index c3a7e65..613238f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
@@ -99,6 +99,6 @@
mRemoteInputCallback.onLockedRemoteInput(
mock(ExpandableNotificationRow.class), mock(View.class));
- verify(mStatusBarKeyguardViewManager).showGenericBouncer(true);
+ verify(mStatusBarKeyguardViewManager).showBouncer(true);
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt
index 6d8d902..a052008 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt
@@ -16,31 +16,59 @@
package com.android.systemui.statusbar.pipeline.mobile.util
+import android.telephony.TelephonyDisplayInfo
import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.settingslib.mobile.MobileMappings.Config
import com.android.settingslib.mobile.TelephonyIcons
class FakeMobileMappingsProxy : MobileMappingsProxy {
+ // The old [NetworkControllerDataTest] infra requires us to be able to use the real
+ // impl sometimes
+ var useRealImpl = false
+
+ private var realImpl = MobileMappingsProxyImpl()
private var iconMap = mapOf<String, MobileIconGroup>()
private var defaultIcons = TelephonyIcons.THREE_G
fun setIconMap(map: Map<String, MobileIconGroup>) {
iconMap = map
}
- override fun mapIconSets(config: Config): Map<String, MobileIconGroup> = iconMap
+ override fun mapIconSets(config: Config): Map<String, MobileIconGroup> {
+ if (useRealImpl) {
+ return realImpl.mapIconSets(config)
+ }
+ return iconMap
+ }
fun getIconMap() = iconMap
fun setDefaultIcons(group: MobileIconGroup) {
defaultIcons = group
}
- override fun getDefaultIcons(config: Config): MobileIconGroup = defaultIcons
+ override fun getDefaultIcons(config: Config): MobileIconGroup {
+ if (useRealImpl) {
+ return realImpl.getDefaultIcons(config)
+ }
+ return defaultIcons
+ }
+
+ /** This is only used in the old pipeline, use the real impl always */
+ override fun getIconKey(displayInfo: TelephonyDisplayInfo): String {
+ return realImpl.getIconKey(displayInfo)
+ }
+
fun getDefaultIcons(): MobileIconGroup = defaultIcons
override fun toIconKey(networkType: Int): String {
+ if (useRealImpl) {
+ return realImpl.toIconKeyOverride(networkType)
+ }
return networkType.toString()
}
override fun toIconKeyOverride(networkType: Int): String {
+ if (useRealImpl) {
+ return realImpl.toIconKeyOverride(networkType)
+ }
return toIconKey(networkType) + "_override"
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
index 91b5c35..09f0d4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
@@ -35,6 +35,8 @@
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.time.FakeSystemClock
+import com.android.systemui.util.wakelock.WakeLock
+import com.android.systemui.util.wakelock.WakeLockFake
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -53,6 +55,9 @@
private lateinit var fakeClock: FakeSystemClock
private lateinit var fakeExecutor: FakeExecutor
+ private lateinit var fakeWakeLockBuilder: WakeLockFake.Builder
+ private lateinit var fakeWakeLock: WakeLockFake
+
@Mock
private lateinit var logger: TemporaryViewLogger
@Mock
@@ -74,6 +79,10 @@
fakeClock = FakeSystemClock()
fakeExecutor = FakeExecutor(fakeClock)
+ fakeWakeLock = WakeLockFake()
+ fakeWakeLockBuilder = WakeLockFake.Builder(context)
+ fakeWakeLockBuilder.setWakeLock(fakeWakeLock)
+
underTest = TestController(
context,
logger,
@@ -82,7 +91,9 @@
accessibilityManager,
configurationController,
powerManager,
+ fakeWakeLockBuilder,
)
+ underTest.start()
}
@Test
@@ -108,29 +119,47 @@
)
)
- verify(logger).logViewAddition("Fake Window Title")
+ verify(logger).logViewAddition("id", "Fake Window Title")
}
@Test
- fun displayView_screenOff_screenWakes() {
- whenever(powerManager.isScreenOn).thenReturn(false)
-
+ fun displayView_wakeLockAcquired() {
underTest.displayView(getState())
- verify(powerManager).wakeUp(any(), any(), any())
+ assertThat(fakeWakeLock.isHeld).isTrue()
}
@Test
- fun displayView_screenAlreadyOn_screenNotWoken() {
+ fun displayView_screenAlreadyOn_wakeLockAcquired() {
whenever(powerManager.isScreenOn).thenReturn(true)
underTest.displayView(getState())
- verify(powerManager, never()).wakeUp(any(), any(), any())
+ assertThat(fakeWakeLock.isHeld).isTrue()
}
@Test
- fun displayView_twiceWithSameWindowTitle_viewNotAddedTwice() {
+ fun displayView_wakeLockCanBeReleasedAfterTimeOut() {
+ underTest.displayView(getState())
+ assertThat(fakeWakeLock.isHeld).isTrue()
+
+ fakeClock.advanceTime(TIMEOUT_MS + 1)
+
+ assertThat(fakeWakeLock.isHeld).isFalse()
+ }
+
+ @Test
+ fun displayView_removeView_wakeLockCanBeReleased() {
+ underTest.displayView(getState())
+ assertThat(fakeWakeLock.isHeld).isTrue()
+
+ underTest.removeView("id", "test reason")
+
+ assertThat(fakeWakeLock.isHeld).isFalse()
+ }
+
+ @Test
+ fun displayView_twice_viewNotAddedTwice() {
underTest.displayView(getState())
reset(windowManager)
@@ -234,21 +263,143 @@
}
@Test
+ fun multipleViewsWithDifferentIds_recentActiveViewIsDisplayed() {
+ underTest.displayView(ViewInfo("First name", id = "id1"))
+
+ verify(windowManager).addView(any(), any())
+
+ reset(windowManager)
+ underTest.displayView(ViewInfo("Second name", id = "id2"))
+ underTest.removeView("id2", "test reason")
+
+ verify(windowManager).removeView(any())
+
+ fakeClock.advanceTime(DISPLAY_VIEW_DELAY + 1)
+
+ assertThat(underTest.mostRecentViewInfo?.id).isEqualTo("id1")
+ assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("First name")
+
+ reset(windowManager)
+ fakeClock.advanceTime(TIMEOUT_MS + 1)
+
+ verify(windowManager).removeView(any())
+ assertThat(underTest.activeViews.size).isEqualTo(0)
+ }
+
+ @Test
+ fun multipleViewsWithDifferentIds_oldViewRemoved_recentViewIsDisplayed() {
+ underTest.displayView(ViewInfo("First name", id = "id1"))
+
+ verify(windowManager).addView(any(), any())
+
+ reset(windowManager)
+ underTest.displayView(ViewInfo("Second name", id = "id2"))
+ underTest.removeView("id1", "test reason")
+
+ verify(windowManager, never()).removeView(any())
+ assertThat(underTest.mostRecentViewInfo?.id).isEqualTo("id2")
+ assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("Second name")
+
+ fakeClock.advanceTime(TIMEOUT_MS + 1)
+
+ verify(windowManager).removeView(any())
+ assertThat(underTest.activeViews.size).isEqualTo(0)
+ }
+
+ @Test
+ fun multipleViewsWithDifferentIds_threeDifferentViews_recentActiveViewIsDisplayed() {
+ underTest.displayView(ViewInfo("First name", id = "id1"))
+ underTest.displayView(ViewInfo("Second name", id = "id2"))
+ underTest.displayView(ViewInfo("Third name", id = "id3"))
+
+ verify(windowManager).addView(any(), any())
+
+ reset(windowManager)
+ underTest.removeView("id3", "test reason")
+
+ verify(windowManager).removeView(any())
+
+ fakeClock.advanceTime(DISPLAY_VIEW_DELAY + 1)
+
+ assertThat(underTest.mostRecentViewInfo?.id).isEqualTo("id2")
+ assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("Second name")
+
+ reset(windowManager)
+ underTest.removeView("id2", "test reason")
+
+ verify(windowManager).removeView(any())
+
+ fakeClock.advanceTime(DISPLAY_VIEW_DELAY + 1)
+
+ assertThat(underTest.mostRecentViewInfo?.id).isEqualTo("id1")
+ assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("First name")
+
+ reset(windowManager)
+ fakeClock.advanceTime(TIMEOUT_MS + 1)
+
+ verify(windowManager).removeView(any())
+ assertThat(underTest.activeViews.size).isEqualTo(0)
+ }
+
+ @Test
+ fun multipleViewsWithDifferentIds_oneViewStateChanged_stackHasRecentState() {
+ underTest.displayView(ViewInfo("First name", id = "id1"))
+ underTest.displayView(ViewInfo("New name", id = "id1"))
+
+ verify(windowManager).addView(any(), any())
+
+ reset(windowManager)
+ underTest.displayView(ViewInfo("Second name", id = "id2"))
+ underTest.removeView("id2", "test reason")
+
+ verify(windowManager).removeView(any())
+
+ fakeClock.advanceTime(DISPLAY_VIEW_DELAY + 1)
+
+ assertThat(underTest.mostRecentViewInfo?.id).isEqualTo("id1")
+ assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("New name")
+ assertThat(underTest.activeViews[0].second.name).isEqualTo("New name")
+
+ reset(windowManager)
+ fakeClock.advanceTime(TIMEOUT_MS + 1)
+
+ verify(windowManager).removeView(any())
+ assertThat(underTest.activeViews.size).isEqualTo(0)
+ }
+
+ @Test
+ fun multipleViewsWithDifferentIds_viewsTimeouts_noViewLeftToDisplay() {
+ underTest.displayView(ViewInfo("First name", id = "id1"))
+ fakeClock.advanceTime(TIMEOUT_MS / 3)
+ underTest.displayView(ViewInfo("Second name", id = "id2"))
+ fakeClock.advanceTime(TIMEOUT_MS / 3)
+ underTest.displayView(ViewInfo("Third name", id = "id3"))
+
+ reset(windowManager)
+ fakeClock.advanceTime(TIMEOUT_MS + 1)
+
+ verify(windowManager).removeView(any())
+ verify(windowManager, never()).addView(any(), any())
+ assertThat(underTest.activeViews.size).isEqualTo(0)
+ }
+
+ @Test
fun removeView_viewRemovedAndRemovalLogged() {
// First, add the view
underTest.displayView(getState())
// Then, remove it
val reason = "test reason"
- underTest.removeView(reason)
+ val deviceId = "id"
+ underTest.removeView(deviceId, reason)
verify(windowManager).removeView(any())
- verify(logger).logViewRemoval(reason)
+ verify(logger).logViewRemoval(deviceId, reason)
}
@Test
fun removeView_noAdd_viewNotRemoved() {
- underTest.removeView("reason")
+ underTest.removeView("id", "reason")
verify(windowManager, never()).removeView(any())
}
@@ -269,6 +420,7 @@
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
powerManager: PowerManager,
+ wakeLockBuilder: WakeLock.Builder,
) : TemporaryViewDisplayController<ViewInfo, TemporaryViewLogger>(
context,
logger,
@@ -278,13 +430,12 @@
configurationController,
powerManager,
R.layout.chipbar,
+ wakeLockBuilder,
) {
var mostRecentViewInfo: ViewInfo? = null
override val windowLayoutParams = commonWindowLayoutParams
- override fun start() {}
-
override fun updateView(newInfo: ViewInfo, currentView: ViewGroup) {
mostRecentViewInfo = newInfo
}
@@ -292,13 +443,16 @@
override fun getTouchableRegion(view: View, outRect: Rect) {
outRect.setEmpty()
}
+
+ override fun start() {}
}
inner class ViewInfo(
val name: String,
override val windowTitle: String = "Window Title",
override val wakeReason: String = "WAKE_REASON",
- override val timeoutMs: Int = 1
+ override val timeoutMs: Int = 1,
+ override val id: String = "id",
) : TemporaryViewInfo()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt
index d155050..116b8fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt
@@ -44,7 +44,7 @@
@Test
fun logViewAddition_bufferHasLog() {
- logger.logViewAddition("Test Window Title")
+ logger.logViewAddition("test id", "Test Window Title")
val stringWriter = StringWriter()
buffer.dump(PrintWriter(stringWriter), tailLength = 0)
@@ -57,7 +57,8 @@
@Test
fun logViewRemoval_bufferHasTagAndReason() {
val reason = "test reason"
- logger.logViewRemoval(reason)
+ val deviceId = "test id"
+ logger.logViewRemoval(deviceId, reason)
val stringWriter = StringWriter()
buffer.dump(PrintWriter(stringWriter), tailLength = 0)
@@ -65,6 +66,7 @@
assertThat(actualString).contains(TAG)
assertThat(actualString).contains(reason)
+ assertThat(actualString).contains(deviceId)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
index f643973..47c84ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
@@ -43,6 +43,7 @@
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.util.view.ViewUtil
+import com.android.systemui.util.wakelock.WakeLockFake
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -69,6 +70,8 @@
@Mock private lateinit var falsingCollector: FalsingCollector
@Mock private lateinit var viewUtil: ViewUtil
@Mock private lateinit var vibratorHelper: VibratorHelper
+ private lateinit var fakeWakeLockBuilder: WakeLockFake.Builder
+ private lateinit var fakeWakeLock: WakeLockFake
private lateinit var fakeClock: FakeSystemClock
private lateinit var fakeExecutor: FakeExecutor
private lateinit var uiEventLoggerFake: UiEventLoggerFake
@@ -81,6 +84,10 @@
fakeClock = FakeSystemClock()
fakeExecutor = FakeExecutor(fakeClock)
+ fakeWakeLock = WakeLockFake()
+ fakeWakeLockBuilder = WakeLockFake.Builder(context)
+ fakeWakeLockBuilder.setWakeLock(fakeWakeLock)
+
uiEventLoggerFake = UiEventLoggerFake()
underTest =
@@ -96,6 +103,7 @@
falsingCollector,
viewUtil,
vibratorHelper,
+ fakeWakeLockBuilder,
)
underTest.start()
}
@@ -369,6 +377,7 @@
windowTitle = WINDOW_TITLE,
wakeReason = WAKE_REASON,
timeoutMs = TIMEOUT,
+ id = DEVICE_ID,
)
}
@@ -393,3 +402,4 @@
private const val TIMEOUT = 10000
private const val WINDOW_TITLE = "Test Chipbar Window Title"
private const val WAKE_REASON = "TEST_CHIPBAR_WAKE_REASON"
+private const val DEVICE_ID = "id"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt
index 574f70e..beedf9f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt
@@ -27,6 +27,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.view.ViewUtil
+import com.android.systemui.util.wakelock.WakeLock
/** A fake implementation of [ChipbarCoordinator] for testing. */
class FakeChipbarCoordinator(
@@ -41,6 +42,7 @@
falsingCollector: FalsingCollector,
viewUtil: ViewUtil,
vibratorHelper: VibratorHelper,
+ wakeLockBuilder: WakeLock.Builder,
) :
ChipbarCoordinator(
context,
@@ -54,6 +56,7 @@
falsingCollector,
viewUtil,
vibratorHelper,
+ wakeLockBuilder,
) {
override fun animateViewOut(view: ViewGroup, onAnimationEnd: Runnable) {
// Just bypass the animation in tests
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/CreateUserActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/CreateUserActivityTest.kt
new file mode 100644
index 0000000..51afbcb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/CreateUserActivityTest.kt
@@ -0,0 +1,55 @@
+package com.android.systemui.user
+
+import android.app.Dialog
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.nullable
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@TestableLooper.RunWithLooper
+class CreateUserActivityTest : SysuiTestCase() {
+ open class CreateUserActivityTestable :
+ CreateUserActivity(
+ /* userCreator = */ mock(),
+ /* editUserInfoController = */ mock {
+ val dialog: Dialog = mock()
+ whenever(
+ createDialog(
+ /* activity = */ nullable(),
+ /* activityStarter = */ nullable(),
+ /* oldUserIcon = */ nullable(),
+ /* defaultUserName = */ nullable(),
+ /* title = */ nullable(),
+ /* successCallback = */ nullable(),
+ /* cancelCallback = */ nullable()
+ )
+ )
+ .thenReturn(dialog)
+ },
+ /* activityManager = */ mock(),
+ /* activityStarter = */ mock(),
+ )
+
+ @get:Rule val activityRule = ActivityScenarioRule(CreateUserActivityTestable::class.java)
+
+ @Test
+ fun onBackPressed_finishActivity() {
+ activityRule.scenario.onActivity { activity ->
+ assertThat(activity.isFinishing).isFalse()
+
+ activity.onBackPressed()
+
+ assertThat(activity.isFinishing).isTrue()
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplRefactoredTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplRefactoredTest.kt
index 525d837..7c7f0e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplRefactoredTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplRefactoredTest.kt
@@ -145,6 +145,25 @@
assertThat(userInfos).isEqualTo(expectedUsers)
}
+ @Test
+ fun `userTrackerCallback - updates selectedUserInfo`() = runSelfCancelingTest {
+ underTest = create(this)
+ var selectedUserInfo: UserInfo? = null
+ underTest.selectedUserInfo.onEach { selectedUserInfo = it }.launchIn(this)
+ setUpUsers(
+ count = 2,
+ selectedIndex = 0,
+ )
+ tracker.onProfileChanged()
+ assertThat(selectedUserInfo?.id == 0)
+ setUpUsers(
+ count = 2,
+ selectedIndex = 1,
+ )
+ tracker.onProfileChanged()
+ assertThat(selectedUserInfo?.id == 1)
+ }
+
private fun setUpUsers(
count: Int,
isLastGuestUser: Boolean = false,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java
index fe01f84..6e109ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java
@@ -42,7 +42,9 @@
@Before
public void setUp() {
- mInner = WakeLock.createPartialInner(mContext, WakeLockTest.class.getName());
+ mInner = WakeLock.createWakeLockInner(mContext,
+ WakeLockTest.class.getName(),
+ PowerManager.PARTIAL_WAKE_LOCK);
mWakeLock = WakeLock.wrap(mInner, 20000);
}
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 fa7ebf6a..d42f757 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -77,6 +77,7 @@
import android.view.ViewTreeObserver;
import android.view.WindowManager;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.internal.colorextraction.ColorExtractor;
@@ -86,6 +87,7 @@
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -156,6 +158,7 @@
import java.util.List;
import java.util.Optional;
+@FlakyTest
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -394,6 +397,7 @@
mCommonNotifCollection,
mNotifPipeline,
mSysUiState,
+ mock(FeatureFlags.class),
syncExecutor);
mBubblesManager.addNotifCallback(mNotifCallback);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingManagerFake.java
index 34c83bd..d47e88f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingManagerFake.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingManagerFake.java
@@ -39,6 +39,7 @@
private boolean mShouldEnforceBouncer;
private boolean mIsReportingEnabled;
private boolean mIsFalseRobustTap;
+ private boolean mIsFalseLongTap;
private boolean mDestroyed;
private boolean mIsProximityNear;
@@ -87,6 +88,10 @@
mIsProximityNear = proxNear;
}
+ public void setFalseLongTap(boolean falseLongTap) {
+ mIsFalseLongTap = falseLongTap;
+ }
+
@Override
public boolean isSimpleTap() {
checkDestroyed();
@@ -100,6 +105,12 @@
}
@Override
+ public boolean isFalseLongTap(int penalty) {
+ checkDestroyed();
+ return mIsFalseLongTap;
+ }
+
+ @Override
public boolean isFalseDoubleTap() {
checkDestroyed();
return mIsFalseDoubleTap;
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
index a60b773..6c82cef 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
@@ -21,14 +21,14 @@
class FakeFeatureFlags : FeatureFlags {
private val booleanFlags = mutableMapOf<Int, Boolean>()
private val stringFlags = mutableMapOf<Int, String>()
+ private val intFlags = mutableMapOf<Int, Int>()
private val knownFlagNames = mutableMapOf<Int, String>()
private val flagListeners = mutableMapOf<Int, MutableSet<FlagListenable.Listener>>()
private val listenerFlagIds = mutableMapOf<FlagListenable.Listener, MutableSet<Int>>()
init {
- Flags.flagFields.forEach { field ->
- val flag: Flag<*> = field.get(null) as Flag<*>
- knownFlagNames[flag.id] = field.name
+ FlagsFactory.knownFlags.forEach { entry: Map.Entry<String, Flag<*>> ->
+ knownFlagNames[entry.value.id] = entry.key
}
}
@@ -87,14 +87,16 @@
override fun isEnabled(flag: ResourceBooleanFlag): Boolean = requireBooleanValue(flag.id)
- override fun isEnabled(flag: DeviceConfigBooleanFlag): Boolean = requireBooleanValue(flag.id)
-
override fun isEnabled(flag: SysPropBooleanFlag): Boolean = requireBooleanValue(flag.id)
override fun getString(flag: StringFlag): String = requireStringValue(flag.id)
override fun getString(flag: ResourceStringFlag): String = requireStringValue(flag.id)
+ override fun getInt(flag: IntFlag): Int = requireIntValue(flag.id)
+
+ override fun getInt(flag: ResourceIntFlag): Int = requireIntValue(flag.id)
+
override fun addListener(flag: Flag<*>, listener: FlagListenable.Listener) {
flagListeners.getOrPut(flag.id) { mutableSetOf() }.add(listener)
listenerFlagIds.getOrPut(listener) { mutableSetOf() }.add(flag.id)
@@ -118,11 +120,16 @@
private fun requireBooleanValue(flagId: Int): Boolean {
return booleanFlags[flagId]
- ?: error("Flag ${flagName(flagId)} was accessed but not specified.")
+ ?: error("Flag ${flagName(flagId)} was accessed as boolean but not specified.")
}
private fun requireStringValue(flagId: Int): String {
return stringFlags[flagId]
- ?: error("Flag ${flagName(flagId)} was accessed but not specified.")
+ ?: error("Flag ${flagName(flagId)} was accessed as string but not specified.")
+ }
+
+ private fun requireIntValue(flagId: Int): Int {
+ return intFlags[flagId]
+ ?: error("Flag ${flagName(flagId)} was accessed as int but not specified.")
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 627bd09..a798f40 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -44,6 +44,9 @@
private val _isDozing = MutableStateFlow(false)
override val isDozing: Flow<Boolean> = _isDozing
+ private val _isDreaming = MutableStateFlow(false)
+ override val isDreaming: Flow<Boolean> = _isDreaming
+
private val _dozeAmount = MutableStateFlow(0f)
override val dozeAmount: Flow<Float> = _dozeAmount
@@ -53,9 +56,14 @@
private val _wakefulnessState = MutableStateFlow(WakefulnessModel.ASLEEP)
override val wakefulnessState: Flow<WakefulnessModel> = _wakefulnessState
+ private val _isUdfpsSupported = MutableStateFlow(false)
+
private val _isBouncerShowing = MutableStateFlow(false)
override val isBouncerShowing: Flow<Boolean> = _isBouncerShowing
+ private val _isKeyguardGoingAway = MutableStateFlow(false)
+ override val isKeyguardGoingAway: Flow<Boolean> = _isKeyguardGoingAway
+
private val _biometricUnlockState = MutableStateFlow(BiometricUnlockModel.NONE)
override val biometricUnlockState: Flow<BiometricUnlockModel> = _biometricUnlockState
@@ -86,4 +94,8 @@
fun setDozeAmount(dozeAmount: Float) {
_dozeAmount.value = dozeAmount
}
+
+ override fun isUdfpsSupported(): Boolean {
+ return _isUdfpsSupported.value
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
index 9726bf8..a7eadba 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
@@ -68,4 +68,8 @@
callbacks.forEach { it.onUserChanged(_userId, userContext) }
}
+
+ fun onProfileChanged() {
+ callbacks.forEach { it.onProfilesChanged(_userProfiles) }
+ }
}
diff --git a/packages/VpnDialogs/res/values-de/strings.xml b/packages/VpnDialogs/res/values-de/strings.xml
index f38e395..1de7805 100644
--- a/packages/VpnDialogs/res/values-de/strings.xml
+++ b/packages/VpnDialogs/res/values-de/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="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" 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. <br /> <br /> Wenn das VPN aktiv ist, wird oben im Display <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>
diff --git a/packages/VpnDialogs/res/values-it/strings.xml b/packages/VpnDialogs/res/values-it/strings.xml
index c443c51..118fb6a 100644
--- a/packages/VpnDialogs/res/values-it/strings.xml
+++ b/packages/VpnDialogs/res/values-it/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="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" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vuole impostare una connessione VPN per monitorare il traffico di rete. Accetta soltanto se ritieni la fonte attendibile. Quando la connessione VPN è attiva, in alto sullo schermo appare 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>
diff --git a/packages/overlays/NavigationBarMode3ButtonOverlay/res/values-sw600dp/config.xml b/packages/overlays/NavigationBarMode3ButtonOverlay/res/values-sw600dp/config.xml
new file mode 100644
index 0000000..8e466e0
--- /dev/null
+++ b/packages/overlays/NavigationBarMode3ButtonOverlay/res/values-sw600dp/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2022, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources>
+ <!-- Controls whether seamless rotation should be allowed even though the navbar can move
+ (which normally prevents seamless rotation). Allow seamless rotation because the bar
+ is relatively small in large screen and its appearance is similar to gestural mode
+ even if it jumps to another side for display orientation change. -->
+ <bool name="config_allowSeamlessRotationDespiteNavBarMoving">true</bool>
+</resources>
+
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index 9897a07..382aa87 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -829,6 +829,15 @@
return null;
}
+
+ @Override
+ public boolean isCaptureProcessProgressAvailable() {
+ if (LATENCY_IMPROVEMENTS_SUPPORTED) {
+ return mAdvancedExtender.isCaptureProcessProgressAvailable();
+ }
+
+ return false;
+ }
}
private class CaptureCallbackStub implements SessionProcessorImpl.CaptureCallback {
@@ -922,6 +931,15 @@
Log.e(TAG, "Failed to notify capture complete due to remote exception!");
}
}
+
+ @Override
+ public void onCaptureProcessProgressed(int progress) {
+ try {
+ mCaptureCallback.onCaptureProcessProgressed(progress);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Remote client doesn't respond to capture progress callbacks!");
+ }
+ }
}
private class RequestCallbackStub extends IRequestCallback.Stub {
@@ -1414,6 +1432,15 @@
}
@Override
+ public boolean isCaptureProcessProgressAvailable() {
+ if (LATENCY_IMPROVEMENTS_SUPPORTED) {
+ return mImageExtender.isCaptureProcessProgressAvailable();
+ }
+
+ return false;
+ }
+
+ @Override
public CaptureStageImpl onEnableSession() {
return initializeParcelable(mImageExtender.onEnableSession(), mCameraId);
}
@@ -1570,6 +1597,15 @@
}
@Override
+ public void onCaptureProcessProgressed(int progress) {
+ try {
+ mProcessResult.onCaptureProcessProgressed(progress);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Remote client doesn't respond to capture progress callbacks!");
+ }
+ }
+
+ @Override
public void onCaptureCompleted(long shutterTimestamp,
List<Pair<CaptureResult.Key, Object>> result) {
if (result == null) {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 553146d..84f2b63 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -168,7 +168,7 @@
"android.hardware.rebootescrow-V1-java",
"android.hardware.soundtrigger-V2.3-java",
"android.hardware.power.stats-V1-java",
- "android.hardware.power-V3-java",
+ "android.hardware.power-V4-java",
"android.hidl.manager-V1.2-java",
"capture_state_listener-aidl-java",
"icu4j_calendar_astronomer",
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index fcfee5b..6b6351f 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -802,12 +802,20 @@
break;
}
case H_CLOUD_MEDIA_PROVIDER_CHANGED: {
- final Object listener = msg.obj;
- if (listener instanceof StorageManagerInternal.CloudProviderChangeListener) {
- notifyCloudMediaProviderChangedAsync(
- (StorageManagerInternal.CloudProviderChangeListener) listener);
+ // We send this message in two cases:
+ // 1. After the cloud provider has been set/updated for a user.
+ // In this case Message's #arg1 is set to UserId, and #obj is set to the
+ // authority of the new cloud provider.
+ // 2. After a new CloudProviderChangeListener is registered.
+ // In this case Message's #obj is set to the CloudProviderChangeListener.
+ if (msg.obj instanceof StorageManagerInternal.CloudProviderChangeListener) {
+ final StorageManagerInternal.CloudProviderChangeListener listener =
+ (StorageManagerInternal.CloudProviderChangeListener) msg.obj;
+ notifyCloudMediaProviderChangedAsync(listener);
} else {
- onCloudMediaProviderChangedAsync(msg.arg1);
+ final int userId = msg.arg1;
+ final String authority = (String) msg.obj;
+ onCloudMediaProviderChangedAsync(userId, authority);
}
break;
}
@@ -1686,17 +1694,15 @@
@NonNull StorageManagerInternal.CloudProviderChangeListener listener) {
synchronized (mCloudMediaProviders) {
for (int i = mCloudMediaProviders.size() - 1; i >= 0; --i) {
- listener.onCloudProviderChanged(
- mCloudMediaProviders.keyAt(i), mCloudMediaProviders.valueAt(i));
+ final int userId = mCloudMediaProviders.keyAt(i);
+ final String authority = mCloudMediaProviders.valueAt(i);
+ listener.onCloudProviderChanged(userId, authority);
}
}
}
- private void onCloudMediaProviderChangedAsync(int userId) {
- final String authority;
- synchronized (mCloudMediaProviders) {
- authority = mCloudMediaProviders.get(userId);
- }
+ private void onCloudMediaProviderChangedAsync(
+ @UserIdInt int userId, @Nullable String authority) {
for (StorageManagerInternal.CloudProviderChangeListener listener :
mStorageManagerInternal.mCloudProviderChangeListeners) {
listener.onCloudProviderChanged(userId, authority);
@@ -4831,7 +4837,7 @@
public void registerCloudProviderChangeListener(
@NonNull StorageManagerInternal.CloudProviderChangeListener listener) {
mCloudProviderChangeListeners.add(listener);
- mHandler.obtainMessage(H_CLOUD_MEDIA_PROVIDER_CHANGED, listener);
+ mHandler.obtainMessage(H_CLOUD_MEDIA_PROVIDER_CHANGED, listener).sendToTarget();
}
}
}
diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java
index e40f001..933d259 100644
--- a/services/core/java/com/android/server/SystemService.java
+++ b/services/core/java/com/android/server/SystemService.java
@@ -473,18 +473,6 @@
}
/**
- * The {@link UserManager#isUserVisible() user visibility} changed.
- *
- * <p>This callback is called before the user starts or is switched to (or after it stops), when
- * its visibility changed because of that action.
- *
- * @hide
- */
- // NOTE: change visible to int if this method becomes a @SystemApi
- public void onUserVisibilityChanged(@NonNull TargetUser user, boolean visible) {
- }
-
- /**
* Called when an existing user is stopping, for system services to finalize any per-user
* state they maintain for running users. This is called prior to sending the SHUTDOWN
* broadcast to the user; it is a good place to stop making use of any resources of that
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index 953e850..c3cd135 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -82,10 +82,6 @@
private static final String USER_STOPPING = "Stop"; // Logged as onUserStopping()
private static final String USER_STOPPED = "Cleanup"; // Logged as onUserStopped()
private static final String USER_COMPLETED_EVENT = "CompletedEvent"; // onUserCompletedEvent()
- private static final String USER_VISIBLE = "Visible"; // Logged on onUserVisible() and
- // onUserStarting() (when visible is true)
- private static final String USER_INVISIBLE = "Invisible"; // Logged on onUserStopping()
- // (when visibilityChanged is true)
// The default number of threads to use if lifecycle thread pool is enabled.
private static final int DEFAULT_MAX_USER_POOL_THREADS = 3;
@@ -354,58 +350,17 @@
/**
* Starts the given user.
*/
- public void onUserStarting(@NonNull TimingsTraceAndSlog t, @UserIdInt int userId,
- boolean visible) {
- EventLog.writeEvent(EventLogTags.SSM_USER_STARTING, userId, visible ? 1 : 0);
+ public void onUserStarting(@NonNull TimingsTraceAndSlog t, @UserIdInt int userId) {
+ EventLog.writeEvent(EventLogTags.SSM_USER_STARTING, userId);
final TargetUser targetUser = newTargetUser(userId);
synchronized (mTargetUsers) {
mTargetUsers.put(userId, targetUser);
}
-
- if (visible) {
- // Must send the user visiiblity change first, for 2 reasons:
- // 1. Automotive need to update the user-zone mapping ASAP and it's one of the few
- // services listening to this event (OTOH, there are manyy listeners to USER_STARTING
- // and some can take a while to process it)
- // 2. When a user is switched from bg to fg, the onUserVisibilityChanged() callback is
- // called onUserSwitching(), so calling it before onUserStarting() make it more
- // consistent with that
- EventLog.writeEvent(EventLogTags.SSM_USER_VISIBILITY_CHANGED, userId, /* visible= */ 1);
- onUser(t, USER_VISIBLE, /* prevUser= */ null, targetUser);
- }
onUser(t, USER_STARTING, /* prevUser= */ null, targetUser);
}
/**
- * Updates the user visibility.
- *
- * <p><b>NOTE: </b>this method should only be called when a user that is already running become
- * visible; if the user is starting visible, callers should call
- * {@link #onUserStarting(TimingsTraceAndSlog, int, boolean)} instead.
- */
- public void onUserVisible(@UserIdInt int userId) {
- EventLog.writeEvent(EventLogTags.SSM_USER_VISIBILITY_CHANGED, userId, /* visible= */ 1);
- onUser(USER_VISIBLE, userId);
- }
-
- /**
- * Updates the visibility of the system user.
- *
- * <p>Since the system user never stops, this method must be called when it's switched from / to
- * foreground.
- */
- public void onSystemUserVisibilityChanged(boolean visible) {
- int userId = UserHandle.USER_SYSTEM;
- EventLog.writeEvent(EventLogTags.SSM_USER_VISIBILITY_CHANGED, userId, visible ? 1 : 0);
- if (visible) {
- onUser(USER_VISIBLE, userId);
- } else {
- onUser(USER_INVISIBLE, userId);
- }
- }
-
- /**
* Unlocks the given user.
*/
public void onUserUnlocking(@UserIdInt int userId) {
@@ -452,12 +407,9 @@
/**
* Stops the given user.
*/
- public void onUserStopping(@UserIdInt int userId, boolean visibilityChanged) {
- EventLog.writeEvent(EventLogTags.SSM_USER_STOPPING, userId, visibilityChanged ? 1 : 0);
+ public void onUserStopping(@UserIdInt int userId) {
+ EventLog.writeEvent(EventLogTags.SSM_USER_STOPPING, userId);
onUser(USER_STOPPING, userId);
- if (visibilityChanged) {
- onUser(USER_INVISIBLE, userId);
- }
}
/**
@@ -580,12 +532,6 @@
threadPool.submit(getOnUserCompletedEventRunnable(
t, service, serviceName, curUser, completedEventType));
break;
- case USER_VISIBLE:
- service.onUserVisibilityChanged(curUser, /* visible= */ true);
- break;
- case USER_INVISIBLE:
- service.onUserVisibilityChanged(curUser, /* visible= */ false);
- break;
default:
throw new IllegalArgumentException(onWhat + " what?");
}
diff --git a/services/core/java/com/android/server/VpnManagerService.java b/services/core/java/com/android/server/VpnManagerService.java
index 3f1d1fe..ae50b23 100644
--- a/services/core/java/com/android/server/VpnManagerService.java
+++ b/services/core/java/com/android/server/VpnManagerService.java
@@ -186,6 +186,10 @@
synchronized (mVpns) {
for (int i = 0; i < mVpns.size(); i++) {
pw.println(mVpns.keyAt(i) + ": " + mVpns.valueAt(i).getPackage());
+ pw.increaseIndent();
+ mVpns.valueAt(i).dump(pw);
+ pw.decreaseIndent();
+ pw.println();
}
pw.decreaseIndent();
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 672ee0e..c7c2655 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -528,7 +528,7 @@
private Map<Account, Integer> getAccountsAndVisibilityForPackage(String packageName,
List<String> accountTypes, Integer callingUid, UserAccounts accounts) {
if (!packageExistsForUser(packageName, accounts.userId)) {
- Log.d(TAG, "Package not found " + packageName);
+ Log.w(TAG, "getAccountsAndVisibilityForPackage#Package not found " + packageName);
return new LinkedHashMap<>();
}
@@ -677,7 +677,7 @@
restoreCallingIdentity(identityToken);
}
} catch (NameNotFoundException e) {
- Log.d(TAG, "Package not found " + e.getMessage());
+ Log.w(TAG, "resolveAccountVisibility#Package not found " + e.getMessage());
return AccountManager.VISIBILITY_NOT_VISIBLE;
}
@@ -756,7 +756,7 @@
}
return true;
} catch (NameNotFoundException e) {
- Log.d(TAG, "Package not found " + e.getMessage());
+ Log.w(TAG, "isPreOApplication#Package not found " + e.getMessage());
return true;
}
}
@@ -840,6 +840,7 @@
}
if (notify) {
+ Log.i(TAG, "Notifying visibility changed for package=" + packageName);
for (Entry<String, Integer> packageToVisibility : packagesToVisibility
.entrySet()) {
int oldVisibility = packageToVisibility.getValue();
@@ -850,9 +851,14 @@
}
}
for (String packageNameToNotify : accountRemovedReceivers) {
- sendAccountRemovedBroadcast(account, packageNameToNotify, accounts.userId);
+ sendAccountRemovedBroadcast(
+ account,
+ packageNameToNotify,
+ accounts.userId,
+ /*useCase=*/"setAccountVisibility");
}
- sendAccountsChangedBroadcast(accounts.userId);
+ sendAccountsChangedBroadcast(
+ accounts.userId, account.type, /*useCase=*/"setAccountVisibility");
}
return true;
}
@@ -973,6 +979,8 @@
* @param accounts UserAccount that currently hosts the account
*/
private void notifyPackage(String packageName, UserAccounts accounts) {
+ Log.i(TAG, "notifying package=" + packageName + " for userId=" + accounts.userId
+ +", sending broadcast of " + AccountManager.ACTION_VISIBLE_ACCOUNTS_CHANGED);
Intent intent = new Intent(AccountManager.ACTION_VISIBLE_ACCOUNTS_CHANGED);
intent.setPackage(packageName);
intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
@@ -1059,13 +1067,21 @@
|| AccountManager.PACKAGE_NAME_KEY_LEGACY_NOT_VISIBLE.equals(packageName));
}
- private void sendAccountsChangedBroadcast(int userId) {
- Log.i(TAG, "the accounts changed, sending broadcast of "
- + ACCOUNTS_CHANGED_INTENT.getAction());
+ private void sendAccountsChangedBroadcast(
+ int userId, String accountType, @NonNull String useCase) {
+ Objects.requireNonNull(useCase, "useCase can't be null");
+ Log.i(TAG, "the accountType= " + (accountType == null ? "" : accountType)
+ + " changed with useCase=" + useCase + " for userId=" + userId
+ + ", sending broadcast of " + ACCOUNTS_CHANGED_INTENT.getAction());
mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
}
- private void sendAccountRemovedBroadcast(Account account, String packageName, int userId) {
+ private void sendAccountRemovedBroadcast(
+ Account account, String packageName, int userId, @NonNull String useCase) {
+ Objects.requireNonNull(useCase, "useCase can't be null");
+ Log.i(TAG, "the account with type=" + account.type + " removed while useCase="
+ + useCase + " for userId=" + userId + ", sending broadcast of "
+ + AccountManager.ACTION_ACCOUNT_REMOVED);
Intent intent = new Intent(AccountManager.ACTION_ACCOUNT_REMOVED);
intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
intent.setPackage(packageName);
@@ -1212,6 +1228,8 @@
accountsDb.endTransaction();
}
accountDeleted = true;
+ Log.i(TAG, "validateAccountsInternal#Deleted UserId="
+ + accounts.userId + ", AccountId=" + accountId);
logRecord(AccountsDb.DEBUG_ACTION_AUTHENTICATOR_REMOVE,
AccountsDb.TABLE_ACCOUNTS, accountId, accounts);
@@ -1228,7 +1246,11 @@
}
}
for (String packageName : accountRemovedReceivers) {
- sendAccountRemovedBroadcast(account, packageName, accounts.userId);
+ sendAccountRemovedBroadcast(
+ account,
+ packageName,
+ accounts.userId,
+ /*useCase=*/"validateAccounts");
}
} else {
ArrayList<String> accountNames = accountNamesByType.get(account.type);
@@ -1253,7 +1275,10 @@
AccountManager.invalidateLocalAccountsDataCaches();
} finally {
if (accountDeleted) {
- sendAccountsChangedBroadcast(accounts.userId);
+ sendAccountsChangedBroadcast(
+ accounts.userId,
+ /*accountType=*/"ambiguous",
+ /*useCase=*/"validateAccounts");
}
}
}
@@ -1845,7 +1870,7 @@
}
if (accounts.accountsDb.findAllDeAccounts().size() > 100) {
Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString()
- + ", skipping since more than 50 accounts on device exist");
+ + ", skipping since more than 100 accounts on device exist");
return false;
}
long accountId = accounts.accountsDb.insertCeAccount(account, password);
@@ -1899,7 +1924,9 @@
sendNotificationAccountUpdated(account, accounts);
// Only send LOGIN_ACCOUNTS_CHANGED when the database changed.
- sendAccountsChangedBroadcast(accounts.userId);
+ Log.i(TAG, "callingUid=" + callingUid + ", userId=" + accounts.userId
+ + " added account");
+ sendAccountsChangedBroadcast(accounts.userId, account.type, /*useCase=*/"addAccount");
logAddAccountExplicitlyMetrics(opPackageName, account.type, packageToVisibility);
return true;
@@ -2089,6 +2116,8 @@
final long identityToken = clearCallingIdentity();
try {
UserAccounts accounts = getUserAccounts(userId);
+ Log.i(TAG, "callingUid=" + callingUid + ", userId=" + accounts.userId
+ + " performing rename account");
Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName);
Bundle result = new Bundle();
result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name);
@@ -2199,9 +2228,14 @@
}
sendNotificationAccountUpdated(resultAccount, accounts);
- sendAccountsChangedBroadcast(accounts.userId);
+ sendAccountsChangedBroadcast(
+ accounts.userId, accountToRename.type, /*useCase=*/"renameAccount");
for (String packageName : accountRemovedReceivers) {
- sendAccountRemovedBroadcast(accountToRename, packageName, accounts.userId);
+ sendAccountRemovedBroadcast(
+ accountToRename,
+ packageName,
+ accounts.userId,
+ /*useCase=*/"renameAccount");
}
AccountManager.invalidateLocalAccountsDataCaches();
@@ -2433,9 +2467,13 @@
}
// Only broadcast LOGIN_ACCOUNTS_CHANGED if a change occurred.
- sendAccountsChangedBroadcast(accounts.userId);
+ Log.i(TAG, "callingUid=" + callingUid + ", userId=" + accounts.userId
+ + " removed account");
+ sendAccountsChangedBroadcast(
+ accounts.userId, account.type, /*useCase=*/"removeAccount");
for (String packageName : accountRemovedReceivers) {
- sendAccountRemovedBroadcast(account, packageName, accounts.userId);
+ sendAccountRemovedBroadcast(
+ account, packageName, accounts.userId, /*useCase=*/"removeAccount");
}
String action = userUnlocked ? AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE
: AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE_DE;
@@ -2709,7 +2747,9 @@
if (isChanged) {
// Send LOGIN_ACCOUNTS_CHANGED only if the something changed.
sendNotificationAccountUpdated(account, accounts);
- sendAccountsChangedBroadcast(accounts.userId);
+ Log.i(TAG, "callingUid=" + callingUid + " changed password");
+ sendAccountsChangedBroadcast(
+ accounts.userId, account.type, /*useCase=*/"setPassword");
}
}
}
@@ -3480,10 +3520,10 @@
@Override
protected String toDebugString(long now) {
- String requiredFeaturesStr = TextUtils.join(",", requiredFeatures);
return super.toDebugString(now) + ", startAddAccountSession" + ", accountType "
+ accountType + ", requiredFeatures "
- + (requiredFeatures != null ? requiredFeaturesStr : null);
+ + (requiredFeatures != null
+ ? TextUtils.join(",", requiredFeatures) : "null");
}
}.bind();
} finally {
@@ -4063,7 +4103,7 @@
int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
return hasAccountAccess(account, packageName, uid);
} catch (NameNotFoundException e) {
- Log.d(TAG, "Package not found " + e.getMessage());
+ Log.w(TAG, "hasAccountAccess#Package not found " + e.getMessage());
return false;
}
}
@@ -4195,7 +4235,7 @@
}
final long token = Binder.clearCallingIdentity();
try {
- AccountAndUser[] allAccounts = getAllAccounts();
+ AccountAndUser[] allAccounts = getAllAccountsForSystemProcess();
for (int i = allAccounts.length - 1; i >= 0; i--) {
if (allAccounts[i].account.equals(account)) {
return true;
@@ -4345,10 +4385,11 @@
/**
* Returns accounts for all running users, ignores visibility values.
*
+ * Should only be called by System process.
* @hide
*/
@NonNull
- public AccountAndUser[] getRunningAccounts() {
+ public AccountAndUser[] getRunningAccountsForSystem() {
final int[] runningUserIds;
try {
runningUserIds = ActivityManager.getService().getRunningUserIds();
@@ -4356,26 +4397,34 @@
// Running in system_server; should never happen
throw new RuntimeException(e);
}
- return getAccounts(runningUserIds);
+ return getAccountsForSystem(runningUserIds);
}
/**
* Returns accounts for all users, ignores visibility values.
*
+ * Should only be called by system process
+ *
* @hide
*/
@NonNull
- public AccountAndUser[] getAllAccounts() {
+ public AccountAndUser[] getAllAccountsForSystemProcess() {
final List<UserInfo> users = getUserManager().getAliveUsers();
final int[] userIds = new int[users.size()];
for (int i = 0; i < userIds.length; i++) {
userIds[i] = users.get(i).id;
}
- return getAccounts(userIds);
+ return getAccountsForSystem(userIds);
}
+ /**
+ * Returns all accounts for the given user, ignores all visibility checks.
+ * This should only be called by system process.
+ *
+ * @hide
+ */
@NonNull
- private AccountAndUser[] getAccounts(int[] userIds) {
+ private AccountAndUser[] getAccountsForSystem(int[] userIds) {
final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
for (int userId : userIds) {
UserAccounts userAccounts = getUserAccounts(userId);
@@ -4384,7 +4433,7 @@
userAccounts,
null /* type */,
Binder.getCallingUid(),
- null /* packageName */,
+ "android"/* packageName */,
false /* include managed not visible*/);
for (Account account : accounts) {
runningAccounts.add(new AccountAndUser(account, userId));
@@ -4795,6 +4844,7 @@
private abstract class Session extends IAccountAuthenticatorResponse.Stub
implements IBinder.DeathRecipient, ServiceConnection {
+ private final Object mSessionLock = new Object();
IAccountManagerResponse mResponse;
final String mAccountType;
final boolean mExpectActivityLaunch;
@@ -4985,9 +5035,11 @@
}
private void unbind() {
- if (mAuthenticator != null) {
- mAuthenticator = null;
- mContext.unbindService(this);
+ synchronized (mSessionLock) {
+ if (mAuthenticator != null) {
+ mAuthenticator = null;
+ mContext.unbindService(this);
+ }
}
}
@@ -4997,12 +5049,14 @@
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
- mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
- try {
- run();
- } catch (RemoteException e) {
- onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
- "remote exception");
+ synchronized (mSessionLock) {
+ mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
+ try {
+ run();
+ } catch (RemoteException e) {
+ onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
+ "remote exception");
+ }
}
}
@@ -5355,7 +5409,7 @@
}
} else {
Account[] accounts = getAccountsFromCache(userAccounts, null /* type */,
- Process.SYSTEM_UID, null /* packageName */, false);
+ Process.SYSTEM_UID, "android" /* packageName */, false);
fout.println("Accounts: " + accounts.length);
for (Account account : accounts) {
fout.println(" " + account.toString());
@@ -5550,7 +5604,7 @@
return true;
}
} catch (PackageManager.NameNotFoundException e) {
- Log.d(TAG, "Package not found " + e.getMessage());
+ Log.w(TAG, "isPrivileged#Package not found " + e.getMessage());
}
}
} finally {
@@ -6074,7 +6128,7 @@
}
}
} catch (NameNotFoundException e) {
- Log.d(TAG, "Package not found " + e.getMessage());
+ Log.w(TAG, "filterSharedAccounts#Package not found " + e.getMessage());
}
Map<Account, Integer> filtered = new LinkedHashMap<>();
for (Map.Entry<Account, Integer> entry : unfiltered.entrySet()) {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index c677edc..d0f245f 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1197,7 +1197,7 @@
}
FrameworkStatsLog.write(SERVICE_REQUEST_EVENT_REPORTED, uid, callingUid,
- ActivityManagerService.getShortAction(service.getAction()),
+ service.getAction(),
SERVICE_REQUEST_EVENT_REPORTED__REQUEST_TYPE__START, false,
r.app == null || r.app.getThread() == null
? SERVICE_REQUEST_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a17b5e2..7d64077 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -11111,11 +11111,13 @@
final long pss;
final long swapPss;
final long mRss;
- final int id;
+ final int id; // pid
+ final int userId;
final boolean hasActivities;
ArrayList<MemItem> subitems;
MemItem(String label, String shortLabel, long pss, long swapPss, long rss, int id,
+ @UserIdInt int userId,
boolean hasActivities) {
this.isProc = true;
this.label = label;
@@ -11124,6 +11126,7 @@
this.swapPss = swapPss;
this.mRss = rss;
this.id = id;
+ this.userId = userId;
this.hasActivities = hasActivities;
}
@@ -11135,6 +11138,7 @@
this.swapPss = swapPss;
this.mRss = rss;
this.id = id;
+ this.userId = UserHandle.USER_SYSTEM;
this.hasActivities = false;
}
}
@@ -11169,8 +11173,9 @@
pw.printf("%s%s: %-60s (%s in swap)\n", prefix, stringifyKBSize(mi.pss),
mi.label, stringifyKBSize(mi.swapPss));
} else {
- pw.printf("%s%s: %s\n", prefix, stringifyKBSize(dumpPss ? mi.pss : mi.mRss),
- mi.label);
+ pw.printf("%s%s: %s%s\n", prefix, stringifyKBSize(dumpPss ? mi.pss : mi.mRss),
+ mi.label,
+ mi.userId != UserHandle.USER_SYSTEM ? " (user " + mi.userId + ")" : "");
}
} else if (mi.isProc) {
pw.print("proc,"); pw.print(tag); pw.print(","); pw.print(mi.shortLabel);
@@ -11662,7 +11667,7 @@
ss[INDEX_TOTAL_MEMTRACK_GL] += memtrackGl;
MemItem pssItem = new MemItem(r.processName + " (pid " + pid +
(hasActivities ? " / activities)" : ")"), r.processName, myTotalPss,
- myTotalSwapPss, myTotalRss, pid, hasActivities);
+ myTotalSwapPss, myTotalRss, pid, r.userId, hasActivities);
procMems.add(pssItem);
procMemsMap.put(pid, pssItem);
@@ -11759,7 +11764,7 @@
MemItem pssItem = new MemItem(st.name + " (pid " + st.pid + ")",
st.name, myTotalPss, info.getSummaryTotalSwapPss(), myTotalRss,
- st.pid, false);
+ st.pid, UserHandle.getUserId(st.uid), false);
procMems.add(pssItem);
ss[INDEX_NATIVE_PSS] += info.nativePss;
@@ -12305,7 +12310,7 @@
ss[INDEX_TOTAL_RSS] += myTotalRss;
MemItem pssItem = new MemItem(r.processName + " (pid " + pid +
(hasActivities ? " / activities)" : ")"), r.processName, myTotalPss,
- myTotalSwapPss, myTotalRss, pid, hasActivities);
+ myTotalSwapPss, myTotalRss, pid, r.userId, hasActivities);
procMems.add(pssItem);
procMemsMap.put(pid, pssItem);
@@ -12393,7 +12398,7 @@
MemItem pssItem = new MemItem(st.name + " (pid " + st.pid + ")",
st.name, myTotalPss, info.getSummaryTotalSwapPss(), myTotalRss,
- st.pid, false);
+ st.pid, UserHandle.getUserId(st.uid), false);
procMems.add(pssItem);
ss[INDEX_NATIVE_PSS] += info.nativePss;
@@ -17426,6 +17431,21 @@
}
@Override
+ public int broadcastIntentWithCallback(Intent intent,
+ IIntentReceiver resultTo,
+ String[] requiredPermissions,
+ int userId, int[] appIdAllowList,
+ @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
+ @Nullable Bundle bOptions) {
+ // Sending broadcasts with a finish callback without the need for the broadcasts
+ // delivery to be serialized is only supported by modern queue. So, when modern
+ // queue is disabled, we continue to send broadcasts in a serialized fashion.
+ final boolean serialized = !isModernQueueEnabled();
+ return broadcastIntent(intent, resultTo, requiredPermissions, serialized, userId,
+ appIdAllowList, filterExtrasForReceiver, bOptions);
+ }
+
+ @Override
public ComponentName startServiceInPackage(int uid, Intent service, String resolvedType,
boolean fgRequired, String callingPackage, @Nullable String callingFeatureId,
int userId, boolean allowBackgroundActivityStarts,
@@ -19030,6 +19050,10 @@
return mOomAdjuster.mCachedAppOptimizer.useFreezer();
}
+ public boolean isAppFreezerExemptInstPkg() {
+ return mOomAdjuster.mCachedAppOptimizer.freezerExemptInstPkg();
+ }
+
/**
* Resets the state of the {@link com.android.server.am.AppErrors} instance.
* This is intended for testing within the CTS only and is protected by
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 75d1f68..d1bcf87 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -731,6 +731,8 @@
@Override
@EnforcePermission(BATTERY_STATS)
public List<BatteryUsageStats> getBatteryUsageStats(List<BatteryUsageStatsQuery> queries) {
+ super.getBatteryUsageStats_enforcePermission();
+
awaitCompletion();
if (mBatteryUsageStatsProvider.shouldUpdateStats(queries,
@@ -846,6 +848,8 @@
@Override
@EnforcePermission(BATTERY_STATS)
public long computeBatteryScreenOffRealtimeMs() {
+ super.computeBatteryScreenOffRealtimeMs_enforcePermission();
+
synchronized (mStats) {
final long curTimeUs = SystemClock.elapsedRealtimeNanos() / 1000;
long timeUs = mStats.computeBatteryScreenOffRealtime(curTimeUs,
@@ -857,6 +861,8 @@
@Override
@EnforcePermission(BATTERY_STATS)
public long getScreenOffDischargeMah() {
+ super.getScreenOffDischargeMah_enforcePermission();
+
synchronized (mStats) {
long dischargeUah = mStats.getUahDischargeScreenOff(BatteryStats.STATS_SINCE_CHARGED);
return dischargeUah / 1000;
@@ -866,6 +872,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteEvent(final int code, final String name, final int uid) {
+ super.noteEvent_enforcePermission();
+
if (name == null) {
// TODO(b/194733136): Replace with an IllegalArgumentException throw.
Slog.wtfStack(TAG, "noteEvent called with null name. code = " + code);
@@ -886,6 +894,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteSyncStart(final String name, final int uid) {
+ super.noteSyncStart_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -902,6 +912,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteSyncFinish(final String name, final int uid) {
+ super.noteSyncFinish_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -919,6 +931,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteJobStart(final String name, final int uid) {
+ super.noteJobStart_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -934,6 +948,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteJobFinish(final String name, final int uid, final int stopReason) {
+ super.noteJobFinish_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1007,6 +1023,8 @@
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteStartWakelock(final int uid, final int pid, final String name,
final String historyName, final int type, final boolean unimportantForLogging) {
+ super.noteStartWakelock_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1023,6 +1041,8 @@
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteStopWakelock(final int uid, final int pid, final String name,
final String historyName, final int type) {
+ super.noteStopWakelock_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1039,6 +1059,8 @@
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteStartWakelockFromSource(final WorkSource ws, final int pid, final String name,
final String historyName, final int type, final boolean unimportantForLogging) {
+ super.noteStartWakelockFromSource_enforcePermission();
+
final WorkSource localWs = ws != null ? new WorkSource(ws) : null;
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -1058,6 +1080,8 @@
final String historyName, final int type, final WorkSource newWs, final int newPid,
final String newName, final String newHistoryName, final int newType,
final boolean newUnimportantForLogging) {
+ super.noteChangeWakelockFromSource_enforcePermission();
+
final WorkSource localWs = ws != null ? new WorkSource(ws) : null;
final WorkSource localNewWs = newWs != null ? new WorkSource(newWs) : null;
synchronized (mLock) {
@@ -1077,6 +1101,8 @@
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteStopWakelockFromSource(final WorkSource ws, final int pid, final String name,
final String historyName, final int type) {
+ super.noteStopWakelockFromSource_enforcePermission();
+
final WorkSource localWs = ws != null ? new WorkSource(ws) : null;
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -1094,6 +1120,8 @@
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteLongPartialWakelockStart(final String name, final String historyName,
final int uid) {
+ super.noteLongPartialWakelockStart_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1110,6 +1138,8 @@
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteLongPartialWakelockStartFromSource(final String name, final String historyName,
final WorkSource workSource) {
+ super.noteLongPartialWakelockStartFromSource_enforcePermission();
+
final WorkSource localWs = workSource != null ? new WorkSource(workSource) : null;
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -1127,6 +1157,8 @@
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteLongPartialWakelockFinish(final String name, final String historyName,
final int uid) {
+ super.noteLongPartialWakelockFinish_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1143,6 +1175,8 @@
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteLongPartialWakelockFinishFromSource(final String name, final String historyName,
final WorkSource workSource) {
+ super.noteLongPartialWakelockFinishFromSource_enforcePermission();
+
final WorkSource localWs = workSource != null ? new WorkSource(workSource) : null;
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -1159,6 +1193,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteStartSensor(final int uid, final int sensor) {
+ super.noteStartSensor_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1175,6 +1211,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteStopSensor(final int uid, final int sensor) {
+ super.noteStopSensor_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1191,6 +1229,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteVibratorOn(final int uid, final long durationMillis) {
+ super.noteVibratorOn_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1205,6 +1245,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteVibratorOff(final int uid) {
+ super.noteVibratorOff_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1219,6 +1261,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteGpsChanged(final WorkSource oldWs, final WorkSource newWs) {
+ super.noteGpsChanged_enforcePermission();
+
final WorkSource localOldWs = oldWs != null ? new WorkSource(oldWs) : null;
final WorkSource localNewWs = newWs != null ? new WorkSource(newWs) : null;
synchronized (mLock) {
@@ -1235,6 +1279,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteGpsSignalQuality(final int signalLevel) {
+ super.noteGpsSignalQuality_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1249,6 +1295,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteScreenState(final int state) {
+ super.noteScreenState_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1267,6 +1315,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteScreenBrightness(final int brightness) {
+ super.noteScreenBrightness_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1282,6 +1332,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteUserActivity(final int uid, final int event) {
+ super.noteUserActivity_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1296,6 +1348,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteWakeUp(final String reason, final int reasonUid) {
+ super.noteWakeUp_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1310,6 +1364,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteInteractive(final boolean interactive) {
+ super.noteInteractive_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
mHandler.post(() -> {
@@ -1323,6 +1379,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteConnectivityChanged(final int type, final String extra) {
+ super.noteConnectivityChanged_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1338,6 +1396,8 @@
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteMobileRadioPowerState(final int powerState, final long timestampNs,
final int uid) {
+ super.noteMobileRadioPowerState_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1364,6 +1424,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void notePhoneOn() {
+ super.notePhoneOn_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1378,6 +1440,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void notePhoneOff() {
+ super.notePhoneOff_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1392,6 +1456,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void notePhoneSignalStrength(final SignalStrength signalStrength) {
+ super.notePhoneSignalStrength_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1407,6 +1473,8 @@
@EnforcePermission(UPDATE_DEVICE_STATS)
public void notePhoneDataConnectionState(final int dataType, final boolean hasData,
final int serviceType, final int nrFrequency) {
+ super.notePhoneDataConnectionState_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1422,6 +1490,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void notePhoneState(final int state) {
+ super.notePhoneState_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1437,6 +1507,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteWifiOn() {
+ super.noteWifiOn_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1453,6 +1525,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteWifiOff() {
+ super.noteWifiOff_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1469,6 +1543,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteStartAudio(final int uid) {
+ super.noteStartAudio_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1485,6 +1561,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteStopAudio(final int uid) {
+ super.noteStopAudio_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1501,6 +1579,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteStartVideo(final int uid) {
+ super.noteStartVideo_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1517,6 +1597,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteStopVideo(final int uid) {
+ super.noteStopVideo_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1533,6 +1615,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteResetAudio() {
+ super.noteResetAudio_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1549,6 +1633,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteResetVideo() {
+ super.noteResetVideo_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1565,6 +1651,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteFlashlightOn(final int uid) {
+ super.noteFlashlightOn_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1581,6 +1669,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteFlashlightOff(final int uid) {
+ super.noteFlashlightOff_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1597,6 +1687,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteStartCamera(final int uid) {
+ super.noteStartCamera_enforcePermission();
+
if (DBG) Slog.d(TAG, "begin noteStartCamera");
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -1615,6 +1707,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteStopCamera(final int uid) {
+ super.noteStopCamera_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1631,6 +1725,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteResetCamera() {
+ super.noteResetCamera_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1647,6 +1743,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteResetFlashlight() {
+ super.noteResetFlashlight_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1663,6 +1761,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteWifiRadioPowerState(final int powerState, final long tsNanos, final int uid) {
+ super.noteWifiRadioPowerState_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1694,6 +1794,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteWifiRunning(final WorkSource ws) {
+ super.noteWifiRunning_enforcePermission();
+
final WorkSource localWs = ws != null ? new WorkSource(ws) : null;
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -1712,6 +1814,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteWifiRunningChanged(final WorkSource oldWs, final WorkSource newWs) {
+ super.noteWifiRunningChanged_enforcePermission();
+
final WorkSource localOldWs = oldWs != null ? new WorkSource(oldWs) : null;
final WorkSource localNewWs = newWs != null ? new WorkSource(newWs) : null;
synchronized (mLock) {
@@ -1733,6 +1837,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteWifiStopped(final WorkSource ws) {
+ super.noteWifiStopped_enforcePermission();
+
final WorkSource localWs = ws != null ? new WorkSource(ws) : ws;
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -1750,6 +1856,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteWifiState(final int wifiState, final String accessPoint) {
+ super.noteWifiState_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
mHandler.post(() -> {
@@ -1763,6 +1871,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteWifiSupplicantStateChanged(final int supplState, final boolean failedAuth) {
+ super.noteWifiSupplicantStateChanged_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1778,6 +1888,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteWifiRssiChanged(final int newRssi) {
+ super.noteWifiRssiChanged_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1792,6 +1904,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteFullWifiLockAcquired(final int uid) {
+ super.noteFullWifiLockAcquired_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1806,6 +1920,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteFullWifiLockReleased(final int uid) {
+ super.noteFullWifiLockReleased_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1820,6 +1936,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteWifiScanStarted(final int uid) {
+ super.noteWifiScanStarted_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1834,6 +1952,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteWifiScanStopped(final int uid) {
+ super.noteWifiScanStopped_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1848,6 +1968,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteWifiMulticastEnabled(final int uid) {
+ super.noteWifiMulticastEnabled_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1862,6 +1984,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteWifiMulticastDisabled(final int uid) {
+ super.noteWifiMulticastDisabled_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -1876,6 +2000,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteFullWifiLockAcquiredFromSource(final WorkSource ws) {
+ super.noteFullWifiLockAcquiredFromSource_enforcePermission();
+
final WorkSource localWs = ws != null ? new WorkSource(ws) : null;
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -1892,6 +2018,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteFullWifiLockReleasedFromSource(final WorkSource ws) {
+ super.noteFullWifiLockReleasedFromSource_enforcePermission();
+
final WorkSource localWs = ws != null ? new WorkSource(ws) : null;
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -1908,6 +2036,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteWifiScanStartedFromSource(final WorkSource ws) {
+ super.noteWifiScanStartedFromSource_enforcePermission();
+
final WorkSource localWs = ws != null ? new WorkSource(ws) : null;
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -1923,6 +2053,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteWifiScanStoppedFromSource(final WorkSource ws) {
+ super.noteWifiScanStoppedFromSource_enforcePermission();
+
final WorkSource localWs = ws != null ? new WorkSource(ws) : null;
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -1938,6 +2070,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteWifiBatchedScanStartedFromSource(final WorkSource ws, final int csph) {
+ super.noteWifiBatchedScanStartedFromSource_enforcePermission();
+
final WorkSource localWs = ws != null ? new WorkSource(ws) : null;
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -1954,6 +2088,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteWifiBatchedScanStoppedFromSource(final WorkSource ws) {
+ super.noteWifiBatchedScanStoppedFromSource_enforcePermission();
+
final WorkSource localWs = ws != null ? new WorkSource(ws) : null;
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -1970,6 +2106,8 @@
@Override
@EnforcePermission(anyOf = {NETWORK_STACK, PERMISSION_MAINLINE_NETWORK_STACK})
public void noteNetworkInterfaceForTransports(final String iface, int[] transportTypes) {
+ super.noteNetworkInterfaceForTransports_enforcePermission();
+
synchronized (mLock) {
mHandler.post(() -> {
mStats.noteNetworkInterfaceForTransports(iface, transportTypes);
@@ -1983,6 +2121,8 @@
// During device boot, qtaguid isn't enabled until after the inital
// loading of battery stats. Now that they're enabled, take our initial
// snapshot for future delta calculation.
+ super.noteNetworkStatsEnabled_enforcePermission();
+
synchronized (mLock) {
// Still schedule it on the handler to make sure we have existing pending works done
mHandler.post(() -> {
@@ -1996,6 +2136,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteDeviceIdleMode(final int mode, final String activeReason, final int activeUid) {
+ super.noteDeviceIdleMode_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -2039,6 +2181,8 @@
@Override
@EnforcePermission(BLUETOOTH_CONNECT)
public void noteBluetoothOn(int uid, int reason, String packageName) {
+ super.noteBluetoothOn_enforcePermission();
+
FrameworkStatsLog.write_non_chained(FrameworkStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED,
Binder.getCallingUid(), null,
FrameworkStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__ENABLED,
@@ -2051,6 +2195,8 @@
@Override
@EnforcePermission(BLUETOOTH_CONNECT)
public void noteBluetoothOff(int uid, int reason, String packageName) {
+ super.noteBluetoothOff_enforcePermission();
+
FrameworkStatsLog.write_non_chained(FrameworkStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED,
Binder.getCallingUid(), null,
FrameworkStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__DISABLED,
@@ -2060,6 +2206,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteBleScanStarted(final WorkSource ws, final boolean isUnoptimized) {
+ super.noteBleScanStarted_enforcePermission();
+
final WorkSource localWs = ws != null ? new WorkSource(ws) : null;
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -2076,6 +2224,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteBleScanStopped(final WorkSource ws, final boolean isUnoptimized) {
+ super.noteBleScanStopped_enforcePermission();
+
final WorkSource localWs = ws != null ? new WorkSource(ws) : null;
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -2092,6 +2242,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteBleScanReset() {
+ super.noteBleScanReset_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -2106,6 +2258,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteBleScanResults(final WorkSource ws, final int numNewResults) {
+ super.noteBleScanResults_enforcePermission();
+
final WorkSource localWs = ws != null ? new WorkSource(ws) : null;
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -2122,6 +2276,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteWifiControllerActivity(final WifiActivityEnergyInfo info) {
+ super.noteWifiControllerActivity_enforcePermission();
+
if (info == null || !info.isValid()) {
Slog.e(TAG, "invalid wifi data given: " + info);
return;
@@ -2142,6 +2298,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteBluetoothControllerActivity(final BluetoothActivityEnergyInfo info) {
+ super.noteBluetoothControllerActivity_enforcePermission();
+
if (info == null || !info.isValid()) {
Slog.e(TAG, "invalid bluetooth data given: " + info);
return;
@@ -2162,6 +2320,8 @@
@Override
@EnforcePermission(UPDATE_DEVICE_STATS)
public void noteModemControllerActivity(final ModemActivityInfo info) {
+ super.noteModemControllerActivity_enforcePermission();
+
if (info == null) {
Slog.e(TAG, "invalid modem data given: " + info);
return;
@@ -2188,6 +2348,8 @@
public void setBatteryState(final int status, final int health, final int plugType,
final int level, final int temp, final int volt, final int chargeUAh,
final int chargeFullUAh, final long chargeTimeToFullSeconds) {
+ super.setBatteryState_enforcePermission();
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -2230,12 +2392,16 @@
@Override
@EnforcePermission(BATTERY_STATS)
public long getAwakeTimeBattery() {
+ super.getAwakeTimeBattery_enforcePermission();
+
return mStats.getAwakeTimeBattery();
}
@Override
@EnforcePermission(BATTERY_STATS)
public long getAwakeTimePlugged() {
+ super.getAwakeTimePlugged_enforcePermission();
+
return mStats.getAwakeTimePlugged();
}
@@ -2738,6 +2904,8 @@
@EnforcePermission(anyOf = {UPDATE_DEVICE_STATS, BATTERY_STATS})
public CellularBatteryStats getCellularBatteryStats() {
// Wait for the completion of pending works if there is any
+ super.getCellularBatteryStats_enforcePermission();
+
awaitCompletion();
synchronized (mStats) {
return mStats.getCellularBatteryStats();
@@ -2752,6 +2920,8 @@
@EnforcePermission(anyOf = {UPDATE_DEVICE_STATS, BATTERY_STATS})
public WifiBatteryStats getWifiBatteryStats() {
// Wait for the completion of pending works if there is any
+ super.getWifiBatteryStats_enforcePermission();
+
awaitCompletion();
synchronized (mStats) {
return mStats.getWifiBatteryStats();
@@ -2766,6 +2936,8 @@
@EnforcePermission(BATTERY_STATS)
public GpsBatteryStats getGpsBatteryStats() {
// Wait for the completion of pending works if there is any
+ super.getGpsBatteryStats_enforcePermission();
+
awaitCompletion();
synchronized (mStats) {
return mStats.getGpsBatteryStats();
@@ -2780,6 +2952,8 @@
@EnforcePermission(BATTERY_STATS)
public WakeLockStats getWakeLockStats() {
// Wait for the completion of pending works if there is any
+ super.getWakeLockStats_enforcePermission();
+
awaitCompletion();
synchronized (mStats) {
return mStats.getWakeLockStats();
@@ -2794,6 +2968,8 @@
@EnforcePermission(BATTERY_STATS)
public BluetoothBatteryStats getBluetoothBatteryStats() {
// Wait for the completion of pending works if there is any
+ super.getBluetoothBatteryStats_enforcePermission();
+
awaitCompletion();
synchronized (mStats) {
return mStats.getBluetoothBatteryStats();
@@ -2901,6 +3077,8 @@
*/
@EnforcePermission(POWER_SAVER)
public boolean setChargingStateUpdateDelayMillis(int delayMillis) {
+ super.setChargingStateUpdateDelayMillis_enforcePermission();
+
final long ident = Binder.clearCallingIdentity();
try {
@@ -3051,6 +3229,8 @@
@Override
@EnforcePermission(DEVICE_POWER)
public void setChargerAcOnline(boolean online, boolean forceUpdate) {
+ super.setChargerAcOnline_enforcePermission();
+
mBatteryManagerInternal.setChargerAcOnline(online, forceUpdate);
}
@@ -3060,6 +3240,8 @@
@Override
@EnforcePermission(DEVICE_POWER)
public void setBatteryLevel(int level, boolean forceUpdate) {
+ super.setBatteryLevel_enforcePermission();
+
mBatteryManagerInternal.setBatteryLevel(level, forceUpdate);
}
@@ -3069,6 +3251,8 @@
@Override
@EnforcePermission(DEVICE_POWER)
public void unplugBattery(boolean forceUpdate) {
+ super.unplugBattery_enforcePermission();
+
mBatteryManagerInternal.unplugBattery(forceUpdate);
}
@@ -3078,6 +3262,8 @@
@Override
@EnforcePermission(DEVICE_POWER)
public void resetBattery(boolean forceUpdate) {
+ super.resetBattery_enforcePermission();
+
mBatteryManagerInternal.resetBattery(forceUpdate);
}
@@ -3087,6 +3273,8 @@
@Override
@EnforcePermission(DEVICE_POWER)
public void suspendBatteryInput() {
+ super.suspendBatteryInput_enforcePermission();
+
mBatteryManagerInternal.suspendBatteryInput();
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java
index 417a0e5..56909e3 100644
--- a/services/core/java/com/android/server/am/BroadcastConstants.java
+++ b/services/core/java/com/android/server/am/BroadcastConstants.java
@@ -141,7 +141,8 @@
*/
public int MAX_RUNNING_PROCESS_QUEUES = DEFAULT_MAX_RUNNING_PROCESS_QUEUES;
private static final String KEY_MAX_RUNNING_PROCESS_QUEUES = "bcast_max_running_process_queues";
- private static final int DEFAULT_MAX_RUNNING_PROCESS_QUEUES = 4;
+ private static final int DEFAULT_MAX_RUNNING_PROCESS_QUEUES =
+ ActivityManager.isLowRamDeviceStatic() ? 2 : 4;
/**
* For {@link BroadcastQueueModernImpl}: Maximum number of active broadcasts
@@ -150,7 +151,8 @@
*/
public int MAX_RUNNING_ACTIVE_BROADCASTS = DEFAULT_MAX_RUNNING_ACTIVE_BROADCASTS;
private static final String KEY_MAX_RUNNING_ACTIVE_BROADCASTS = "bcast_max_running_active_broadcasts";
- private static final int DEFAULT_MAX_RUNNING_ACTIVE_BROADCASTS = 16;
+ private static final int DEFAULT_MAX_RUNNING_ACTIVE_BROADCASTS =
+ ActivityManager.isLowRamDeviceStatic() ? 8 : 16;
/**
* For {@link BroadcastQueueModernImpl}: Maximum number of pending
@@ -159,7 +161,8 @@
*/
public int MAX_PENDING_BROADCASTS = DEFAULT_MAX_PENDING_BROADCASTS;
private static final String KEY_MAX_PENDING_BROADCASTS = "bcast_max_pending_broadcasts";
- private static final int DEFAULT_MAX_PENDING_BROADCASTS = 256;
+ private static final int DEFAULT_MAX_PENDING_BROADCASTS =
+ ActivityManager.isLowRamDeviceStatic() ? 128 : 256;
/**
* For {@link BroadcastQueueModernImpl}: Delay to apply to normal
@@ -175,7 +178,7 @@
*/
public long DELAY_CACHED_MILLIS = DEFAULT_DELAY_CACHED_MILLIS;
private static final String KEY_DELAY_CACHED_MILLIS = "bcast_delay_cached_millis";
- private static final long DEFAULT_DELAY_CACHED_MILLIS = 0;
+ private static final long DEFAULT_DELAY_CACHED_MILLIS = +30_000;
/**
* For {@link BroadcastQueueModernImpl}: Delay to apply to urgent
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index c994f13..47ca427 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -643,6 +643,7 @@
static final int REASON_CONTAINS_INTERACTIVE = 14;
static final int REASON_CONTAINS_RESULT_TO = 15;
static final int REASON_CONTAINS_INSTRUMENTED = 16;
+ static final int REASON_CONTAINS_MANIFEST = 17;
@IntDef(flag = false, prefix = { "REASON_" }, value = {
REASON_EMPTY,
@@ -658,6 +659,7 @@
REASON_CONTAINS_INTERACTIVE,
REASON_CONTAINS_RESULT_TO,
REASON_CONTAINS_INSTRUMENTED,
+ REASON_CONTAINS_MANIFEST,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Reason {}
@@ -677,6 +679,7 @@
case REASON_CONTAINS_INTERACTIVE: return "CONTAINS_INTERACTIVE";
case REASON_CONTAINS_RESULT_TO: return "CONTAINS_RESULT_TO";
case REASON_CONTAINS_INSTRUMENTED: return "CONTAINS_INSTRUMENTED";
+ case REASON_CONTAINS_MANIFEST: return "CONTAINS_MANIFEST";
default: return Integer.toString(reason);
}
}
@@ -725,6 +728,9 @@
} else if (mCountResultTo > 0) {
mRunnableAt = runnableAt;
mRunnableAtReason = REASON_CONTAINS_RESULT_TO;
+ } else if (mCountManifest > 0) {
+ mRunnableAt = runnableAt;
+ mRunnableAtReason = REASON_CONTAINS_MANIFEST;
} else if (mProcessCached) {
mRunnableAt = runnableAt + constants.DELAY_CACHED_MILLIS;
mRunnableAtReason = REASON_CACHED;
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index c3839a9..6793876 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -599,6 +599,7 @@
// If nothing to dispatch, send any pending result immediately
if (r.receivers.isEmpty()) {
scheduleResultTo(r);
+ notifyFinishBroadcast(r);
}
traceEnd(cookie);
@@ -1402,30 +1403,34 @@
final boolean recordFinished = (r.terminalCount == r.receivers.size());
if (recordFinished) {
- mService.notifyBroadcastFinishedLocked(r);
- mHistory.addBroadcastToHistoryLocked(r);
+ notifyFinishBroadcast(r);
+ }
+ }
- r.finishTime = SystemClock.uptimeMillis();
- r.nextReceiver = r.receivers.size();
- BroadcastQueueImpl.logBootCompletedBroadcastCompletionLatencyIfPossible(r);
+ private void notifyFinishBroadcast(@NonNull BroadcastRecord r) {
+ mService.notifyBroadcastFinishedLocked(r);
+ mHistory.addBroadcastToHistoryLocked(r);
- if (r.intent.getComponent() == null && r.intent.getPackage() == null
- && (r.intent.getFlags() & Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
- int manifestCount = 0;
- int manifestSkipCount = 0;
- for (int i = 0; i < r.receivers.size(); i++) {
- if (r.receivers.get(i) instanceof ResolveInfo) {
- manifestCount++;
- if (r.delivery[i] == BroadcastRecord.DELIVERY_SKIPPED) {
- manifestSkipCount++;
- }
+ r.finishTime = SystemClock.uptimeMillis();
+ r.nextReceiver = r.receivers.size();
+ BroadcastQueueImpl.logBootCompletedBroadcastCompletionLatencyIfPossible(r);
+
+ if (r.intent.getComponent() == null && r.intent.getPackage() == null
+ && (r.intent.getFlags() & Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
+ int manifestCount = 0;
+ int manifestSkipCount = 0;
+ for (int i = 0; i < r.receivers.size(); i++) {
+ if (r.receivers.get(i) instanceof ResolveInfo) {
+ manifestCount++;
+ if (r.delivery[i] == BroadcastRecord.DELIVERY_SKIPPED) {
+ manifestSkipCount++;
}
}
-
- final long dispatchTime = SystemClock.uptimeMillis() - r.enqueueTime;
- mService.addBroadcastStatLocked(r.intent.getAction(), r.callerPackage,
- manifestCount, manifestSkipCount, dispatchTime);
}
+
+ final long dispatchTime = SystemClock.uptimeMillis() - r.enqueueTime;
+ mService.addBroadcastStatLocked(r.intent.getAction(), r.callerPackage,
+ manifestCount, manifestSkipCount, dispatchTime);
}
}
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index cbf0aae..2d7b0dc 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -89,6 +89,8 @@
"compact_proc_state_throttle";
@VisibleForTesting static final String KEY_FREEZER_DEBOUNCE_TIMEOUT =
"freeze_debounce_timeout";
+ @VisibleForTesting static final String KEY_FREEZER_EXEMPT_INST_PKG =
+ "freeze_exempt_inst_pkg";
// RSS Indices
private static final int RSS_TOTAL_INDEX = 0;
@@ -137,6 +139,7 @@
@VisibleForTesting static final String DEFAULT_COMPACT_PROC_STATE_THROTTLE =
String.valueOf(ActivityManager.PROCESS_STATE_RECEIVER);
@VisibleForTesting static final long DEFAULT_FREEZER_DEBOUNCE_TIMEOUT = 600_000L;
+ @VisibleForTesting static final Boolean DEFAULT_FREEZER_EXEMPT_INST_PKG = true;
@VisibleForTesting static final Uri CACHED_APP_FREEZER_ENABLED_URI = Settings.Global.getUriFor(
Settings.Global.CACHED_APPS_FREEZER_ENABLED);
@@ -277,6 +280,8 @@
for (String name : properties.getKeyset()) {
if (KEY_FREEZER_DEBOUNCE_TIMEOUT.equals(name)) {
updateFreezerDebounceTimeout();
+ } else if (KEY_FREEZER_EXEMPT_INST_PKG.equals(name)) {
+ updateFreezerExemptInstPkg();
}
}
}
@@ -357,6 +362,7 @@
private boolean mFreezerOverride = false;
@VisibleForTesting volatile long mFreezerDebounceTimeout = DEFAULT_FREEZER_DEBOUNCE_TIMEOUT;
+ @VisibleForTesting volatile boolean mFreezerExemptInstPkg = DEFAULT_FREEZER_EXEMPT_INST_PKG;
// Maps process ID to last compaction statistics for processes that we've fully compacted. Used
// when evaluating throttles that we only consider for "full" compaction, so we don't store
@@ -566,6 +572,15 @@
}
}
+ /**
+ * Returns whether freezer exempts INSTALL_PACKAGES.
+ */
+ public boolean freezerExemptInstPkg() {
+ synchronized (mPhenotypeFlagLock) {
+ return mUseFreezer && mFreezerExemptInstPkg;
+ }
+ }
+
@GuardedBy("mProcLock")
void dump(PrintWriter pw) {
pw.println("CachedAppOptimizer settings");
@@ -647,6 +662,7 @@
pw.println(" " + KEY_USE_FREEZER + "=" + mUseFreezer);
pw.println(" " + KEY_FREEZER_STATSD_SAMPLE_RATE + "=" + mFreezerStatsdSampleRate);
pw.println(" " + KEY_FREEZER_DEBOUNCE_TIMEOUT + "=" + mFreezerDebounceTimeout);
+ pw.println(" " + KEY_FREEZER_EXEMPT_INST_PKG + "=" + mFreezerExemptInstPkg);
synchronized (mProcLock) {
int size = mFrozenProcesses.size();
pw.println(" Apps frozen: " + size);
@@ -1007,6 +1023,7 @@
KEY_USE_FREEZER, DEFAULT_USE_FREEZER)) {
mUseFreezer = isFreezerSupported();
updateFreezerDebounceTimeout();
+ updateFreezerExemptInstPkg();
} else {
mUseFreezer = false;
}
@@ -1194,6 +1211,15 @@
if (mFreezerDebounceTimeout < 0) {
mFreezerDebounceTimeout = DEFAULT_FREEZER_DEBOUNCE_TIMEOUT;
}
+ Slog.d(TAG_AM, "Freezer timeout set to " + mFreezerDebounceTimeout);
+ }
+
+ @GuardedBy("mPhenotypeFlagLock")
+ private void updateFreezerExemptInstPkg() {
+ mFreezerExemptInstPkg = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
+ KEY_FREEZER_EXEMPT_INST_PKG, DEFAULT_FREEZER_EXEMPT_INST_PKG);
+ Slog.d(TAG_AM, "Freezer exemption set to " + mFreezerExemptInstPkg);
}
private boolean parseProcStateThrottle(String procStateThrottleString) {
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index a97173d..16055b9 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -81,6 +81,7 @@
import com.android.server.LocalServices;
import com.android.server.RescueParty;
import com.android.server.pm.UserManagerInternal;
+import com.android.server.pm.UserManagerService;
import com.android.server.pm.pkg.AndroidPackage;
import java.io.FileDescriptor;
@@ -163,7 +164,7 @@
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, int callingUid, String callingPackage, String callingTag,
boolean stable, int userId) {
- ContentProviderRecord cpr;
+ ContentProviderRecord cpr = null;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
boolean providerRunning = false;
@@ -185,8 +186,21 @@
checkTime(startTime, "getContentProviderImpl: getProviderByName");
- // First check if this content provider has been published...
- cpr = mProviderMap.getProviderByName(name, userId);
+ UserManagerService userManagerService = UserManagerService.getInstance();
+
+ /*
+ For clone user profile and allowed authority, skipping finding provider and redirecting
+ it to owner profile. Ideally clone profile should not have MediaProvider instance
+ installed and mProviderMap would not have entry for clone user. This is just fallback
+ check to ensure even if MediaProvider is installed in Clone Profile, it should not be
+ used and redirect to owner user's MediaProvider.
+ */
+ //todo(b/236121588) MediaProvider should not be installed in clone profile.
+ if (!isAuthorityRedirectedForCloneProfile(name)
+ || !userManagerService.isMediaSharedWithParent(userId)) {
+ // First check if this content provider has been published...
+ cpr = mProviderMap.getProviderByName(name, userId);
+ }
// If that didn't work, check if it exists for user 0 and then
// verify that it's a singleton provider before using it.
if (cpr == null && userId != UserHandle.USER_SYSTEM) {
@@ -201,11 +215,9 @@
userId = UserHandle.USER_SYSTEM;
checkCrossUser = false;
} else if (isAuthorityRedirectedForCloneProfile(name)) {
- UserManagerInternal umInternal = LocalServices.getService(
- UserManagerInternal.class);
- UserInfo userInfo = umInternal.getUserInfo(userId);
-
- if (userInfo != null && userInfo.isCloneProfile()) {
+ if (userManagerService.isMediaSharedWithParent(userId)) {
+ UserManagerInternal umInternal = LocalServices.getService(
+ UserManagerInternal.class);
userId = umInternal.getProfileParentId(userId);
checkCrossUser = false;
}
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 60e6754..ea3c8dc 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -107,21 +107,21 @@
30079 uc_dispatch_user_switch (oldUserId|1|5),(newUserId|1|5)
30080 uc_continue_user_switch (oldUserId|1|5),(newUserId|1|5)
30081 uc_send_user_broadcast (userId|1|5),(IntentAction|3)
+
# Tags below are used by SystemServiceManager - although it's technically part of am, these are
# also user switch events and useful to be analyzed together with events above.
-30082 ssm_user_starting (userId|1|5),(visible|1)
+30082 ssm_user_starting (userId|1|5)
30083 ssm_user_switching (oldUserId|1|5),(newUserId|1|5)
30084 ssm_user_unlocking (userId|1|5)
30085 ssm_user_unlocked (userId|1|5)
-30086 ssm_user_stopping (userId|1|5),(visibilityChanged|1)
+30086 ssm_user_stopping (userId|1|5)
30087 ssm_user_stopped (userId|1|5)
30088 ssm_user_completed_event (userId|1|5),(eventFlag|1|5)
-30089 ssm_user_visibility_changed (userId|1|5),(visible|1)
+
+# Similarly, tags below are used by UserManagerService
+30091 um_user_visibility_changed (userId|1|5),(visible|1)
# Foreground service start/stop events.
30100 am_foreground_service_start (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3)
30101 am_foreground_service_denied (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3)
30102 am_foreground_service_stop (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3)
-
-
-
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 3f5f3bc..fed0b11 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -396,11 +396,16 @@
resolvedType = key.requestResolvedType;
}
- // Apply any launch flags from the ActivityOptions. This is to ensure that the caller
- // can specify a consistent launch mode even if the PendingIntent is immutable
+ // Apply any launch flags from the ActivityOptions. This is used only by SystemUI
+ // to ensure that we can launch the pending intent with a consistent launch mode even
+ // if the provided PendingIntent is immutable (ie. to force an activity to launch into
+ // a new task, or to launch multiple instances if supported by the app)
final ActivityOptions opts = ActivityOptions.fromBundle(options);
if (opts != null) {
- finalIntent.addFlags(opts.getPendingIntentLaunchFlags());
+ // TODO(b/254490217): Move this check into SafeActivityOptions
+ if (controller.mAtmInternal.isCallerRecents(Binder.getCallingUid())) {
+ finalIntent.addFlags(opts.getPendingIntentLaunchFlags());
+ }
}
// Extract options before clearing calling identity
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 42bfc4c..ecea96e 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1693,7 +1693,8 @@
app.info.packageName);
externalStorageAccess = storageManagerInternal.hasExternalStorageAccess(uid,
app.info.packageName);
- if (pm.checkPermission(Manifest.permission.INSTALL_PACKAGES,
+ if (mService.isAppFreezerExemptInstPkg()
+ && pm.checkPermission(Manifest.permission.INSTALL_PACKAGES,
app.info.packageName, userId)
== PackageManager.PERMISSION_GRANTED) {
Slog.i(TAG, app.info.packageName + " is exempt from freezer");
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index 33e4070..438a2d43 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -567,6 +567,8 @@
@android.annotation.EnforcePermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
@Override
public byte[] getCurrentStats(List<ParcelFileDescriptor> historic) {
+ super.getCurrentStats_enforcePermission();
+
Parcel current = Parcel.obtain();
synchronized (mLock) {
long now = SystemClock.uptimeMillis();
@@ -623,6 +625,8 @@
public long getCommittedStatsMerged(long highWaterMarkMs, int section, boolean doAggregate,
List<ParcelFileDescriptor> committedStats, ProcessStats mergedStats) {
+ super.getCommittedStatsMerged_enforcePermission();
+
long newHighWaterMark = highWaterMarkMs;
mFileLock.lock();
try {
@@ -709,6 +713,8 @@
@android.annotation.EnforcePermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
@Override
public ParcelFileDescriptor getStatsOverTime(long minTime) {
+ super.getStatsOverTime_enforcePermission();
+
Parcel current = Parcel.obtain();
long curTime;
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index af55980..9213327 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -134,6 +134,7 @@
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -176,9 +177,7 @@
static final int START_USER_SWITCH_FG_MSG = 120;
static final int COMPLETE_USER_SWITCH_MSG = 130;
static final int USER_COMPLETED_EVENT_MSG = 140;
- static final int USER_VISIBLE_MSG = 150;
-
- private static final int NO_ARG2 = 0;
+ static final int USER_VISIBILITY_CHANGED_MSG = 150;
// Message constant to clear {@link UserJourneySession} from {@link mUserIdToUserJourneyMap} if
// the user journey, defined in the UserLifecycleJourneyReported atom for statsd, is not
@@ -200,6 +199,14 @@
private static final int USER_SWITCH_CALLBACKS_TIMEOUT_MS = 5 * 1000;
/**
+ * Amount of time waited for {@link WindowManagerService#dismissKeyguard} callbacks to be
+ * called after dismissing the keyguard.
+ * Otherwise, we should move on to unfreeze the screen {@link #unfreezeScreen}
+ * and report user switch is complete {@link #REPORT_USER_SWITCH_COMPLETE_MSG}.
+ */
+ private static final int DISMISS_KEYGUARD_TIMEOUT_MS = 2 * 1000;
+
+ /**
* Time after last scheduleOnUserCompletedEvent() call at which USER_COMPLETED_EVENT_MSG will be
* scheduled (although it may fire sooner instead).
* When it fires, {@link #reportOnUserCompletedEvent} will be processed.
@@ -430,10 +437,13 @@
/** @see #getLastUserUnlockingUptime */
private volatile long mLastUserUnlockingUptime = 0;
+ // TODO(b/244333150) remove this array and let UserVisibilityMediator call the listeners
+ // directly, as that class should be responsible for all user visibility logic (for example,
+ // when the foreground user is switched out, its profiles also become invisible)
/**
* List of visible users (as defined by {@link UserManager#isUserVisible()}).
*
- * <p>It's only used to call {@link SystemServiceManager} when the visibility is changed upon
+ * <p>It's only used to call {@link UserManagerInternal} when the visibility is changed upon
* the user starting or stopping.
*
* <p>Note: only the key is used, not the value.
@@ -1087,10 +1097,7 @@
synchronized (mLock) {
visibleBefore = mVisibleUsers.get(userId);
if (visibleBefore) {
- if (DEBUG_MU) {
- Slogf.d(TAG, "Removing %d from mVisibleUsers", userId);
- }
- mVisibleUsers.delete(userId);
+ deleteVisibleUserLocked(userId);
visibilityChanged = true;
} else {
visibilityChanged = false;
@@ -1139,6 +1146,20 @@
}
}
+ private void addVisibleUserLocked(@UserIdInt int userId) {
+ if (DEBUG_MU) {
+ Slogf.d(TAG, "adding %d to mVisibleUsers", userId);
+ }
+ mVisibleUsers.put(userId, true);
+ }
+
+ private void deleteVisibleUserLocked(@UserIdInt int userId) {
+ if (DEBUG_MU) {
+ Slogf.d(TAG, "deleting %d from mVisibleUsers", userId);
+ }
+ mVisibleUsers.delete(userId);
+ }
+
private void finishUserStopping(final int userId, final UserState uss,
final boolean allowDelayedLocking, final boolean visibilityChanged) {
EventLog.writeEvent(EventLogTags.UC_FINISH_USER_STOPPING, userId);
@@ -1157,7 +1178,10 @@
mInjector.batteryStatsServiceNoteEvent(
BatteryStats.HistoryItem.EVENT_USER_RUNNING_FINISH,
Integer.toString(userId), userId);
- mInjector.getSystemServiceManager().onUserStopping(userId, visibilityChanged);
+ mInjector.getSystemServiceManager().onUserStopping(userId);
+ if (visibilityChanged) {
+ mInjector.onUserVisibilityChanged(userId, /* visible= */ false);
+ }
Runnable finishUserStoppedAsync = () ->
mHandler.post(() -> finishUserStopped(uss, allowDelayedLocking));
@@ -1626,7 +1650,12 @@
return false;
}
- mInjector.getUserManagerInternal().assignUserToDisplay(userId, displayId);
+ if (!userInfo.preCreated) {
+ // TODO(b/244644281): UMI should return whether the user is visible. And if fails,
+ // the user should not be in the mediator's started users structure
+ mInjector.getUserManagerInternal().assignUserToDisplay(userId,
+ userInfo.profileGroupId, foreground, displayId);
+ }
// TODO(b/239982558): might need something similar for bg users on secondary display
if (foreground && isUserSwitchUiEnabled()) {
@@ -1678,12 +1707,23 @@
// Make sure the old user is no longer considering the display to be on.
mInjector.reportGlobalUsageEvent(UsageEvents.Event.SCREEN_NON_INTERACTIVE);
boolean userSwitchUiEnabled;
+ // TODO(b/244333150): temporary state until the callback logic is moved to
+ // UserVisibilityManager
+ int previousCurrentUserId; boolean notifyPreviousCurrentUserId;
synchronized (mLock) {
+ previousCurrentUserId = mCurrentUserId;
+ notifyPreviousCurrentUserId = mVisibleUsers.get(previousCurrentUserId);
+ if (notifyPreviousCurrentUserId) {
+ deleteVisibleUserLocked(previousCurrentUserId);
+ }
mCurrentUserId = userId;
mTargetUserId = UserHandle.USER_NULL; // reset, mCurrentUserId has caught up
userSwitchUiEnabled = mUserSwitchUiEnabled;
}
mInjector.updateUserConfiguration();
+ // TODO(b/244644281): updateProfileRelatedCaches() is called on both if and else
+ // parts, ideally it should be moved outside, but for now it's not as there are many
+ // calls to external components here afterwards
updateProfileRelatedCaches();
mInjector.getWindowManager().setCurrentUser(userId);
mInjector.reportCurWakefulnessUsageEvent();
@@ -1696,6 +1736,11 @@
mInjector.getWindowManager().lockNow(null);
}
}
+ if (notifyPreviousCurrentUserId) {
+ mHandler.sendMessage(mHandler.obtainMessage(USER_VISIBILITY_CHANGED_MSG,
+ previousCurrentUserId, 0));
+ }
+
} else {
final Integer currentUserIdInt = mCurrentUserId;
updateProfileRelatedCaches();
@@ -1721,10 +1766,7 @@
&& mInjector.getUserManagerInternal().isUserVisible(userId);
if (visible) {
synchronized (mLock) {
- if (DEBUG_MU) {
- Slogf.d(TAG, "Adding %d to mVisibleUsers", userId);
- }
- mVisibleUsers.put(userId, true);
+ addVisibleUserLocked(userId);
}
}
@@ -1767,12 +1809,15 @@
mHandler.sendMessage(mHandler.obtainMessage(USER_START_MSG, userId,
visible ? 1 : 0));
t.traceEnd();
- } else if (visible) {
+ }
+
+ if (visible) {
// User was already running and became visible (for example, when switching to a
// user that was started in the background before), so it's necessary to explicitly
// notify the services (while when the user starts from BOOTING, USER_START_MSG
// takes care of that.
- mHandler.sendMessage(mHandler.obtainMessage(USER_VISIBLE_MSG, userId, NO_ARG2));
+ mHandler.sendMessage(mHandler.obtainMessage(USER_VISIBILITY_CHANGED_MSG, userId,
+ visible ? 1 : 0));
}
t.traceBegin("sendMessages");
@@ -2075,6 +2120,8 @@
}
private void timeoutUserSwitch(UserState uss, int oldUserId, int newUserId) {
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
+ t.traceBegin("timeoutUserSwitch-" + oldUserId + "-to-" + newUserId);
synchronized (mLock) {
Slogf.e(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId);
mTimeoutUserSwitchCallbacks = mCurWaitingUserSwitchCallbacks;
@@ -2084,6 +2131,7 @@
mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_CALLBACKS_TIMEOUT_MSG,
oldUserId, newUserId), USER_SWITCH_CALLBACKS_TIMEOUT_MS);
}
+ t.traceEnd();
}
private void timeoutUserSwitchCallbacks(int oldUserId, int newUserId) {
@@ -2141,6 +2189,8 @@
+ " ms after dispatchUserSwitch.");
}
+ TimingsTraceAndSlog t2 = new TimingsTraceAndSlog(TAG);
+ t2.traceBegin("onUserSwitchingReply-" + name);
curWaitingUserSwitchCallbacks.remove(name);
// Continue switching if all callbacks have been notified and
// user switching session is still valid
@@ -2149,11 +2199,15 @@
== mCurWaitingUserSwitchCallbacks)) {
sendContinueUserSwitchLU(uss, oldUserId, newUserId);
}
+ t2.traceEnd();
}
}
};
+ t.traceBegin("onUserSwitching-" + name);
mUserSwitchObservers.getBroadcastItem(i).onUserSwitching(newUserId, callback);
+ t.traceEnd();
} catch (RemoteException e) {
+ // Ignore
}
}
} else {
@@ -2167,10 +2221,13 @@
@GuardedBy("mLock")
private void sendContinueUserSwitchLU(UserState uss, int oldUserId, int newUserId) {
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
+ t.traceBegin("sendContinueUserSwitchLU-" + oldUserId + "-to-" + newUserId);
mCurWaitingUserSwitchCallbacks = null;
mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
mHandler.sendMessage(mHandler.obtainMessage(CONTINUE_USER_SWITCH_MSG,
oldUserId, newUserId, uss));
+ t.traceEnd();
}
@VisibleForTesting
@@ -2552,7 +2609,8 @@
if (!UserManager.isHeadlessSystemUserMode()) {
// Don't need to call on HSUM because it will be called when the system user is
// restarted on background
- mInjector.onUserStarting(UserHandle.USER_SYSTEM, /* visible= */ true);
+ mInjector.onUserStarting(UserHandle.USER_SYSTEM);
+ mInjector.onUserVisibilityChanged(UserHandle.USER_SYSTEM, /* visible= */ true);
}
}
@@ -2564,12 +2622,12 @@
int userId = UserHandle.USER_SYSTEM;
synchronized (mLock) {
if (visible) {
- mVisibleUsers.put(userId, true);
+ addVisibleUserLocked(userId);
} else {
- mVisibleUsers.delete(userId);
+ deleteVisibleUserLocked(userId);
}
}
- mInjector.notifySystemUserVisibilityChanged(visible);
+ mInjector.onUserVisibilityChanged(userId, visible);
t.traceEnd();
}
@@ -3069,7 +3127,7 @@
logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_START_USER,
USER_LIFECYCLE_EVENT_STATE_BEGIN);
- mInjector.onUserStarting(/* userId= */ msg.arg1, /* visible= */ msg.arg2 == 1);
+ mInjector.onUserStarting(/* userId= */ msg.arg1);
scheduleOnUserCompletedEvent(msg.arg1,
UserCompletedEventType.EVENT_TYPE_USER_STARTING,
USER_COMPLETED_EVENT_DELAY_MS);
@@ -3150,8 +3208,9 @@
case COMPLETE_USER_SWITCH_MSG:
completeUserSwitch(msg.arg1);
break;
- case USER_VISIBLE_MSG:
- mInjector.getSystemServiceManager().onUserVisible(/* userId= */ msg.arg1);
+ case USER_VISIBILITY_CHANGED_MSG:
+ mInjector.onUserVisibilityChanged(/* userId= */ msg.arg1,
+ /* visible= */ msg.arg2 == 1);
break;
}
return false;
@@ -3653,20 +3712,28 @@
}
protected void dismissKeyguard(Runnable runnable, String reason) {
+ final AtomicBoolean isFirst = new AtomicBoolean(true);
+ final Runnable runOnce = () -> {
+ if (isFirst.getAndSet(false)) {
+ runnable.run();
+ }
+ };
+
+ mHandler.postDelayed(runOnce, DISMISS_KEYGUARD_TIMEOUT_MS);
getWindowManager().dismissKeyguard(new IKeyguardDismissCallback.Stub() {
@Override
public void onDismissError() throws RemoteException {
- mHandler.post(runnable);
+ mHandler.post(runOnce);
}
@Override
public void onDismissSucceeded() throws RemoteException {
- mHandler.post(runnable);
+ mHandler.post(runOnce);
}
@Override
public void onDismissCancelled() throws RemoteException {
- mHandler.post(runnable);
+ mHandler.post(runOnce);
}
}, reason);
}
@@ -3675,12 +3742,12 @@
return UserManager.isUsersOnSecondaryDisplaysEnabled();
}
- void onUserStarting(int userId, boolean visible) {
- getSystemServiceManager().onUserStarting(TimingsTraceAndSlog.newAsyncLog(), userId,
- visible);
+ void onUserStarting(@UserIdInt int userId) {
+ getSystemServiceManager().onUserStarting(TimingsTraceAndSlog.newAsyncLog(), userId);
}
- void notifySystemUserVisibilityChanged(boolean visible) {
- getSystemServiceManager().onSystemUserVisibilityChanged(visible);
+
+ void onUserVisibilityChanged(@UserIdInt int userId, boolean visible) {
+ getUserManagerInternal().onUserVisibilityChanged(userId, visible);
}
}
}
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index efa2f25..31d707d 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -883,6 +883,7 @@
@Override
public void onUserStarting(@NonNull TargetUser user) {
+ Slog.d(TAG, "Starting user " + user.getUserIdentifier());
mService.onUserStarting(user,
Environment.getDataSystemDeDirectory(user.getUserIdentifier()));
}
@@ -1047,6 +1048,8 @@
"com.android.server.app.GameManagerService");
if (!mSettings.containsKey(userId)) {
+ Slog.d(TAG, "Failed to set game mode for package " + packageName
+ + " as user " + userId + " is not started");
return;
}
GameManagerSettings userSettings = mSettings.get(userId);
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 4358ee2..acfc2a7 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -410,6 +410,17 @@
}
@Override
+ public void resetLockout(int userId, byte[] hardwareAuthToken) throws RemoteException {
+ checkInternalPermission();
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mBiometricService.resetLockout(userId, hardwareAuthToken);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public CharSequence getButtonLabel(
int userId,
String opPackageName,
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index cd30d26..d971953 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -72,6 +72,7 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.DumpUtils;
import com.android.server.SystemService;
+import com.android.server.biometrics.log.BiometricContext;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -100,6 +101,7 @@
private final List<EnabledOnKeyguardCallback> mEnabledOnKeyguardCallbacks;
private final Random mRandom = new Random();
@NonNull private final Supplier<Long> mRequestCounter;
+ @NonNull private final BiometricContext mBiometricContext;
@VisibleForTesting
IStatusBarService mStatusBarService;
@@ -776,6 +778,18 @@
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override // Binder call
+ public void resetLockout(
+ int userId, byte[] hardwareAuthToken) {
+ super.resetLockout_enforcePermission();
+
+ Slog.d(TAG, "resetLockout(userId=" + userId
+ + ", hat=" + (hardwareAuthToken == null ? "null " : "present") + ")");
+ mBiometricContext.getAuthSessionCoordinator()
+ .resetLockoutFor(userId, Authenticators.BIOMETRIC_STRONG, -1);
+ }
+
+ @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
+ @Override // Binder call
public int getCurrentStrength(int sensorId) {
super.getCurrentStrength_enforcePermission();
@@ -984,6 +998,10 @@
final AtomicLong generator = new AtomicLong(0);
return () -> generator.incrementAndGet();
}
+
+ public BiometricContext getBiometricContext(Context context) {
+ return BiometricContext.getInstance(context);
+ }
}
/**
@@ -1010,6 +1028,7 @@
mSettingObserver = mInjector.getSettingObserver(context, mHandler,
mEnabledOnKeyguardCallbacks);
mRequestCounter = mInjector.getRequestGenerator();
+ mBiometricContext = injector.getBiometricContext(context);
try {
injector.getActivityManagerService().registerUserSwitchObserver(
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index aec98f0..3813fd1 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -48,8 +48,6 @@
* the PreAuthInfo should not change any sensor state.
*/
class PreAuthInfo {
- private static final String TAG = "BiometricService/PreAuthInfo";
-
static final int AUTHENTICATOR_OK = 1;
static final int BIOMETRIC_NO_HARDWARE = 2;
static final int BIOMETRIC_DISABLED_BY_DEVICE_POLICY = 3;
@@ -62,24 +60,7 @@
static final int BIOMETRIC_LOCKOUT_TIMED = 10;
static final int BIOMETRIC_LOCKOUT_PERMANENT = 11;
static final int BIOMETRIC_SENSOR_PRIVACY_ENABLED = 12;
- @IntDef({AUTHENTICATOR_OK,
- BIOMETRIC_NO_HARDWARE,
- BIOMETRIC_DISABLED_BY_DEVICE_POLICY,
- BIOMETRIC_INSUFFICIENT_STRENGTH,
- BIOMETRIC_INSUFFICIENT_STRENGTH_AFTER_DOWNGRADE,
- BIOMETRIC_HARDWARE_NOT_DETECTED,
- BIOMETRIC_NOT_ENROLLED,
- BIOMETRIC_NOT_ENABLED_FOR_APPS,
- CREDENTIAL_NOT_ENROLLED,
- BIOMETRIC_LOCKOUT_TIMED,
- BIOMETRIC_LOCKOUT_PERMANENT,
- BIOMETRIC_SENSOR_PRIVACY_ENABLED})
- @Retention(RetentionPolicy.SOURCE)
- @interface AuthenticatorStatus {}
-
- private final boolean mBiometricRequested;
- private final int mBiometricStrengthRequested;
-
+ private static final String TAG = "BiometricService/PreAuthInfo";
final boolean credentialRequested;
// Sensors that can be used for this request (e.g. strong enough, enrolled, enabled).
final List<BiometricSensor> eligibleSensors;
@@ -90,6 +71,25 @@
final boolean ignoreEnrollmentState;
final int userId;
final Context context;
+ private final boolean mBiometricRequested;
+ private final int mBiometricStrengthRequested;
+ private PreAuthInfo(boolean biometricRequested, int biometricStrengthRequested,
+ boolean credentialRequested, List<BiometricSensor> eligibleSensors,
+ List<Pair<BiometricSensor, Integer>> ineligibleSensors, boolean credentialAvailable,
+ boolean confirmationRequested, boolean ignoreEnrollmentState, int userId,
+ Context context) {
+ mBiometricRequested = biometricRequested;
+ mBiometricStrengthRequested = biometricStrengthRequested;
+ this.credentialRequested = credentialRequested;
+
+ this.eligibleSensors = eligibleSensors;
+ this.ineligibleSensors = ineligibleSensors;
+ this.credentialAvailable = credentialAvailable;
+ this.confirmationRequested = confirmationRequested;
+ this.ignoreEnrollmentState = ignoreEnrollmentState;
+ this.userId = userId;
+ this.context = context;
+ }
static PreAuthInfo create(ITrustManager trustManager,
DevicePolicyManager devicePolicyManager,
@@ -158,7 +158,8 @@
*
* @return @AuthenticatorStatus
*/
- private static @AuthenticatorStatus int getStatusForBiometricAuthenticator(
+ private static @AuthenticatorStatus
+ int getStatusForBiometricAuthenticator(
DevicePolicyManager devicePolicyManager,
BiometricService.SettingObserver settingObserver,
BiometricSensor sensor, int userId, String opPackageName,
@@ -200,7 +201,6 @@
}
}
-
final @LockoutTracker.LockoutMode int lockoutMode =
sensor.impl.getLockoutModeForUser(userId);
if (lockoutMode == LockoutTracker.LOCKOUT_TIMED) {
@@ -248,8 +248,8 @@
/**
* @param modality one of {@link BiometricAuthenticator#TYPE_FINGERPRINT},
- * {@link BiometricAuthenticator#TYPE_IRIS} or {@link BiometricAuthenticator#TYPE_FACE}
- * @return
+ * {@link BiometricAuthenticator#TYPE_IRIS} or
+ * {@link BiometricAuthenticator#TYPE_FACE}
*/
private static int mapModalityToDevicePolicyType(int modality) {
switch (modality) {
@@ -265,24 +265,6 @@
}
}
- private PreAuthInfo(boolean biometricRequested, int biometricStrengthRequested,
- boolean credentialRequested, List<BiometricSensor> eligibleSensors,
- List<Pair<BiometricSensor, Integer>> ineligibleSensors, boolean credentialAvailable,
- boolean confirmationRequested, boolean ignoreEnrollmentState, int userId,
- Context context) {
- mBiometricRequested = biometricRequested;
- mBiometricStrengthRequested = biometricStrengthRequested;
- this.credentialRequested = credentialRequested;
-
- this.eligibleSensors = eligibleSensors;
- this.ineligibleSensors = ineligibleSensors;
- this.credentialAvailable = credentialAvailable;
- this.confirmationRequested = confirmationRequested;
- this.ignoreEnrollmentState = ignoreEnrollmentState;
- this.userId = userId;
- this.context = context;
- }
-
private Pair<BiometricSensor, Integer> calculateErrorByPriority() {
// If the caller requested STRONG, and the device contains both STRONG and non-STRONG
// sensors, prioritize BIOMETRIC_NOT_ENROLLED over the weak sensor's
@@ -303,6 +285,7 @@
* surface, combined with the actual sensor/credential and user/system settings, calculate the
* internal {@link AuthenticatorStatus} that should be returned to the client. Note that this
* will need to be converted into the public API constant.
+ *
* @return Pair<Modality, Error> with error being the internal {@link AuthenticatorStatus} code
*/
private Pair<Integer, Integer> getInternalStatus() {
@@ -391,7 +374,8 @@
/**
* @return public BiometricManager result for the current request.
*/
- @BiometricManager.BiometricError int getCanAuthenticateResult() {
+ @BiometricManager.BiometricError
+ int getCanAuthenticateResult() {
// TODO: Convert this directly
return Utils.biometricConstantsToBiometricManager(
Utils.authenticatorStatusToBiometricConstant(
@@ -401,6 +385,7 @@
/**
* For the given request, generate the appropriate reason why authentication cannot be started.
* Note that for some errors, modality is intentionally cleared.
+ *
* @return Pair<Modality, Error> with modality being filtered if necessary, and error
* being one of the public {@link android.hardware.biometrics.BiometricConstants} codes.
*/
@@ -443,7 +428,8 @@
* @return bitmask representing the modalities that are running or could be running for the
* current session.
*/
- @BiometricAuthenticator.Modality int getEligibleModalities() {
+ @BiometricAuthenticator.Modality
+ int getEligibleModalities() {
@BiometricAuthenticator.Modality int modalities = 0;
for (BiometricSensor sensor : eligibleSensors) {
modalities |= sensor.modality;
@@ -474,7 +460,7 @@
+ ", StrengthRequested: " + mBiometricStrengthRequested
+ ", CredentialRequested: " + credentialRequested);
string.append(", Eligible:{");
- for (BiometricSensor sensor: eligibleSensors) {
+ for (BiometricSensor sensor : eligibleSensors) {
string.append(sensor.id).append(" ");
}
string.append("}");
@@ -489,4 +475,20 @@
string.append(", ");
return string.toString();
}
+
+ @IntDef({AUTHENTICATOR_OK,
+ BIOMETRIC_NO_HARDWARE,
+ BIOMETRIC_DISABLED_BY_DEVICE_POLICY,
+ BIOMETRIC_INSUFFICIENT_STRENGTH,
+ BIOMETRIC_INSUFFICIENT_STRENGTH_AFTER_DOWNGRADE,
+ BIOMETRIC_HARDWARE_NOT_DETECTED,
+ BIOMETRIC_NOT_ENROLLED,
+ BIOMETRIC_NOT_ENABLED_FOR_APPS,
+ CREDENTIAL_NOT_ENROLLED,
+ BIOMETRIC_LOCKOUT_TIMED,
+ BIOMETRIC_LOCKOUT_PERMANENT,
+ BIOMETRIC_SENSOR_PRIVACY_ENABLED})
+ @Retention(RetentionPolicy.SOURCE)
+ @interface AuthenticatorStatus {
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java b/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
index 23b2714..d456736 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
@@ -44,7 +44,7 @@
/**
* A default provider for {@link BiometricContext}.
*/
-final class BiometricContextProvider implements BiometricContext {
+public final class BiometricContextProvider implements BiometricContext {
private static final String TAG = "BiometricContextProvider";
@@ -83,7 +83,8 @@
private boolean mIsAwake = false;
@VisibleForTesting
- BiometricContextProvider(@NonNull AmbientDisplayConfiguration ambientDisplayConfiguration,
+ public BiometricContextProvider(
+ @NonNull AmbientDisplayConfiguration ambientDisplayConfiguration,
@NonNull IStatusBarService service, @Nullable Handler handler,
AuthSessionCoordinator authSessionCoordinator) {
mAmbientDisplayConfiguration = ambientDisplayConfiguration;
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthResultCoordinator.java b/services/core/java/com/android/server/biometrics/sensors/AuthResultCoordinator.java
index bdae5f3..a48a9d1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthResultCoordinator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthResultCoordinator.java
@@ -75,7 +75,10 @@
* Adds auth success for a given strength to the current operation list.
*/
void authenticatedFor(@Authenticators.Types int strength) {
- updateState(strength, (old) -> AUTHENTICATOR_UNLOCKED | old);
+ // Only strong unlocks matter.
+ if (strength == Authenticators.BIOMETRIC_STRONG) {
+ updateState(strength, (old) -> AUTHENTICATOR_UNLOCKED | old);
+ }
}
/**
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthSessionCoordinator.java b/services/core/java/com/android/server/biometrics/sensors/AuthSessionCoordinator.java
index 5bc9d23..1aee5d4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthSessionCoordinator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthSessionCoordinator.java
@@ -75,6 +75,7 @@
mUserId = userId;
mIsAuthenticating = true;
mAuthOperations.clear();
+ mTimedLockouts.clear();
mAuthResultCoordinator = new AuthResultCoordinator();
mRingBuffer.addApiCall("internal : onAuthSessionStarted(" + userId + ")");
}
@@ -88,7 +89,6 @@
*/
void endAuthSession() {
if (mIsAuthenticating) {
- mAuthOperations.clear();
final long currentTime = mClock.millis();
for (Pair<Integer, Long> timedLockouts : mTimedLockouts) {
mMultiBiometricLockoutState.increaseLockoutTime(mUserId, timedLockouts.first,
@@ -109,16 +109,24 @@
}
}
+
mRingBuffer.addApiCall("internal : onAuthSessionEnded(" + mUserId + ")");
- mIsAuthenticating = false;
+ clearSession();
}
}
+ private void clearSession() {
+ mIsAuthenticating = false;
+ mTimedLockouts.clear();
+ mAuthOperations.clear();
+ }
+
/**
- * @return true if a user can authenticate with a given strength.
+ * Returns the current lockout state for a given user/strength.
*/
- public boolean getCanAuthFor(int userId, @Authenticators.Types int strength) {
- return mMultiBiometricLockoutState.canUserAuthenticate(userId, strength);
+ @LockoutTracker.LockoutMode
+ public int getLockoutStateFor(int userId, @Authenticators.Types int strength) {
+ return mMultiBiometricLockoutState.getLockoutState(userId, strength);
}
@Override
@@ -145,19 +153,8 @@
}
@Override
- public void authenticatedFor(int userId, @Authenticators.Types int biometricStrength,
- int sensorId, long requestId) {
- final String authStr =
- "authenticatedFor(userId=" + userId + ", strength=" + biometricStrength
- + " , sensorId=" + sensorId + ", requestId= " + requestId + ")";
- mRingBuffer.addApiCall(authStr);
- mAuthResultCoordinator.authenticatedFor(biometricStrength);
- attemptToFinish(userId, sensorId, authStr);
- }
-
- @Override
- public void lockedOutFor(int userId, @Authenticators.Types int biometricStrength,
- int sensorId, long requestId) {
+ public void lockedOutFor(int userId, @Authenticators.Types int biometricStrength, int sensorId,
+ long requestId) {
final String lockedOutStr =
"lockOutFor(userId=" + userId + ", biometricStrength=" + biometricStrength
+ ", sensorId=" + sensorId + ", requestId=" + requestId + ")";
@@ -179,12 +176,16 @@
}
@Override
- public void authEndedFor(int userId, @Authenticators.Types int biometricStrength,
- int sensorId, long requestId) {
+ public void authEndedFor(int userId, @Authenticators.Types int biometricStrength, int sensorId,
+ long requestId, boolean wasSuccessful) {
final String authEndedStr =
"authEndedFor(userId=" + userId + " ,biometricStrength=" + biometricStrength
- + ", sensorId=" + sensorId + ", requestId=" + requestId + ")";
+ + ", sensorId=" + sensorId + ", requestId=" + requestId + ", wasSuccessful="
+ + wasSuccessful + ")";
mRingBuffer.addApiCall(authEndedStr);
+ if (wasSuccessful) {
+ mAuthResultCoordinator.authenticatedFor(biometricStrength);
+ }
attemptToFinish(userId, sensorId, authEndedStr);
}
@@ -195,6 +196,12 @@
"resetLockoutFor(userId=" + userId + " ,biometricStrength=" + biometricStrength
+ ", requestId=" + requestId + ")";
mRingBuffer.addApiCall(resetLockStr);
+ if (biometricStrength == Authenticators.BIOMETRIC_STRONG) {
+ clearSession();
+ } else {
+ // Lockouts cannot be reset by non-strong biometrics
+ return;
+ }
mMultiBiometricLockoutState.setAuthenticatorTo(userId, biometricStrength,
true /*canAuthenticate */);
mMultiBiometricLockoutState.clearLockoutTime(userId, biometricStrength);
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthSessionListener.java b/services/core/java/com/android/server/biometrics/sensors/AuthSessionListener.java
index d97f793..6bddf14 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthSessionListener.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthSessionListener.java
@@ -28,16 +28,10 @@
void authStartedFor(int userId, int sensorId, long requestId);
/**
- * Indicates a successful authentication occurred for a sensor of a given strength.
- */
- void authenticatedFor(int userId, @Authenticators.Types int biometricStrength, int sensorId,
- long requestId);
-
- /**
* Indicates authentication ended for a sensor of a given strength.
*/
void authEndedFor(int userId, @Authenticators.Types int biometricStrength, int sensorId,
- long requestId);
+ long requestId, boolean wasSuccessful);
/**
* Indicates a lockout occurred for a sensor of a given strength.
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 8a24ff6..57d28f9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -81,9 +81,12 @@
@State
protected int mState = STATE_NEW;
private long mStartTimeMs;
-
private boolean mAuthAttempted;
private boolean mAuthSuccess = false;
+ private final int mSensorStrength;
+ // This is used to determine if we should use the old lockout counter (HIDL) or the new lockout
+ // counter implementation (AIDL)
+ private final boolean mShouldUseLockoutTracker;
public AuthenticationClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
@@ -92,7 +95,7 @@
@NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
boolean isStrongBiometric, @Nullable TaskStackListener taskStackListener,
@NonNull LockoutTracker lockoutTracker, boolean allowBackgroundAuthentication,
- boolean shouldVibrate, boolean isKeyguardBypassEnabled) {
+ boolean shouldVibrate, boolean isKeyguardBypassEnabled, int sensorStrength) {
super(context, lazyDaemon, token, listener, targetUserId, owner, cookie, sensorId,
shouldVibrate, biometricLogger, biometricContext);
mIsStrongBiometric = isStrongBiometric;
@@ -105,22 +108,13 @@
mIsRestricted = restricted;
mAllowBackgroundAuthentication = allowBackgroundAuthentication;
mIsKeyguardBypassEnabled = isKeyguardBypassEnabled;
+ mShouldUseLockoutTracker = lockoutTracker != null;
+ mSensorStrength = sensorStrength;
}
@LockoutTracker.LockoutMode
public int handleFailedAttempt(int userId) {
- @LockoutTracker.LockoutMode final int lockoutMode =
- mLockoutTracker.getLockoutModeForUser(userId);
- final PerformanceTracker performanceTracker =
- PerformanceTracker.getInstanceForSensorId(getSensorId());
-
- if (lockoutMode == LockoutTracker.LOCKOUT_PERMANENT) {
- performanceTracker.incrementPermanentLockoutForUser(userId);
- } else if (lockoutMode == LockoutTracker.LOCKOUT_TIMED) {
- performanceTracker.incrementTimedLockoutForUser(userId);
- }
-
- return lockoutMode;
+ return LockoutTracker.LOCKOUT_NONE;
}
protected long getStartTimeMs() {
@@ -273,10 +267,12 @@
cancel();
} else {
// Allow system-defined limit of number of attempts before giving up
- @LockoutTracker.LockoutMode final int lockoutMode =
- handleFailedAttempt(getTargetUserId());
- if (lockoutMode != LockoutTracker.LOCKOUT_NONE) {
- markAlreadyDone();
+ if (mShouldUseLockoutTracker) {
+ @LockoutTracker.LockoutMode final int lockoutMode =
+ handleFailedAttempt(getTargetUserId());
+ if (lockoutMode != LockoutTracker.LOCKOUT_NONE) {
+ markAlreadyDone();
+ }
}
try {
@@ -309,13 +305,6 @@
@Override
public void onAcquired(int acquiredInfo, int vendorCode) {
super.onAcquired(acquiredInfo, vendorCode);
-
- @LockoutTracker.LockoutMode final int lockoutMode =
- mLockoutTracker.getLockoutModeForUser(getTargetUserId());
- if (lockoutMode == LockoutTracker.LOCKOUT_NONE) {
- PerformanceTracker pt = PerformanceTracker.getInstanceForSensorId(getSensorId());
- pt.incrementAcquireForUser(getTargetUserId(), isCryptoOperation());
- }
}
@Override
@@ -331,8 +320,14 @@
public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
- @LockoutTracker.LockoutMode final int lockoutMode =
- mLockoutTracker.getLockoutModeForUser(getTargetUserId());
+ final @LockoutTracker.LockoutMode int lockoutMode;
+ if (mShouldUseLockoutTracker) {
+ lockoutMode = mLockoutTracker.getLockoutModeForUser(getTargetUserId());
+ } else {
+ lockoutMode = getBiometricContext().getAuthSessionCoordinator()
+ .getLockoutStateFor(getTargetUserId(), mSensorStrength);
+ }
+
if (lockoutMode != LockoutTracker.LOCKOUT_NONE) {
Slog.v(TAG, "In lockout mode(" + lockoutMode + ") ; disallowing authentication");
int errorCode = lockoutMode == LockoutTracker.LOCKOUT_TIMED
@@ -406,6 +401,14 @@
return mAuthSuccess;
}
+ protected int getSensorStrength() {
+ return mSensorStrength;
+ }
+
+ protected LockoutTracker getLockoutTracker() {
+ return mLockoutTracker;
+ }
+
protected int getShowOverlayReason() {
if (isKeyguard()) {
return BiometricOverlayConstants.REASON_AUTH_KEYGUARD;
diff --git a/services/core/java/com/android/server/biometrics/sensors/MultiBiometricLockoutState.java b/services/core/java/com/android/server/biometrics/sensors/MultiBiometricLockoutState.java
index 6605d49..c24a989 100644
--- a/services/core/java/com/android/server/biometrics/sensors/MultiBiometricLockoutState.java
+++ b/services/core/java/com/android/server/biometrics/sensors/MultiBiometricLockoutState.java
@@ -75,6 +75,9 @@
// fall through
case Authenticators.BIOMETRIC_CONVENIENCE:
authMap.get(BIOMETRIC_CONVENIENCE).mPermanentlyLockedOut = !canAuth;
+ return;
+ default:
+ Slog.e(TAG, "increaseLockoutTime called for invalid strength : " + strength);
}
}
@@ -89,6 +92,9 @@
// fall through
case Authenticators.BIOMETRIC_CONVENIENCE:
authMap.get(BIOMETRIC_CONVENIENCE).increaseLockoutTo(duration);
+ return;
+ default:
+ Slog.e(TAG, "increaseLockoutTime called for invalid strength : " + strength);
}
}
@@ -103,22 +109,34 @@
// fall through
case Authenticators.BIOMETRIC_CONVENIENCE:
authMap.get(BIOMETRIC_CONVENIENCE).setTimedLockout(0);
+ return;
+ default:
+ Slog.e(TAG, "clearLockoutTime called for invalid strength : " + strength);
}
}
/**
- * Indicates if a user can perform an authentication operation with a given
- * {@link Authenticators.Types}
+ * Retrieves the lockout state for a user of a specified strength.
*
* @param userId The user.
* @param strength The strength of biometric that is requested to authenticate.
- * @return If a user can authenticate with a given biometric of this strength.
*/
- boolean canUserAuthenticate(int userId, @Authenticators.Types int strength) {
- final boolean canAuthenticate = getAuthMapForUser(userId).get(strength).canAuthenticate();
- Slog.d(TAG, "canUserAuthenticate(userId=" + userId + ", strength=" + strength + ") ="
- + canAuthenticate);
- return canAuthenticate;
+ @LockoutTracker.LockoutMode
+ int getLockoutState(int userId, @Authenticators.Types int strength) {
+ final Map<Integer, AuthenticatorState> authMap = getAuthMapForUser(userId);
+ if (!authMap.containsKey(strength)) {
+ Slog.e(TAG, "Error, getLockoutState for unknown strength: " + strength
+ + " returning LOCKOUT_NONE");
+ return LockoutTracker.LOCKOUT_NONE;
+ }
+ final AuthenticatorState state = authMap.get(strength);
+ if (state.mPermanentlyLockedOut) {
+ return LockoutTracker.LOCKOUT_PERMANENT;
+ } else if (state.isTimedLockout()) {
+ return LockoutTracker.LOCKOUT_TIMED;
+ } else {
+ return LockoutTracker.LOCKOUT_NONE;
+ }
}
@Override
@@ -152,7 +170,15 @@
}
boolean canAuthenticate() {
- return !mPermanentlyLockedOut && mClock.millis() - mTimedLockout >= 0;
+ return !mPermanentlyLockedOut && !isTimedLockout();
+ }
+
+ boolean isTimedLockout() {
+ return mClock.millis() - mTimedLockout < 0;
+ }
+
+ void setTimedLockout(long duration) {
+ mTimedLockout = duration;
}
/**
@@ -162,10 +188,6 @@
mTimedLockout = Math.max(mTimedLockout, duration);
}
- void setTimedLockout(long duration) {
- mTimedLockout = duration;
- }
-
String toString(long currentTime) {
final String duration =
mTimedLockout - currentTime > 0 ? (mTimedLockout - currentTime) + "ms" : "none";
diff --git a/services/core/java/com/android/server/biometrics/sensors/PerformanceTracker.java b/services/core/java/com/android/server/biometrics/sensors/PerformanceTracker.java
index 42b22b0..eed2bdd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/PerformanceTracker.java
+++ b/services/core/java/com/android/server/biometrics/sensors/PerformanceTracker.java
@@ -85,7 +85,7 @@
}
}
- void incrementAcquireForUser(int userId, boolean isCrypto) {
+ public void incrementAcquireForUser(int userId, boolean isCrypto) {
createUserEntryIfNecessary(userId);
if (isCrypto) {
@@ -95,13 +95,13 @@
}
}
- void incrementTimedLockoutForUser(int userId) {
+ public void incrementTimedLockoutForUser(int userId) {
createUserEntryIfNecessary(userId);
mAllUsersInfo.get(userId).mTimedLockout++;
}
- void incrementPermanentLockoutForUser(int userId) {
+ public void incrementPermanentLockoutForUser(int userId) {
createUserEntryIfNecessary(userId);
mAllUsersInfo.get(userId).mPermanentLockout++;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 33d3b64..2f147c4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -200,6 +200,8 @@
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public void scheduleWatchdog() {
+ super.scheduleWatchdog_enforcePermission();
+
final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for scheduling watchdog");
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index c27d71f..b1cb257 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -47,7 +47,7 @@
import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutConsumer;
-import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.PerformanceTracker;
import com.android.server.biometrics.sensors.face.UsageStats;
import java.util.ArrayList;
@@ -63,8 +63,6 @@
@NonNull
private final UsageStats mUsageStats;
@NonNull
- private final LockoutCache mLockoutCache;
- @NonNull
private final AuthSessionCoordinator mAuthSessionCoordinator;
@Nullable
private final NotificationManager mNotificationManager;
@@ -72,7 +70,6 @@
private final int[] mBiometricPromptIgnoreListVendor;
private final int[] mKeyguardIgnoreList;
private final int[] mKeyguardIgnoreListVendor;
- private final int mBiometricStrength;
@Nullable
private ICancellationSignal mCancellationSignal;
@Nullable
@@ -88,12 +85,12 @@
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
boolean isStrongBiometric, @NonNull UsageStats usageStats,
@NonNull LockoutCache lockoutCache, boolean allowBackgroundAuthentication,
- boolean isKeyguardBypassEnabled, @Authenticators.Types int biometricStrength) {
+ boolean isKeyguardBypassEnabled, @Authenticators.Types int sensorStrength) {
this(context, lazyDaemon, token, requestId, listener, targetUserId, operationId,
restricted, owner, cookie, requireConfirmation, sensorId, logger, biometricContext,
- isStrongBiometric, usageStats, lockoutCache, allowBackgroundAuthentication,
- isKeyguardBypassEnabled, context.getSystemService(SensorPrivacyManager.class),
- biometricStrength);
+ isStrongBiometric, usageStats, lockoutCache /* lockoutCache */,
+ allowBackgroundAuthentication, isKeyguardBypassEnabled,
+ context.getSystemService(SensorPrivacyManager.class), sensorStrength);
}
@VisibleForTesting
@@ -109,13 +106,11 @@
@Authenticators.Types int biometricStrength) {
super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
owner, cookie, requireConfirmation, sensorId, logger, biometricContext,
- isStrongBiometric, null /* taskStackListener */, lockoutCache,
- allowBackgroundAuthentication,
- false /* shouldVibrate */,
- isKeyguardBypassEnabled);
+ isStrongBiometric, null /* taskStackListener */, null /* lockoutCache */,
+ allowBackgroundAuthentication, false /* shouldVibrate */,
+ isKeyguardBypassEnabled, biometricStrength);
setRequestId(requestId);
mUsageStats = usageStats;
- mLockoutCache = lockoutCache;
mNotificationManager = context.getSystemService(NotificationManager.class);
mSensorPrivacyManager = sensorPrivacyManager;
mAuthSessionCoordinator = biometricContext.getAuthSessionCoordinator();
@@ -129,14 +124,12 @@
R.array.config_face_acquire_keyguard_ignorelist);
mKeyguardIgnoreListVendor = resources.getIntArray(
R.array.config_face_acquire_vendor_keyguard_ignorelist);
- mBiometricStrength = biometricStrength;
}
@Override
public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
mState = STATE_STARTED;
- mAuthSessionCoordinator.authStartedFor(getTargetUserId(), getSensorId(), getRequestId());
}
@NonNull
@@ -221,9 +214,6 @@
0 /* error */,
0 /* vendorError */,
getTargetUserId()));
- mAuthSessionCoordinator
- .authenticatedFor(getTargetUserId(), mBiometricStrength, getSensorId(),
- getRequestId());
}
@Override
@@ -239,8 +229,6 @@
if (error == BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL) {
BiometricNotificationUtils.showReEnrollmentNotification(getContext());
}
- mAuthSessionCoordinator.authEndedFor(getTargetUserId(), mBiometricStrength, getSensorId(),
- getRequestId());
super.onError(error, vendorCode);
}
@@ -263,6 +251,8 @@
mLastAcquire = acquireInfo;
final boolean shouldSend = shouldSendAcquiredMessage(acquireInfo, vendorCode);
onAcquiredInternal(acquireInfo, vendorCode, shouldSend);
+ PerformanceTracker pt = PerformanceTracker.getInstanceForSensorId(getSensorId());
+ pt.incrementAcquireForUser(getTargetUserId(), isCryptoOperation());
}
/**
@@ -290,35 +280,39 @@
@Override
public void onLockoutTimed(long durationMillis) {
- mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED);
+ mAuthSessionCoordinator.lockOutTimed(getTargetUserId(), getSensorStrength(), getSensorId(),
+ durationMillis, getRequestId());
// Lockout metrics are logged as an error code.
final int error = BiometricFaceConstants.FACE_ERROR_LOCKOUT;
getLogger().logOnError(getContext(), getOperationContext(),
error, 0 /* vendorCode */, getTargetUserId());
+ PerformanceTracker.getInstanceForSensorId(getSensorId())
+ .incrementTimedLockoutForUser(getTargetUserId());
+
try {
getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
}
- mAuthSessionCoordinator.lockOutTimed(getTargetUserId(), mBiometricStrength, getSensorId(),
- durationMillis, getRequestId());
}
@Override
public void onLockoutPermanent() {
- mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_PERMANENT);
+ mAuthSessionCoordinator.lockedOutFor(getTargetUserId(), getSensorStrength(), getSensorId(),
+ getRequestId());
// Lockout metrics are logged as an error code.
final int error = BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT;
getLogger().logOnError(getContext(), getOperationContext(),
error, 0 /* vendorCode */, getTargetUserId());
+ PerformanceTracker.getInstanceForSensorId(getSensorId())
+ .incrementPermanentLockoutForUser(getTargetUserId());
+
try {
getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
}
- mAuthSessionCoordinator.lockedOutFor(getTargetUserId(), mBiometricStrength, getSensorId(),
- getRequestId());
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 6488185..89852a1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -50,10 +50,11 @@
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BaseClientMonitor;
-import com.android.server.biometrics.sensors.BiometricStateCallback;
import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.BiometricStateCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
@@ -82,20 +83,34 @@
private boolean mTestHalEnabled;
- @NonNull private final Context mContext;
- @NonNull private final BiometricStateCallback mBiometricStateCallback;
- @NonNull private final String mHalInstanceName;
- @NonNull @VisibleForTesting
+ @NonNull
+ @VisibleForTesting
final SparseArray<Sensor> mSensors; // Map of sensors that this HAL supports
- @NonNull private final Handler mHandler;
- @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher;
- @NonNull private final UsageStats mUsageStats;
- @NonNull private final ActivityTaskManager mActivityTaskManager;
- @NonNull private final BiometricTaskStackListener mTaskStackListener;
+ @NonNull
+ private final Context mContext;
+ @NonNull
+ private final BiometricStateCallback mBiometricStateCallback;
+ @NonNull
+ private final String mHalInstanceName;
+ @NonNull
+ private final Handler mHandler;
+ @NonNull
+ private final LockoutResetDispatcher mLockoutResetDispatcher;
+ @NonNull
+ private final UsageStats mUsageStats;
+ @NonNull
+ private final ActivityTaskManager mActivityTaskManager;
+ @NonNull
+ private final BiometricTaskStackListener mTaskStackListener;
// for requests that do not use biometric prompt
- @NonNull private final AtomicLong mRequestCounter = new AtomicLong(0);
- @NonNull private final BiometricContext mBiometricContext;
- @Nullable private IFace mDaemon;
+ @NonNull
+ private final AtomicLong mRequestCounter = new AtomicLong(0);
+ @NonNull
+ private final BiometricContext mBiometricContext;
+ @NonNull
+ private final AuthSessionCoordinator mAuthSessionCoordinator;
+ @Nullable
+ private IFace mDaemon;
private final class BiometricTaskStackListener extends TaskStackListener {
@Override
@@ -141,6 +156,7 @@
mActivityTaskManager = ActivityTaskManager.getInstance();
mTaskStackListener = new BiometricTaskStackListener();
mBiometricContext = biometricContext;
+ mAuthSessionCoordinator = mBiometricContext.getAuthSessionCoordinator();
for (SensorProps prop : props) {
final int sensorId = prop.commonProps.sensorId;
@@ -312,7 +328,8 @@
@Override
public int getLockoutModeForUser(int sensorId, int userId) {
- return mSensors.get(sensorId).getLockoutCache().getLockoutModeForUser(userId);
+ return mBiometricContext.getAuthSessionCoordinator().getLockoutStateFor(userId,
+ Utils.getCurrentStrength(sensorId));
}
@Override
@@ -423,7 +440,6 @@
boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled) {
mHandler.post(() -> {
final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
- final int biometricStrength = Utils.getCurrentStrength(sensorId);
final FaceAuthenticationClient client = new FaceAuthenticationClient(
mContext, mSensors.get(sensorId).getLazySession(), token, requestId, callback,
userId, operationId, restricted, opPackageName, cookie,
@@ -431,8 +447,24 @@
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
mBiometricContext, isStrongBiometric,
mUsageStats, mSensors.get(sensorId).getLockoutCache(),
- allowBackgroundAuthentication, isKeyguardBypassEnabled, biometricStrength);
- scheduleForSensor(sensorId, client, mBiometricStateCallback);
+ allowBackgroundAuthentication, isKeyguardBypassEnabled,
+ Utils.getCurrentStrength(sensorId)
+ );
+ scheduleForSensor(sensorId, client, new ClientMonitorCallback() {
+ @Override
+ public void onClientStarted(
+ BaseClientMonitor clientMonitor) {
+ mAuthSessionCoordinator.authStartedFor(userId, sensorId, requestId);
+ }
+
+ @Override
+ public void onClientFinished(
+ BaseClientMonitor clientMonitor,
+ boolean success) {
+ mAuthSessionCoordinator.authEndedFor(userId, Utils.getCurrentStrength(sensorId),
+ sensorId, requestId, success);
+ }
+ });
});
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
index 32bed48..759c52a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
@@ -89,9 +89,9 @@
void onLockoutCleared() {
resetLocalLockoutStateToNone(getSensorId(), getTargetUserId(), mLockoutCache,
mLockoutResetDispatcher);
+ mCallback.onClientFinished(this, true /* success */);
getBiometricContext().getAuthSessionCoordinator()
.resetLockoutFor(getTargetUserId(), mBiometricStrength, getRequestId());
- mCallback.onClientFinished(this, true /* success */);
}
public boolean interruptsPrecedingClients() {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index 0f5cdc3..0d30ddd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -637,7 +637,7 @@
public void onBinderDied() {
final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (client.isInterruptable()) {
+ if (client != null && client.isInterruptable()) {
Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
final ErrorConsumer errorConsumer = (ErrorConsumer) client;
errorConsumer.onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE,
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index 0e0ee19..1adc5e3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -677,8 +677,9 @@
opPackageName, cookie, false /* requireConfirmation */, mSensorId,
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
mBiometricContext, isStrongBiometric, mLockoutTracker,
- mUsageStats, allowBackgroundAuthentication, isKeyguardBypassEnabled);
- mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
+ mUsageStats, allowBackgroundAuthentication, isKeyguardBypassEnabled,
+ Utils.getCurrentStrength(mSensorId));
+ mScheduler.scheduleClientMonitor(client);
});
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
index 91eec7d..d4a7f08 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
@@ -23,6 +23,7 @@
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricFaceConstants;
+import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.face.FaceManager;
import android.os.IBinder;
@@ -39,6 +40,7 @@
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.PerformanceTracker;
import com.android.server.biometrics.sensors.face.UsageStats;
import java.util.ArrayList;
@@ -70,12 +72,12 @@
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
boolean isStrongBiometric, @NonNull LockoutTracker lockoutTracker,
@NonNull UsageStats usageStats, boolean allowBackgroundAuthentication,
- boolean isKeyguardBypassEnabled) {
+ boolean isKeyguardBypassEnabled, @Authenticators.Types int sensorStrength) {
super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
owner, cookie, requireConfirmation, sensorId, logger, biometricContext,
isStrongBiometric, null /* taskStackListener */,
lockoutTracker, allowBackgroundAuthentication, false /* shouldVibrate */,
- isKeyguardBypassEnabled);
+ isKeyguardBypassEnabled, sensorStrength);
setRequestId(requestId);
mUsageStats = usageStats;
mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
@@ -154,6 +156,21 @@
}
@Override
+ public @LockoutTracker.LockoutMode int handleFailedAttempt(int userId) {
+ @LockoutTracker.LockoutMode final int lockoutMode =
+ getLockoutTracker().getLockoutModeForUser(userId);
+ final PerformanceTracker performanceTracker =
+ PerformanceTracker.getInstanceForSensorId(getSensorId());
+ if (lockoutMode == LockoutTracker.LOCKOUT_PERMANENT) {
+ performanceTracker.incrementPermanentLockoutForUser(userId);
+ } else if (lockoutMode == LockoutTracker.LOCKOUT_TIMED) {
+ performanceTracker.incrementTimedLockoutForUser(userId);
+ }
+
+ return lockoutMode;
+ }
+
+ @Override
public void onAuthenticated(BiometricAuthenticator.Identifier identifier,
boolean authenticated, ArrayList<Byte> token) {
super.onAuthenticated(identifier, authenticated, token);
@@ -204,6 +221,12 @@
if (acquireInfo == FaceManager.FACE_ACQUIRED_RECALIBRATE) {
BiometricNotificationUtils.showReEnrollmentNotification(getContext());
}
+ @LockoutTracker.LockoutMode final int lockoutMode =
+ getLockoutTracker().getLockoutModeForUser(getTargetUserId());
+ if (lockoutMode == LockoutTracker.LOCKOUT_NONE) {
+ PerformanceTracker pt = PerformanceTracker.getInstanceForSensorId(getSensorId());
+ pt.incrementAcquireForUser(getTargetUserId(), isCryptoOperation());
+ }
final boolean shouldSend = shouldSend(acquireInfo, vendorCode);
onAcquiredInternal(acquireInfo, vendorCode, shouldSend);
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 70470e9..dca9ef2 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
@@ -942,6 +942,8 @@
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public void setUdfpsOverlay(@NonNull IUdfpsOverlay controller) {
+ super.setUdfpsOverlay_enforcePermission();
+
for (ServiceProvider provider : mRegistry.getProviders()) {
provider.setUdfpsOverlay(controller);
}
@@ -960,6 +962,8 @@
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public void scheduleWatchdog() {
+ super.scheduleWatchdog_enforcePermission();
+
final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for scheduling watchdog");
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 49bd44b..893c9b1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -56,7 +56,7 @@
import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutConsumer;
-import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.PerformanceTracker;
import com.android.server.biometrics.sensors.SensorOverlays;
import com.android.server.biometrics.sensors.fingerprint.PowerPressHandler;
import com.android.server.biometrics.sensors.fingerprint.Udfps;
@@ -75,8 +75,6 @@
private static final int MESSAGE_AUTH_SUCCESS = 2;
private static final int MESSAGE_FINGER_UP = 3;
@NonNull
- private final LockoutCache mLockoutCache;
- @NonNull
private final SensorOverlays mSensorOverlays;
@NonNull
private final FingerprintSensorPropertiesInternal mSensorProps;
@@ -85,7 +83,6 @@
private final Handler mHandler;
private final int mSkipWaitForPowerAcquireMessage;
private final int mSkipWaitForPowerVendorAcquireMessage;
- private final int mBiometricStrength;
private final long mFingerUpIgnoresPower = 500;
private final AuthSessionCoordinator mAuthSessionCoordinator;
@Nullable
@@ -97,6 +94,7 @@
private long mSideFpsLastAcquireStartTime;
private Runnable mAuthSuccessRunnable;
private final Clock mClock;
+ private boolean mDidFinishSfps;
FingerprintAuthenticationClient(
@NonNull Context context,
@@ -140,12 +138,12 @@
biometricContext,
isStrongBiometric,
taskStackListener,
- lockoutCache,
+ null /* lockoutCache */,
allowBackgroundAuthentication,
false /* shouldVibrate */,
- false /* isKeyguardBypassEnabled */);
+ false /* isKeyguardBypassEnabled */,
+ biometricStrength);
setRequestId(requestId);
- mLockoutCache = lockoutCache;
mSensorOverlays = new SensorOverlays(udfpsOverlayController,
sidefpsController, udfpsOverlay);
mSensorProps = sensorProps;
@@ -166,7 +164,6 @@
mSkipWaitForPowerVendorAcquireMessage =
context.getResources().getInteger(
R.integer.config_sidefpsSkipWaitForPowerVendorAcquireMessage);
- mBiometricStrength = biometricStrength;
mAuthSessionCoordinator = biometricContext.getAuthSessionCoordinator();
mSideFpsLastAcquireStartTime = -1;
mClock = clock;
@@ -196,8 +193,6 @@
} else {
mState = STATE_STARTED;
}
- mAuthSessionCoordinator.authStartedFor(getTargetUserId(), getSensorId(),
- getRequestId());
}
@NonNull
@@ -209,10 +204,9 @@
@Override
protected void handleLifecycleAfterAuth(boolean authenticated) {
- if (authenticated) {
+ if (authenticated && !mDidFinishSfps) {
mCallback.onClientFinished(this, true /* success */);
- mAuthSessionCoordinator.authenticatedFor(
- getTargetUserId(), mBiometricStrength, getSensorId(), getRequestId());
+ mDidFinishSfps = true;
}
}
@@ -304,6 +298,8 @@
});
}
}
+ PerformanceTracker pt = PerformanceTracker.getInstanceForSensorId(getSensorId());
+ pt.incrementAcquireForUser(getTargetUserId(), isCryptoOperation());
}
@@ -316,8 +312,6 @@
}
mSensorOverlays.hide(getSensorId());
- mAuthSessionCoordinator.authEndedFor(getTargetUserId(), mBiometricStrength, getSensorId(),
- getRequestId());
}
@Override
@@ -455,7 +449,8 @@
@Override
public void onLockoutTimed(long durationMillis) {
- mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED);
+ mAuthSessionCoordinator.lockOutTimed(getTargetUserId(), getSensorStrength(), getSensorId(),
+ durationMillis, getRequestId());
// Lockout metrics are logged as an error code.
final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
getLogger()
@@ -466,6 +461,9 @@
0 /* vendorCode */,
getTargetUserId());
+ PerformanceTracker.getInstanceForSensorId(getSensorId())
+ .incrementTimedLockoutForUser(getTargetUserId());
+
try {
getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
} catch (RemoteException e) {
@@ -474,13 +472,12 @@
mSensorOverlays.hide(getSensorId());
mCallback.onClientFinished(this, false /* success */);
- mAuthSessionCoordinator.lockOutTimed(getTargetUserId(), mBiometricStrength, getSensorId(),
- durationMillis, getRequestId());
}
@Override
public void onLockoutPermanent() {
- mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_PERMANENT);
+ mAuthSessionCoordinator.lockedOutFor(getTargetUserId(), getSensorStrength(), getSensorId(),
+ getRequestId());
// Lockout metrics are logged as an error code.
final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
getLogger()
@@ -491,6 +488,9 @@
0 /* vendorCode */,
getTargetUserId());
+ PerformanceTracker.getInstanceForSensorId(getSensorId())
+ .incrementPermanentLockoutForUser(getTargetUserId());
+
try {
getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
} catch (RemoteException e) {
@@ -499,8 +499,6 @@
mSensorOverlays.hide(getSensorId());
mCallback.onClientFinished(this, false /* success */);
- mAuthSessionCoordinator.lockedOutFor(getTargetUserId(), mBiometricStrength, getSensorId(),
- getRequestId());
}
@Override
@@ -508,15 +506,17 @@
if (mSensorProps.isAnySidefpsType()) {
Slog.i(TAG, "(sideFPS): onPowerPressed");
mHandler.post(() -> {
+ if (mDidFinishSfps) {
+ return;
+ }
Slog.i(TAG, "(sideFPS): finishing auth");
// Ignore auths after a power has been detected
mHandler.removeMessages(MESSAGE_AUTH_SUCCESS);
// Do not call onError() as that will send an additional callback to coex.
- onErrorInternal(BiometricConstants.BIOMETRIC_ERROR_POWER_PRESSED,
- 0, true);
+ mDidFinishSfps = true;
+ onErrorInternal(BiometricConstants.BIOMETRIC_ERROR_POWER_PRESSED, 0, true);
+ stopHalOperation();
mSensorOverlays.hide(getSensorId());
- mAuthSessionCoordinator.authEndedFor(getTargetUserId(),
- mBiometricStrength, getSensorId(), getRequestId());
});
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index a42ff9a..b42b1c6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -58,6 +58,7 @@
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
@@ -94,15 +95,23 @@
private boolean mTestHalEnabled;
- @NonNull private final Context mContext;
- @NonNull private final BiometricStateCallback mBiometricStateCallback;
- @NonNull private final String mHalInstanceName;
- @NonNull @VisibleForTesting
+ @NonNull
+ @VisibleForTesting
final SparseArray<Sensor> mSensors; // Map of sensors that this HAL supports
- @NonNull private final Handler mHandler;
- @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher;
- @NonNull private final ActivityTaskManager mActivityTaskManager;
- @NonNull private final BiometricTaskStackListener mTaskStackListener;
+ @NonNull
+ private final Context mContext;
+ @NonNull
+ private final BiometricStateCallback mBiometricStateCallback;
+ @NonNull
+ private final String mHalInstanceName;
+ @NonNull
+ private final Handler mHandler;
+ @NonNull
+ private final LockoutResetDispatcher mLockoutResetDispatcher;
+ @NonNull
+ private final ActivityTaskManager mActivityTaskManager;
+ @NonNull
+ private final BiometricTaskStackListener mTaskStackListener;
// for requests that do not use biometric prompt
@NonNull private final AtomicLong mRequestCounter = new AtomicLong(0);
@NonNull private final BiometricContext mBiometricContext;
@@ -110,6 +119,7 @@
@Nullable private IUdfpsOverlayController mUdfpsOverlayController;
@Nullable private ISidefpsController mSidefpsController;
@Nullable private IUdfpsOverlay mUdfpsOverlay;
+ private AuthSessionCoordinator mAuthSessionCoordinator;
private final class BiometricTaskStackListener extends TaskStackListener {
@Override
@@ -154,6 +164,7 @@
mActivityTaskManager = ActivityTaskManager.getInstance();
mTaskStackListener = new BiometricTaskStackListener();
mBiometricContext = biometricContext;
+ mAuthSessionCoordinator = mBiometricContext.getAuthSessionCoordinator();
final List<SensorLocationInternal> workaroundLocations = getWorkaroundSensorProps(context);
@@ -179,11 +190,11 @@
true /* resetLockoutRequiresHardwareAuthToken */,
!workaroundLocations.isEmpty() ? workaroundLocations :
Arrays.stream(prop.sensorLocations).map(location ->
- new SensorLocationInternal(
- location.display,
- location.sensorLocationX,
- location.sensorLocationY,
- location.sensorRadius))
+ new SensorLocationInternal(
+ location.display,
+ location.sensorLocationX,
+ location.sensorLocationY,
+ location.sensorRadius))
.collect(Collectors.toList()));
final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler,
internalProp, lockoutResetDispatcher, gestureAvailabilityDispatcher,
@@ -347,7 +358,7 @@
mSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
sensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN),
mBiometricContext);
scheduleForSensor(sensorId, client);
});
@@ -445,8 +456,30 @@
mUdfpsOverlayController, mSidefpsController, mUdfpsOverlay,
allowBackgroundAuthentication,
mSensors.get(sensorId).getSensorProperties(), mHandler,
- Utils.getCurrentStrength(sensorId), SystemClock.elapsedRealtimeClock());
- scheduleForSensor(sensorId, client, mBiometricStateCallback);
+ Utils.getCurrentStrength(sensorId),
+ SystemClock.elapsedRealtimeClock());
+ scheduleForSensor(sensorId, client, new ClientMonitorCallback() {
+
+ @Override
+ public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
+ mBiometricStateCallback.onClientStarted(clientMonitor);
+ mAuthSessionCoordinator.authStartedFor(userId, sensorId, requestId);
+ }
+
+ @Override
+ public void onBiometricAction(int action) {
+ mBiometricStateCallback.onBiometricAction(action);
+ }
+
+ @Override
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
+ boolean success) {
+ mBiometricStateCallback.onClientFinished(clientMonitor, success);
+ mAuthSessionCoordinator.authEndedFor(userId, Utils.getCurrentStrength(sensorId),
+ sensorId, requestId, success);
+ }
+ });
+
});
}
@@ -584,7 +617,8 @@
@Override
public int getLockoutModeForUser(int sensorId, int userId) {
- return mSensors.get(sensorId).getLockoutCache().getLockoutModeForUser(userId);
+ return mBiometricContext.getAuthSessionCoordinator().getLockoutStateFor(userId,
+ Utils.getCurrentStrength(sensorId));
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
index 22f504c..0b2421b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
@@ -93,9 +93,9 @@
void onLockoutCleared() {
resetLocalLockoutStateToNone(getSensorId(), getTargetUserId(), mLockoutCache,
mLockoutResetDispatcher);
+ mCallback.onClientFinished(this, true /* success */);
getBiometricContext().getAuthSessionCoordinator()
.resetLockoutFor(getTargetUserId(), mBiometricStrength, getRequestId());
- mCallback.onClientFinished(this, true /* success */);
}
/**
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index dbc96df..1f30363 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -667,7 +667,8 @@
mBiometricContext, isStrongBiometric,
mTaskStackListener, mLockoutTracker,
mUdfpsOverlayController, mSidefpsController, mUdfpsOverlay,
- allowBackgroundAuthentication, mSensorProperties);
+ allowBackgroundAuthentication, mSensorProperties,
+ Utils.getCurrentStrength(mSensorId));
mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
});
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
index 56fa36e..089317e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
@@ -23,6 +23,7 @@
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricFingerprintConstants;
+import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.ISidefpsController;
@@ -42,6 +43,7 @@
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.PerformanceTracker;
import com.android.server.biometrics.sensors.SensorOverlays;
import com.android.server.biometrics.sensors.fingerprint.Udfps;
import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
@@ -79,12 +81,13 @@
@Nullable ISidefpsController sidefpsController,
@Nullable IUdfpsOverlay udfpsOverlay,
boolean allowBackgroundAuthentication,
- @NonNull FingerprintSensorPropertiesInternal sensorProps) {
+ @NonNull FingerprintSensorPropertiesInternal sensorProps,
+ @Authenticators.Types int sensorStrength) {
super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
owner, cookie, requireConfirmation, sensorId, logger, biometricContext,
- isStrongBiometric, taskStackListener, lockoutTracker,
- allowBackgroundAuthentication, false /* shouldVibrate */,
- false /* isKeyguardBypassEnabled */);
+ isStrongBiometric, taskStackListener, lockoutTracker, allowBackgroundAuthentication,
+ false /* shouldVibrate */, false /* isKeyguardBypassEnabled */,
+ sensorStrength);
setRequestId(requestId);
mLockoutFrameworkImpl = lockoutTracker;
mSensorOverlays = new SensorOverlays(udfpsOverlayController,
@@ -167,6 +170,18 @@
}
@Override
+ public void onAcquired(int acquiredInfo, int vendorCode) {
+ super.onAcquired(acquiredInfo, vendorCode);
+
+ @LockoutTracker.LockoutMode final int lockoutMode =
+ getLockoutTracker().getLockoutModeForUser(getTargetUserId());
+ if (lockoutMode == LockoutTracker.LOCKOUT_NONE) {
+ PerformanceTracker pt = PerformanceTracker.getInstanceForSensorId(getSensorId());
+ pt.incrementAcquireForUser(getTargetUserId(), isCryptoOperation());
+ }
+ }
+
+ @Override
public boolean wasUserDetected() {
// TODO: Update if it needs to be used for fingerprint, i.e. success/reject, error_timeout
return false;
@@ -175,7 +190,17 @@
@Override
public @LockoutTracker.LockoutMode int handleFailedAttempt(int userId) {
mLockoutFrameworkImpl.addFailedAttemptForUser(userId);
- return super.handleFailedAttempt(userId);
+ @LockoutTracker.LockoutMode final int lockoutMode =
+ getLockoutTracker().getLockoutModeForUser(userId);
+ final PerformanceTracker performanceTracker =
+ PerformanceTracker.getInstanceForSensorId(getSensorId());
+ if (lockoutMode == LockoutTracker.LOCKOUT_PERMANENT) {
+ performanceTracker.incrementPermanentLockoutForUser(userId);
+ } else if (lockoutMode == LockoutTracker.LOCKOUT_TIMED) {
+ performanceTracker.incrementTimedLockoutForUser(userId);
+ }
+
+ return lockoutMode;
}
@Override
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
index 71ba296..4fcfea2 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
@@ -169,7 +169,7 @@
synchronized (mLock) {
List<RadioManager.ModuleProperties> moduleList = new ArrayList<>(mModules.size());
for (int i = 0; i < mModules.size(); i++) {
- moduleList.add(mModules.valueAt(i).mProperties);
+ moduleList.add(mModules.valueAt(i).getProperties());
}
return moduleList;
}
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
index c6dc431..d4c7242 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
@@ -54,11 +54,11 @@
private static final int RADIO_EVENT_LOGGER_QUEUE_SIZE = 25;
private final IBroadcastRadio mService;
- public final RadioManager.ModuleProperties mProperties;
private final Object mLock;
private final Handler mHandler;
private final RadioLogger mLogger;
+ private final RadioManager.ModuleProperties mProperties;
/**
* Tracks antenna state reported by HAL (if any).
@@ -217,6 +217,10 @@
return mService;
}
+ public RadioManager.ModuleProperties getProperties() {
+ return mProperties;
+ }
+
void setInternalHalCallback() throws RemoteException {
synchronized (mLock) {
mService.setTunerCallback(mHalTunerCallback);
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
index 5605737..4c37609 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
@@ -33,6 +33,7 @@
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import java.util.Collection;
import java.util.HashMap;
@@ -132,10 +133,22 @@
}
}
+ @VisibleForTesting
+ BroadcastRadioService(int nextModuleId, Object lock, IServiceManager manager) {
+ mNextModuleId = nextModuleId;
+ mLock = lock;
+ Objects.requireNonNull(manager, "Service manager cannot be null");
+ try {
+ manager.registerForNotifications(IBroadcastRadio.kInterfaceName, "", mServiceListener);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Failed to register for service notifications: ", ex);
+ }
+ }
+
public @NonNull Collection<RadioManager.ModuleProperties> listModules() {
Slog.v(TAG, "List HIDL 2.0 modules");
synchronized (mLock) {
- return mModules.values().stream().map(module -> module.mProperties)
+ return mModules.values().stream().map(module -> module.getProperties())
.collect(Collectors.toList());
}
}
@@ -154,7 +167,7 @@
public ITuner openSession(int moduleId, @Nullable RadioManager.BandConfig legacyConfig,
boolean withAudio, @NonNull ITunerCallback callback) throws RemoteException {
- Slog.v(TAG, "Open HIDL 2.0 session");
+ Slog.v(TAG, "Open HIDL 2.0 session with module id " + moduleId);
Objects.requireNonNull(callback);
if (!withAudio) {
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
index 726cdc3..3daf1db 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
@@ -52,8 +52,13 @@
import java.util.stream.Collectors;
class Convert {
+
private static final String TAG = "BcRadio2Srv.convert";
+ private Convert() {
+ throw new UnsupportedOperationException("Convert class is noninstantiable");
+ }
+
static void throwOnError(String action, int result) {
switch (result) {
case Result.OK:
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
index 0a23e38..5913e068 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -58,7 +58,7 @@
private static final int RADIO_EVENT_LOGGER_QUEUE_SIZE = 25;
@NonNull private final IBroadcastRadio mService;
- @NonNull public final RadioManager.ModuleProperties mProperties;
+ @NonNull private final RadioManager.ModuleProperties mProperties;
private final Object mLock;
@NonNull private final Handler mHandler;
@@ -177,6 +177,10 @@
return mService;
}
+ public RadioManager.ModuleProperties getProperties() {
+ return mProperties;
+ }
+
public @NonNull TunerSession openSession(@NonNull android.hardware.radio.ITunerCallback userCb)
throws RemoteException {
mEventLogger.logRadioEvent("Open TunerSession");
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Utils.java b/services/core/java/com/android/server/broadcastradio/hal2/Utils.java
index 384c9ba..f6e90ef 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/Utils.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/Utils.java
@@ -28,6 +28,11 @@
};
class Utils {
+
+ private Utils() {
+ throw new UnsupportedOperationException("Utils class is noninstantiable");
+ }
+
private static final String TAG = "BcRadio2Srv.utils";
static FrequencyBand getBand(int freq) {
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 6a000d9..0741d46 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -127,6 +127,8 @@
import android.system.keystore2.KeyPermission;
import android.text.TextUtils;
import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
+import android.util.LocalLog;
import android.util.Log;
import android.util.Range;
@@ -296,6 +298,10 @@
return mVpnProfileStore;
}
+ private static final int MAX_EVENTS_LOGS = 20;
+ private final LocalLog mUnderlyNetworkChanges = new LocalLog(MAX_EVENTS_LOGS);
+ private final LocalLog mVpnManagerEvents = new LocalLog(MAX_EVENTS_LOGS);
+
/**
* Whether to keep the connection active after rebooting, or upgrading or reinstalling. This
* only applies to {@link VpnService} connections.
@@ -841,6 +847,9 @@
int errorCode, @NonNull final String packageName, @Nullable final String sessionKey,
@NonNull final VpnProfileState profileState, @Nullable final Network underlyingNetwork,
@Nullable final NetworkCapabilities nc, @Nullable final LinkProperties lp) {
+ mVpnManagerEvents.log("Event class=" + getVpnManagerEventClassName(errorClass)
+ + ", err=" + getVpnManagerEventErrorName(errorCode) + " for " + packageName
+ + " on session " + sessionKey);
final Intent intent = buildVpnManagerEventIntent(category, errorClass, errorCode,
packageName, sessionKey, profileState, underlyingNetwork, nc, lp);
return sendEventToVpnManagerApp(intent, packageName);
@@ -1572,6 +1581,7 @@
? Arrays.asList(mConfig.underlyingNetworks) : null);
mNetworkCapabilities = capsBuilder.build();
+ logUnderlyNetworkChanges(mNetworkCapabilities.getUnderlyingNetworks());
mNetworkAgent = mDeps.newNetworkAgent(mContext, mLooper, NETWORKTYPE /* logtag */,
mNetworkCapabilities, lp,
new NetworkScore.Builder().setLegacyInt(VPN_DEFAULT_SCORE).build(),
@@ -1599,6 +1609,11 @@
}
}
+ private void logUnderlyNetworkChanges(List<Network> networks) {
+ mUnderlyNetworkChanges.log("Switch to "
+ + ((networks != null) ? TextUtils.join(", ", networks) : "null"));
+ }
+
private void agentDisconnect(NetworkAgent networkAgent) {
if (networkAgent != null) {
networkAgent.unregister();
@@ -4372,6 +4387,7 @@
// TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from
// ConnectivityServiceTest.
if (SdkLevel.isAtLeastT()) {
+ mVpnManagerEvents.log(packageName + " stopped");
sendEventToVpnManagerApp(intent, packageName);
}
}
@@ -4539,8 +4555,10 @@
/** Proxy to allow different testing setups */
// TODO: b/240492694 Remove VpnNetworkAgentWrapper and this method when
// NetworkAgent#setUnderlyingNetworks can be un-finalized.
- private static void doSetUnderlyingNetworks(
+ private void doSetUnderlyingNetworks(
@NonNull NetworkAgent agent, @NonNull List<Network> networks) {
+ logUnderlyNetworkChanges(networks);
+
if (agent instanceof VpnNetworkAgentWrapper) {
((VpnNetworkAgentWrapper) agent).doSetUnderlyingNetworks(networks);
} else {
@@ -4659,4 +4677,57 @@
static Range<Integer> createUidRangeForUser(int userId) {
return new Range<Integer>(userId * PER_USER_RANGE, (userId + 1) * PER_USER_RANGE - 1);
}
+
+ private String getVpnManagerEventClassName(int code) {
+ switch (code) {
+ case VpnManager.ERROR_CLASS_NOT_RECOVERABLE:
+ return "ERROR_CLASS_NOT_RECOVERABLE";
+ case VpnManager.ERROR_CLASS_RECOVERABLE:
+ return "ERROR_CLASS_RECOVERABLE";
+ default:
+ return "UNKNOWN_CLASS";
+ }
+ }
+
+ private String getVpnManagerEventErrorName(int code) {
+ switch (code) {
+ case VpnManager.ERROR_CODE_NETWORK_UNKNOWN_HOST:
+ return "ERROR_CODE_NETWORK_UNKNOWN_HOST";
+ case VpnManager.ERROR_CODE_NETWORK_PROTOCOL_TIMEOUT:
+ return "ERROR_CODE_NETWORK_PROTOCOL_TIMEOUT";
+ case VpnManager.ERROR_CODE_NETWORK_IO:
+ return "ERROR_CODE_NETWORK_IO";
+ case VpnManager.ERROR_CODE_NETWORK_LOST:
+ return "ERROR_CODE_NETWORK_LOST";
+ default:
+ return "UNKNOWN_ERROR";
+ }
+ }
+
+ /** Dumps VPN state. */
+ public void dump(IndentingPrintWriter pw) {
+ synchronized (Vpn.this) {
+ pw.println("Active package name: " + mPackage);
+ pw.println("Active vpn type: " + getActiveVpnType());
+ pw.println("NetworkCapabilities: " + mNetworkCapabilities);
+ if (isIkev2VpnRunner()) {
+ final IkeV2VpnRunner runner = ((IkeV2VpnRunner) mVpnRunner);
+ pw.println("Token: " + runner.mSessionKey);
+ pw.println("MOBIKE " + (runner.mMobikeEnabled ? "enabled" : "disabled"));
+ if (mDataStallSuspected) pw.println("Data stall suspected");
+ if (runner.mScheduledHandleDataStallFuture != null) {
+ pw.println("Reset session scheduled");
+ }
+ }
+ pw.println("mUnderlyNetworkChanges (most recent first):");
+ pw.increaseIndent();
+ mUnderlyNetworkChanges.reverseDump(pw);
+ pw.decreaseIndent();
+
+ pw.println("mVpnManagerEvent (most recent first):");
+ pw.increaseIndent();
+ mVpnManagerEvents.reverseDump(pw);
+ pw.decreaseIndent();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/content/SyncAdapterStateFetcher.java b/services/core/java/com/android/server/content/SyncAdapterStateFetcher.java
index ffaf364..108cddc 100644
--- a/services/core/java/com/android/server/content/SyncAdapterStateFetcher.java
+++ b/services/core/java/com/android/server/content/SyncAdapterStateFetcher.java
@@ -17,8 +17,8 @@
import android.app.ActivityManagerInternal;
import android.app.usage.UsageStatsManagerInternal;
+import android.content.pm.UserPackage;
import android.os.SystemClock;
-import android.util.Pair;
import com.android.server.LocalServices;
@@ -26,8 +26,7 @@
class SyncAdapterStateFetcher {
- private final HashMap<Pair<Integer, String>, Integer> mBucketCache =
- new HashMap<>();
+ private final HashMap<UserPackage, Integer> mBucketCache = new HashMap<>();
public SyncAdapterStateFetcher() {
}
@@ -36,7 +35,7 @@
* Return sync adapter state with a cache.
*/
public int getStandbyBucket(int userId, String packageName) {
- final Pair<Integer, String> key = Pair.create(userId, packageName);
+ final UserPackage key = UserPackage.of(userId, packageName);
final Integer cached = mBucketCache.get(key);
if (cached != null) {
return cached;
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 73afa60..eb81e70 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -2215,7 +2215,8 @@
pw.print("Storage low: "); pw.println(storageLowIntent != null);
pw.print("Clock valid: "); pw.println(mSyncStorageEngine.isClockValid());
- final AccountAndUser[] accounts = AccountManagerService.getSingleton().getAllAccounts();
+ final AccountAndUser[] accounts =
+ AccountManagerService.getSingleton().getAllAccountsForSystemProcess();
pw.print("Accounts: ");
if (accounts != INITIAL_ACCOUNTS_ARRAY) {
@@ -3274,7 +3275,8 @@
private void updateRunningAccountsH(EndPoint syncTargets) {
synchronized (mAccountsLock) {
AccountAndUser[] oldAccounts = mRunningAccounts;
- mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts();
+ mRunningAccounts =
+ AccountManagerService.getSingleton().getRunningAccountsForSystem();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Slog.v(TAG, "Accounts list: ");
for (AccountAndUser acc : mRunningAccounts) {
@@ -3316,7 +3318,8 @@
}
// Cancel all jobs from non-existent accounts.
- AccountAndUser[] allAccounts = AccountManagerService.getSingleton().getAllAccounts();
+ AccountAndUser[] allAccounts =
+ AccountManagerService.getSingleton().getAllAccountsForSystemProcess();
List<SyncOperation> ops = getAllPendingSyncs();
for (int i = 0, opsSize = ops.size(); i < opsSize; i++) {
SyncOperation op = ops.get(i);
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index e9856d0..df4c471 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -1133,7 +1133,7 @@
public void registerReceiver(Context context,
BroadcastReceiver receiver, IntentFilter filter) {
- context.registerReceiver(receiver, filter);
+ context.registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED_UNAUDITED);
}
public void unregisterReceiver(Context context,
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index e907ebf..78b697d 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1716,7 +1716,20 @@
final Point userPreferredResolution =
mPersistentDataStore.getUserPreferredResolution(device);
final float refreshRate = mPersistentDataStore.getUserPreferredRefreshRate(device);
- if (userPreferredResolution == null && Float.isNaN(refreshRate)) {
+ // If value in persistentDataStore is null, preserving the mode from systemPreferredMode.
+ // This is required because in some devices, user-preferred mode was not stored in
+ // persistentDataStore, but was stored in a config which is returned through
+ // systemPreferredMode.
+ if ((userPreferredResolution == null && Float.isNaN(refreshRate))
+ || (userPreferredResolution.equals(0, 0) && refreshRate == 0.0f)) {
+ Display.Mode systemPreferredMode = device.getSystemPreferredDisplayModeLocked();
+ if (systemPreferredMode == null) {
+ return;
+ }
+ storeModeInPersistentDataStoreLocked(
+ display.getDisplayIdLocked(), systemPreferredMode.getPhysicalWidth(),
+ systemPreferredMode.getPhysicalHeight(), systemPreferredMode.getRefreshRate());
+ device.setUserPreferredDisplayModeLocked(systemPreferredMode);
return;
}
Display.Mode.Builder modeBuilder = new Display.Mode.Builder();
@@ -1902,8 +1915,9 @@
if (displayDevice == null) {
return;
}
- if (mLogicalDisplayMapper.getDisplayLocked(displayDevice)
- .getDisplayInfoLocked().type == Display.TYPE_INTERNAL) {
+ if (mLogicalDisplayMapper.getDisplayLocked(displayDevice) != null
+ && mLogicalDisplayMapper.getDisplayLocked(displayDevice)
+ .getDisplayInfoLocked().type == Display.TYPE_INTERNAL && c != null) {
FrameworkStatsLog.write(FrameworkStatsLog.BRIGHTNESS_CONFIGURATION_UPDATED,
c.getCurve().first,
c.getCurve().second,
@@ -2620,7 +2634,8 @@
// initPowerManagement has not yet been called.
return;
}
- if (mBrightnessTracker == null) {
+
+ if (mBrightnessTracker == null && display.getDisplayIdLocked() == Display.DEFAULT_DISPLAY) {
mBrightnessTracker = new BrightnessTracker(mContext, null);
}
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 726326f..a5e5c24 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -526,22 +526,28 @@
ranges, ranges);
}
- if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE
- || primarySummary.disableRefreshRateSwitching) {
+ boolean modeSwitchingDisabled =
+ mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE
+ || mModeSwitchingType
+ == DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY;
+
+ if (modeSwitchingDisabled || primarySummary.disableRefreshRateSwitching) {
float fps = baseMode.getRefreshRate();
primarySummary.minPhysicalRefreshRate = primarySummary.maxPhysicalRefreshRate = fps;
- if (mRenderFrameRateIsPhysicalRefreshRate) {
- primarySummary.minRenderFrameRate = primarySummary.maxRenderFrameRate = fps;
- }
- if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE) {
- primarySummary.minRenderFrameRate = primarySummary.maxRenderFrameRate = fps;
+ if (modeSwitchingDisabled) {
appRequestSummary.minPhysicalRefreshRate =
appRequestSummary.maxPhysicalRefreshRate = fps;
- appRequestSummary.minRenderFrameRate =
- appRequestSummary.maxRenderFrameRate = fps;
}
}
+ if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE
+ || mRenderFrameRateIsPhysicalRefreshRate) {
+ primarySummary.minRenderFrameRate = primarySummary.minPhysicalRefreshRate;
+ primarySummary.maxRenderFrameRate = primarySummary.maxPhysicalRefreshRate;
+ appRequestSummary.minRenderFrameRate = appRequestSummary.minPhysicalRefreshRate;
+ appRequestSummary.maxRenderFrameRate = appRequestSummary.maxPhysicalRefreshRate;
+ }
+
boolean allowGroupSwitching =
mModeSwitchingType == DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS;
@@ -842,6 +848,8 @@
return "SWITCHING_TYPE_WITHIN_GROUPS";
case DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS:
return "SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS";
+ case DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY:
+ return "SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY";
default:
return "Unknown SwitchingType " + type;
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 422e98f..838bb53 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -927,7 +927,7 @@
// Initialize all of the brightness tracking state
final float brightness = convertToNits(mPowerState.getScreenBrightness());
- if (brightness >= PowerManager.BRIGHTNESS_MIN) {
+ if (mBrightnessTracker != null && brightness >= PowerManager.BRIGHTNESS_MIN) {
mBrightnessTracker.start(brightness);
}
mBrightnessSettingListener = brightnessValue -> {
@@ -1059,7 +1059,9 @@
}
loadAmbientLightSensor();
- if (mBrightnessTracker != null) {
+ // BrightnessTracker should only use one light sensor, we want to use the light sensor
+ // from the default display and not e.g. temporary displays when switching layouts.
+ if (mBrightnessTracker != null && mDisplayId == Display.DEFAULT_DISPLAY) {
mBrightnessTracker.setLightSensor(mLightSensor);
}
@@ -2485,7 +2487,7 @@
boolean hadUserDataPoint) {
final float brightnessInNits = convertToNits(brightness);
if (mPowerRequest.useAutoBrightness && brightnessInNits >= 0.0f
- && mAutomaticBrightnessController != null) {
+ && mAutomaticBrightnessController != null && mBrightnessTracker != null) {
// We only want to track changes on devices that can actually map the display backlight
// values into a physical brightness unit since the value provided by the API is in
// nits and not using the arbitrary backlight units.
@@ -2838,18 +2840,22 @@
event.getThermalMax() == PowerManager.BRIGHTNESS_MAX
? -1f : convertToNits(event.getThermalMax());
- FrameworkStatsLog.write(FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED,
- convertToNits(event.getInitialBrightness()),
- convertToNits(event.getBrightness()),
- event.getSlowAmbientLux(),
- event.getPhysicalDisplayId(),
- event.isShortTermModelActive(),
- appliedLowPowerMode,
- appliedRbcStrength,
- appliedHbmMaxNits,
- appliedThermalCapNits,
- event.isAutomaticBrightnessEnabled(),
- FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__REASON__REASON_MANUAL);
+ if (mLogicalDisplay.getPrimaryDisplayDeviceLocked() != null
+ && mLogicalDisplay.getPrimaryDisplayDeviceLocked()
+ .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL) {
+ FrameworkStatsLog.write(FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED,
+ convertToNits(event.getInitialBrightness()),
+ convertToNits(event.getBrightness()),
+ event.getSlowAmbientLux(),
+ event.getPhysicalDisplayId(),
+ event.isShortTermModelActive(),
+ appliedLowPowerMode,
+ appliedRbcStrength,
+ appliedHbmMaxNits,
+ appliedThermalCapNits,
+ event.isAutomaticBrightnessEnabled(),
+ FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__REASON__REASON_MANUAL);
+ }
}
private final class DisplayControllerHandler extends Handler {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 3c1bf0b..c06101f 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -26,8 +26,6 @@
import android.content.res.Resources;
import android.database.ContentObserver;
import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.display.AmbientBrightnessDayStats;
import android.hardware.display.BrightnessChangeEvent;
@@ -54,7 +52,6 @@
import android.util.MutableFloat;
import android.util.MutableInt;
import android.util.Slog;
-import android.util.TimeUtils;
import android.view.Display;
import com.android.internal.R;
@@ -109,7 +106,7 @@
private static final String SCREEN_OFF_BLOCKED_TRACE_NAME = "Screen off blocked";
private static final boolean DEBUG = false;
- private static final boolean DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT = false;
+
// If true, uses the color fade on animation.
// We might want to turn this off if we cannot get a guarantee that the screen
@@ -123,31 +120,19 @@
private static final int COLOR_FADE_OFF_ANIMATION_DURATION_MILLIS = 400;
private static final int MSG_UPDATE_POWER_STATE = 1;
- private static final int MSG_PROXIMITY_SENSOR_DEBOUNCED = 2;
- private static final int MSG_SCREEN_ON_UNBLOCKED = 3;
- private static final int MSG_SCREEN_OFF_UNBLOCKED = 4;
- private static final int MSG_CONFIGURE_BRIGHTNESS = 5;
- private static final int MSG_SET_TEMPORARY_BRIGHTNESS = 6;
- private static final int MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT = 7;
- private static final int MSG_IGNORE_PROXIMITY = 8;
- private static final int MSG_STOP = 9;
- private static final int MSG_UPDATE_BRIGHTNESS = 10;
- private static final int MSG_UPDATE_RBC = 11;
- private static final int MSG_BRIGHTNESS_RAMP_DONE = 12;
- private static final int MSG_STATSD_HBM_BRIGHTNESS = 13;
-
- private static final int PROXIMITY_UNKNOWN = -1;
- private static final int PROXIMITY_NEGATIVE = 0;
- private static final int PROXIMITY_POSITIVE = 1;
-
- // Proximity sensor debounce delay in milliseconds for positive or negative transitions.
- private static final int PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY = 0;
- private static final int PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY = 250;
+ private static final int MSG_SCREEN_ON_UNBLOCKED = 2;
+ private static final int MSG_SCREEN_OFF_UNBLOCKED = 3;
+ private static final int MSG_CONFIGURE_BRIGHTNESS = 4;
+ private static final int MSG_SET_TEMPORARY_BRIGHTNESS = 5;
+ private static final int MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT = 6;
+ private static final int MSG_STOP = 7;
+ private static final int MSG_UPDATE_BRIGHTNESS = 8;
+ private static final int MSG_UPDATE_RBC = 9;
+ private static final int MSG_BRIGHTNESS_RAMP_DONE = 10;
+ private static final int MSG_STATSD_HBM_BRIGHTNESS = 11;
private static final int BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS = 500;
- // Trigger proximity if distance is less than 5 cm.
- private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f;
// State machine constants for tracking initial brightness ramp skipping when enabled.
private static final int RAMP_STATE_SKIP_NONE = 0;
@@ -200,9 +185,6 @@
// Tracker for brightness settings changes.
private final SettingsObserver mSettingsObserver;
- // The proximity sensor, or null if not available or needed.
- private Sensor mProximitySensor;
-
// The doze screen brightness.
private final float mScreenBrightnessDozeConfig;
@@ -266,10 +248,6 @@
@GuardedBy("mLock")
private DisplayPowerRequest mPendingRequestLocked;
- // True if a request has been made to wait for the proximity sensor to go negative.
- @GuardedBy("mLock")
- private boolean mPendingWaitForNegativeProximityLocked;
-
// True if the pending power request or wait for negative proximity flag
// has been changed since the last update occurred.
@GuardedBy("mLock")
@@ -296,37 +274,7 @@
// Must only be accessed on the handler thread.
private DisplayPowerState mPowerState;
- // True if the device should wait for negative proximity sensor before
- // waking up the screen. This is set to false as soon as a negative
- // proximity sensor measurement is observed or when the device is forced to
- // go to sleep by the user. While true, the screen remains off.
- private boolean mWaitingForNegativeProximity;
- // True if the device should not take into account the proximity sensor
- // until either the proximity sensor state changes, or there is no longer a
- // request to listen to proximity sensor.
- private boolean mIgnoreProximityUntilChanged;
-
- // The actual proximity sensor threshold value.
- private float mProximityThreshold;
-
- // Set to true if the proximity sensor listener has been registered
- // with the sensor manager.
- private boolean mProximitySensorEnabled;
-
- // The debounced proximity sensor state.
- private int mProximity = PROXIMITY_UNKNOWN;
-
- // The raw non-debounced proximity sensor state.
- private int mPendingProximity = PROXIMITY_UNKNOWN;
-
- // -1 if fully debounced. Else, represents the time in ms when the debounce suspend blocker will
- // be removed. Applies for both positive and negative proximity flips.
- private long mPendingProximityDebounceTime = -1;
-
- // True if the screen was turned off because of the proximity sensor.
- // When the screen turns on again, we report user activity to the power manager.
- private boolean mScreenOffBecauseOfProximity;
// The currently active screen on unblocker. This field is non-null whenever
// we are waiting for a callback to release it and unblock the screen.
@@ -407,6 +355,9 @@
// a medium of communication between this class and the PowerManagerService.
private final WakelockController mWakelockController;
+ // Tracks and manages the proximity state of the associated display.
+ private final DisplayPowerProximityStateController mDisplayPowerProximityStateController;
+
// A record of state for skipping brightness ramps.
private int mSkipRampState = RAMP_STATE_SKIP_NONE;
@@ -491,13 +442,20 @@
mClock = mInjector.getClock();
mLogicalDisplay = logicalDisplay;
mDisplayId = mLogicalDisplay.getDisplayIdLocked();
+ mSensorManager = sensorManager;
+ mHandler = new DisplayControllerHandler(handler.getLooper());
+ mDisplayDeviceConfig = logicalDisplay.getPrimaryDisplayDeviceLocked()
+ .getDisplayDeviceConfig();
mWakelockController = mInjector.getWakelockController(mDisplayId, callbacks);
+ mDisplayPowerProximityStateController = mInjector.getDisplayPowerProximityStateController(
+ mWakelockController, mDisplayDeviceConfig, mHandler.getLooper(),
+ () -> updatePowerState(), mDisplayId, mSensorManager);
mTag = "DisplayPowerController2[" + mDisplayId + "]";
mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
mDisplayStatsId = mUniqueDisplayId.hashCode();
- mHandler = new DisplayControllerHandler(handler.getLooper());
+
mLastBrightnessEvent = new BrightnessEvent(mDisplayId);
mTempBrightnessEvent = new BrightnessEvent(mDisplayId);
@@ -508,7 +466,6 @@
}
mSettingsObserver = new SettingsObserver(mHandler);
- mSensorManager = sensorManager;
mWindowManagerPolicy = LocalServices.getService(WindowManagerPolicy.class);
mBlanker = blanker;
mContext = context;
@@ -545,9 +502,6 @@
mAllowAutoBrightnessWhileDozingConfig = resources.getBoolean(
R.bool.config_allowAutoBrightnessWhileDozing);
- mDisplayDeviceConfig = logicalDisplay.getPrimaryDisplayDeviceLocked()
- .getDisplayDeviceConfig();
-
loadBrightnessRampRates();
mSkipScreenOnBrightnessRamp = resources.getBoolean(
R.bool.config_skipScreenOnBrightnessRamp);
@@ -611,8 +565,6 @@
mBrightnessBucketsInDozeConfig = resources.getBoolean(
R.bool.config_displayBrightnessBucketsInDoze);
- loadProximitySensor();
-
mCurrentScreenBrightnessSetting = getScreenBrightnessSetting();
mScreenBrightnessForVr = getScreenBrightnessForVrSetting();
mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
@@ -653,7 +605,7 @@
*/
@Override
public boolean isProximitySensorAvailable() {
- return mProximitySensor != null;
+ return mDisplayPowerProximityStateController.isProximitySensorAvailable();
}
/**
@@ -727,13 +679,8 @@
return true;
}
- boolean changed = false;
-
- if (waitForNegativeProximity
- && !mPendingWaitForNegativeProximityLocked) {
- mPendingWaitForNegativeProximityLocked = true;
- changed = true;
- }
+ boolean changed = mDisplayPowerProximityStateController
+ .setPendingWaitForNegativeProximityLocked(waitForNegativeProximity);
if (mPendingRequestLocked == null) {
mPendingRequestLocked = new DisplayPowerRequest(request);
@@ -790,6 +737,7 @@
mDisplayStatsId = mUniqueDisplayId.hashCode();
mDisplayDeviceConfig = config;
loadFromDisplayDeviceConfig(token, info);
+ mDisplayPowerProximityStateController.notifyDisplayDeviceChanged(config);
updatePowerState();
}
});
@@ -838,7 +786,6 @@
// All properties that depend on the associated DisplayDevice and the DDC must be
// updated here.
loadBrightnessRampRates();
- loadProximitySensor();
loadNitsRange(mContext.getResources());
setUpAutoBrightness(mContext.getResources(), mHandler);
reloadReduceBrightColours();
@@ -903,7 +850,7 @@
// Initialize all of the brightness tracking state
final float brightness = convertToNits(mPowerState.getScreenBrightness());
- if (brightness >= PowerManager.BRIGHTNESS_MIN) {
+ if (mBrightnessTracker != null && brightness >= PowerManager.BRIGHTNESS_MIN) {
mBrightnessTracker.start(brightness);
}
mBrightnessSettingListener = brightnessValue -> {
@@ -1035,7 +982,9 @@
}
loadAmbientLightSensor();
- if (mBrightnessTracker != null) {
+ // BrightnessTracker should only use one light sensor, we want to use the light sensor
+ // from the default display and not e.g. temporary displays when switching layouts.
+ if (mBrightnessTracker != null && mDisplayId == Display.DEFAULT_DISPLAY) {
mBrightnessTracker.setLightSensor(mLightSensor);
}
@@ -1132,7 +1081,7 @@
/** Clean up all resources that are accessed via the {@link #mHandler} thread. */
private void cleanupHandlerThreadAfterStop() {
- setProximitySensorEnabled(false);
+ mDisplayPowerProximityStateController.cleanup();
mHbmController.stop();
mBrightnessThrottler.stop();
mHandler.removeCallbacksAndMessages(null);
@@ -1177,7 +1126,7 @@
if (mPowerRequest == null) {
mPowerRequest = new DisplayPowerRequest(mPendingRequestLocked);
- updatePendingProximityRequestsLocked();
+ mDisplayPowerProximityStateController.updatePendingProximityRequestsLocked();
mPendingRequestChangedLocked = false;
mustInitialize = true;
// Assume we're on and bright until told otherwise, since that's the state we turn
@@ -1186,7 +1135,7 @@
} else if (mPendingRequestChangedLocked) {
previousPolicy = mPowerRequest.policy;
mPowerRequest.copyFrom(mPendingRequestLocked);
- updatePendingProximityRequestsLocked();
+ mDisplayPowerProximityStateController.updatePendingProximityRequestsLocked();
mPendingRequestChangedLocked = false;
mDisplayReadyLocked = false;
} else {
@@ -1229,55 +1178,11 @@
}
assert (state != Display.STATE_UNKNOWN);
- boolean skipRampBecauseOfProximityChangeToNegative = false;
- // Apply the proximity sensor.
- if (mProximitySensor != null) {
- if (mPowerRequest.useProximitySensor && state != Display.STATE_OFF) {
- // At this point the policy says that the screen should be on, but we've been
- // asked to listen to the prox sensor to adjust the display state, so lets make
- // sure the sensor is on.
- setProximitySensorEnabled(true);
- if (!mScreenOffBecauseOfProximity
- && mProximity == PROXIMITY_POSITIVE
- && !mIgnoreProximityUntilChanged) {
- // Prox sensor already reporting "near" so we should turn off the screen.
- // Also checked that we aren't currently set to ignore the proximity sensor
- // temporarily.
- mScreenOffBecauseOfProximity = true;
- sendOnProximityPositiveWithWakelock();
- }
- } else if (mWaitingForNegativeProximity
- && mScreenOffBecauseOfProximity
- && mProximity == PROXIMITY_POSITIVE
- && state != Display.STATE_OFF) {
- // The policy says that we should have the screen on, but it's off due to the prox
- // and we've been asked to wait until the screen is far from the user to turn it
- // back on. Let keep the prox sensor on so we can tell when it's far again.
- setProximitySensorEnabled(true);
- } else {
- // We haven't been asked to use the prox sensor and we're not waiting on the screen
- // to turn back on...so lets shut down the prox sensor.
- setProximitySensorEnabled(false);
- mWaitingForNegativeProximity = false;
- }
-
- if (mScreenOffBecauseOfProximity
- && (mProximity != PROXIMITY_POSITIVE || mIgnoreProximityUntilChanged)) {
- // The screen *was* off due to prox being near, but now it's "far" so lets turn
- // the screen back on. Also turn it back on if we've been asked to ignore the
- // prox sensor temporarily.
- mScreenOffBecauseOfProximity = false;
- skipRampBecauseOfProximityChangeToNegative = true;
- sendOnProximityNegativeWithWakelock();
- }
- } else {
- mWaitingForNegativeProximity = false;
- mIgnoreProximityUntilChanged = false;
- }
+ mDisplayPowerProximityStateController.updateProximityState(mPowerRequest, state);
if (!mLogicalDisplay.isEnabled()
|| mLogicalDisplay.getPhase() == LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION
- || mScreenOffBecauseOfProximity) {
+ || mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity()) {
state = Display.STATE_OFF;
}
@@ -1548,7 +1453,8 @@
final boolean wasOrWillBeInVr =
(state == Display.STATE_VR || oldState == Display.STATE_VR);
final boolean initialRampSkip = (state == Display.STATE_ON && mSkipRampState
- != RAMP_STATE_SKIP_NONE) || skipRampBecauseOfProximityChangeToNegative;
+ != RAMP_STATE_SKIP_NONE) || mDisplayPowerProximityStateController
+ .shouldSkipRampBecauseOfProximityChangeToNegative();
// While dozing, sometimes the brightness is split into buckets. Rather than animating
// through the buckets, which is unlikely to be smooth in the first place, just jump
// right to the suggested brightness.
@@ -1768,7 +1674,7 @@
*/
@Override
public void ignoreProximitySensorUntilChanged() {
- mHandler.sendEmptyMessage(MSG_IGNORE_PROXIMITY);
+ mDisplayPowerProximityStateController.ignoreProximitySensorUntilChanged();
}
@Override
@@ -1934,7 +1840,7 @@
|| mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) {
// If we are trying to turn screen off, give policy a chance to do something before we
// actually turn the screen off.
- if (isOff && !mScreenOffBecauseOfProximity) {
+ if (isOff && !mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity()) {
if (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_ON
|| mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) {
setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_OFF);
@@ -1964,7 +1870,7 @@
// it is only removed once the window manager tells us that the activity has
// finished drawing underneath.
if (isOff && mReportedScreenStateToPolicy != REPORTED_TO_POLICY_SCREEN_OFF
- && !mScreenOffBecauseOfProximity) {
+ && !mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity()) {
setReportedScreenState(REPORTED_TO_POLICY_SCREEN_OFF);
unblockScreenOn();
mWindowManagerPolicy.screenTurnedOff(mDisplayId);
@@ -2006,22 +1912,6 @@
fallbackType);
}
- private void loadProximitySensor() {
- if (DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT) {
- return;
- }
- final DisplayDeviceConfig.SensorData proxSensor =
- mDisplayDeviceConfig.getProximitySensor();
- final int fallbackType = mDisplayId == Display.DEFAULT_DISPLAY
- ? Sensor.TYPE_PROXIMITY : SensorUtils.NO_FALLBACK;
- mProximitySensor = SensorUtils.findSensor(mSensorManager, proxSensor.type, proxSensor.name,
- fallbackType);
- if (mProximitySensor != null) {
- mProximityThreshold = Math.min(mProximitySensor.getMaximumRange(),
- TYPICAL_PROXIMITY_THRESHOLD);
- }
- }
-
private float clampScreenBrightnessForVr(float value) {
return MathUtils.constrain(
value, mScreenBrightnessForVrRangeMinimum,
@@ -2223,98 +2113,6 @@
private final Runnable mCleanListener = this::sendUpdatePowerState;
- private void setProximitySensorEnabled(boolean enable) {
- if (enable) {
- if (!mProximitySensorEnabled) {
- // Register the listener.
- // Proximity sensor state already cleared initially.
- mProximitySensorEnabled = true;
- mIgnoreProximityUntilChanged = false;
- mSensorManager.registerListener(mProximitySensorListener, mProximitySensor,
- SensorManager.SENSOR_DELAY_NORMAL, mHandler);
- }
- } else {
- if (mProximitySensorEnabled) {
- // Unregister the listener.
- // Clear the proximity sensor state for next time.
- mProximitySensorEnabled = false;
- mProximity = PROXIMITY_UNKNOWN;
- mIgnoreProximityUntilChanged = false;
- mPendingProximity = PROXIMITY_UNKNOWN;
- mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);
- mSensorManager.unregisterListener(mProximitySensorListener);
- // release wake lock(must be last)
- boolean proxDebounceSuspendBlockerReleased =
- mWakelockController.releaseWakelock(
- WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE);
- if (proxDebounceSuspendBlockerReleased) {
- mPendingProximityDebounceTime = -1;
- }
- }
- }
- }
-
- private void handleProximitySensorEvent(long time, boolean positive) {
- if (mProximitySensorEnabled) {
- if (mPendingProximity == PROXIMITY_NEGATIVE && !positive) {
- return; // no change
- }
- if (mPendingProximity == PROXIMITY_POSITIVE && positive) {
- return; // no change
- }
-
- // Only accept a proximity sensor reading if it remains
- // stable for the entire debounce delay. We hold a wake lock while
- // debouncing the sensor.
- mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);
- if (positive) {
- mPendingProximity = PROXIMITY_POSITIVE;
- mPendingProximityDebounceTime = time + PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY;
- mWakelockController.acquireWakelock(
- WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE); // acquire wake lock
- } else {
- mPendingProximity = PROXIMITY_NEGATIVE;
- mPendingProximityDebounceTime = time + PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY;
- mWakelockController.acquireWakelock(
- WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE); // acquire wake lock
- }
-
- // Debounce the new sensor reading.
- debounceProximitySensor();
- }
- }
-
- private void debounceProximitySensor() {
- if (mProximitySensorEnabled
- && mPendingProximity != PROXIMITY_UNKNOWN
- && mPendingProximityDebounceTime >= 0) {
- final long now = mClock.uptimeMillis();
- if (mPendingProximityDebounceTime <= now) {
- if (mProximity != mPendingProximity) {
- // if the status of the sensor changed, stop ignoring.
- mIgnoreProximityUntilChanged = false;
- Slog.i(mTag, "No longer ignoring proximity [" + mPendingProximity + "]");
- }
- // Sensor reading accepted. Apply the change then release the wake lock.
- mProximity = mPendingProximity;
- updatePowerState();
- // (must be last)
- boolean proxDebounceSuspendBlockerReleased =
- mWakelockController.releaseWakelock(
- WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE);
- if (proxDebounceSuspendBlockerReleased) {
- mPendingProximityDebounceTime = -1;
- }
-
- } else {
- // Need to wait a little longer.
- // Debounce again later. We continue holding a wake lock while waiting.
- Message msg = mHandler.obtainMessage(MSG_PROXIMITY_SENSOR_DEBOUNCED);
- mHandler.sendMessageAtTime(msg, mPendingProximityDebounceTime);
- }
- }
- }
-
private void sendOnStateChangedWithWakelock() {
boolean wakeLockAcquired = mWakelockController.acquireWakelock(
WakelockController.WAKE_LOCK_STATE_CHANGED);
@@ -2439,7 +2237,7 @@
boolean hadUserDataPoint) {
final float brightnessInNits = convertToNits(brightness);
if (mPowerRequest.useAutoBrightness && brightnessInNits >= 0.0f
- && mAutomaticBrightnessController != null) {
+ && mAutomaticBrightnessController != null && mBrightnessTracker != null) {
// We only want to track changes on devices that can actually map the display backlight
// values into a physical brightness unit since the value provided by the API is in
// nits and not using the arbitrary backlight units.
@@ -2459,39 +2257,6 @@
return mAutomaticBrightnessController.convertToNits(brightness);
}
- @GuardedBy("mLock")
- private void updatePendingProximityRequestsLocked() {
- mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked;
- mPendingWaitForNegativeProximityLocked = false;
-
- if (mIgnoreProximityUntilChanged) {
- // Also, lets stop waiting for negative proximity if we're ignoring it.
- mWaitingForNegativeProximity = false;
- }
- }
-
- private void ignoreProximitySensorUntilChangedInternal() {
- if (!mIgnoreProximityUntilChanged
- && mProximity == PROXIMITY_POSITIVE) {
- // Only ignore if it is still reporting positive (near)
- mIgnoreProximityUntilChanged = true;
- Slog.i(mTag, "Ignoring proximity");
- updatePowerState();
- }
- }
-
- private void sendOnProximityPositiveWithWakelock() {
- mWakelockController.acquireWakelock(WakelockController.WAKE_LOCK_PROXIMITY_POSITIVE);
- mHandler.post(mWakelockController.getOnProximityPositiveRunnable());
- }
-
-
- private void sendOnProximityNegativeWithWakelock() {
- mWakelockController.acquireWakelock(WakelockController.WAKE_LOCK_PROXIMITY_NEGATIVE);
- mHandler.post(mWakelockController.getOnProximityNegativeRunnable());
- }
-
-
@Override
public void dump(final PrintWriter pw) {
synchronized (mLock) {
@@ -2505,8 +2270,6 @@
pw.println(" mDisplayReadyLocked=" + mDisplayReadyLocked);
pw.println(" mPendingRequestLocked=" + mPendingRequestLocked);
pw.println(" mPendingRequestChangedLocked=" + mPendingRequestChangedLocked);
- pw.println(" mPendingWaitForNegativeProximityLocked="
- + mPendingWaitForNegativeProximityLocked);
pw.println(" mPendingUpdatePowerStateLocked=" + mPendingUpdatePowerStateLocked);
}
@@ -2541,7 +2304,6 @@
}
pw.println(" mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig);
pw.println(" mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig);
-
mHandler.runWithScissors(() -> dumpLocal(pw), 1000);
}
@@ -2549,15 +2311,6 @@
pw.println();
pw.println("Display Power Controller Thread State:");
pw.println(" mPowerRequest=" + mPowerRequest);
- pw.println(" mWaitingForNegativeProximity=" + mWaitingForNegativeProximity);
- pw.println(" mProximitySensor=" + mProximitySensor);
- pw.println(" mProximitySensorEnabled=" + mProximitySensorEnabled);
- pw.println(" mProximityThreshold=" + mProximityThreshold);
- pw.println(" mProximity=" + proximityToString(mProximity));
- pw.println(" mPendingProximity=" + proximityToString(mPendingProximity));
- pw.println(" mPendingProximityDebounceTime="
- + TimeUtils.formatUptime(mPendingProximityDebounceTime));
- pw.println(" mScreenOffBecauseOfProximity=" + mScreenOffBecauseOfProximity);
pw.println(" mLastUserSetScreenBrightness=" + mLastUserSetScreenBrightness);
pw.println(" mPendingScreenBrightnessSetting="
+ mPendingScreenBrightnessSetting);
@@ -2629,21 +2382,13 @@
if (mWakelockController != null) {
mWakelockController.dumpLocal(pw);
}
- }
- private static String proximityToString(int state) {
- switch (state) {
- case PROXIMITY_UNKNOWN:
- return "Unknown";
- case PROXIMITY_NEGATIVE:
- return "Negative";
- case PROXIMITY_POSITIVE:
- return "Positive";
- default:
- return Integer.toString(state);
+ if (mDisplayPowerProximityStateController != null) {
+ mDisplayPowerProximityStateController.dumpLocal(pw);
}
}
+
private static String reportedToPolicyToString(int state) {
switch (state) {
case REPORTED_TO_POLICY_SCREEN_OFF:
@@ -2793,10 +2538,6 @@
updatePowerState();
break;
- case MSG_PROXIMITY_SENSOR_DEBOUNCED:
- debounceProximitySensor();
- break;
-
case MSG_SCREEN_ON_UNBLOCKED:
if (mPendingScreenOnUnblocker == msg.obj) {
unblockScreenOn();
@@ -2825,10 +2566,6 @@
updatePowerState();
break;
- case MSG_IGNORE_PROXIMITY:
- ignoreProximitySensorUntilChangedInternal();
- break;
-
case MSG_STOP:
cleanupHandlerThreadAfterStop();
break;
@@ -2858,23 +2595,6 @@
}
}
- private final SensorEventListener mProximitySensorListener = new SensorEventListener() {
- @Override
- public void onSensorChanged(SensorEvent event) {
- if (mProximitySensorEnabled) {
- final long time = mClock.uptimeMillis();
- final float distance = event.values[0];
- boolean positive = distance >= 0.0f && distance < mProximityThreshold;
- handleProximitySensorEvent(time, positive);
- }
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- // Not used.
- }
- };
-
private final class SettingsObserver extends ContentObserver {
SettingsObserver(Handler handler) {
@@ -2964,6 +2684,15 @@
DisplayPowerCallbacks displayPowerCallbacks) {
return new WakelockController(displayId, displayPowerCallbacks);
}
+
+ DisplayPowerProximityStateController getDisplayPowerProximityStateController(
+ WakelockController wakelockController, DisplayDeviceConfig displayDeviceConfig,
+ Looper looper, Runnable nudgeUpdatePowerState,
+ int displayId, SensorManager sensorManager) {
+ return new DisplayPowerProximityStateController(wakelockController, displayDeviceConfig,
+ looper, nudgeUpdatePowerState,
+ displayId, sensorManager);
+ }
}
static class CachedBrightnessInfo {
diff --git a/services/core/java/com/android/server/display/DisplayPowerProximityStateController.java b/services/core/java/com/android/server/display/DisplayPowerProximityStateController.java
new file mode 100644
index 0000000..5b64dd5
--- /dev/null
+++ b/services/core/java/com/android/server/display/DisplayPowerProximityStateController.java
@@ -0,0 +1,476 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.display.DisplayManagerInternal;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.util.TimeUtils;
+import android.view.Display;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.display.utils.SensorUtils;
+
+import java.io.PrintWriter;
+
+/**
+ * Maintains the proximity state of the display.
+ * Internally listens for proximity updates and schedules a power state update when the proximity
+ * state changes.
+ */
+public final class DisplayPowerProximityStateController {
+ private static final int MSG_PROXIMITY_SENSOR_DEBOUNCED = 1;
+ private static final int MSG_IGNORE_PROXIMITY = 2;
+
+ private static final int PROXIMITY_UNKNOWN = -1;
+ private static final int PROXIMITY_NEGATIVE = 0;
+ private static final int PROXIMITY_POSITIVE = 1;
+
+ private static final boolean DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT = false;
+ // Proximity sensor debounce delay in milliseconds for positive transitions.
+ private static final int PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY = 0;
+ // Proximity sensor debounce delay in milliseconds for negative transitions.
+ private static final int PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY = 250;
+ // Trigger proximity if distance is less than 5 cm.
+ private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f;
+
+ private final String mTag;
+ // A lock to handle the deadlock and race conditions.
+ private final Object mLock = new Object();
+ // The manager which lets us access the device's ProximitySensor
+ private final SensorManager mSensorManager;
+ // An entity which manages the wakelocks.
+ private final WakelockController mWakelockController;
+ // A handler to process all the events on this thread in a synchronous manner
+ private final DisplayPowerProximityStateHandler mHandler;
+ // A runnable to execute the utility to update the power state.
+ private final Runnable mNudgeUpdatePowerState;
+ // A listener which listen's to the events emitted by the proximity sensor.
+ private final SensorEventListener mProximitySensorListener = new SensorEventListener() {
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ if (mProximitySensorEnabled) {
+ final long time = SystemClock.uptimeMillis();
+ final float distance = event.values[0];
+ boolean positive = distance >= 0.0f && distance < mProximityThreshold;
+ handleProximitySensorEvent(time, positive);
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // Not used.
+ }
+ };
+
+ // The proximity sensor, or null if not available or needed.
+ private Sensor mProximitySensor;
+
+ // The configurations for the associated display
+ private DisplayDeviceConfig mDisplayDeviceConfig;
+
+ // True if a request has been made to wait for the proximity sensor to go negative.
+ @GuardedBy("mLock")
+ private boolean mPendingWaitForNegativeProximityLocked;
+
+ // True if the device should wait for negative proximity sensor before
+ // waking up the screen. This is set to false as soon as a negative
+ // proximity sensor measurement is observed or when the device is forced to
+ // go to sleep by the user. While true, the screen remains off.
+ private boolean mWaitingForNegativeProximity;
+
+ // True if the device should not take into account the proximity sensor
+ // until either the proximity sensor state changes, or there is no longer a
+ // request to listen to proximity sensor.
+ private boolean mIgnoreProximityUntilChanged;
+
+ // Set to true if the proximity sensor listener has been registered
+ // with the sensor manager.
+ private boolean mProximitySensorEnabled;
+
+ // The raw non-debounced proximity sensor state.
+ private int mPendingProximity = PROXIMITY_UNKNOWN;
+
+ // -1 if fully debounced. Else, represents the time in ms when the debounce suspend blocker will
+ // be removed. Applies for both positive and negative proximity flips.
+ private long mPendingProximityDebounceTime = -1;
+
+ // True if the screen was turned off because of the proximity sensor.
+ // When the screen turns on again, we report user activity to the power manager.
+ private boolean mScreenOffBecauseOfProximity;
+
+ // The debounced proximity sensor state.
+ private int mProximity = PROXIMITY_UNKNOWN;
+
+ // The actual proximity sensor threshold value.
+ private float mProximityThreshold;
+
+ // A flag representing if the ramp is to be skipped when the proximity changes from positive
+ // to negative
+ private boolean mSkipRampBecauseOfProximityChangeToNegative = false;
+
+ // The DisplayId of the associated Logical Display.
+ private int mDisplayId;
+
+ /**
+ * Create a new instance of DisplayPowerProximityStateController.
+ *
+ * @param wakeLockController WakelockController used to acquire/release wakelocks
+ * @param displayDeviceConfig DisplayDeviceConfig instance from which the configs(Proximity
+ * Sensor) are to be loaded
+ * @param looper A looper onto which the handler is to be associated.
+ * @param nudgeUpdatePowerState A runnable to execute the utility to update the power state
+ * @param displayId The DisplayId of the associated Logical Display.
+ * @param sensorManager The manager which lets us access the display's ProximitySensor
+ */
+ public DisplayPowerProximityStateController(
+ WakelockController wakeLockController, DisplayDeviceConfig displayDeviceConfig,
+ Looper looper,
+ Runnable nudgeUpdatePowerState, int displayId, SensorManager sensorManager) {
+ mWakelockController = wakeLockController;
+ mHandler = new DisplayPowerProximityStateHandler(looper);
+ mNudgeUpdatePowerState = nudgeUpdatePowerState;
+ mDisplayDeviceConfig = displayDeviceConfig;
+ mDisplayId = displayId;
+ mTag = "DisplayPowerProximityStateController[" + mDisplayId + "]";
+ mSensorManager = sensorManager;
+ loadProximitySensor();
+ }
+
+ /**
+ * Manages the pending state of the proximity.
+ */
+ public void updatePendingProximityRequestsLocked() {
+ synchronized (mLock) {
+ mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked;
+ mPendingWaitForNegativeProximityLocked = false;
+
+ if (mIgnoreProximityUntilChanged) {
+ // Also, lets stop waiting for negative proximity if we're ignoring it.
+ mWaitingForNegativeProximity = false;
+ }
+ }
+ }
+
+ /**
+ * Clean up all resources that are accessed via the {@link #mHandler} thread.
+ */
+ public void cleanup() {
+ setProximitySensorEnabled(false);
+ }
+
+ /**
+ * Returns true if the proximity sensor screen-off function is available.
+ */
+ public boolean isProximitySensorAvailable() {
+ return mProximitySensor != null;
+ }
+
+ /**
+ * Sets the flag to indicate that the system is waiting for the negative proximity event
+ */
+ public boolean setPendingWaitForNegativeProximityLocked(
+ boolean requestWaitForNegativeProximity) {
+ synchronized (mLock) {
+ if (requestWaitForNegativeProximity
+ && !mPendingWaitForNegativeProximityLocked) {
+ mPendingWaitForNegativeProximityLocked = true;
+ return true;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Updates the proximity state of the display, based on the newly received DisplayPowerRequest
+ * and the target display state
+ */
+ public void updateProximityState(
+ DisplayManagerInternal.DisplayPowerRequest displayPowerRequest,
+ int displayState) {
+ mSkipRampBecauseOfProximityChangeToNegative = false;
+ if (mProximitySensor != null) {
+ if (displayPowerRequest.useProximitySensor && displayState != Display.STATE_OFF) {
+ // At this point the policy says that the screen should be on, but we've been
+ // asked to listen to the prox sensor to adjust the display state, so lets make
+ // sure the sensor is on.
+ setProximitySensorEnabled(true);
+ if (!mScreenOffBecauseOfProximity
+ && mProximity == PROXIMITY_POSITIVE
+ && !mIgnoreProximityUntilChanged) {
+ // Prox sensor already reporting "near" so we should turn off the screen.
+ // Also checked that we aren't currently set to ignore the proximity sensor
+ // temporarily.
+ mScreenOffBecauseOfProximity = true;
+ sendOnProximityPositiveWithWakelock();
+ }
+ } else if (mWaitingForNegativeProximity
+ && mScreenOffBecauseOfProximity
+ && mProximity == PROXIMITY_POSITIVE
+ && displayState != Display.STATE_OFF) {
+ // The policy says that we should have the screen on, but it's off due to the prox
+ // and we've been asked to wait until the screen is far from the user to turn it
+ // back on. Let keep the prox sensor on so we can tell when it's far again.
+ setProximitySensorEnabled(true);
+ } else {
+ // We haven't been asked to use the prox sensor and we're not waiting on the screen
+ // to turn back on...so let's shut down the prox sensor.
+ setProximitySensorEnabled(false);
+ mWaitingForNegativeProximity = false;
+ }
+
+ if (mScreenOffBecauseOfProximity
+ && (mProximity != PROXIMITY_POSITIVE || mIgnoreProximityUntilChanged)) {
+ // The screen *was* off due to prox being near, but now it's "far" so lets turn
+ // the screen back on. Also turn it back on if we've been asked to ignore the
+ // prox sensor temporarily.
+ mScreenOffBecauseOfProximity = false;
+ mSkipRampBecauseOfProximityChangeToNegative = true;
+ sendOnProximityNegativeWithWakelock();
+ }
+ } else {
+ mWaitingForNegativeProximity = false;
+ mIgnoreProximityUntilChanged = false;
+ }
+ }
+
+ /**
+ * A utility to check if the brightness change ramp is to be skipped because the proximity was
+ * changed from positive to negative.
+ */
+ public boolean shouldSkipRampBecauseOfProximityChangeToNegative() {
+ return mSkipRampBecauseOfProximityChangeToNegative;
+ }
+
+ /**
+ * Represents of the screen is currently turned off because of the proximity state.
+ */
+ public boolean isScreenOffBecauseOfProximity() {
+ return mScreenOffBecauseOfProximity;
+ }
+
+ /**
+ * Ignores the proximity sensor until the sensor state changes, but only if the sensor is
+ * currently enabled and forcing the screen to be dark.
+ */
+ public void ignoreProximitySensorUntilChanged() {
+ mHandler.sendEmptyMessage(MSG_IGNORE_PROXIMITY);
+ }
+
+ /**
+ * This adjusts the state of this class when a change in the DisplayDevice is detected.
+ */
+ public void notifyDisplayDeviceChanged(DisplayDeviceConfig displayDeviceConfig) {
+ this.mDisplayDeviceConfig = displayDeviceConfig;
+ loadProximitySensor();
+ }
+
+ /**
+ * Used to dump the state.
+ *
+ * @param pw The PrintWriter used to dump the state.
+ */
+ public void dumpLocal(PrintWriter pw) {
+ pw.println();
+ pw.println("DisplayPowerProximityStateController:");
+ synchronized (mLock) {
+ pw.println(" mPendingWaitForNegativeProximityLocked="
+ + mPendingWaitForNegativeProximityLocked);
+ }
+ pw.println(" mDisplayId=" + mDisplayId);
+ pw.println(" mWaitingForNegativeProximity=" + mWaitingForNegativeProximity);
+ pw.println(" mIgnoreProximityUntilChanged=" + mIgnoreProximityUntilChanged);
+ pw.println(" mProximitySensor=" + mProximitySensor);
+ pw.println(" mProximitySensorEnabled=" + mProximitySensorEnabled);
+ pw.println(" mProximityThreshold=" + mProximityThreshold);
+ pw.println(" mProximity=" + proximityToString(mProximity));
+ pw.println(" mPendingProximity=" + proximityToString(mPendingProximity));
+ pw.println(" mPendingProximityDebounceTime="
+ + TimeUtils.formatUptime(mPendingProximityDebounceTime));
+ pw.println(" mScreenOffBecauseOfProximity=" + mScreenOffBecauseOfProximity);
+ pw.println(" mSkipRampBecauseOfProximityChangeToNegative="
+ + mSkipRampBecauseOfProximityChangeToNegative);
+ }
+
+ private void ignoreProximitySensorUntilChangedInternal() {
+ if (!mIgnoreProximityUntilChanged
+ && mProximity == PROXIMITY_POSITIVE) {
+ // Only ignore if it is still reporting positive (near)
+ mIgnoreProximityUntilChanged = true;
+ Slog.i(mTag, "Ignoring proximity");
+ mNudgeUpdatePowerState.run();
+ }
+ }
+
+ private void sendOnProximityPositiveWithWakelock() {
+ mWakelockController.acquireWakelock(WakelockController.WAKE_LOCK_PROXIMITY_POSITIVE);
+ mHandler.post(mWakelockController.getOnProximityPositiveRunnable());
+ }
+
+ private void sendOnProximityNegativeWithWakelock() {
+ mWakelockController.acquireWakelock(WakelockController.WAKE_LOCK_PROXIMITY_NEGATIVE);
+ mHandler.post(mWakelockController.getOnProximityNegativeRunnable());
+ }
+
+ private void loadProximitySensor() {
+ if (DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT) {
+ return;
+ }
+ final DisplayDeviceConfig.SensorData proxSensor =
+ mDisplayDeviceConfig.getProximitySensor();
+ final int fallbackType = mDisplayId == Display.DEFAULT_DISPLAY
+ ? Sensor.TYPE_PROXIMITY : SensorUtils.NO_FALLBACK;
+ mProximitySensor = SensorUtils.findSensor(mSensorManager, proxSensor.type, proxSensor.name,
+ fallbackType);
+ if (mProximitySensor != null) {
+ mProximityThreshold = Math.min(mProximitySensor.getMaximumRange(),
+ TYPICAL_PROXIMITY_THRESHOLD);
+ }
+ }
+
+ private void setProximitySensorEnabled(boolean enable) {
+ if (enable) {
+ if (!mProximitySensorEnabled) {
+ // Register the listener.
+ // Proximity sensor state already cleared initially.
+ mProximitySensorEnabled = true;
+ mIgnoreProximityUntilChanged = false;
+ mSensorManager.registerListener(mProximitySensorListener, mProximitySensor,
+ SensorManager.SENSOR_DELAY_NORMAL, mHandler);
+ }
+ } else {
+ if (mProximitySensorEnabled) {
+ // Unregister the listener.
+ // Clear the proximity sensor state for next time.
+ mProximitySensorEnabled = false;
+ mProximity = PROXIMITY_UNKNOWN;
+ mIgnoreProximityUntilChanged = false;
+ mPendingProximity = PROXIMITY_UNKNOWN;
+ mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);
+ mSensorManager.unregisterListener(mProximitySensorListener);
+ // release wake lock(must be last)
+ boolean proxDebounceSuspendBlockerReleased =
+ mWakelockController.releaseWakelock(
+ WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE);
+ if (proxDebounceSuspendBlockerReleased) {
+ mPendingProximityDebounceTime = -1;
+ }
+ }
+ }
+ }
+
+ private void handleProximitySensorEvent(long time, boolean positive) {
+ if (mProximitySensorEnabled) {
+ if (mPendingProximity == PROXIMITY_NEGATIVE && !positive) {
+ return; // no change
+ }
+ if (mPendingProximity == PROXIMITY_POSITIVE && positive) {
+ return; // no change
+ }
+
+ // Only accept a proximity sensor reading if it remains
+ // stable for the entire debounce delay. We hold a wake lock while
+ // debouncing the sensor.
+ mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);
+ if (positive) {
+ mPendingProximity = PROXIMITY_POSITIVE;
+ mPendingProximityDebounceTime = time + PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY;
+ mWakelockController.acquireWakelock(
+ WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE); // acquire wake lock
+ } else {
+ mPendingProximity = PROXIMITY_NEGATIVE;
+ mPendingProximityDebounceTime = time + PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY;
+ mWakelockController.acquireWakelock(
+ WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE); // acquire wake lock
+ }
+
+ // Debounce the new sensor reading.
+ debounceProximitySensor();
+ }
+ }
+
+ private void debounceProximitySensor() {
+ if (mProximitySensorEnabled
+ && mPendingProximity != PROXIMITY_UNKNOWN
+ && mPendingProximityDebounceTime >= 0) {
+ final long now = SystemClock.uptimeMillis();
+ if (mPendingProximityDebounceTime <= now) {
+ if (mProximity != mPendingProximity) {
+ // if the status of the sensor changed, stop ignoring.
+ mIgnoreProximityUntilChanged = false;
+ Slog.i(mTag, "No longer ignoring proximity [" + mPendingProximity + "]");
+ }
+ // Sensor reading accepted. Apply the change then release the wake lock.
+ mProximity = mPendingProximity;
+ mNudgeUpdatePowerState.run();
+ // (must be last)
+ boolean proxDebounceSuspendBlockerReleased =
+ mWakelockController.releaseWakelock(
+ WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE);
+ if (proxDebounceSuspendBlockerReleased) {
+ mPendingProximityDebounceTime = -1;
+ }
+
+ } else {
+ // Need to wait a little longer.
+ // Debounce again later. We continue holding a wake lock while waiting.
+ Message msg = mHandler.obtainMessage(MSG_PROXIMITY_SENSOR_DEBOUNCED);
+ mHandler.sendMessageAtTime(msg, mPendingProximityDebounceTime);
+ }
+ }
+ }
+
+ private class DisplayPowerProximityStateHandler extends Handler {
+ DisplayPowerProximityStateHandler(Looper looper) {
+ super(looper, null, true /*async*/);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_PROXIMITY_SENSOR_DEBOUNCED:
+ debounceProximitySensor();
+ break;
+
+ case MSG_IGNORE_PROXIMITY:
+ ignoreProximitySensorUntilChangedInternal();
+ break;
+ }
+ }
+ }
+
+ private String proximityToString(int state) {
+ switch (state) {
+ case PROXIMITY_UNKNOWN:
+ return "Unknown";
+ case PROXIMITY_NEGATIVE:
+ return "Negative";
+ case PROXIMITY_POSITIVE:
+ return "Positive";
+ default:
+ return Integer.toString(state);
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 87327cb..facc6b2 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -491,10 +491,6 @@
}
}
- private ComponentName getActiveDreamComponentInternal(boolean doze) {
- return chooseDreamForUser(doze, ActivityManager.getCurrentUser());
- }
-
/**
* If doze is true, returns the doze component for the user.
* Otherwise, returns the system dream component, if present.
@@ -647,7 +643,7 @@
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, DREAM_WAKE_LOCK_TAG);
final Binder dreamToken = mCurrentDream.token;
mHandler.post(wakeLock.wrap(() -> {
- mAtmInternal.notifyDreamStateChanged(true);
+ mAtmInternal.notifyActiveDreamChanged(name);
mController.startDream(dreamToken, name, isPreviewMode, canDoze, userId, wakeLock,
mDreamOverlayServiceName, reason);
}));
@@ -672,7 +668,7 @@
@GuardedBy("mLock")
private void cleanupDreamLocked() {
- mHandler.post(() -> mAtmInternal.notifyDreamStateChanged(false /*dreaming*/));
+ mHandler.post(() -> mAtmInternal.notifyActiveDreamChanged(null));
if (mCurrentDream == null) {
return;
@@ -1013,11 +1009,6 @@
}
@Override
- public ComponentName getActiveDreamComponent(boolean doze) {
- return getActiveDreamComponentInternal(doze);
- }
-
- @Override
public void requestDream() {
requestDreamInternal();
}
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
index 2817d1b..28dc318 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -26,6 +26,7 @@
import android.graphics.fonts.FontManager;
import android.graphics.fonts.FontUpdateRequest;
import android.graphics.fonts.SystemFonts;
+import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.os.ResultReceiver;
import android.os.SharedMemory;
@@ -35,8 +36,10 @@
import android.util.AndroidException;
import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
+import android.util.Log;
import android.util.Slog;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.graphics.fonts.IFontManager;
import com.android.internal.security.VerityUtils;
@@ -47,7 +50,9 @@
import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.PrintWriter;
import java.nio.ByteBuffer;
import java.nio.DirectByteBuffer;
@@ -155,9 +160,30 @@
}
private static class FsverityUtilImpl implements UpdatableFontDir.FsverityUtil {
+
+ private final String[] mDerCertPaths;
+
+ FsverityUtilImpl(String[] derCertPaths) {
+ mDerCertPaths = derCertPaths;
+ }
+
@Override
- public boolean hasFsverity(String filePath) {
- return VerityUtils.hasFsverity(filePath);
+ public boolean isFromTrustedProvider(String fontPath, byte[] pkcs7Signature) {
+ final byte[] digest = VerityUtils.getFsverityDigest(fontPath);
+ if (digest == null) {
+ Log.w(TAG, "Failed to get fs-verity digest for " + fontPath);
+ return false;
+ }
+ for (String certPath : mDerCertPaths) {
+ try (InputStream is = new FileInputStream(certPath)) {
+ if (VerityUtils.verifyPkcs7DetachedSignature(pkcs7Signature, digest, is)) {
+ return true;
+ }
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to read certificate file: " + certPath);
+ }
+ }
+ return false;
}
@Override
@@ -175,11 +201,15 @@
@NonNull
private final Context mContext;
+ private final boolean mIsSafeMode;
+
private final Object mUpdatableFontDirLock = new Object();
+ private String mDebugCertFilePath = null;
+
@GuardedBy("mUpdatableFontDirLock")
@Nullable
- private final UpdatableFontDir mUpdatableFontDir;
+ private UpdatableFontDir mUpdatableFontDir;
// mSerializedFontMapLock can be acquired while holding mUpdatableFontDirLock.
// mUpdatableFontDirLock should not be newly acquired while holding mSerializedFontMapLock.
@@ -195,22 +225,43 @@
UpdatableFontDir.deleteAllFiles(new File(FONT_FILES_DIR), new File(CONFIG_XML_FILE));
}
mContext = context;
- mUpdatableFontDir = createUpdatableFontDir(safeMode);
+ mIsSafeMode = safeMode;
initialize();
}
@Nullable
- private static UpdatableFontDir createUpdatableFontDir(boolean safeMode) {
+ private UpdatableFontDir createUpdatableFontDir() {
// Never read updatable font files in safe mode.
- if (safeMode) return null;
+ if (mIsSafeMode) return null;
// If apk verity is supported, fs-verity should be available.
if (!VerityUtils.isFsVeritySupported()) return null;
+
+ String[] certs = mContext.getResources().getStringArray(
+ R.array.config_fontManagerServiceCerts);
+
+ if (mDebugCertFilePath != null && (Build.IS_USERDEBUG || Build.IS_ENG)) {
+ String[] tmp = new String[certs.length + 1];
+ System.arraycopy(certs, 0, tmp, 0, certs.length);
+ tmp[certs.length] = mDebugCertFilePath;
+ certs = tmp;
+ }
+
return new UpdatableFontDir(new File(FONT_FILES_DIR), new OtfFontFileParser(),
- new FsverityUtilImpl(), new File(CONFIG_XML_FILE));
+ new FsverityUtilImpl(certs), new File(CONFIG_XML_FILE));
+ }
+
+ /**
+ * Add debug certificate to the cert list. This must be called only on userdebug/eng
+ * build.
+ * @param debugCertPath a debug certificate file path
+ */
+ public void addDebugCertificate(@Nullable String debugCertPath) {
+ mDebugCertFilePath = debugCertPath;
}
private void initialize() {
synchronized (mUpdatableFontDirLock) {
+ mUpdatableFontDir = createUpdatableFontDir();
if (mUpdatableFontDir == null) {
setSerializedFontMap(serializeSystemServerFontMap());
return;
@@ -233,12 +284,12 @@
/* package */ void update(int baseVersion, List<FontUpdateRequest> requests)
throws SystemFontException {
- if (mUpdatableFontDir == null) {
- throw new SystemFontException(
- FontManager.RESULT_ERROR_FONT_UPDATER_DISABLED,
- "The font updater is disabled.");
- }
synchronized (mUpdatableFontDirLock) {
+ if (mUpdatableFontDir == null) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_FONT_UPDATER_DISABLED,
+ "The font updater is disabled.");
+ }
// baseVersion == -1 only happens from shell command. This is filtered and treated as
// error from SystemApi call.
if (baseVersion != -1 && mUpdatableFontDir.getConfigVersion() != baseVersion) {
@@ -273,10 +324,10 @@
}
/* package */ Map<String, File> getFontFileMap() {
- if (mUpdatableFontDir == null) {
- return Collections.emptyMap();
- }
synchronized (mUpdatableFontDirLock) {
+ if (mUpdatableFontDir == null) {
+ return Collections.emptyMap();
+ }
return mUpdatableFontDir.getPostScriptMap();
}
}
@@ -302,10 +353,10 @@
* Returns an active system font configuration.
*/
public @NonNull FontConfig getSystemFontConfig() {
- if (mUpdatableFontDir == null) {
- return SystemFonts.getSystemPreinstalledFontConfig();
- }
synchronized (mUpdatableFontDirLock) {
+ if (mUpdatableFontDir == null) {
+ return SystemFonts.getSystemPreinstalledFontConfig();
+ }
return mUpdatableFontDir.getSystemFontConfig();
}
}
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
index 88145bd..4cd0d6e 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
@@ -28,6 +28,7 @@
import android.graphics.fonts.FontVariationAxis;
import android.graphics.fonts.SystemFonts;
import android.os.Binder;
+import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.ShellCommand;
@@ -103,6 +104,10 @@
w.println("update-family [family definition XML path]");
w.println(" Update font families with the new definitions.");
w.println();
+ w.println("install-debug-cert [cert file path]");
+ w.println(" Install debug certificate file. This command can be used only on userdebug");
+ w.println(" or eng device with root user.");
+ w.println();
w.println("clear");
w.println(" Remove all installed font files and reset to the initial state.");
w.println();
@@ -322,6 +327,33 @@
return 0;
}
+ private int installCert(ShellCommand shell) throws SystemFontException {
+ if (!(Build.IS_USERDEBUG || Build.IS_ENG)) {
+ throw new SecurityException("Only userdebug/eng device can add debug certificate");
+ }
+ if (Binder.getCallingUid() != Process.ROOT_UID) {
+ throw new SecurityException("Only root can add debug certificate");
+ }
+
+ String certPath = shell.getNextArg();
+ if (certPath == null) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_INVALID_DEBUG_CERTIFICATE,
+ "Cert file path argument is required.");
+ }
+ File file = new File(certPath);
+ if (!file.isFile()) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_INVALID_DEBUG_CERTIFICATE,
+ "Cert file (" + file + ") is not found");
+ }
+
+ mService.addDebugCertificate(certPath);
+ mService.restart();
+ shell.getOutPrintWriter().println("Success");
+ return 0;
+ }
+
private int update(ShellCommand shell) throws SystemFontException {
String fontPath = shell.getNextArg();
if (fontPath == null) {
@@ -494,6 +526,8 @@
return restart(shell);
case "status":
return status(shell);
+ case "install-debug-cert":
+ return installCert(shell);
default:
return shell.handleDefaultCommands(cmd);
}
diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
index 743b4d9..457d5b7 100644
--- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
+++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
@@ -40,6 +40,8 @@
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashMap;
@@ -59,6 +61,8 @@
private static final String TAG = "UpdatableFontDir";
private static final String RANDOM_DIR_PREFIX = "~~";
+ private static final String FONT_SIGNATURE_FILE = "font.fsv_sig";
+
/** Interface to mock font file access in tests. */
interface FontFileParser {
String getPostScriptName(File file) throws IOException;
@@ -72,7 +76,7 @@
/** Interface to mock fs-verity in tests. */
interface FsverityUtil {
- boolean hasFsverity(String path);
+ boolean isFromTrustedProvider(String path, byte[] pkcs7Signature);
void setUpFsverity(String path, byte[] pkcs7Signature) throws IOException;
@@ -188,12 +192,35 @@
FileUtils.deleteContentsAndDir(dir);
continue;
}
+
+ File signatureFile = new File(dir, FONT_SIGNATURE_FILE);
+ if (!signatureFile.exists()) {
+ Slog.i(TAG, "The signature file is missing.");
+ FileUtils.deleteContentsAndDir(dir);
+ continue;
+ }
+ byte[] signature;
+ try {
+ signature = Files.readAllBytes(Paths.get(signatureFile.getAbsolutePath()));
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to read signature file.");
+ return;
+ }
+
File[] files = dir.listFiles();
- if (files == null || files.length != 1) {
+ if (files == null || files.length != 2) {
Slog.e(TAG, "Unexpected files in dir: " + dir);
return;
}
- FontFileInfo fontFileInfo = validateFontFile(files[0]);
+
+ File fontFile;
+ if (files[0].equals(signatureFile)) {
+ fontFile = files[1];
+ } else {
+ fontFile = files[0];
+ }
+
+ FontFileInfo fontFileInfo = validateFontFile(fontFile, signature);
if (fontConfig == null) {
fontConfig = getSystemFontConfig();
}
@@ -359,9 +386,25 @@
} catch (ErrnoException e) {
throw new SystemFontException(
FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
- "Failed to change mode to 711", e);
+ "Failed to change font file mode to 644", e);
}
- FontFileInfo fontFileInfo = validateFontFile(newFontFile);
+ File signatureFile = new File(newDir, FONT_SIGNATURE_FILE);
+ try (FileOutputStream out = new FileOutputStream(signatureFile)) {
+ out.write(pkcs7Signature);
+ } catch (IOException e) {
+ // TODO: Do we need new error code for signature write failure?
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
+ "Failed to write font signature file to storage.", e);
+ }
+ try {
+ Os.chmod(signatureFile.getAbsolutePath(), 0600);
+ } catch (ErrnoException e) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
+ "Failed to change the signature file mode to 600", e);
+ }
+ FontFileInfo fontFileInfo = validateFontFile(newFontFile, pkcs7Signature);
// Try to create Typeface and treat as failure something goes wrong.
try {
@@ -478,8 +521,9 @@
* is higher than the currently used font.
*/
@NonNull
- private FontFileInfo validateFontFile(File file) throws SystemFontException {
- if (!mFsverityUtil.hasFsverity(file.getAbsolutePath())) {
+ private FontFileInfo validateFontFile(File file, byte[] pkcs7Signature)
+ throws SystemFontException {
+ if (!mFsverityUtil.isFromTrustedProvider(file.getAbsolutePath(), pkcs7Signature)) {
throw new SystemFontException(
FontManager.RESULT_ERROR_VERIFICATION_FAILURE,
"Font validation failed. Fs-verity is not enabled: " + file);
diff --git a/services/core/java/com/android/server/input/BatteryController.java b/services/core/java/com/android/server/input/BatteryController.java
index 9d4f181..c83fa2d 100644
--- a/services/core/java/com/android/server/input/BatteryController.java
+++ b/services/core/java/com/android/server/input/BatteryController.java
@@ -32,6 +32,7 @@
import android.os.UEventObserver;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
import android.view.InputDevice;
@@ -382,24 +383,28 @@
}
}
- public void dump(PrintWriter pw, String prefix) {
+ public void dump(PrintWriter pw) {
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
synchronized (mLock) {
- final String indent = prefix + " ";
- final String indent2 = indent + " ";
-
- pw.println(prefix + TAG + ":");
- pw.println(indent + "State: Polling = " + mIsPolling
+ ipw.println(TAG + ":");
+ ipw.increaseIndent();
+ ipw.println("State: Polling = " + mIsPolling
+ ", Interactive = " + mIsInteractive);
- pw.println(indent + "Listeners: " + mListenerRecords.size() + " battery listeners");
+ ipw.println("Listeners: " + mListenerRecords.size() + " battery listeners");
+ ipw.increaseIndent();
for (int i = 0; i < mListenerRecords.size(); i++) {
- pw.println(indent2 + i + ": " + mListenerRecords.valueAt(i));
+ ipw.println(i + ": " + mListenerRecords.valueAt(i));
}
+ ipw.decreaseIndent();
- pw.println(indent + "Device Monitors: " + mDeviceMonitors.size() + " monitors");
+ ipw.println("Device Monitors: " + mDeviceMonitors.size() + " monitors");
+ ipw.increaseIndent();
for (int i = 0; i < mDeviceMonitors.size(); i++) {
- pw.println(indent2 + i + ": " + mDeviceMonitors.valueAt(i));
+ ipw.println(i + ": " + mDeviceMonitors.valueAt(i));
}
+ ipw.decreaseIndent();
+ ipw.decreaseIndent();
}
}
diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java
index 7eb5a10..6234421 100644
--- a/services/core/java/com/android/server/input/InputManagerInternal.java
+++ b/services/core/java/com/android/server/input/InputManagerInternal.java
@@ -136,12 +136,6 @@
public abstract InputChannel createInputChannel(String inputChannelName);
/**
- * Pilfer pointers from the input channel with the given token so that ongoing gestures are
- * canceled for all other channels.
- */
- public abstract void pilferPointers(IBinder token);
-
- /**
* Increments keyboard backlight level if the device has an associated keyboard backlight
* {@see Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT}
*/
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 31f63d8..d15f68c 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -91,6 +91,7 @@
import android.provider.Settings.SettingNotFoundException;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
@@ -302,9 +303,9 @@
private final AdditionalDisplayInputProperties mCurrentDisplayProperties =
new AdditionalDisplayInputProperties();
@GuardedBy("mAdditionalDisplayInputPropertiesLock")
- private int mIconType = PointerIcon.TYPE_NOT_SPECIFIED;
+ private int mPointerIconType = PointerIcon.TYPE_NOT_SPECIFIED;
@GuardedBy("mAdditionalDisplayInputPropertiesLock")
- private PointerIcon mIcon;
+ private PointerIcon mPointerIcon;
// Holds all the registered gesture monitors that are implemented as spy windows. The spy
// windows are mapped by their InputChannel tokens.
@@ -2325,12 +2326,12 @@
throw new IllegalArgumentException("Use setCustomPointerIcon to set custom pointers");
}
synchronized (mAdditionalDisplayInputPropertiesLock) {
- mIcon = null;
- mIconType = iconType;
+ mPointerIcon = null;
+ mPointerIconType = iconType;
if (!mCurrentDisplayProperties.pointerIconVisible) return;
- mNative.setPointerIconType(mIconType);
+ mNative.setPointerIconType(mPointerIconType);
}
}
@@ -2339,12 +2340,12 @@
public void setCustomPointerIcon(PointerIcon icon) {
Objects.requireNonNull(icon);
synchronized (mAdditionalDisplayInputPropertiesLock) {
- mIconType = PointerIcon.TYPE_CUSTOM;
- mIcon = icon;
+ mPointerIconType = PointerIcon.TYPE_CUSTOM;
+ mPointerIcon = icon;
if (!mCurrentDisplayProperties.pointerIconVisible) return;
- mNative.setCustomPointerIcon(mIcon);
+ mNative.setCustomPointerIcon(mPointerIcon);
}
}
@@ -2676,80 +2677,93 @@
@EnforcePermission(Manifest.permission.BLUETOOTH)
@Override
public String getInputDeviceBluetoothAddress(int deviceId) {
+ super.getInputDeviceBluetoothAddress_enforcePermission();
+
return mNative.getBluetoothAddress(deviceId);
}
+ @EnforcePermission(Manifest.permission.MONITOR_INPUT)
+ @Override
+ public void pilferPointers(IBinder inputChannelToken) {
+ Objects.requireNonNull(inputChannelToken);
+ mNative.pilferPointers(inputChannelToken);
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
- pw.println("INPUT MANAGER (dumpsys input)\n");
+ ipw.println("INPUT MANAGER (dumpsys input)\n");
String dumpStr = mNative.dump();
if (dumpStr != null) {
pw.println(dumpStr);
}
- pw.println("Input Manager Service (Java) State:");
- dumpAssociations(pw, " " /*prefix*/);
- dumpSpyWindowGestureMonitors(pw, " " /*prefix*/);
- dumpDisplayInputPropertiesValues(pw, " " /*prefix*/);
- mBatteryController.dump(pw, " " /*prefix*/);
- mKeyboardBacklightController.dump(pw, " " /*prefix*/);
+ ipw.println("Input Manager Service (Java) State:");
+ ipw.increaseIndent();
+ dumpAssociations(ipw);
+ dumpSpyWindowGestureMonitors(ipw);
+ dumpDisplayInputPropertiesValues(ipw);
+ mBatteryController.dump(ipw);
+ mKeyboardBacklightController.dump(ipw);
}
- private void dumpAssociations(PrintWriter pw, String prefix) {
+ private void dumpAssociations(IndentingPrintWriter pw) {
if (!mStaticAssociations.isEmpty()) {
- pw.println(prefix + "Static Associations:");
+ pw.println("Static Associations:");
mStaticAssociations.forEach((k, v) -> {
- pw.print(prefix + " port: " + k);
+ pw.print(" port: " + k);
pw.println(" display: " + v);
});
}
synchronized (mAssociationsLock) {
if (!mRuntimeAssociations.isEmpty()) {
- pw.println(prefix + "Runtime Associations:");
+ pw.println("Runtime Associations:");
mRuntimeAssociations.forEach((k, v) -> {
- pw.print(prefix + " port: " + k);
+ pw.print(" port: " + k);
pw.println(" display: " + v);
});
}
if (!mUniqueIdAssociations.isEmpty()) {
- pw.println(prefix + "Unique Id Associations:");
+ pw.println("Unique Id Associations:");
mUniqueIdAssociations.forEach((k, v) -> {
- pw.print(prefix + " port: " + k);
+ pw.print(" port: " + k);
pw.println(" uniqueId: " + v);
});
}
}
}
- private void dumpSpyWindowGestureMonitors(PrintWriter pw, String prefix) {
+ private void dumpSpyWindowGestureMonitors(IndentingPrintWriter pw) {
synchronized (mInputMonitors) {
if (mInputMonitors.isEmpty()) return;
- pw.println(prefix + "Gesture Monitors (implemented as spy windows):");
+ pw.println("Gesture Monitors (implemented as spy windows):");
int i = 0;
for (final GestureMonitorSpyWindow monitor : mInputMonitors.values()) {
- pw.append(prefix + " " + i++ + ": ").println(monitor.dump());
+ pw.append(" " + i++ + ": ").println(monitor.dump());
}
}
}
- private void dumpDisplayInputPropertiesValues(PrintWriter pw, String prefix) {
+ private void dumpDisplayInputPropertiesValues(IndentingPrintWriter pw) {
synchronized (mAdditionalDisplayInputPropertiesLock) {
if (mAdditionalDisplayInputProperties.size() != 0) {
- pw.println(prefix + "mAdditionalDisplayInputProperties:");
+ pw.println("mAdditionalDisplayInputProperties:");
+ pw.increaseIndent();
for (int i = 0; i < mAdditionalDisplayInputProperties.size(); i++) {
- pw.println(prefix + " displayId: "
+ pw.println("displayId: "
+ mAdditionalDisplayInputProperties.keyAt(i));
final AdditionalDisplayInputProperties properties =
mAdditionalDisplayInputProperties.valueAt(i);
- pw.println(prefix + " pointerAcceleration: " + properties.pointerAcceleration);
- pw.println(prefix + " pointerIconVisible: " + properties.pointerIconVisible);
+ pw.println("pointerAcceleration: " + properties.pointerAcceleration);
+ pw.println("pointerIconVisible: " + properties.pointerIconVisible);
}
+ pw.decreaseIndent();
}
if (mOverriddenPointerDisplayId != Display.INVALID_DISPLAY) {
- pw.println(prefix + "mOverriddenPointerDisplayId: " + mOverriddenPointerDisplayId);
+ pw.println("mOverriddenPointerDisplayId: " + mOverriddenPointerDisplayId);
}
}
}
@@ -3775,11 +3789,6 @@
}
@Override
- public void pilferPointers(IBinder token) {
- mNative.pilferPointers(token);
- }
-
- @Override
public void incrementKeyboardBacklight(int deviceId) {
mKeyboardBacklightController.incrementKeyboardBacklight(deviceId);
}
@@ -3839,11 +3848,11 @@
if (properties.pointerIconVisible != mCurrentDisplayProperties.pointerIconVisible) {
mCurrentDisplayProperties.pointerIconVisible = properties.pointerIconVisible;
if (properties.pointerIconVisible) {
- if (mIconType == PointerIcon.TYPE_CUSTOM) {
- Objects.requireNonNull(mIcon);
- mNative.setCustomPointerIcon(mIcon);
+ if (mPointerIconType == PointerIcon.TYPE_CUSTOM) {
+ Objects.requireNonNull(mPointerIcon);
+ mNative.setCustomPointerIcon(mPointerIcon);
} else {
- mNative.setPointerIconType(mIconType);
+ mNative.setPointerIconType(mPointerIconType);
}
} else {
mNative.setPointerIconType(PointerIcon.TYPE_NULL);
diff --git a/services/core/java/com/android/server/input/KeyboardBacklightController.java b/services/core/java/com/android/server/input/KeyboardBacklightController.java
index e33f28c..b207e27 100644
--- a/services/core/java/com/android/server/input/KeyboardBacklightController.java
+++ b/services/core/java/com/android/server/input/KeyboardBacklightController.java
@@ -24,6 +24,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
@@ -216,12 +217,14 @@
return null;
}
- void dump(PrintWriter pw, String prefix) {
- pw.println(prefix + TAG + ": " + mKeyboardBacklights.size() + " keyboard backlights");
+ void dump(PrintWriter pw) {
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
+ ipw.println(TAG + ": " + mKeyboardBacklights.size() + " keyboard backlights");
+ ipw.increaseIndent();
for (int i = 0; i < mKeyboardBacklights.size(); i++) {
Light light = mKeyboardBacklights.get(i);
- pw.println(prefix + " " + i + ": { id: " + light.getId() + ", name: " + light.getName()
- + " }");
+ ipw.println(i + ": { id: " + light.getId() + ", name: " + light.getName() + " }");
}
+ ipw.decreaseIndent();
}
}
diff --git a/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java
index 8e6452b..4b040fa 100644
--- a/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java
+++ b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java
@@ -58,6 +58,7 @@
private static final String NODE_IMI = "imi";
private static final String ATTR_ID = "id";
private static final String ATTR_LABEL = "label";
+ private static final String ATTR_NAME_OVERRIDE = "nameOverride";
private static final String ATTR_ICON = "icon";
private static final String ATTR_IME_SUBTYPE_ID = "subtypeId";
private static final String ATTR_IME_SUBTYPE_LOCALE = "imeSubtypeLocale";
@@ -161,6 +162,7 @@
}
out.attributeInt(null, ATTR_ICON, subtype.getIconResId());
out.attributeInt(null, ATTR_LABEL, subtype.getNameResId());
+ out.attribute(null, ATTR_NAME_OVERRIDE, subtype.getNameOverride().toString());
out.attribute(null, ATTR_IME_SUBTYPE_LOCALE, subtype.getLocale());
out.attribute(null, ATTR_IME_SUBTYPE_LANGUAGE_TAG,
subtype.getLanguageTag());
@@ -243,6 +245,8 @@
}
final int icon = parser.getAttributeInt(null, ATTR_ICON);
final int label = parser.getAttributeInt(null, ATTR_LABEL);
+ final String untranslatableName = parser.getAttributeValue(null,
+ ATTR_NAME_OVERRIDE);
final String imeSubtypeLocale =
parser.getAttributeValue(null, ATTR_IME_SUBTYPE_LOCALE);
final String languageTag =
@@ -258,6 +262,7 @@
final InputMethodSubtype.InputMethodSubtypeBuilder
builder = new InputMethodSubtype.InputMethodSubtypeBuilder()
.setSubtypeNameResId(label)
+ .setSubtypeNameOverride(untranslatableName)
.setSubtypeIconResId(icon)
.setSubtypeLocale(imeSubtypeLocale)
.setLanguageTag(languageTag)
diff --git a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
index a4830be..1c7294f 100644
--- a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
+++ b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
@@ -18,9 +18,12 @@
import static android.view.InputDevice.SOURCE_STYLUS;
+import android.Manifest;
import android.annotation.AnyThread;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.UiThread;
+import android.hardware.input.InputManager;
import android.os.IBinder;
import android.os.Looper;
import android.util.Slog;
@@ -141,6 +144,7 @@
* input events and disposing the input event receiver.
* @return the handwriting session to send to the IME, or null if the request was invalid.
*/
+ @RequiresPermission(Manifest.permission.MONITOR_INPUT)
@UiThread
@Nullable
HandwritingSession startHandwritingSession(
@@ -169,7 +173,7 @@
}
if (DEBUG) Slog.d(TAG, "Starting handwriting session in display: " + mCurrentDisplayId);
- mInputManagerInternal.pilferPointers(mHandwritingSurface.getInputChannel().getToken());
+ InputManager.getInstance().pilferPointers(mHandwritingSurface.getInputChannel().getToken());
// Stop processing more events.
mHandwritingEventReceiver.dispose();
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index fcdb1f5..4d1c5ae 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3204,7 +3204,7 @@
void setInputMethodLocked(String id, int subtypeId) {
InputMethodInfo info = mMethodMap.get(id);
if (info == null) {
- throw new IllegalArgumentException("Unknown id: " + id);
+ throw getExceptionForUnknownImeId(id);
}
// See if we need to notify a subtype change within the same IME.
@@ -3946,12 +3946,25 @@
}
}
+ @NonNull
+ private static IllegalArgumentException getExceptionForUnknownImeId(
+ @Nullable String imeId) {
+ return new IllegalArgumentException("Unknown id: " + imeId);
+ }
+
@BinderThread
private void setInputMethod(@NonNull IBinder token, String id) {
+ final int callingUid = Binder.getCallingUid();
+ final int userId = UserHandle.getUserId(callingUid);
synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
return;
}
+ final InputMethodInfo imi = mMethodMap.get(id);
+ if (imi == null || !canCallerAccessInputMethod(
+ imi.getPackageName(), callingUid, userId, mSettings)) {
+ throw getExceptionForUnknownImeId(id);
+ }
setInputMethodWithSubtypeIdLocked(token, id, NOT_A_SUBTYPE_ID);
}
}
@@ -3959,14 +3972,20 @@
@BinderThread
private void setInputMethodAndSubtype(@NonNull IBinder token, String id,
InputMethodSubtype subtype) {
+ final int callingUid = Binder.getCallingUid();
+ final int userId = UserHandle.getUserId(callingUid);
synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
return;
}
+ final InputMethodInfo imi = mMethodMap.get(id);
+ if (imi == null || !canCallerAccessInputMethod(
+ imi.getPackageName(), callingUid, userId, mSettings)) {
+ throw getExceptionForUnknownImeId(id);
+ }
if (subtype != null) {
setInputMethodWithSubtypeIdLocked(token, id,
- SubtypeUtils.getSubtypeIdFromHashCode(mMethodMap.get(id),
- subtype.hashCode()));
+ SubtypeUtils.getSubtypeIdFromHashCode(imi, subtype.hashCode()));
} else {
setInputMethod(token, id);
}
diff --git a/services/core/java/com/android/server/locales/AppUpdateTracker.java b/services/core/java/com/android/server/locales/AppUpdateTracker.java
new file mode 100644
index 0000000..3474f1e
--- /dev/null
+++ b/services/core/java/com/android/server/locales/AppUpdateTracker.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locales;
+
+import android.app.LocaleConfig;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.os.LocaleList;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.FeatureFlagUtils;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+
+/**
+ * Track when a app is being updated.
+ */
+public class AppUpdateTracker {
+ private static final String TAG = "AppUpdateTracker";
+
+ private final Context mContext;
+ private final LocaleManagerService mLocaleManagerService;
+ private final LocaleManagerBackupHelper mBackupHelper;
+
+ AppUpdateTracker(Context context, LocaleManagerService localeManagerService,
+ LocaleManagerBackupHelper backupHelper) {
+ mContext = context;
+ mLocaleManagerService = localeManagerService;
+ mBackupHelper = backupHelper;
+ }
+
+ /**
+ * <p><b>Note:</b> This is invoked by service's common monitor
+ * {@link LocaleManagerServicePackageMonitor#onPackageUpdateFinished} when a package is upgraded
+ * on device.
+ */
+ public void onPackageUpdateFinished(String packageName, int uid) {
+ Log.d(TAG, "onPackageUpdateFinished " + packageName);
+ int userId = UserHandle.getUserId(uid);
+ cleanApplicationLocalesIfNeeded(packageName, userId);
+ }
+
+ /**
+ * When the user has set per-app locales for a specific application from a delegate selector,
+ * and then the LocaleConfig of that application is removed in the upgraded version, the per-app
+ * locales needs to be reset to system default locales to avoid the user being unable to change
+ * system locales setting.
+ */
+ private void cleanApplicationLocalesIfNeeded(String packageName, int userId) {
+ Set<String> packageNames = new ArraySet<>();
+ SharedPreferences delegateAppLocalePackages = mBackupHelper.getPersistedInfo();
+ if (delegateAppLocalePackages != null) {
+ packageNames = delegateAppLocalePackages.getStringSet(Integer.toString(userId),
+ new ArraySet<>());
+ }
+
+ try {
+ LocaleList appLocales = mLocaleManagerService.getApplicationLocales(packageName,
+ userId);
+ if (appLocales.isEmpty() || isLocalesExistedInLocaleConfig(appLocales, packageName,
+ userId) || !packageNames.contains(packageName)) {
+ return;
+ }
+ } catch (RemoteException | IllegalArgumentException e) {
+ Slog.e(TAG, "Exception when getting locales for " + packageName, e);
+ return;
+ }
+
+ Slog.d(TAG, "Clear app locales for " + packageName);
+ try {
+ mLocaleManagerService.setApplicationLocales(packageName, userId,
+ LocaleList.forLanguageTags(""), false);
+ } catch (RemoteException | IllegalArgumentException e) {
+ Slog.e(TAG, "Could not clear locales for " + packageName, e);
+ }
+ }
+
+ /**
+ * Check whether the LocaleConfig is existed and the per-app locales is presented in the
+ * LocaleConfig file after the application is upgraded.
+ */
+ private boolean isLocalesExistedInLocaleConfig(LocaleList appLocales, String packageName,
+ int userId) {
+ LocaleList packageLocalesList = getPackageLocales(packageName, userId);
+ HashSet<Locale> packageLocales = new HashSet<>();
+
+ if (isSettingsAppLocalesOptIn()) {
+ if (packageLocalesList == null || packageLocalesList.isEmpty()) {
+ // The app locale feature is not enabled by the app
+ Slog.d(TAG, "opt-in: the app locale feature is not enabled");
+ return false;
+ }
+ } else {
+ if (packageLocalesList != null && packageLocalesList.isEmpty()) {
+ // The app locale feature is not enabled by the app
+ Slog.d(TAG, "opt-out: the app locale feature is not enabled");
+ return false;
+ }
+ }
+
+ if (packageLocalesList != null && !packageLocalesList.isEmpty()) {
+ // The app has added the supported locales into the LocaleConfig
+ for (int i = 0; i < packageLocalesList.size(); i++) {
+ packageLocales.add(packageLocalesList.get(i));
+ }
+ if (!matchesLocale(packageLocales, appLocales)) {
+ // The set app locales do not match with the list of app supported locales
+ Slog.d(TAG, "App locales: " + appLocales.toLanguageTags()
+ + " are not existed in the supported locale list");
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Get locales from LocaleConfig.
+ */
+ @VisibleForTesting
+ public LocaleList getPackageLocales(String packageName, int userId) {
+ try {
+ LocaleConfig localeConfig = new LocaleConfig(
+ mContext.createPackageContextAsUser(packageName, 0, UserHandle.of(userId)));
+ if (localeConfig.getStatus() == LocaleConfig.STATUS_SUCCESS) {
+ return localeConfig.getSupportedLocales();
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "Can not found the package name : " + packageName + " / " + e);
+ }
+ return null;
+ }
+
+ /**
+ * Check whether the feature to show per-app locales list in Settings is enabled.
+ */
+ @VisibleForTesting
+ public boolean isSettingsAppLocalesOptIn() {
+ return FeatureFlagUtils.isEnabled(mContext,
+ FeatureFlagUtils.SETTINGS_APP_LOCALE_OPT_IN_ENABLED);
+ }
+
+ private boolean matchesLocale(HashSet<Locale> supported, LocaleList appLocales) {
+ if (supported.size() <= 0 || appLocales.size() <= 0) {
+ return true;
+ }
+
+ for (int i = 0; i < appLocales.size(); i++) {
+ final Locale appLocale = appLocales.get(i);
+ if (supported.stream().anyMatch(
+ locale -> LocaleList.matchesLanguageAndScript(locale, appLocale))) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java
index 67c931f..898c6f1 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java
@@ -27,14 +27,17 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.os.Environment;
import android.os.HandlerThread;
import android.os.LocaleList;
import android.os.RemoteException;
import android.os.UserHandle;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.util.Xml;
@@ -44,18 +47,20 @@
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
-import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.time.Clock;
import java.time.Duration;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.Set;
/**
* Helper class for managing backup and restore of app-specific locales.
@@ -68,9 +73,14 @@
private static final String PACKAGE_XML_TAG = "package";
private static final String ATTR_PACKAGE_NAME = "name";
private static final String ATTR_LOCALES = "locales";
- private static final String ATTR_CREATION_TIME_MILLIS = "creationTimeMillis";
+ private static final String ATTR_DELEGATE_SELECTOR = "delegate_selector";
private static final String SYSTEM_BACKUP_PACKAGE_KEY = "android";
+ /**
+ * The name of the xml file used to persist the target package name that sets per-app locales
+ * from the delegate selector.
+ */
+ private static final String LOCALES_FROM_DELEGATE_PREFS = "LocalesFromDelegatePrefs.xml";
// Stage data would be deleted on reboot since it's stored in memory. So it's retained until
// retention period OR next reboot, whichever happens earlier.
private static final Duration STAGE_DATA_RETENTION_PERIOD = Duration.ofDays(3);
@@ -85,23 +95,28 @@
// SparseArray because it is more memory-efficient than a HashMap.
private final SparseArray<StagedData> mStagedData;
+ // SharedPreferences to store packages whose app-locale was set by a delegate, as opposed to
+ // the application setting the app-locale itself.
+ private final SharedPreferences mDelegateAppLocalePackages;
private final BroadcastReceiver mUserMonitor;
LocaleManagerBackupHelper(LocaleManagerService localeManagerService,
PackageManager packageManager, HandlerThread broadcastHandlerThread) {
this(localeManagerService.mContext, localeManagerService, packageManager, Clock.systemUTC(),
- new SparseArray<>(), broadcastHandlerThread);
+ new SparseArray<>(), broadcastHandlerThread, null);
}
@VisibleForTesting LocaleManagerBackupHelper(Context context,
LocaleManagerService localeManagerService,
PackageManager packageManager, Clock clock, SparseArray<StagedData> stagedData,
- HandlerThread broadcastHandlerThread) {
+ HandlerThread broadcastHandlerThread, SharedPreferences delegateAppLocalePackages) {
mContext = context;
mLocaleManagerService = localeManagerService;
mPackageManager = packageManager;
mClock = clock;
mStagedData = stagedData;
+ mDelegateAppLocalePackages = delegateAppLocalePackages != null ? delegateAppLocalePackages
+ : createPersistedInfo();
mUserMonitor = new UserMonitor();
IntentFilter filter = new IntentFilter();
@@ -127,20 +142,29 @@
cleanStagedDataForOldEntriesLocked();
}
- HashMap<String, String> pkgStates = new HashMap<>();
+ HashMap<String, LocalesInfo> pkgStates = new HashMap<>();
for (ApplicationInfo appInfo : mPackageManager.getInstalledApplicationsAsUser(
PackageManager.ApplicationInfoFlags.of(0), userId)) {
try {
LocaleList appLocales = mLocaleManagerService.getApplicationLocales(
appInfo.packageName,
userId);
- // Backup locales only for apps which do have app-specific overrides.
+ // Backup locales and package names for per-app locales set from a delegate
+ // selector only for apps which do have app-specific overrides.
if (!appLocales.isEmpty()) {
if (DEBUG) {
Slog.d(TAG, "Add package=" + appInfo.packageName + " locales="
+ appLocales.toLanguageTags() + " to backup payload");
}
- pkgStates.put(appInfo.packageName, appLocales.toLanguageTags());
+ boolean localeSetFromDelegate = false;
+ if (mDelegateAppLocalePackages != null) {
+ localeSetFromDelegate = mDelegateAppLocalePackages.getStringSet(
+ Integer.toString(userId), Collections.<String>emptySet()).contains(
+ appInfo.packageName);
+ }
+ LocalesInfo localesInfo = new LocalesInfo(appLocales.toLanguageTags(),
+ localeSetFromDelegate);
+ pkgStates.put(appInfo.packageName, localesInfo);
}
} catch (RemoteException | IllegalArgumentException e) {
Slog.e(TAG, "Exception when getting locales for package: " + appInfo.packageName,
@@ -200,7 +224,7 @@
final ByteArrayInputStream inputStream = new ByteArrayInputStream(payload);
- HashMap<String, String> pkgStates;
+ HashMap<String, LocalesInfo> pkgStates;
try {
// Parse the input blob into a list of BackupPackageState.
final TypedXmlPullParser parser = Xml.newFastPullParser();
@@ -222,16 +246,17 @@
StagedData stagedData = new StagedData(mClock.millis(), new HashMap<>());
for (String pkgName : pkgStates.keySet()) {
- String languageTags = pkgStates.get(pkgName);
+ LocalesInfo localesInfo = pkgStates.get(pkgName);
// Check if the application is already installed for the concerned user.
if (isPackageInstalledForUser(pkgName, userId)) {
// Don't apply the restore if the locales have already been set for the app.
- checkExistingLocalesAndApplyRestore(pkgName, languageTags, userId);
+ checkExistingLocalesAndApplyRestore(pkgName, localesInfo, userId);
} else {
// Stage the data if the app isn't installed.
- stagedData.mPackageStates.put(pkgName, languageTags);
+ stagedData.mPackageStates.put(pkgName, localesInfo);
if (DEBUG) {
- Slog.d(TAG, "Add locales=" + languageTags
+ Slog.d(TAG, "Add locales=" + localesInfo.mLocales
+ + " fromDelegate=" + localesInfo.mSetFromDelegate
+ " package=" + pkgName + " for lazy restore.");
}
}
@@ -276,9 +301,11 @@
* {@link LocaleManagerServicePackageMonitor#onPackageDataCleared} when a package's data
* is cleared.
*/
- void onPackageDataCleared() {
+ void onPackageDataCleared(String packageName, int uid) {
try {
notifyBackupManager();
+ int userId = UserHandle.getUserId(uid);
+ removePackageFromPersistedInfo(packageName, userId);
} catch (Exception e) {
Slog.e(TAG, "Exception in onPackageDataCleared.", e);
}
@@ -289,9 +316,11 @@
* {@link LocaleManagerServicePackageMonitor#onPackageRemoved} when a package is removed
* from device.
*/
- void onPackageRemoved() {
+ void onPackageRemoved(String packageName, int uid) {
try {
notifyBackupManager();
+ int userId = UserHandle.getUserId(uid);
+ removePackageFromPersistedInfo(packageName, userId);
} catch (Exception e) {
Slog.e(TAG, "Exception in onPackageRemoved.", e);
}
@@ -317,7 +346,12 @@
* case, we want to keep the user settings and discard the restore.
*/
private void checkExistingLocalesAndApplyRestore(@NonNull String pkgName,
- @NonNull String languageTags, int userId) {
+ LocalesInfo localesInfo, int userId) {
+ if (localesInfo == null) {
+ Slog.w(TAG, "No locales info for " + pkgName);
+ return;
+ }
+
try {
LocaleList currLocales = mLocaleManagerService.getApplicationLocales(
pkgName,
@@ -325,16 +359,17 @@
if (!currLocales.isEmpty()) {
return;
}
- } catch (RemoteException e) {
+ } catch (RemoteException | IllegalArgumentException e) {
Slog.e(TAG, "Could not check for current locales before restoring", e);
}
// Restore the locale immediately
try {
mLocaleManagerService.setApplicationLocales(pkgName, userId,
- LocaleList.forLanguageTags(languageTags));
+ LocaleList.forLanguageTags(localesInfo.mLocales), localesInfo.mSetFromDelegate);
if (DEBUG) {
- Slog.d(TAG, "Restored locales=" + languageTags + " for package=" + pkgName);
+ Slog.d(TAG, "Restored locales=" + localesInfo.mLocales + " fromDelegate="
+ + localesInfo.mSetFromDelegate + " for package=" + pkgName);
}
} catch (RemoteException | IllegalArgumentException e) {
Slog.e(TAG, "Could not restore locales for " + pkgName, e);
@@ -348,18 +383,21 @@
/**
* Parses the backup data from the serialized xml input stream.
*/
- private @NonNull HashMap<String, String> readFromXml(XmlPullParser parser)
+ private @NonNull HashMap<String, LocalesInfo> readFromXml(TypedXmlPullParser parser)
throws IOException, XmlPullParserException {
- HashMap<String, String> packageStates = new HashMap<>();
+ HashMap<String, LocalesInfo> packageStates = new HashMap<>();
int depth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, depth)) {
if (parser.getName().equals(PACKAGE_XML_TAG)) {
String packageName = parser.getAttributeValue(/* namespace= */ null,
ATTR_PACKAGE_NAME);
String languageTags = parser.getAttributeValue(/* namespace= */ null, ATTR_LOCALES);
+ boolean delegateSelector = parser.getAttributeBoolean(/* namespace= */ null,
+ ATTR_DELEGATE_SELECTOR);
if (!TextUtils.isEmpty(packageName) && !TextUtils.isEmpty(languageTags)) {
- packageStates.put(packageName, languageTags);
+ LocalesInfo localesInfo = new LocalesInfo(languageTags, delegateSelector);
+ packageStates.put(packageName, localesInfo);
}
}
}
@@ -369,8 +407,8 @@
/**
* Converts the list of app backup data into a serialized xml stream.
*/
- private static void writeToXml(OutputStream stream, @NonNull HashMap<String, String> pkgStates)
- throws IOException {
+ private static void writeToXml(OutputStream stream,
+ @NonNull HashMap<String, LocalesInfo> pkgStates) throws IOException {
if (pkgStates.isEmpty()) {
// No need to write anything at all if pkgStates is empty.
return;
@@ -384,7 +422,9 @@
for (String pkg : pkgStates.keySet()) {
out.startTag(/* namespace= */ null, PACKAGE_XML_TAG);
out.attribute(/* namespace= */ null, ATTR_PACKAGE_NAME, pkg);
- out.attribute(/* namespace= */ null, ATTR_LOCALES, pkgStates.get(pkg));
+ out.attribute(/* namespace= */ null, ATTR_LOCALES, pkgStates.get(pkg).mLocales);
+ out.attributeBoolean(/* namespace= */ null, ATTR_DELEGATE_SELECTOR,
+ pkgStates.get(pkg).mSetFromDelegate);
out.endTag(/*namespace= */ null, PACKAGE_XML_TAG);
}
@@ -394,14 +434,24 @@
static class StagedData {
final long mCreationTimeMillis;
- final HashMap<String, String> mPackageStates;
+ final HashMap<String, LocalesInfo> mPackageStates;
- StagedData(long creationTimeMillis, HashMap<String, String> pkgStates) {
+ StagedData(long creationTimeMillis, HashMap<String, LocalesInfo> pkgStates) {
mCreationTimeMillis = creationTimeMillis;
mPackageStates = pkgStates;
}
}
+ static class LocalesInfo {
+ final String mLocales;
+ final boolean mSetFromDelegate;
+
+ LocalesInfo(String locales, boolean setFromDelegate) {
+ mLocales = locales;
+ mSetFromDelegate = setFromDelegate;
+ }
+ }
+
/**
* Broadcast listener to capture user removed event.
*
@@ -416,6 +466,7 @@
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
synchronized (mStagedDataLock) {
deleteStagedDataLocked(userId);
+ removeProfileFromPersistedInfo(userId);
}
}
} catch (Exception e) {
@@ -443,11 +494,11 @@
StagedData stagedData = mStagedData.get(userId);
for (String pkgName : stagedData.mPackageStates.keySet()) {
- String languageTags = stagedData.mPackageStates.get(pkgName);
+ LocalesInfo localesInfo = stagedData.mPackageStates.get(pkgName);
if (pkgName.equals(packageName)) {
- checkExistingLocalesAndApplyRestore(pkgName, languageTags, userId);
+ checkExistingLocalesAndApplyRestore(pkgName, localesInfo, userId);
// Remove the restored entry from the staged data list.
stagedData.mPackageStates.remove(pkgName);
@@ -463,4 +514,98 @@
}
}
}
+
+ SharedPreferences createPersistedInfo() {
+ final File prefsFile = new File(
+ Environment.getDataSystemDeDirectory(UserHandle.USER_SYSTEM),
+ LOCALES_FROM_DELEGATE_PREFS);
+ return mContext.createDeviceProtectedStorageContext().getSharedPreferences(prefsFile,
+ Context.MODE_PRIVATE);
+ }
+
+ public SharedPreferences getPersistedInfo() {
+ return mDelegateAppLocalePackages;
+ }
+
+ private void removePackageFromPersistedInfo(String packageName, @UserIdInt int userId) {
+ if (mDelegateAppLocalePackages == null) {
+ Slog.w(TAG, "Failed to persist data into the shared preference!");
+ return;
+ }
+
+ String key = Integer.toString(userId);
+ Set<String> packageNames = new ArraySet<>(
+ mDelegateAppLocalePackages.getStringSet(key, new ArraySet<>()));
+ if (packageNames.contains(packageName)) {
+ if (DEBUG) {
+ Slog.d(TAG, "remove " + packageName + " from persisted info");
+ }
+ packageNames.remove(packageName);
+ SharedPreferences.Editor editor = mDelegateAppLocalePackages.edit();
+ editor.putStringSet(key, packageNames);
+
+ // commit and log the result.
+ if (!editor.commit()) {
+ Slog.e(TAG, "Failed to commit data!");
+ }
+ }
+ }
+
+ private void removeProfileFromPersistedInfo(@UserIdInt int userId) {
+ String key = Integer.toString(userId);
+
+ if (mDelegateAppLocalePackages == null || !mDelegateAppLocalePackages.contains(key)) {
+ Slog.w(TAG, "The profile is not existed in the persisted info");
+ return;
+ }
+
+ if (!mDelegateAppLocalePackages.edit().remove(key).commit()) {
+ Slog.e(TAG, "Failed to commit data!");
+ }
+ }
+
+ /**
+ * Persists the package name of per-app locales set from a delegate selector.
+ *
+ * <p>This information is used when the user has set per-app locales for a specific application
+ * from the delegate selector, and then the LocaleConfig of that application is removed in the
+ * upgraded version, the per-app locales needs to be reset to system default locales to avoid
+ * the user being unable to change system locales setting.
+ */
+ void persistLocalesModificationInfo(@UserIdInt int userId, String packageName,
+ boolean fromDelegate, boolean emptyLocales) {
+ if (mDelegateAppLocalePackages == null) {
+ Slog.w(TAG, "Failed to persist data into the shared preference!");
+ return;
+ }
+
+ SharedPreferences.Editor editor = mDelegateAppLocalePackages.edit();
+ String user = Integer.toString(userId);
+ Set<String> packageNames = new ArraySet<>(
+ mDelegateAppLocalePackages.getStringSet(user, new ArraySet<>()));
+ if (fromDelegate && !emptyLocales) {
+ if (!packageNames.contains(packageName)) {
+ if (DEBUG) {
+ Slog.d(TAG, "persist package: " + packageName);
+ }
+ packageNames.add(packageName);
+ editor.putStringSet(user, packageNames);
+ }
+ } else {
+ // Remove the package name if per-app locales was not set from the delegate selector
+ // or they were set to empty.
+ if (packageNames.contains(packageName)) {
+ if (DEBUG) {
+ Slog.d(TAG, "remove package: " + packageName);
+ }
+ packageNames.remove(packageName);
+ editor.putStringSet(user, packageNames);
+ }
+ }
+
+ // commit and log the result.
+ if (!editor.commit()) {
+ Slog.e(TAG, "failed to commit locale setter info");
+ }
+ }
}
diff --git a/services/core/java/com/android/server/locales/LocaleManagerService.java b/services/core/java/com/android/server/locales/LocaleManagerService.java
index 4ce0320..39b9f1f 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerService.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerService.java
@@ -94,9 +94,11 @@
mBackupHelper = new LocaleManagerBackupHelper(this,
mPackageManager, broadcastHandlerThread);
+ AppUpdateTracker appUpdateTracker =
+ new AppUpdateTracker(mContext, this, mBackupHelper);
mPackageMonitor = new LocaleManagerServicePackageMonitor(mBackupHelper,
- systemAppUpdateTracker);
+ systemAppUpdateTracker, appUpdateTracker);
mPackageMonitor.register(context, broadcastHandlerThread.getLooper(),
UserHandle.ALL,
true);
@@ -147,8 +149,9 @@
private final class LocaleManagerBinderService extends ILocaleManager.Stub {
@Override
public void setApplicationLocales(@NonNull String appPackageName, @UserIdInt int userId,
- @NonNull LocaleList locales) throws RemoteException {
- LocaleManagerService.this.setApplicationLocales(appPackageName, userId, locales);
+ @NonNull LocaleList locales, boolean fromDelegate) throws RemoteException {
+ LocaleManagerService.this.setApplicationLocales(appPackageName, userId, locales,
+ fromDelegate);
}
@Override
@@ -178,7 +181,8 @@
* Sets the current UI locales for a specified app.
*/
public void setApplicationLocales(@NonNull String appPackageName, @UserIdInt int userId,
- @NonNull LocaleList locales) throws RemoteException, IllegalArgumentException {
+ @NonNull LocaleList locales, boolean fromDelegate)
+ throws RemoteException, IllegalArgumentException {
AppLocaleChangedAtomRecord atomRecordForMetrics = new
AppLocaleChangedAtomRecord(Binder.getCallingUid());
try {
@@ -203,6 +207,8 @@
enforceChangeConfigurationPermission(atomRecordForMetrics);
}
+ mBackupHelper.persistLocalesModificationInfo(userId, appPackageName, fromDelegate,
+ locales.isEmpty());
final long token = Binder.clearCallingIdentity();
try {
setApplicationLocalesUnchecked(appPackageName, userId, locales,
diff --git a/services/core/java/com/android/server/locales/LocaleManagerServicePackageMonitor.java b/services/core/java/com/android/server/locales/LocaleManagerServicePackageMonitor.java
index 32080ef..1a38f0c 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerServicePackageMonitor.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerServicePackageMonitor.java
@@ -34,11 +34,13 @@
final class LocaleManagerServicePackageMonitor extends PackageMonitor {
private LocaleManagerBackupHelper mBackupHelper;
private SystemAppUpdateTracker mSystemAppUpdateTracker;
+ private AppUpdateTracker mAppUpdateTracker;
LocaleManagerServicePackageMonitor(LocaleManagerBackupHelper localeManagerBackupHelper,
- SystemAppUpdateTracker systemAppUpdateTracker) {
+ SystemAppUpdateTracker systemAppUpdateTracker, AppUpdateTracker appUpdateTracker) {
mBackupHelper = localeManagerBackupHelper;
mSystemAppUpdateTracker = systemAppUpdateTracker;
+ mAppUpdateTracker = appUpdateTracker;
}
@Override
@@ -48,16 +50,17 @@
@Override
public void onPackageDataCleared(String packageName, int uid) {
- mBackupHelper.onPackageDataCleared();
+ mBackupHelper.onPackageDataCleared(packageName, uid);
}
@Override
public void onPackageRemoved(String packageName, int uid) {
- mBackupHelper.onPackageRemoved();
+ mBackupHelper.onPackageRemoved(packageName, uid);
}
@Override
public void onPackageUpdateFinished(String packageName, int uid) {
+ mAppUpdateTracker.onPackageUpdateFinished(packageName, uid);
mSystemAppUpdateTracker.onPackageUpdateFinished(packageName, uid);
}
}
diff --git a/services/core/java/com/android/server/locales/LocaleManagerShellCommand.java b/services/core/java/com/android/server/locales/LocaleManagerShellCommand.java
index 803b5a3..c5069e5 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerShellCommand.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerShellCommand.java
@@ -56,7 +56,8 @@
pw.println("Locale manager (locale) shell commands:");
pw.println(" help");
pw.println(" Print this help text.");
- pw.println(" set-app-locales <PACKAGE_NAME> [--user <USER_ID>] [--locales <LOCALE_INFO>]");
+ pw.println(" set-app-locales <PACKAGE_NAME> [--user <USER_ID>] [--locales <LOCALE_INFO>]"
+ + "[--delegate <FROM_DELEGATE>]");
pw.println(" Set the locales for the specified app.");
pw.println(" --user <USER_ID>: apply for the given user, "
+ "the current user is used when unspecified.");
@@ -64,6 +65,8 @@
+ "as a single String separated by commas");
pw.println(" Empty locale list is used when unspecified.");
pw.println(" eg. en,en-US,hi ");
+ pw.println(" --delegate <FROM_DELEGATE>: The locales are set from a delegate, "
+ + "the value could be true or false. false is the default when unspecified.");
pw.println(" get-app-locales <PACKAGE_NAME> [--user <USER_ID>]");
pw.println(" Get the locales for the specified app.");
pw.println(" --user <USER_ID>: get for the given user, "
@@ -77,6 +80,7 @@
if (packageName != null) {
int userId = ActivityManager.getCurrentUser();
LocaleList locales = LocaleList.getEmptyLocaleList();
+ boolean fromDelegate = false;
do {
String option = getNextOption();
if (option == null) {
@@ -91,6 +95,10 @@
locales = parseLocales();
break;
}
+ case "--delegate": {
+ fromDelegate = parseFromDelegate();
+ break;
+ }
default: {
throw new IllegalArgumentException("Unknown option: " + option);
}
@@ -98,7 +106,7 @@
} while (true);
try {
- mBinderService.setApplicationLocales(packageName, userId, locales);
+ mBinderService.setApplicationLocales(packageName, userId, locales, fromDelegate);
} catch (RemoteException e) {
getOutPrintWriter().println("Remote Exception: " + e);
} catch (IllegalArgumentException e) {
@@ -148,12 +156,26 @@
}
private LocaleList parseLocales() {
- if (getRemainingArgsCount() <= 0) {
+ String locales = getNextArg();
+ if (locales == null) {
return LocaleList.getEmptyLocaleList();
+ } else {
+ if (locales.startsWith("-")) {
+ throw new IllegalArgumentException("Unknown locales: " + locales);
+ }
+ return LocaleList.forLanguageTags(locales);
}
- String[] args = peekRemainingArgs();
- String inputLocales = args[0];
- LocaleList locales = LocaleList.forLanguageTags(inputLocales);
- return locales;
+ }
+
+ private boolean parseFromDelegate() {
+ String result = getNextArg();
+ if (result == null) {
+ return false;
+ } else {
+ if (result.startsWith("-")) {
+ throw new IllegalArgumentException("Unknown source: " + result);
+ }
+ return Boolean.parseBoolean(result);
+ }
}
}
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index dcec0aa..2669d21 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -140,7 +140,9 @@
import com.android.server.location.provider.proxy.ProxyLocationProvider;
import com.android.server.location.settings.LocationSettings;
import com.android.server.location.settings.LocationUserSettings;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
+import com.android.server.utils.Slogf;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -308,6 +310,10 @@
permissionManagerInternal.setLocationExtraPackagesProvider(
userId -> mContext.getResources().getStringArray(
com.android.internal.R.array.config_locationExtraPackageNames));
+
+ // TODO(b/241604546): properly handle this callback
+ LocalServices.getService(UserManagerInternal.class).addUserVisibilityListener(
+ (u, v) -> Slogf.i(TAG, "onUserVisibilityChanged(): %d -> %b", u, v));
}
@Nullable
diff --git a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
index 1fa56bc..2d015a5d 100644
--- a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
+++ b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
@@ -25,6 +25,7 @@
import android.location.GnssMeasurementCorrections;
import android.location.GnssMeasurementsEvent;
import android.location.GnssNavigationMessage;
+import android.location.GnssSignalType;
import android.location.GnssStatus;
import android.location.Location;
import android.os.Binder;
@@ -1144,6 +1145,13 @@
onCapabilitiesChanged(oldCapabilities, mCapabilities);
}
+ @NativeEntryPoint
+ void setSignalTypeCapabilities(List<GnssSignalType> signalTypes) {
+ GnssCapabilities oldCapabilities = mCapabilities;
+ mCapabilities = oldCapabilities.withSignalTypes(signalTypes);
+ onCapabilitiesChanged(oldCapabilities, mCapabilities);
+ }
+
private void onCapabilitiesChanged(GnssCapabilities oldCapabilities,
GnssCapabilities newCapabilities) {
Binder.withCleanCallingIdentity(() -> {
diff --git a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
index f144cf8..46f486d 100644
--- a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
+++ b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.hardware.biometrics.BiometricManager;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintManager;
@@ -48,11 +49,13 @@
@NonNull private final Handler mHandler;
@Nullable private FingerprintManager mFingerprintManager;
@Nullable private FaceManager mFaceManager;
+ @Nullable private BiometricManager mBiometricManager;
// Entries added by LockSettingsService once a user's synthetic password is known. At this point
// things are still keyed by userId.
@NonNull private final ArrayList<UserAuthInfo> mPendingResetLockoutsForFingerprint;
@NonNull private final ArrayList<UserAuthInfo> mPendingResetLockoutsForFace;
+ @NonNull private final ArrayList<UserAuthInfo> mPendingResetLockouts;
/**
* Authentication info for a successful user unlock via Synthetic Password. This can be used to
@@ -125,7 +128,6 @@
}
@Nullable private FaceResetLockoutTask mFaceResetLockoutTask;
-
private final FaceResetLockoutTask.FinishCallback mFaceFinishCallback = () -> {
mFaceResetLockoutTask = null;
};
@@ -135,12 +137,14 @@
mHandler = handler;
mPendingResetLockoutsForFingerprint = new ArrayList<>();
mPendingResetLockoutsForFace = new ArrayList<>();
+ mPendingResetLockouts = new ArrayList<>();
}
public void systemReady(@Nullable FingerprintManager fingerprintManager,
- @Nullable FaceManager faceManager) {
+ @Nullable FaceManager faceManager, @Nullable BiometricManager biometricManager) {
mFingerprintManager = fingerprintManager;
mFaceManager = faceManager;
+ mBiometricManager = biometricManager;
}
/**
@@ -151,7 +155,7 @@
* Note that this should only ever be invoked for successful authentications, otherwise it will
* consume a Gatekeeper authentication attempt and potentially wipe the user/device.
*
- * @param userId The user that the operation will apply for.
+ * @param userId The user that the operation will apply for.
* @param gatekeeperPassword The Gatekeeper Password
*/
void addPendingLockoutResetForUser(int userId, @NonNull byte[] gatekeeperPassword) {
@@ -167,6 +171,12 @@
mPendingResetLockoutsForFingerprint.add(new UserAuthInfo(userId,
gatekeeperPassword));
}
+
+ if (mBiometricManager != null) {
+ Slog.d(TAG, "Fingerprint addPendingLockoutResetForUser: " + userId);
+ mPendingResetLockouts.add(new UserAuthInfo(userId,
+ gatekeeperPassword));
+ }
});
}
@@ -184,6 +194,14 @@
new ArrayList<>(mPendingResetLockoutsForFingerprint));
mPendingResetLockoutsForFingerprint.clear();
}
+
+ if (!mPendingResetLockouts.isEmpty()) {
+ Slog.d(TAG, "Processing pending resetLockouts(Generic)");
+ processPendingLockoutsGeneric(
+ new ArrayList<>(mPendingResetLockouts));
+ mPendingResetLockouts.clear();
+ }
+
});
}
@@ -257,6 +275,17 @@
}
}
+ private void processPendingLockoutsGeneric(List<UserAuthInfo> pendingResetLockouts) {
+ for (UserAuthInfo user : pendingResetLockouts) {
+ Slog.d(TAG, "Resetting biometric lockout for user: " + user.userId);
+ final byte[] hat = requestHatFromGatekeeperPassword(mSpManager, user,
+ 0 /* challenge */);
+ if (hat != null) {
+ mBiometricManager.resetLockout(user.userId, hat);
+ }
+ }
+ }
+
@Nullable
private static byte[] requestHatFromGatekeeperPassword(
@NonNull SyntheticPasswordManager spManager,
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index c899cf2..92b685c7 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -70,6 +70,7 @@
import android.database.ContentObserver;
import android.database.sqlite.SQLiteDatabase;
import android.hardware.authsecret.V1_0.IAuthSecret;
+import android.hardware.biometrics.BiometricManager;
import android.hardware.face.Face;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.Fingerprint;
@@ -552,6 +553,10 @@
}
}
+ public BiometricManager getBiometricManager() {
+ return (BiometricManager) mContext.getSystemService(Context.BIOMETRIC_SERVICE);
+ }
+
public int settingsGlobalGetInt(ContentResolver contentResolver, String keyName,
int defaultValue) {
return Settings.Global.getInt(contentResolver, keyName, defaultValue);
@@ -837,7 +842,7 @@
// TODO: maybe skip this for split system user mode.
mStorage.prefetchUser(UserHandle.USER_SYSTEM);
mBiometricDeferredQueue.systemReady(mInjector.getFingerprintManager(),
- mInjector.getFaceManager());
+ mInjector.getFaceManager(), mInjector.getBiometricManager());
}
private void loadEscrowData() {
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index db036b0..d836df5 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -36,6 +36,7 @@
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.AtomicFile;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -52,6 +53,7 @@
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
@@ -308,48 +310,45 @@
private void writeFile(File path, byte[] data) {
synchronized (mFileWriteLock) {
- RandomAccessFile raf = null;
+ // Use AtomicFile to guarantee atomicity of the file write, including when an existing
+ // file is replaced with a new one. This method is usually used to create new files,
+ // but there are some edge cases in which it is used to replace an existing file.
+ AtomicFile file = new AtomicFile(path);
+ FileOutputStream out = null;
try {
- // Write the data to the file, requiring each write to be synchronized to the
- // underlying storage device immediately to avoid data loss in case of power loss.
- raf = new RandomAccessFile(path, "rws");
- // Truncate the file if the data is empty.
- if (data == null || data.length == 0) {
- raf.setLength(0);
- } else {
- raf.write(data, 0, data.length);
- }
- raf.close();
- fsyncDirectory(path.getParentFile());
+ out = file.startWrite();
+ out.write(data);
+ file.finishWrite(out);
+ out = null;
} catch (IOException e) {
- Slog.e(TAG, "Error writing to file " + e);
+ Slog.e(TAG, "Error writing file " + path, e);
} finally {
- if (raf != null) {
- try {
- raf.close();
- } catch (IOException e) {
- Slog.e(TAG, "Error closing file " + e);
- }
- }
+ file.failWrite(out);
}
+ // For performance reasons, AtomicFile only syncs the file itself, not also the parent
+ // directory. The latter must be done explicitly here, as some callers need a guarantee
+ // that the file really exists on-disk when this returns.
+ fsyncDirectory(path.getParentFile());
mCache.putFile(path, data);
}
}
private void deleteFile(File path) {
synchronized (mFileWriteLock) {
+ // Zeroize the file to try to make its contents unrecoverable. This is *not* guaranteed
+ // to be effective, and in fact it usually isn't, but it doesn't hurt. We also don't
+ // bother zeroizing |path|.new, which may exist from an interrupted AtomicFile write.
if (path.exists()) {
- // Zeroize the file to try to make its contents unrecoverable. This is *not*
- // guaranteed to be effective, and in fact it usually isn't, but it doesn't hurt.
try (RandomAccessFile raf = new RandomAccessFile(path, "rws")) {
final int fileSize = (int) raf.length();
raf.write(new byte[fileSize]);
} catch (Exception e) {
Slog.w(TAG, "Failed to zeroize " + path, e);
}
- path.delete();
- mCache.putFile(path, null);
}
+ // To ensure that |path|.new is deleted if it exists, use AtomicFile.delete() here.
+ new AtomicFile(path).delete();
+ mCache.putFile(path, null);
}
}
diff --git a/services/core/java/com/android/server/logcat/LogcatManagerService.java b/services/core/java/com/android/server/logcat/LogcatManagerService.java
index fdc5bab..497ed03 100644
--- a/services/core/java/com/android/server/logcat/LogcatManagerService.java
+++ b/services/core/java/com/android/server/logcat/LogcatManagerService.java
@@ -23,6 +23,7 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -41,7 +42,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ILogAccessDialogCallback;
-import com.android.internal.app.LogAccessDialogActivity;
import com.android.internal.util.ArrayUtils;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -62,6 +62,10 @@
public final class LogcatManagerService extends SystemService {
private static final String TAG = "LogcatManagerService";
private static final boolean DEBUG = false;
+ private static final String TARGET_PACKAGE_NAME = "com.android.systemui";
+ private static final String TARGET_ACTIVITY_NAME =
+ "com.android.systemui.logcat.LogAccessDialogActivity";
+ public static final String EXTRA_CALLBACK = "EXTRA_CALLBACK";
/** How long to wait for the user to approve/decline before declining automatically */
@VisibleForTesting
@@ -442,6 +446,7 @@
mClock.get() + PENDING_CONFIRMATION_TIMEOUT_MILLIS);
final Intent mIntent = createIntent(client);
mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mIntent.setComponent(new ComponentName(TARGET_PACKAGE_NAME, TARGET_ACTIVITY_NAME));
mContext.startActivityAsUser(mIntent, UserHandle.SYSTEM);
}
@@ -536,13 +541,13 @@
* Create the Intent for LogAccessDialogActivity.
*/
public Intent createIntent(LogAccessClient client) {
- final Intent intent = new Intent(mContext, LogAccessDialogActivity.class);
+ final Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.putExtra(Intent.EXTRA_PACKAGE_NAME, client.mPackageName);
intent.putExtra(Intent.EXTRA_UID, client.mUid);
- intent.putExtra(LogAccessDialogActivity.EXTRA_CALLBACK, mDialogCallback.asBinder());
+ intent.putExtra(EXTRA_CALLBACK, mDialogCallback.asBinder());
return intent;
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 5dcbb16..c3a5558 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -4967,16 +4967,7 @@
}
enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
- // If the caller is system, take the package name from the rule's owner rather than
- // from the caller's package.
- String rulePkg = pkg;
- if (isCallingUidSystem()) {
- if (automaticZenRule.getOwner() != null) {
- rulePkg = automaticZenRule.getOwner().getPackageName();
- }
- }
-
- return mZenModeHelper.addAutomaticZenRule(rulePkg, automaticZenRule,
+ return mZenModeHelper.addAutomaticZenRule(pkg, automaticZenRule,
"addAutomaticZenRule");
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 4b2c88c..f2c78ad 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -326,7 +326,7 @@
public String addAutomaticZenRule(String pkg, AutomaticZenRule automaticZenRule,
String reason) {
- if (!ZenModeConfig.SYSTEM_AUTHORITY.equals(pkg)) {
+ if (!isSystemRule(automaticZenRule)) {
PackageItemInfo component = getServiceInfo(automaticZenRule.getOwner());
if (component == null) {
component = getActivityInfo(automaticZenRule.getConfigurationActivity());
@@ -582,6 +582,11 @@
}
}
+ private boolean isSystemRule(AutomaticZenRule rule) {
+ return rule.getOwner() != null
+ && ZenModeConfig.SYSTEM_AUTHORITY.equals(rule.getOwner().getPackageName());
+ }
+
private ServiceInfo getServiceInfo(ComponentName owner) {
Intent queryIntent = new Intent();
queryIntent.setComponent(owner);
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index d856d54..d72aacc 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -152,8 +152,6 @@
@GuardedBy("mLock") @Status private int mLastExecutionStatus = STATUS_OK;
- @GuardedBy("mLock") private long mLastExecutionStartTimeMs;
- @GuardedBy("mLock") private long mLastExecutionDurationIncludingSleepMs;
@GuardedBy("mLock") private long mLastExecutionStartUptimeMs;
@GuardedBy("mLock") private long mLastExecutionDurationMs;
@@ -234,10 +232,6 @@
writer.println(mDisableJobSchedulerJobs);
writer.print("mLastExecutionStatus:");
writer.println(mLastExecutionStatus);
- writer.print("mLastExecutionStartTimeMs:");
- writer.println(mLastExecutionStartTimeMs);
- writer.print("mLastExecutionDurationIncludingSleepMs:");
- writer.println(mLastExecutionDurationIncludingSleepMs);
writer.print("mLastExecutionStartUptimeMs:");
writer.println(mLastExecutionStartUptimeMs);
writer.print("mLastExecutionDurationMs:");
@@ -564,8 +558,6 @@
private boolean runIdleOptimization(
PackageManagerService pm, List<String> pkgs, boolean isPostBootUpdate) {
synchronized (mLock) {
- mLastExecutionStartTimeMs = SystemClock.elapsedRealtime();
- mLastExecutionDurationIncludingSleepMs = -1;
mLastExecutionStartUptimeMs = SystemClock.uptimeMillis();
mLastExecutionDurationMs = -1;
}
@@ -574,8 +566,6 @@
logStatus(status);
synchronized (mLock) {
mLastExecutionStatus = status;
- mLastExecutionDurationIncludingSleepMs =
- SystemClock.elapsedRealtime() - mLastExecutionStartTimeMs;
mLastExecutionDurationMs = SystemClock.uptimeMillis() - mLastExecutionStartUptimeMs;
}
@@ -979,10 +969,9 @@
synchronized (mLock) {
status = mLastExecutionStatus;
durationMs = mLastExecutionDurationMs;
- durationIncludingSleepMs = mLastExecutionDurationIncludingSleepMs;
}
- mStatsLogger.write(status, params.getStopReason(), durationMs, durationIncludingSleepMs);
+ mStatsLogger.write(status, params.getStopReason(), durationMs);
}
/** Injector pattern for testing purpose */
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 7ea0c04..7dae4c6 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -427,7 +427,7 @@
mPm.snapshotComputer().checkPackageFrozen(pkgName);
}
- final boolean isReplace = request.isReplace();
+ final boolean isReplace = request.isInstallReplace();
// Also need to kill any apps that are dependent on the library, except the case of
// installation of new version static shared library.
if (clientLibPkgs != null) {
@@ -814,6 +814,7 @@
for (InstallRequest request : requests) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage");
+ request.onPrepareStarted();
preparePackageLI(request);
} catch (PrepareFailure prepareFailure) {
request.setError(prepareFailure.error,
@@ -822,6 +823,7 @@
request.setOriginPermission(prepareFailure.mConflictingPermission);
return;
} finally {
+ request.onPrepareFinished();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -834,11 +836,13 @@
request.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
final String packageName = packageToScan.getPackageName();
try {
+ request.onScanStarted();
final ScanResult scanResult = scanPackageTracedLI(request.getParsedPackage(),
request.getParseFlags(), request.getScanFlags(),
System.currentTimeMillis(), request.getUser(),
request.getAbiOverride());
request.setScanResult(scanResult);
+ request.onScanFinished();
if (!scannedPackages.add(packageName)) {
request.setError(
PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE,
@@ -966,7 +970,7 @@
final boolean isRollback =
request.getInstallReason() == PackageManager.INSTALL_REASON_ROLLBACK;
@PackageManagerService.ScanFlags int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
- if (request.isMoveInstall()) {
+ if (request.isInstallMove()) {
// moving a complete application; perform an initial scan on the new install location
scanFlags |= SCAN_INITIAL;
}
@@ -1349,7 +1353,7 @@
}
}
- if (request.isMoveInstall()) {
+ if (request.isInstallMove()) {
// We did an in-place move, so dex is ready to roll
scanFlags |= SCAN_NO_DEX;
scanFlags |= SCAN_MOVE;
@@ -1399,7 +1403,7 @@
doRenameLI(request, parsedPackage);
try {
- setUpFsVerityIfPossible(parsedPackage);
+ setUpFsVerity(parsedPackage);
} catch (Installer.InstallerException | IOException | DigestException
| NoSuchAlgorithmException e) {
throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
@@ -1668,7 +1672,7 @@
ParsedPackage parsedPackage) throws PrepareFailure {
final int status = request.getReturnCode();
final String statusMsg = request.getReturnMsg();
- if (request.isMoveInstall()) {
+ if (request.isInstallMove()) {
if (status != PackageManager.INSTALL_SUCCEEDED) {
mRemovePackageHelper.cleanUpForMoveInstall(request.getMoveToUuid(),
request.getMovePackageName(), request.getMoveFromCodePath());
@@ -1796,13 +1800,10 @@
}
/**
- * Set up fs-verity for the given package if possible. This requires a feature flag of system
- * property to be enabled only if the kernel supports fs-verity.
- *
- * <p>When the feature flag is set to legacy mode, only APK is supported (with some experimental
- * kernel patches). In normal mode, all file format can be supported.
+ * Set up fs-verity for the given package. For older devices that do not support fs-verity,
+ * this is a no-op.
*/
- private void setUpFsVerityIfPossible(AndroidPackage pkg) throws Installer.InstallerException,
+ private void setUpFsVerity(AndroidPackage pkg) throws Installer.InstallerException,
PrepareFailure, IOException, DigestException, NoSuchAlgorithmException {
if (!PackageManagerServiceUtils.isApkVerityEnabled()) {
return;
@@ -1837,17 +1838,22 @@
}
for (Map.Entry<String, String> entry : fsverityCandidates.entrySet()) {
- final String filePath = entry.getKey();
- final String signaturePath = entry.getValue();
-
- // fs-verity is optional for now. Only set up if signature is provided.
- if (new File(signaturePath).exists() && !VerityUtils.hasFsverity(filePath)) {
- try {
- VerityUtils.setUpFsverity(filePath, signaturePath);
- } catch (IOException e) {
- throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
- "Failed to enable fs-verity: " + e);
+ try {
+ final String filePath = entry.getKey();
+ if (VerityUtils.hasFsverity(filePath)) {
+ continue;
}
+
+ // Set up fs-verity with optional signature.
+ final String signaturePath = entry.getValue();
+ String optionalSignaturePath = null;
+ if (new File(signaturePath).exists()) {
+ optionalSignaturePath = signaturePath;
+ }
+ VerityUtils.setUpFsverity(filePath, optionalSignaturePath);
+ } catch (IOException e) {
+ throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
+ "Failed to enable fs-verity: " + e);
}
}
}
@@ -1888,7 +1894,8 @@
final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm);
final DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mPm);
- if (installRequest.isReplace()) {
+ installRequest.onCommitStarted();
+ if (installRequest.isInstallReplace()) {
AndroidPackage oldPackage = mPm.mPackages.get(packageName);
// Set the update and install times
@@ -1905,7 +1912,7 @@
mPm.mAppsFilter.getVisibilityAllowList(mPm.snapshotComputer(),
installRequest.getScannedPackageSetting(),
allUsers, mPm.mSettings.getPackagesLocked());
- if (installRequest.isSystem()) {
+ if (installRequest.isInstallSystem()) {
// Remove existing system package
removePackageHelper.removePackage(oldPackage, true);
if (!disableSystemPackageLPw(oldPackage)) {
@@ -1973,6 +1980,7 @@
mPm.updateSequenceNumberLP(ps, installRequest.getNewUsers());
mPm.updateInstantAppInstallerLocked(packageName);
}
+ installRequest.onCommitFinished();
}
ApplicationPackageManager.invalidateGetPackagesForUidCache();
}
@@ -2223,7 +2231,7 @@
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
| Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
}
- if (installRequest.isReplace()) {
+ if (installRequest.isInstallReplace()) {
mDexManager.notifyPackageUpdated(pkg.getPackageName(),
pkg.getBaseApkPath(), pkg.getSplitCodePaths());
}
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index 573082a..4443710 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -18,8 +18,10 @@
import static android.content.pm.PackageManager.INSTALL_REASON_UNKNOWN;
import static android.content.pm.PackageManager.INSTALL_SCENARIO_DEFAULT;
+import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
import static android.os.Process.INVALID_UID;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP;
import static com.android.server.pm.PackageManagerService.TAG;
import android.annotation.NonNull;
@@ -105,6 +107,12 @@
@Nullable
private ScanResult mScanResult;
+ private boolean mIsInstallInherit;
+ private boolean mIsInstallForUsers;
+
+ @Nullable
+ private final PackageMetrics mPackageMetrics;
+
// New install
InstallRequest(InstallingSession params) {
mUserId = params.getUser().getIdentifier();
@@ -116,6 +124,8 @@
params.mTraceMethod, params.mTraceCookie, params.mSigningDetails,
params.mInstallReason, params.mInstallScenario, params.mForceQueryableOverride,
params.mDataLoaderType, params.mPackageSource);
+ mPackageMetrics = new PackageMetrics(this);
+ mIsInstallInherit = params.mIsInherit;
}
// Install existing package as user
@@ -127,6 +137,8 @@
mPkg = pkg;
mNewUsers = newUsers;
mPostInstallRunnable = runnable;
+ mPackageMetrics = new PackageMetrics(this);
+ mIsInstallForUsers = true;
}
// addForInit
@@ -143,6 +155,7 @@
mParseFlags = parseFlags;
mScanFlags = scanFlags;
mScanResult = scanResult;
+ mPackageMetrics = null; // No real logging from this code path
}
@Nullable
@@ -200,7 +213,7 @@
return mInstallArgs == null ? null : mInstallArgs.mObserver;
}
- public boolean isMoveInstall() {
+ public boolean isInstallMove() {
return mInstallArgs != null && mInstallArgs.mMoveInfo != null;
}
@@ -278,7 +291,7 @@
return mRemovedInfo != null ? mRemovedInfo.mRemovedPackage : null;
}
- public boolean isInstallForExistingUser() {
+ public boolean isInstallExistingForUser() {
return mInstallArgs == null;
}
@@ -406,14 +419,22 @@
return mClearCodeCache;
}
- public boolean isReplace() {
+ public boolean isInstallReplace() {
return mReplace;
}
- public boolean isSystem() {
+ public boolean isInstallSystem() {
return mSystem;
}
+ public boolean isInstallInherit() {
+ return mIsInstallInherit;
+ }
+
+ public boolean isInstallForUsers() {
+ return mIsInstallForUsers;
+ }
+
@Nullable
public PackageSetting getOriginalPackageSetting() {
return mOriginalPs;
@@ -520,6 +541,10 @@
return mScanResult.mRequest.mIsPlatformPackage;
}
+ public boolean isInstantInstall() {
+ return (mScanFlags & SCAN_AS_INSTANT_APP) != 0;
+ }
+
public void assertScanResultExists() {
if (mScanResult == null) {
// Should not happen. This indicates a bug in the installation code flow
@@ -529,7 +554,6 @@
Slog.e(TAG, "ScanResult is null and it should not happen");
}
}
-
}
public void setScanFlags(int scanFlags) {
@@ -658,4 +682,60 @@
mRemovedInfo.mRemovedAppId = appId;
}
}
+
+ public void onPrepareStarted() {
+ if (mPackageMetrics != null) {
+ mPackageMetrics.onStepStarted(PackageMetrics.STEP_PREPARE);
+ }
+ }
+
+ public void onPrepareFinished() {
+ if (mPackageMetrics != null) {
+ mPackageMetrics.onStepFinished(PackageMetrics.STEP_PREPARE);
+ }
+ }
+
+ public void onScanStarted() {
+ if (mPackageMetrics != null) {
+ mPackageMetrics.onStepStarted(PackageMetrics.STEP_SCAN);
+ }
+ }
+
+ public void onScanFinished() {
+ if (mPackageMetrics != null) {
+ mPackageMetrics.onStepFinished(PackageMetrics.STEP_SCAN);
+ }
+ }
+
+ public void onReconcileStarted() {
+ if (mPackageMetrics != null) {
+ mPackageMetrics.onStepStarted(PackageMetrics.STEP_RECONCILE);
+ }
+ }
+
+ public void onReconcileFinished() {
+ if (mPackageMetrics != null) {
+ mPackageMetrics.onStepFinished(PackageMetrics.STEP_RECONCILE);
+ }
+ }
+
+ public void onCommitStarted() {
+ if (mPackageMetrics != null) {
+ mPackageMetrics.onStepStarted(PackageMetrics.STEP_COMMIT);
+ }
+ }
+
+ public void onCommitFinished() {
+ if (mPackageMetrics != null) {
+ mPackageMetrics.onStepFinished(PackageMetrics.STEP_COMMIT);
+ }
+ }
+
+ public void onInstallCompleted() {
+ if (getReturnCode() == INSTALL_SUCCEEDED) {
+ if (mPackageMetrics != null) {
+ mPackageMetrics.onInstallSucceed();
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/InstallingSession.java b/services/core/java/com/android/server/pm/InstallingSession.java
index 88469f5..d13822a 100644
--- a/services/core/java/com/android/server/pm/InstallingSession.java
+++ b/services/core/java/com/android/server/pm/InstallingSession.java
@@ -17,6 +17,7 @@
package com.android.server.pm;
import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.content.pm.PackageInstaller.SessionParams.MODE_INHERIT_EXISTING;
import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
import static android.content.pm.PackageManager.INSTALL_STAGED;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
@@ -93,6 +94,7 @@
final PackageManagerService mPm;
final InstallPackageHelper mInstallPackageHelper;
final RemovePackageHelper mRemovePackageHelper;
+ final boolean mIsInherit;
InstallingSession(OriginInfo originInfo, MoveInfo moveInfo, IPackageInstallObserver2 observer,
int installFlags, InstallSource installSource, String volumeUuid,
@@ -121,6 +123,7 @@
mRequiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST;
mPackageSource = packageSource;
mPackageLite = packageLite;
+ mIsInherit = false;
}
InstallingSession(File stagedDir, IPackageInstallObserver2 observer,
@@ -151,6 +154,7 @@
mRequiredInstalledVersionCode = sessionParams.requiredInstalledVersionCode;
mPackageSource = sessionParams.packageSource;
mPackageLite = packageLite;
+ mIsInherit = sessionParams.mode == MODE_INHERIT_EXISTING;
}
@Override
@@ -506,6 +510,7 @@
mInstallPackageHelper.installPackagesTraced(installRequests);
for (InstallRequest request : installRequests) {
+ request.onInstallCompleted();
doPostInstall(request);
}
}
@@ -531,7 +536,7 @@
}
private void cleanUpForFailedInstall(InstallRequest request) {
- if (request.isMoveInstall()) {
+ if (request.isInstallMove()) {
mRemovePackageHelper.cleanUpForMoveInstall(request.getMoveToUuid(),
request.getMovePackageName(), request.getMoveFromCodePath());
} else {
diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java
index 4e8b0a1..66ef93d 100644
--- a/services/core/java/com/android/server/pm/PackageHandler.java
+++ b/services/core/java/com/android/server/pm/PackageHandler.java
@@ -95,7 +95,7 @@
request.closeFreezer();
request.runPostInstallRunnable();
- if (!request.isInstallForExistingUser()) {
+ if (!request.isInstallExistingForUser()) {
mInstallPackageHelper.handlePackagePostInstall(request, didRestore);
} else if (DEBUG_INSTALL) {
// No post-install when we run restore from installExistingPackageForUser
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 72ec510..5d13a45 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -380,6 +380,14 @@
@GuardedBy("mLock")
private boolean mStageDirInUse = false;
+ /**
+ * True if the installation is already in progress. This is used to prevent the caller
+ * from {@link #commit(IntentSender, boolean) committing} the session again while the
+ * installation is still in progress.
+ */
+ @GuardedBy("mLock")
+ private boolean mInstallationInProgress = false;
+
/** Permissions have been accepted by the user (see {@link #setPermissionsResult}) */
@GuardedBy("mLock")
private boolean mPermissionsManuallyAccepted = false;
@@ -1692,6 +1700,14 @@
}
}
+ synchronized (mLock) {
+ if (mInstallationInProgress) {
+ throw new IllegalStateException("Installation is already in progress. Don't "
+ + "commit session=" + sessionId + " again.");
+ }
+ mInstallationInProgress = true;
+ }
+
dispatchSessionSealed();
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 3c1cba3..9f21f11 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -3707,7 +3707,7 @@
fd = ParcelFileDescriptor.dup(getInFileDescriptor());
}
if (sizeBytes <= 0) {
- getErrPrintWriter().println("Error: must specify a APK size");
+ getErrPrintWriter().println("Error: must specify an APK size");
return 1;
}
diff --git a/services/core/java/com/android/server/pm/PackageMetrics.java b/services/core/java/com/android/server/pm/PackageMetrics.java
new file mode 100644
index 0000000..b725325
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageMetrics.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.annotation.IntDef;
+import android.os.UserManager;
+import android.util.Pair;
+import android.util.SparseArray;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+
+/**
+ * Metrics class for reporting stats to logging infrastructures like Westworld
+ */
+final class PackageMetrics {
+ public static final int STEP_PREPARE = 1;
+ public static final int STEP_SCAN = 2;
+ public static final int STEP_RECONCILE = 3;
+ public static final int STEP_COMMIT = 4;
+
+ @IntDef(prefix = {"STEP_"}, value = {
+ STEP_PREPARE,
+ STEP_SCAN,
+ STEP_RECONCILE,
+ STEP_COMMIT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StepInt {}
+
+ private final long mInstallStartTimestampMillis;
+ private final SparseArray<InstallStep> mInstallSteps = new SparseArray<>();
+ private final InstallRequest mInstallRequest;
+
+ PackageMetrics(InstallRequest installRequest) {
+ // New instance is used for tracking installation metrics only.
+ // Other metrics should use static methods of this class.
+ mInstallStartTimestampMillis = System.currentTimeMillis();
+ mInstallRequest = installRequest;
+ }
+
+ public void onInstallSucceed() {
+ final long installDurationMillis =
+ System.currentTimeMillis() - mInstallStartTimestampMillis;
+ // write to stats
+ final Pair<int[], long[]> stepDurations = getInstallStepDurations();
+ final int[] newUsers = mInstallRequest.getNewUsers();
+ final int[] originalUsers = mInstallRequest.getOriginUsers();
+ FrameworkStatsLog.write(FrameworkStatsLog.PACKAGE_INSTALLATION_SESSION_REPORTED,
+ 0 /* session_id */,
+ null /* package_name */,
+ mInstallRequest.getUid() /* uid */,
+ newUsers /* user_ids */,
+ getUserTypes(newUsers) /* user_types */,
+ originalUsers /* original_user_ids */,
+ getUserTypes(originalUsers) /* original_user_types */,
+ mInstallRequest.getReturnCode() /* public_return_code */,
+ 0 /* internal_error_code */,
+ 0 /* apks_size_bytes */,
+ 0 /* version_code */,
+ stepDurations.first /* install_steps */,
+ stepDurations.second /* step_duration_millis */,
+ installDurationMillis /* total_duration_millis */,
+ mInstallRequest.getInstallFlags() /* install_flags */,
+ -1 /* installer_package_uid */,
+ -1 /* original_installer_package_uid */,
+ mInstallRequest.getDataLoaderType() /* data_loader_type */,
+ 0 /* user_action_required_type */,
+ mInstallRequest.isInstantInstall() /* is_instant */,
+ mInstallRequest.isInstallReplace() /* is_replace */,
+ mInstallRequest.isInstallSystem() /* is_system */,
+ mInstallRequest.isInstallInherit() /* is_inherit */,
+ mInstallRequest.isInstallForUsers() /* is_installing_existing_as_user */,
+ mInstallRequest.isInstallMove() /* is_move_install */,
+ false /* is_staged */
+ );
+ }
+
+ public void onStepStarted(@StepInt int step) {
+ mInstallSteps.put(step, new InstallStep());
+ }
+
+ public void onStepFinished(@StepInt int step) {
+ final InstallStep installStep = mInstallSteps.get(step);
+ if (installStep != null) {
+ // Only valid if the start timestamp is set; otherwise no-op
+ installStep.finish();
+ }
+ }
+
+ // List of steps (e.g., 1, 2, 3) and corresponding list of durations (e.g., 200ms, 100ms, 150ms)
+ private Pair<int[], long[]> getInstallStepDurations() {
+ ArrayList<Integer> steps = new ArrayList<>();
+ ArrayList<Long> durations = new ArrayList<>();
+ for (int i = 0; i < mInstallSteps.size(); i++) {
+ final long duration = mInstallSteps.valueAt(i).getDurationMillis();
+ if (duration >= 0) {
+ steps.add(mInstallSteps.keyAt(i));
+ durations.add(mInstallSteps.valueAt(i).getDurationMillis());
+ }
+ }
+ int[] stepsArray = new int[steps.size()];
+ long[] durationsArray = new long[durations.size()];
+ for (int i = 0; i < stepsArray.length; i++) {
+ stepsArray[i] = steps.get(i);
+ durationsArray[i] = durations.get(i);
+ }
+ return new Pair<>(stepsArray, durationsArray);
+ }
+
+ private static int[] getUserTypes(int[] userIds) {
+ if (userIds == null) {
+ return null;
+ }
+ final int[] userTypes = new int[userIds.length];
+ for (int i = 0; i < userTypes.length; i++) {
+ String userType = UserManagerService.getInstance().getUserInfo(userIds[i]).userType;
+ userTypes[i] = UserManager.getUserTypeForStatsd(userType);
+ }
+ return userTypes;
+ }
+
+ private static class InstallStep {
+ private final long mStartTimestampMillis;
+ private long mDurationMillis = -1;
+ InstallStep() {
+ mStartTimestampMillis = System.currentTimeMillis();
+ }
+ void finish() {
+ mDurationMillis = System.currentTimeMillis() - mStartTimestampMillis;
+ }
+ long getDurationMillis() {
+ return mDurationMillis;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
index ffce69e..01d17f6 100644
--- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
+++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
@@ -67,6 +67,7 @@
new ArrayMap<>();
for (InstallRequest installRequest : installRequests) {
+ installRequest.onReconcileStarted();
final String installPackageName = installRequest.getParsedPackage().getPackageName();
// add / replace existing with incoming packages
@@ -90,7 +91,7 @@
final DeletePackageAction deletePackageAction;
// we only want to try to delete for non system apps
- if (installRequest.isReplace() && !installRequest.isSystem()) {
+ if (installRequest.isInstallReplace() && !installRequest.isInstallSystem()) {
final boolean killApp = (installRequest.getScanFlags() & SCAN_DONT_KILL_APP) == 0;
final int deleteFlags = PackageManager.DELETE_KEEP_DATA
| (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
@@ -286,6 +287,10 @@
}
}
+ for (InstallRequest installRequest : installRequests) {
+ installRequest.onReconcileFinished();
+ }
+
return result;
}
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index 56ec8e4..9155830 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -77,6 +77,23 @@
}
/**
+ * Listener for {@link UserManager#isUserVisible() user visibility} changes.
+ */
+ public interface UserVisibilityListener {
+
+ /**
+ * Called when the {@link UserManager#isUserVisible() user visibility} changed.
+ *
+ * <p><b>Note:</b> this method is called independently of
+ * {@link com.android.server.SystemService} callbacks; for example, the call with
+ * {@code visible} {@code true} might be called before the
+ * {@link com.android.server.SystemService#onUserStarting(com.android.server.SystemService.TargetUser)}
+ * call.
+ */
+ void onUserVisibilityChanged(@UserIdInt int userId, boolean visible);
+ }
+
+ /**
* Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} to set
* restrictions enforced by the user.
*
@@ -331,13 +348,18 @@
* <p>On most devices this call will be a no-op, but it will be used on devices that support
* multiple users on multiple displays (like automotives with passenger displays).
*
+ * <p><b>NOTE: </b>this method is meant to be used only by {@code UserController} (when a user
+ * is started)
+ *
* <p><b>NOTE: </b>this method doesn't validate if the display exists, it's up to the caller to
* check it. In fact, one of the intended clients for this method is
* {@code DisplayManagerService}, which will call it when a virtual display is created (another
* client is {@code UserController}, which will call it when a user is started).
- *
*/
- public abstract void assignUserToDisplay(@UserIdInt int userId, int displayId);
+ // TODO(b/244644281): rename to assignUserToDisplayOnStart() and make sure it's called on boot
+ // as well
+ public abstract void assignUserToDisplay(@UserIdInt int userId, @UserIdInt int profileGroupId,
+ boolean foreground, int displayId);
/**
* Unassigns a user from its current display.
@@ -346,7 +368,7 @@
* multiple users on multiple displays (like automotives with passenger displays).
*
* <p><b>NOTE: </b>this method is meant to be used only by {@code UserController} (when a user
- * is stopped) and {@code DisplayManagerService} (when a virtual display is destroyed).
+ * is stopped).
*/
public abstract void unassignUserFromDisplay(@UserIdInt int userId);
@@ -390,4 +412,13 @@
* would make such call).
*/
public abstract @UserIdInt int getUserAssignedToDisplay(int displayId);
+
+ /** Adds a {@link UserVisibilityListener}. */
+ public abstract void addUserVisibilityListener(UserVisibilityListener listener);
+
+ /** Removes a {@link UserVisibilityListener}. */
+ public abstract void removeUserVisibilityListener(UserVisibilityListener listener);
+
+ /** TODO(b/244333150): temporary method until UserVisibilityMediator handles that logic */
+ public abstract void onUserVisibilityChanged(@UserIdInt int userId, boolean visible);
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index f85b11d..d255669 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -96,6 +96,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.EventLog;
import android.util.IndentingPrintWriter;
import android.util.IntArray;
import android.util.Slog;
@@ -124,9 +125,11 @@
import com.android.server.LocalServices;
import com.android.server.LockGuard;
import com.android.server.SystemService;
+import com.android.server.am.EventLogTags;
import com.android.server.am.UserState;
import com.android.server.pm.UserManagerInternal.UserLifecycleListener;
import com.android.server.pm.UserManagerInternal.UserRestrictionsListener;
+import com.android.server.pm.UserManagerInternal.UserVisibilityListener;
import com.android.server.storage.DeviceStorageMonitorInternal;
import com.android.server.utils.Slogf;
import com.android.server.utils.TimingsTraceAndSlog;
@@ -504,6 +507,10 @@
@GuardedBy("mUserLifecycleListeners")
private final ArrayList<UserLifecycleListener> mUserLifecycleListeners = new ArrayList<>();
+ // TODO(b/244333150): temporary array, should belong to UserVisibilityMediator
+ @GuardedBy("mUserVisibilityListeners")
+ private final ArrayList<UserVisibilityListener> mUserVisibilityListeners = new ArrayList<>();
+
private final LockPatternUtils mLockPatternUtils;
private final String ACTION_DISABLE_QUIET_MODE_AFTER_UNLOCK =
@@ -626,7 +633,7 @@
@GuardedBy("mUserStates")
private final WatchedUserStates mUserStates = new WatchedUserStates();
- private final UserVisibilityMediator mUserVisibilityMediator;
+ private final UserVisibilityMediator mUserVisibilityMediator = new UserVisibilityMediator();
private static UserManagerService sInstance;
@@ -749,7 +756,6 @@
mUserStates.put(UserHandle.USER_SYSTEM, UserState.STATE_BOOTING);
mUser0Allocations = DBG_ALLOCATION ? new AtomicInteger() : null;
emulateSystemUserModeIfNeeded();
- mUserVisibilityMediator = new UserVisibilityMediator(this);
}
void systemReady() {
@@ -1776,7 +1782,7 @@
}
@Override
- public List<UserHandle> getVisibleUsers() {
+ public int[] getVisibleUsers() {
if (!hasManageUsersOrPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)) {
throw new SecurityException("Caller needs MANAGE_USERS or INTERACT_ACROSS_USERS "
+ "permission to get list of visible users");
@@ -1784,18 +1790,19 @@
final long ident = Binder.clearCallingIdentity();
try {
// TODO(b/2399825580): refactor into UserDisplayAssigner
+ IntArray visibleUsers;
synchronized (mUsersLock) {
int usersSize = mUsers.size();
- ArrayList<UserHandle> visibleUsers = new ArrayList<>(usersSize);
+ visibleUsers = new IntArray();
for (int i = 0; i < usersSize; i++) {
UserInfo ui = mUsers.valueAt(i).info;
if (!ui.partial && !ui.preCreated && !mRemovingUserIds.get(ui.id)
&& mUserVisibilityMediator.isUserVisible(ui.id)) {
- visibleUsers.add(UserHandle.of(ui.id));
+ visibleUsers.add(ui.id);
}
}
- return visibleUsers;
}
+ return visibleUsers.toArray();
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -5248,7 +5255,8 @@
Slog.w(LOG_TAG, "Unable to notify AppOpsService of removing user.", e);
}
- if (userData.info.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID) {
+ if (userData.info.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
+ && userData.info.isProfile()) {
sendProfileRemovedBroadcast(userData.info.profileGroupId, userData.info.id,
userData.info.userType);
}
@@ -6146,7 +6154,7 @@
dumpUser(pw, UserHandle.parseUserArg(args[1]), sb, now, nowRealtime);
return;
case "--visibility-mediator":
- mUserVisibilityMediator.dump(pw);
+ mUserVisibilityMediator.dump(pw, args);
return;
}
}
@@ -6212,7 +6220,7 @@
} // synchronized (mPackagesLock)
pw.println();
- mUserVisibilityMediator.dump(pw);
+ mUserVisibilityMediator.dump(pw, args);
pw.println();
// Dump some capabilities
@@ -6249,6 +6257,9 @@
synchronized (mUserLifecycleListeners) {
pw.println(" user lifecycle events: " + mUserLifecycleListeners.size());
}
+ synchronized (mUserVisibilityListeners) {
+ pw.println(" user visibility events: " + mUserVisibilityListeners.size());
+ }
// Dump UserTypes
pw.println();
@@ -6788,13 +6799,16 @@
}
@Override
- public void assignUserToDisplay(@UserIdInt int userId, int displayId) {
- mUserVisibilityMediator.assignUserToDisplay(userId, displayId);
+ public void assignUserToDisplay(@UserIdInt int userId, @UserIdInt int profileGroupId,
+ boolean foreground, int displayId) {
+ mUserVisibilityMediator.startUser(userId, profileGroupId, foreground, displayId);
+ mUserVisibilityMediator.assignUserToDisplay(userId, profileGroupId, displayId);
}
@Override
public void unassignUserFromDisplay(@UserIdInt int userId) {
mUserVisibilityMediator.unassignUserFromDisplay(userId);
+ mUserVisibilityMediator.stopUser(userId);
}
@Override
@@ -6816,8 +6830,39 @@
public @UserIdInt int getUserAssignedToDisplay(int displayId) {
return mUserVisibilityMediator.getUserAssignedToDisplay(displayId);
}
+
+ @Override
+ public void addUserVisibilityListener(UserVisibilityListener listener) {
+ synchronized (mUserVisibilityListeners) {
+ mUserVisibilityListeners.add(listener);
+ }
+ }
+
+ @Override
+ public void removeUserVisibilityListener(UserVisibilityListener listener) {
+ synchronized (mUserVisibilityListeners) {
+ mUserVisibilityListeners.remove(listener);
+ }
+ }
+
+ @Override
+ public void onUserVisibilityChanged(@UserIdInt int userId, boolean visible) {
+ EventLog.writeEvent(EventLogTags.UM_USER_VISIBILITY_CHANGED, userId, visible ? 1 : 0);
+ mHandler.post(() -> {
+ UserVisibilityListener[] listeners;
+ synchronized (mUserVisibilityListeners) {
+ listeners = new UserVisibilityListener[mUserVisibilityListeners.size()];
+ mUserVisibilityListeners.toArray(listeners);
+ }
+ for (UserVisibilityListener listener : listeners) {
+ listener.onUserVisibilityChanged(userId, visible);
+ }
+ });
+ }
} // class LocalService
+
+
/**
* Check if user has restrictions
* @param restriction restrictions to check
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index dbd026e..27d74d5 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -148,7 +148,8 @@
UserManager.DISALLOW_SHARING_ADMIN_CONFIGURED_WIFI,
UserManager.DISALLOW_WIFI_DIRECT,
UserManager.DISALLOW_ADD_WIFI_CONFIG,
- UserManager.DISALLOW_CELLULAR_2G
+ UserManager.DISALLOW_CELLULAR_2G,
+ UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO
});
public static final Set<String> DEPRECATED_USER_RESTRICTIONS = Sets.newArraySet(
@@ -197,7 +198,8 @@
UserManager.DISALLOW_WIFI_TETHERING,
UserManager.DISALLOW_WIFI_DIRECT,
UserManager.DISALLOW_ADD_WIFI_CONFIG,
- UserManager.DISALLOW_CELLULAR_2G
+ UserManager.DISALLOW_CELLULAR_2G,
+ UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO
);
/**
@@ -237,7 +239,8 @@
UserManager.DISALLOW_WIFI_TETHERING,
UserManager.DISALLOW_WIFI_DIRECT,
UserManager.DISALLOW_ADD_WIFI_CONFIG,
- UserManager.DISALLOW_CELLULAR_2G
+ UserManager.DISALLOW_CELLULAR_2G,
+ UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO
);
/**
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 5b6196f..c35fe17 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -129,7 +129,8 @@
.setDefaultUserProperties(new UserProperties.Builder()
.setStartWithParent(true)
.setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_WITH_PARENT)
- .setShowInSettings(UserProperties.SHOW_IN_SETTINGS_WITH_PARENT));
+ .setShowInSettings(UserProperties.SHOW_IN_SETTINGS_WITH_PARENT)
+ .setInheritDevicePolicy(UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT));
}
/**
diff --git a/services/core/java/com/android/server/pm/UserVisibilityMediator.java b/services/core/java/com/android/server/pm/UserVisibilityMediator.java
index f725c48..bd81062 100644
--- a/services/core/java/com/android/server/pm/UserVisibilityMediator.java
+++ b/services/core/java/com/android/server/pm/UserVisibilityMediator.java
@@ -15,10 +15,18 @@
*/
package com.android.server.pm;
+import static android.content.pm.UserInfo.NO_PROFILE_GROUP_ID;
+import static android.os.UserHandle.USER_NULL;
+import static android.os.UserHandle.USER_SYSTEM;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.DebugUtils;
+import android.util.Dumpable;
import android.util.IndentingPrintWriter;
import android.util.SparseIntArray;
import android.view.Display;
@@ -29,6 +37,8 @@
import com.android.server.utils.Slogf;
import java.io.PrintWriter;
+import java.util.LinkedHashMap;
+import java.util.Map;
/**
* Class responsible for deciding whether a user is visible (or visible for a given display).
@@ -36,72 +46,146 @@
* <p>This class is thread safe.
*/
// TODO(b/244644281): improve javadoc (for example, explain all cases / modes)
-public final class UserVisibilityMediator {
+public final class UserVisibilityMediator implements Dumpable {
private static final boolean DBG = false; // DO NOT SUBMIT WITH TRUE
private static final String TAG = UserVisibilityMediator.class.getSimpleName();
- private final Object mLock = new Object();
+ private static final String PREFIX_START_USER_RESULT = "START_USER_";
- // TODO(b/244644281): should not depend on service, but keep its own internal state (like
- // current user and profile groups), but it is initially as the code was just moved from UMS
- // "as is". Similarly, it shouldn't need to pass the SparseIntArray on constructor (which was
- // added to UMS for testing purposes)
- private final UserManagerService mService;
+ // TODO(b/242195409): might need to change this if boot logic is refactored for HSUM devices
+ @VisibleForTesting
+ static final int INITIAL_CURRENT_USER_ID = USER_SYSTEM;
+
+ public static final int START_USER_RESULT_SUCCESS_VISIBLE = 1;
+ public static final int START_USER_RESULT_SUCCESS_INVISIBLE = 2;
+ public static final int START_USER_RESULT_FAILURE = -1;
+
+ @IntDef(flag = false, prefix = {PREFIX_START_USER_RESULT}, value = {
+ START_USER_RESULT_SUCCESS_VISIBLE,
+ START_USER_RESULT_SUCCESS_INVISIBLE,
+ START_USER_RESULT_FAILURE
+ })
+ public @interface StartUserResult {}
+
+ private final Object mLock = new Object();
private final boolean mUsersOnSecondaryDisplaysEnabled;
+ @UserIdInt
+ @GuardedBy("mLock")
+ private int mCurrentUserId = INITIAL_CURRENT_USER_ID;
+
@Nullable
@GuardedBy("mLock")
- private final SparseIntArray mUsersOnSecondaryDisplays;
+ private final SparseIntArray mUsersOnSecondaryDisplays = new SparseIntArray();
- UserVisibilityMediator(UserManagerService service) {
- this(service, UserManager.isUsersOnSecondaryDisplaysEnabled(),
- /* usersOnSecondaryDisplays= */ null);
+ /**
+ * Mapping from each started user to its profile group.
+ */
+ @GuardedBy("mLock")
+ private final SparseIntArray mStartedProfileGroupIds = new SparseIntArray();
+
+ UserVisibilityMediator() {
+ this(UserManager.isUsersOnSecondaryDisplaysEnabled());
}
@VisibleForTesting
- UserVisibilityMediator(UserManagerService service, boolean usersOnSecondaryDisplaysEnabled,
- @Nullable SparseIntArray usersOnSecondaryDisplays) {
- mService = service;
+ UserVisibilityMediator(boolean usersOnSecondaryDisplaysEnabled) {
mUsersOnSecondaryDisplaysEnabled = usersOnSecondaryDisplaysEnabled;
- if (mUsersOnSecondaryDisplaysEnabled) {
- mUsersOnSecondaryDisplays = usersOnSecondaryDisplays == null
- ? new SparseIntArray() // default behavior
- : usersOnSecondaryDisplays; // passed by unit test
- } else {
- mUsersOnSecondaryDisplays = null;
+ }
+
+ /**
+ * TODO(b/244644281): merge with assignUserToDisplay() or add javadoc.
+ */
+ public @StartUserResult int startUser(@UserIdInt int userId, @UserIdInt int profileGroupId,
+ boolean foreground, int displayId) {
+ int actualProfileGroupId = profileGroupId == NO_PROFILE_GROUP_ID
+ ? userId
+ : profileGroupId;
+ if (DBG) {
+ Slogf.d(TAG, "startUser(%d, %d, %b, %d): actualProfileGroupId=%d",
+ userId, profileGroupId, foreground, displayId, actualProfileGroupId);
+ }
+ if (foreground && displayId != DEFAULT_DISPLAY) {
+ Slogf.w(TAG, "startUser(%d, %d, %b, %d) failed: cannot start foreground user on "
+ + "secondary display", userId, actualProfileGroupId, foreground, displayId);
+ return START_USER_RESULT_FAILURE;
+ }
+
+ int visibility;
+ synchronized (mLock) {
+ if (isProfile(userId, actualProfileGroupId)) {
+ if (displayId != DEFAULT_DISPLAY) {
+ Slogf.w(TAG, "startUser(%d, %d, %b, %d) failed: cannot start profile user on "
+ + "secondary display", userId, actualProfileGroupId, foreground,
+ displayId);
+ return START_USER_RESULT_FAILURE;
+ }
+ if (foreground) {
+ Slogf.w(TAG, "startUser(%d, %d, %b, %d) failed: cannot start profile user in "
+ + "foreground", userId, actualProfileGroupId, foreground,
+ displayId);
+ return START_USER_RESULT_FAILURE;
+ } else {
+ boolean isParentRunning = mStartedProfileGroupIds
+ .get(actualProfileGroupId) == actualProfileGroupId;
+ if (DBG) {
+ Slogf.d(TAG, "profile parent running: %b", isParentRunning);
+ }
+ visibility = isParentRunning
+ ? START_USER_RESULT_SUCCESS_VISIBLE
+ : START_USER_RESULT_SUCCESS_INVISIBLE;
+ }
+ } else if (foreground) {
+ mCurrentUserId = userId;
+ visibility = START_USER_RESULT_SUCCESS_VISIBLE;
+ } else {
+ visibility = START_USER_RESULT_SUCCESS_INVISIBLE;
+ }
+ if (DBG) {
+ Slogf.d(TAG, "adding user / profile mapping (%d -> %d) and returning %s",
+ userId, actualProfileGroupId, startUserResultToString(visibility));
+ }
+ mStartedProfileGroupIds.put(userId, actualProfileGroupId);
+ }
+ return visibility;
+ }
+
+ /**
+ * TODO(b/244644281): merge with unassignUserFromDisplay() or add javadoc (and unit tests)
+ */
+ public void stopUser(@UserIdInt int userId) {
+ if (DBG) {
+ Slogf.d(TAG, "stopUser(%d)", userId);
+ }
+ synchronized (mLock) {
+ mStartedProfileGroupIds.delete(userId);
}
}
/**
* See {@link UserManagerInternal#assignUserToDisplay(int, int)}.
*/
- public void assignUserToDisplay(int userId, int displayId) {
+ public void assignUserToDisplay(int userId, int profileGroupId, int displayId) {
if (DBG) {
- Slogf.d(TAG, "assignUserToDisplay(%d, %d)", userId, displayId);
+ Slogf.d(TAG, "assignUserToDisplay(%d, %d): mUsersOnSecondaryDisplaysEnabled=%b",
+ userId, displayId, mUsersOnSecondaryDisplaysEnabled);
}
- // NOTE: Using Boolean instead of boolean as it will be re-used below
- Boolean isProfile = null;
- if (displayId == Display.DEFAULT_DISPLAY) {
- if (mUsersOnSecondaryDisplaysEnabled) {
- // Profiles are only supported in the default display, but it cannot return yet
- // as it needs to check if the parent is also assigned to the DEFAULT_DISPLAY
- // (this is done indirectly below when it checks that the profile parent is the
- // current user, as the current user is always assigned to the DEFAULT_DISPLAY).
- isProfile = isProfileUnchecked(userId);
+ if (displayId == DEFAULT_DISPLAY
+ && (!mUsersOnSecondaryDisplaysEnabled || !isProfile(userId, profileGroupId))) {
+ // Don't need to do anything because methods (such as isUserVisible()) already
+ // know that the current user (and their profiles) is assigned to the default display.
+ // But on MUMD devices, it profiles are only supported in the default display, so it
+ // cannot return yet as it needs to check if the parent is also assigned to the
+ // DEFAULT_DISPLAY (this is done indirectly below when it checks that the profile parent
+ // is the current user, as the current user is always assigned to the DEFAULT_DISPLAY).
+ if (DBG) {
+ Slogf.d(TAG, "ignoring on default display");
}
- if (isProfile == null || !isProfile) {
- // Don't need to do anything because methods (such as isUserVisible()) already
- // know that the current user (and their profiles) is assigned to the default
- // display.
- if (DBG) {
- Slogf.d(TAG, "ignoring on default display");
- }
- return;
- }
+ return;
}
if (!mUsersOnSecondaryDisplaysEnabled) {
@@ -119,24 +203,21 @@
Preconditions.checkArgument(userId != currentUserId,
"Cannot assign current user (%d) to other displays", currentUserId);
- if (isProfile == null) {
- isProfile = isProfileUnchecked(userId);
- }
- synchronized (mLock) {
- if (isProfile) {
- // Profile can only start in the same display as parent. And for simplicity,
- // that display must be the DEFAULT_DISPLAY.
- Preconditions.checkArgument(displayId == Display.DEFAULT_DISPLAY,
- "Profile user can only be started in the default display");
- int parentUserId = getProfileParentId(userId);
- Preconditions.checkArgument(parentUserId == currentUserId,
- "Only profile of current user can be assigned to a display");
- if (DBG) {
- Slogf.d(TAG, "Ignoring profile user %d on default display", userId);
- }
- return;
+ if (isProfile(userId, profileGroupId)) {
+ // Profile can only start in the same display as parent. And for simplicity,
+ // that display must be the DEFAULT_DISPLAY.
+ Preconditions.checkArgument(displayId == Display.DEFAULT_DISPLAY,
+ "Profile user can only be started in the default display");
+ int parentUserId = getStartedProfileGroupId(userId);
+ Preconditions.checkArgument(parentUserId == currentUserId,
+ "Only profile of current user can be assigned to a display");
+ if (DBG) {
+ Slogf.d(TAG, "Ignoring profile user %d on default display", userId);
}
+ return;
+ }
+ synchronized (mLock) {
// Check if display is available
for (int i = 0; i < mUsersOnSecondaryDisplays.size(); i++) {
int assignedUserId = mUsersOnSecondaryDisplays.keyAt(i);
@@ -289,7 +370,7 @@
continue;
}
int userId = mUsersOnSecondaryDisplays.keyAt(i);
- if (!isProfileUnchecked(userId)) {
+ if (!isStartedProfile(userId)) {
return userId;
} else if (DBG) {
Slogf.d(TAG, "getUserAssignedToDisplay(%d): skipping user %d because it's "
@@ -307,23 +388,42 @@
}
private void dump(IndentingPrintWriter ipw) {
- ipw.println("UserVisibilityManager");
+ ipw.println("UserVisibilityMediator");
ipw.increaseIndent();
- ipw.print("Supports users on secondary displays: ");
- ipw.println(mUsersOnSecondaryDisplaysEnabled);
+ synchronized (mLock) {
+ ipw.print("Current user id: ");
+ ipw.println(mCurrentUserId);
- if (mUsersOnSecondaryDisplaysEnabled) {
- ipw.print("Users on secondary displays: ");
- synchronized (mLock) {
- ipw.println(mUsersOnSecondaryDisplays);
+ ipw.print("Number of started user / profile group mappings: ");
+ ipw.println(mStartedProfileGroupIds.size());
+ if (mStartedProfileGroupIds.size() > 0) {
+ ipw.increaseIndent();
+ for (int i = 0; i < mStartedProfileGroupIds.size(); i++) {
+ ipw.print("User #");
+ ipw.print(mStartedProfileGroupIds.keyAt(i));
+ ipw.print(" -> profile #");
+ ipw.println(mStartedProfileGroupIds.valueAt(i));
+ }
+ ipw.decreaseIndent();
+ }
+
+ ipw.print("Supports users on secondary displays: ");
+ ipw.println(mUsersOnSecondaryDisplaysEnabled);
+
+ if (mUsersOnSecondaryDisplaysEnabled) {
+ ipw.print("Users on secondary displays: ");
+ synchronized (mLock) {
+ ipw.println(mUsersOnSecondaryDisplays);
+ }
}
}
ipw.decreaseIndent();
}
- void dump(PrintWriter pw) {
+ @Override
+ public void dump(PrintWriter pw, String[] args) {
if (pw instanceof IndentingPrintWriter) {
dump((IndentingPrintWriter) pw);
return;
@@ -331,20 +431,70 @@
dump(new IndentingPrintWriter(pw));
}
- // TODO(b/244644281): remove methods below once this class caches that state
- private @UserIdInt int getCurrentUserId() {
- return mService.getCurrentUserId();
+ @VisibleForTesting
+ Map<Integer, Integer> getUsersOnSecondaryDisplays() {
+ Map<Integer, Integer> map;
+ synchronized (mLock) {
+ int size = mUsersOnSecondaryDisplays.size();
+ map = new LinkedHashMap<>(size);
+ for (int i = 0; i < size; i++) {
+ map.put(mUsersOnSecondaryDisplays.keyAt(i), mUsersOnSecondaryDisplays.valueAt(i));
+ }
+ }
+ Slogf.v(TAG, "getUsersOnSecondaryDisplays(): returning %s", map);
+ return map;
}
- private boolean isCurrentUserOrRunningProfileOfCurrentUser(@UserIdInt int userId) {
- return mService.isCurrentUserOrRunningProfileOfCurrentUser(userId);
+ /**
+ * Gets the user-friendly representation of the {@code result}.
+ */
+ public static String startUserResultToString(@StartUserResult int result) {
+ return DebugUtils.constantToString(UserVisibilityMediator.class, PREFIX_START_USER_RESULT,
+ result);
}
- private boolean isProfileUnchecked(@UserIdInt int userId) {
- return mService.isProfileUnchecked(userId);
+ // TODO(b/244644281): methods below are needed because some APIs use the current users (full and
+ // profiles) state to decide whether a user is visible or not. If we decide to always store that
+ // info into intermediate maps, we should remove them.
+
+ @VisibleForTesting
+ @UserIdInt int getCurrentUserId() {
+ synchronized (mLock) {
+ return mCurrentUserId;
+ }
}
- private @UserIdInt int getProfileParentId(@UserIdInt int userId) {
- return mService.getProfileParentId(userId);
+ @VisibleForTesting
+ boolean isCurrentUserOrRunningProfileOfCurrentUser(@UserIdInt int userId) {
+ synchronized (mLock) {
+ // Special case as NO_PROFILE_GROUP_ID == USER_NULL
+ if (userId == USER_NULL || mCurrentUserId == USER_NULL) {
+ return false;
+ }
+ if (mCurrentUserId == userId) {
+ return true;
+ }
+ return mStartedProfileGroupIds.get(userId, NO_PROFILE_GROUP_ID) == mCurrentUserId;
+ }
+ }
+
+ private static boolean isProfile(@UserIdInt int userId, @UserIdInt int profileGroupId) {
+ return profileGroupId != NO_PROFILE_GROUP_ID && profileGroupId != userId;
+ }
+
+ @VisibleForTesting
+ boolean isStartedProfile(@UserIdInt int userId) {
+ int profileGroupId;
+ synchronized (mLock) {
+ profileGroupId = mStartedProfileGroupIds.get(userId, NO_PROFILE_GROUP_ID);
+ }
+ return isProfile(userId, profileGroupId);
+ }
+
+ @VisibleForTesting
+ @UserIdInt int getStartedProfileGroupId(@UserIdInt int userId) {
+ synchronized (mLock) {
+ return mStartedProfileGroupIds.get(userId, NO_PROFILE_GROUP_ID);
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
index 905bcf9..1407530 100644
--- a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
+++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
@@ -320,12 +320,15 @@
public static class BackgroundDexoptJobStatsLogger {
/** Writes background dexopt job stats to statsd. */
public void write(@BackgroundDexOptService.Status int status,
- @JobParameters.StopReason int cancellationReason, long durationMs,
- long durationIncludingSleepMs) {
- ArtStatsLog.write(ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED,
+ @JobParameters.StopReason int cancellationReason,
+ long durationMs) {
+ ArtStatsLog.write(
+ ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED,
STATUS_MAP.getOrDefault(status,
ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__STATUS__STATUS_UNKNOWN),
- cancellationReason, durationMs, durationIncludingSleepMs);
+ cancellationReason,
+ durationMs,
+ 0); // deprecated, used to be durationIncludingSleepMs
}
}
}
diff --git a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
index 661161f..2a65a01 100644
--- a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
+++ b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
@@ -16,17 +16,18 @@
package com.android.server.pm.permission;
-import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED;
-
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.AlarmManager;
+import android.app.IActivityManager;
+import android.app.IUidObserver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Handler;
+import android.os.RemoteException;
import android.permission.PermissionControllerManager;
import android.provider.DeviceConfig;
import android.util.Log;
@@ -48,7 +49,7 @@
"one_time_permissions_killed_delay_millis";
private final @NonNull Context mContext;
- private final @NonNull ActivityManager mActivityManager;
+ private final @NonNull IActivityManager mIActivityManager;
private final @NonNull AlarmManager mAlarmManager;
private final @NonNull PermissionControllerManager mPermissionControllerManager;
@@ -78,50 +79,15 @@
OneTimePermissionUserManager(@NonNull Context context) {
mContext = context;
- mActivityManager = context.getSystemService(ActivityManager.class);
+ mIActivityManager = ActivityManager.getService();
mAlarmManager = context.getSystemService(AlarmManager.class);
mPermissionControllerManager = new PermissionControllerManager(
mContext, PermissionThread.getHandler());
mHandler = context.getMainThreadHandler();
}
- /**
- * Starts a one-time permission session for a given package. A one-time permission session is
- * ended if app becomes inactive. Inactivity is defined as the package's uid importance level
- * staying > importanceToResetTimer for timeoutMillis milliseconds. If the package's uid
- * importance level goes <= importanceToResetTimer then the timer is reset and doesn't start
- * until going > importanceToResetTimer.
- * <p>
- * When this timeoutMillis is reached if the importance level is <= importanceToKeepSessionAlive
- * then the session is extended until either the importance goes above
- * importanceToKeepSessionAlive which will end the session or <= importanceToResetTimer which
- * will continue the session and reset the timer.
- * </p>
- * <p>
- * Importance levels are defined in {@link android.app.ActivityManager.RunningAppProcessInfo}.
- * </p>
- * <p>
- * Once the session ends PermissionControllerService#onNotifyOneTimePermissionSessionTimeout
- * is invoked.
- * </p>
- * <p>
- * Note that if there is currently an active session for a package a new one isn't created and
- * the existing one isn't changed.
- * </p>
- * @param packageName The package to start a one-time permission session for
- * @param timeoutMillis Number of milliseconds for an app to be in an inactive state
- * @param revokeAfterKilledDelayMillis Number of milliseconds to wait after the process dies
- * before ending the session. Set to -1 to use default value
- * for the device.
- * @param importanceToResetTimer The least important level to uid must be to reset the timer
- * @param importanceToKeepSessionAlive The least important level the uid must be to keep the
- * session alive
- *
- * @hide
- */
void startPackageOneTimeSession(@NonNull String packageName, long timeoutMillis,
- long revokeAfterKilledDelayMillis, int importanceToResetTimer,
- int importanceToKeepSessionAlive) {
+ long revokeAfterKilledDelayMillis) {
int uid;
try {
uid = mContext.getPackageManager().getPackageUid(packageName, 0);
@@ -133,13 +99,11 @@
synchronized (mLock) {
PackageInactivityListener listener = mListeners.get(uid);
if (listener != null) {
- listener.updateSessionParameters(timeoutMillis, revokeAfterKilledDelayMillis,
- importanceToResetTimer, importanceToKeepSessionAlive);
+ listener.updateSessionParameters(timeoutMillis, revokeAfterKilledDelayMillis);
return;
}
listener = new PackageInactivityListener(uid, packageName, timeoutMillis,
- revokeAfterKilledDelayMillis, importanceToResetTimer,
- importanceToKeepSessionAlive);
+ revokeAfterKilledDelayMillis);
mListeners.put(uid, listener);
}
}
@@ -184,34 +148,58 @@
private static final long TIMER_INACTIVE = -1;
+ private static final int STATE_GONE = 0;
+ private static final int STATE_TIMER = 1;
+ private static final int STATE_ACTIVE = 2;
+
private final int mUid;
private final @NonNull String mPackageName;
private long mTimeout;
private long mRevokeAfterKilledDelay;
- private int mImportanceToResetTimer;
- private int mImportanceToKeepSessionAlive;
private boolean mIsAlarmSet;
private boolean mIsFinished;
private long mTimerStart = TIMER_INACTIVE;
- private final ActivityManager.OnUidImportanceListener mStartTimerListener;
- private final ActivityManager.OnUidImportanceListener mSessionKillableListener;
- private final ActivityManager.OnUidImportanceListener mGoneListener;
-
private final Object mInnerLock = new Object();
private final Object mToken = new Object();
+ private final IUidObserver.Stub mObserver = new IUidObserver.Stub() {
+ @Override
+ public void onUidGone(int uid, boolean disabled) {
+ if (uid == mUid) {
+ PackageInactivityListener.this.updateUidState(STATE_GONE);
+ }
+ }
+
+ @Override
+ public void onUidStateChanged(int uid, int procState, long procStateSeq,
+ int capability) {
+ if (uid == mUid) {
+ if (procState > ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
+ && procState != ActivityManager.PROCESS_STATE_NONEXISTENT) {
+ PackageInactivityListener.this.updateUidState(STATE_TIMER);
+ } else {
+ PackageInactivityListener.this.updateUidState(STATE_ACTIVE);
+ }
+ }
+ }
+
+ public void onUidActive(int uid) {
+ }
+ public void onUidIdle(int uid, boolean disabled) {
+ }
+ public void onUidProcAdjChanged(int uid) {
+ }
+ public void onUidCachedChanged(int uid, boolean cached) {
+ }
+ };
private PackageInactivityListener(int uid, @NonNull String packageName, long timeout,
- long revokeAfterkilledDelay, int importanceToResetTimer,
- int importanceToKeepSessionAlive) {
-
+ long revokeAfterkilledDelay) {
Log.i(LOG_TAG,
"Start tracking " + packageName + ". uid=" + uid + " timeout=" + timeout
- + " killedDelay=" + revokeAfterkilledDelay
- + " importanceToResetTimer=" + importanceToResetTimer
- + " importanceToKeepSessionAlive=" + importanceToKeepSessionAlive);
+ + " killedDelay=" + revokeAfterkilledDelay);
mUid = uid;
mPackageName = packageName;
@@ -221,27 +209,24 @@
DeviceConfig.NAMESPACE_PERMISSIONS, PROPERTY_KILLED_DELAY_CONFIG_KEY,
DEFAULT_KILLED_DELAY_MILLIS)
: revokeAfterkilledDelay;
- mImportanceToResetTimer = importanceToResetTimer;
- mImportanceToKeepSessionAlive = importanceToKeepSessionAlive;
- mStartTimerListener =
- (changingUid, importance) -> onImportanceChanged(changingUid, importance);
- mSessionKillableListener =
- (changingUid, importance) -> onImportanceChanged(changingUid, importance);
- mGoneListener =
- (changingUid, importance) -> onImportanceChanged(changingUid, importance);
+ try {
+ mIActivityManager.registerUidObserver(mObserver,
+ ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_PROCSTATE,
+ ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE,
+ null);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Couldn't check uid proc state", e);
+ // Can't register uid observer, just revoke immediately
+ synchronized (mInnerLock) {
+ onPackageInactiveLocked();
+ }
+ }
- mActivityManager.addOnUidImportanceListener(mStartTimerListener,
- importanceToResetTimer);
- mActivityManager.addOnUidImportanceListener(mSessionKillableListener,
- importanceToKeepSessionAlive);
- mActivityManager.addOnUidImportanceListener(mGoneListener, IMPORTANCE_CACHED);
-
- onImportanceChanged(mUid, mActivityManager.getPackageImportance(packageName));
+ updateUidState();
}
- public void updateSessionParameters(long timeoutMillis, long revokeAfterKilledDelayMillis,
- int importanceToResetTimer, int importanceToKeepSessionAlive) {
+ public void updateSessionParameters(long timeoutMillis, long revokeAfterKilledDelayMillis) {
synchronized (mInnerLock) {
mTimeout = Math.min(mTimeout, timeoutMillis);
mRevokeAfterKilledDelay = Math.min(mRevokeAfterKilledDelay,
@@ -250,63 +235,79 @@
DeviceConfig.NAMESPACE_PERMISSIONS,
PROPERTY_KILLED_DELAY_CONFIG_KEY, DEFAULT_KILLED_DELAY_MILLIS)
: revokeAfterKilledDelayMillis);
- mImportanceToResetTimer = Math.min(importanceToResetTimer, mImportanceToResetTimer);
- mImportanceToKeepSessionAlive = Math.min(importanceToKeepSessionAlive,
- mImportanceToKeepSessionAlive);
Log.v(LOG_TAG,
"Updated params for " + mPackageName + ". timeout=" + mTimeout
- + " killedDelay=" + mRevokeAfterKilledDelay
- + " importanceToResetTimer=" + mImportanceToResetTimer
- + " importanceToKeepSessionAlive=" + mImportanceToKeepSessionAlive);
- onImportanceChanged(mUid, mActivityManager.getPackageImportance(mPackageName));
+ + " killedDelay=" + mRevokeAfterKilledDelay);
+ updateUidState();
}
}
- private void onImportanceChanged(int uid, int importance) {
- if (uid != mUid) {
- return;
+ private int getCurrentState() {
+ try {
+ return getStateFromProcState(mIActivityManager.getUidProcessState(mUid, null));
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Couldn't check uid proc state", e);
}
+ return STATE_GONE;
+ }
- Log.v(LOG_TAG, "Importance changed for " + mPackageName + " (" + mUid + ")."
- + " importance=" + importance);
+ private int getStateFromProcState(int procState) {
+ if (procState == ActivityManager.PROCESS_STATE_NONEXISTENT) {
+ return STATE_GONE;
+ } else {
+ if (procState > ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+ return STATE_TIMER;
+ } else {
+ return STATE_ACTIVE;
+ }
+ }
+ }
+
+ private void updateUidState() {
+ updateUidState(getCurrentState());
+ }
+
+ private void updateUidState(int state) {
+ Log.v(LOG_TAG, "Updating state for " + mPackageName + " (" + mUid + ")."
+ + " state=" + state);
synchronized (mInnerLock) {
// Remove any pending inactivity callback
mHandler.removeCallbacksAndMessages(mToken);
- if (importance > IMPORTANCE_CACHED) {
+ if (state == STATE_GONE) {
if (mRevokeAfterKilledDelay == 0) {
onPackageInactiveLocked();
return;
}
// Delay revocation in case app is restarting
mHandler.postDelayed(() -> {
- int imp = mActivityManager.getUidImportance(mUid);
- if (imp > IMPORTANCE_CACHED) {
- onPackageInactiveLocked();
- } else {
- if (DEBUG) {
- Log.d(LOG_TAG, "No longer gone after delayed revocation. "
- + "Rechecking for " + mPackageName + " (" + mUid + ").");
+ int currentState;
+ synchronized (mInnerLock) {
+ currentState = getCurrentState();
+ if (currentState == STATE_GONE) {
+ onPackageInactiveLocked();
+ return;
}
- onImportanceChanged(mUid, imp);
}
+ if (DEBUG) {
+ Log.d(LOG_TAG, "No longer gone after delayed revocation. "
+ + "Rechecking for " + mPackageName + " (" + mUid
+ + ").");
+ }
+ updateUidState(currentState);
}, mToken, mRevokeAfterKilledDelay);
return;
- }
- if (importance > mImportanceToResetTimer) {
+ } else if (state == STATE_TIMER) {
if (mTimerStart == TIMER_INACTIVE) {
if (DEBUG) {
Log.d(LOG_TAG, "Start the timer for "
+ mPackageName + " (" + mUid + ").");
}
mTimerStart = System.currentTimeMillis();
+ setAlarmLocked();
}
- } else {
+ } else if (state == STATE_ACTIVE) {
mTimerStart = TIMER_INACTIVE;
- }
- if (importance > mImportanceToKeepSessionAlive) {
- setAlarmLocked();
- } else {
cancelAlarmLocked();
}
}
@@ -320,19 +321,9 @@
mIsFinished = true;
cancelAlarmLocked();
try {
- mActivityManager.removeOnUidImportanceListener(mStartTimerListener);
- } catch (IllegalArgumentException e) {
- Log.e(LOG_TAG, "Could not remove start timer listener", e);
- }
- try {
- mActivityManager.removeOnUidImportanceListener(mSessionKillableListener);
- } catch (IllegalArgumentException e) {
- Log.e(LOG_TAG, "Could not remove session killable listener", e);
- }
- try {
- mActivityManager.removeOnUidImportanceListener(mGoneListener);
- } catch (IllegalArgumentException e) {
- Log.e(LOG_TAG, "Could not remove gone listener", e);
+ mIActivityManager.unregisterUidObserver(mObserver);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Unable to unregister uid observer.", e);
}
}
}
@@ -396,9 +387,11 @@
mPermissionControllerManager.notifyOneTimePermissionSessionTimeout(
mPackageName);
});
- mActivityManager.removeOnUidImportanceListener(mStartTimerListener);
- mActivityManager.removeOnUidImportanceListener(mSessionKillableListener);
- mActivityManager.removeOnUidImportanceListener(mGoneListener);
+ try {
+ mIActivityManager.unregisterUidObserver(mObserver);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Unable to unregister uid observer.", e);
+ }
synchronized (mLock) {
mListeners.remove(mUid);
}
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 1fa3b3b..5f9ab95 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -385,8 +385,7 @@
@Override
public void startOneTimePermissionSession(String packageName, @UserIdInt int userId,
- long timeoutMillis, long revokeAfterKilledDelayMillis, int importanceToResetTimer,
- int importanceToKeepSessionAlive) {
+ long timeoutMillis, long revokeAfterKilledDelayMillis) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS,
"Must hold " + Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS
@@ -396,8 +395,7 @@
final long token = Binder.clearCallingIdentity();
try {
getOneTimePermissionUserManager(userId).startPackageOneTimeSession(packageName,
- timeoutMillis, revokeAfterKilledDelayMillis, importanceToResetTimer,
- importanceToKeepSessionAlive);
+ timeoutMillis, revokeAfterKilledDelayMillis);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index 595c34c..f80ead6 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -1209,6 +1209,7 @@
public void printOwnersForPackage(@NonNull IndentingPrintWriter writer,
@Nullable String packageName, @Nullable @UserIdInt Integer userId)
throws NameNotFoundException {
+ mEnforcer.assertApprovedQuerent(mConnection.getCallingUid(), mProxy);
final Computer snapshot = mConnection.snapshot();
synchronized (mLock) {
if (packageName == null) {
@@ -1257,6 +1258,7 @@
@Override
public void printOwnersForDomains(@NonNull IndentingPrintWriter writer,
@NonNull List<String> domains, @Nullable @UserIdInt Integer userId) {
+ mEnforcer.assertApprovedQuerent(mConnection.getCallingUid(), mProxy);
final Computer snapshot = mConnection.snapshot();
synchronized (mLock) {
int size = domains.size();
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index ffb652e..e61effa 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -58,6 +58,7 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManagerInternal.PackageListObserver;
import android.content.pm.PermissionInfo;
+import android.content.pm.UserPackage;
import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
@@ -78,7 +79,6 @@
import android.util.ArraySet;
import android.util.Log;
import android.util.LongSparseLongArray;
-import android.util.Pair;
import android.util.Slog;
import android.util.SparseBooleanArray;
@@ -146,7 +146,7 @@
* scheduled for a package/user.
*/
@GuardedBy("mLock")
- private final ArraySet<Pair<String, Integer>> mIsPackageSyncsScheduled = new ArraySet<>();
+ private final ArraySet<UserPackage> mIsPackageSyncsScheduled = new ArraySet<>();
/**
* Whether an async {@link #resetAppOpPermissionsIfNotRequestedForUid} is currently
@@ -223,9 +223,11 @@
this::synchronizePackagePermissionsAndAppOpsAsyncForUser);
mAppOpsCallback = new IAppOpsCallback.Stub() {
- public void opChanged(int op, int uid, String packageName) {
- synchronizePackagePermissionsAndAppOpsAsyncForUser(packageName,
- UserHandle.getUserId(uid));
+ public void opChanged(int op, int uid, @Nullable String packageName) {
+ if (packageName != null) {
+ synchronizePackagePermissionsAndAppOpsAsyncForUser(packageName,
+ UserHandle.getUserId(uid));
+ }
resetAppOpPermissionsIfNotRequestedForUidAsync(uid);
}
};
@@ -372,7 +374,7 @@
@UserIdInt int changedUserId) {
if (isStarted(changedUserId)) {
synchronized (mLock) {
- if (mIsPackageSyncsScheduled.add(new Pair<>(packageName, changedUserId))) {
+ if (mIsPackageSyncsScheduled.add(UserPackage.of(changedUserId, packageName))) {
// TODO(b/165030092): migrate this to PermissionThread.getHandler().
// synchronizePackagePermissionsAndAppOpsForUser is a heavy operation.
// Dispatched on a PermissionThread, it interferes with user switch.
@@ -640,7 +642,7 @@
private void synchronizePackagePermissionsAndAppOpsForUser(@NonNull String packageName,
@UserIdInt int userId) {
synchronized (mLock) {
- mIsPackageSyncsScheduled.remove(new Pair<>(packageName, userId));
+ mIsPackageSyncsScheduled.remove(UserPackage.of(userId, packageName));
}
if (DEBUG) {
diff --git a/services/core/java/com/android/server/policy/SideFpsEventHandler.java b/services/core/java/com/android/server/policy/SideFpsEventHandler.java
index 8582f54..2d76c50 100644
--- a/services/core/java/com/android/server/policy/SideFpsEventHandler.java
+++ b/services/core/java/com/android/server/policy/SideFpsEventHandler.java
@@ -127,7 +127,7 @@
*/
public void notifyPowerPressed() {
Log.i(TAG, "notifyPowerPressed");
- if (mFingerprintManager == null) {
+ if (mFingerprintManager == null && mSideFpsEventHandlerReady.get()) {
mFingerprintManager = mContext.getSystemService(FingerprintManager.class);
}
if (mFingerprintManager == null) {
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index dfa1281..0d13831 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -24,6 +24,7 @@
import android.os.IBinder;
import android.os.IHintManager;
import android.os.IHintSession;
+import android.os.PerformanceHintManager;
import android.os.Process;
import android.os.RemoteException;
import android.util.ArrayMap;
@@ -147,6 +148,8 @@
private static native void nativeReportActualWorkDuration(
long halPtr, long[] actualDurationNanos, long[] timeStampNanos);
+ private static native void nativeSendHint(long halPtr, int hint);
+
private static native long nativeGetHintSessionPreferredRate();
/** Wrapper for HintManager.nativeInit */
@@ -186,6 +189,11 @@
timeStampNanos);
}
+ /** Wrapper for HintManager.sendHint */
+ public void halSendHint(long halPtr, int hint) {
+ nativeSendHint(halPtr, hint);
+ }
+
/** Wrapper for HintManager.nativeGetHintSessionPreferredRate */
public long halGetHintSessionPreferredRate() {
return nativeGetHintSessionPreferredRate();
@@ -475,6 +483,18 @@
}
}
+ @Override
+ public void sendHint(@PerformanceHintManager.Session.Hint int hint) {
+ synchronized (mLock) {
+ if (mHalSessionPtr == 0 || !updateHintAllowed()) {
+ return;
+ }
+ Preconditions.checkArgument(hint >= 0, "the hint ID the hint value should be"
+ + " greater than zero.");
+ mNativeWrapper.halSendHint(mHalSessionPtr, hint);
+ }
+ }
+
private void onProcStateChanged() {
updateHintAllowed();
}
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
index 90540b0..b1fc4f5 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
@@ -36,6 +36,7 @@
import android.os.Handler;
import android.os.SystemClock;
import android.service.timezone.TimeZoneProviderEvent;
+import android.service.timezone.TimeZoneProviderStatus;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -294,6 +295,12 @@
|| stateEnum == PROVIDER_STATE_DESTROYED;
}
+ /** Returns the status reported by the provider, if available. */
+ @Nullable
+ TimeZoneProviderStatus getReportedStatus() {
+ return event == null ? null : event.getTimeZoneProviderStatus();
+ }
+
@Override
public String toString() {
// this.provider is omitted deliberately to avoid recursion, since the provider holds
diff --git a/services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessor.java b/services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessor.java
index 8a6f927..aa2b74e 100644
--- a/services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessor.java
+++ b/services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessor.java
@@ -58,7 +58,7 @@
if (hasInvalidZones(event)) {
TimeZoneProviderStatus providerStatus = new TimeZoneProviderStatus.Builder(
event.getTimeZoneProviderStatus())
- .setTimeZoneResolutionStatus(OPERATION_STATUS_FAILED)
+ .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_FAILED)
.build();
return TimeZoneProviderEvent.createUncertainEvent(
event.getCreationElapsedMillis(), providerStatus);
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 6012993..d944a3b 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -655,7 +655,8 @@
}
private void registerSettingsChangeReceiver(IntentFilter intentFilter) {
- mContext.registerReceiver(mSettingChangeReceiver, intentFilter);
+ mContext.registerReceiver(mSettingChangeReceiver, intentFilter,
+ Context.RECEIVER_EXPORTED_UNAUDITED);
}
@Nullable
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 7dc4f97..b8cd8d9 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1157,6 +1157,8 @@
Slog.w(TAG, "WallpaperService is not connected yet");
return;
}
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
+ t.traceBegin("WPMS.connectLocked-" + wallpaper.wallpaperComponent);
if (DEBUG) Slog.v(TAG, "Adding window token: " + mToken);
mWindowManagerInternal.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId,
null /* options */);
@@ -1173,6 +1175,7 @@
false /* fromUser */, wallpaper, null /* reply */);
}
}
+ t.traceEnd();
}
void disconnectLocked() {
@@ -1322,6 +1325,8 @@
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
+ t.traceBegin("WPMS.onServiceConnected-" + name);
synchronized (mLock) {
if (mWallpaper.connection == this) {
mService = IWallpaperService.Stub.asInterface(service);
@@ -1338,6 +1343,7 @@
mContext.getMainThreadHandler().removeCallbacks(mDisconnectRunnable);
}
}
+ t.traceEnd();
}
@Override
@@ -1545,6 +1551,8 @@
public void engineShown(IWallpaperEngine engine) {
synchronized (mLock) {
if (mReply != null) {
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
+ t.traceBegin("WPMS.mReply.sendResult");
final long ident = Binder.clearCallingIdentity();
try {
mReply.sendResult(null);
@@ -1553,6 +1561,7 @@
} finally {
Binder.restoreCallingIdentity(ident);
}
+ t.traceEnd();
mReply = null;
}
}
@@ -3058,6 +3067,8 @@
return true;
}
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
+ t.traceBegin("WPMS.bindWallpaperComponentLocked-" + componentName);
try {
if (componentName == null) {
componentName = mDefaultWallpaperComponent;
@@ -3190,6 +3201,8 @@
}
Slog.w(TAG, msg);
return false;
+ } finally {
+ t.traceEnd();
}
return true;
}
@@ -3234,7 +3247,10 @@
}
private void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
+ t.traceBegin("WPMS.attachServiceLocked");
conn.forEachDisplayConnector(connector-> connector.connectLocked(conn, wallpaper));
+ t.traceEnd();
}
private void notifyCallbacksLocked(WallpaperData wallpaper) {
@@ -3360,6 +3376,8 @@
}
void saveSettingsLocked(int userId) {
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
+ t.traceBegin("WPMS.saveSettingsLocked-" + userId);
JournaledFile journal = makeJournaledFile(userId);
FileOutputStream fstream = null;
try {
@@ -3388,6 +3406,7 @@
IoUtils.closeQuietly(fstream);
journal.rollback();
}
+ t.traceEnd();
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 44b83096..4fef2a8 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -2225,8 +2225,7 @@
ProtoOutputStream proto = new ProtoOutputStream();
proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
long timeOffsetNs =
- TimeUnit.NANOSECONDS.convert(System.currentTimeMillis(),
- TimeUnit.NANOSECONDS)
+ TimeUnit.MILLISECONDS.toNanos(System.currentTimeMillis())
- SystemClock.elapsedRealtimeNanos();
proto.write(REAL_TO_ELAPSED_TIME_OFFSET_NANOS, timeOffsetNs);
mBuffer.writeTraceToFile(mTraceFile, proto);
diff --git a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
index d0c381e..21b241a 100644
--- a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
+++ b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
@@ -56,8 +56,11 @@
private static final int SURFACE_FLINGER_CALLBACK_WINDOWS_STABLE_TIMES_MS = 35;
// To avoid the surface flinger callbacks always comes within in 2 frames, then no windows
// are reported to the A11y framework, and the animation duration time is 500ms, so setting
- // this value as the max timeout value to force computing changed windows.
- private static final int WINDOWS_CHANGED_NOTIFICATION_MAX_DURATION_TIMES_MS = 500;
+ // this value as the max timeout value to force computing changed windows. However, since
+ // UiAutomator waits 500ms to determine that things are idle. Since we aren't actually idle,
+ // we need to reduce the timeout here a little so that we can deliver an updated state before
+ // UiAutomator reports idle based-on stale information.
+ private static final int WINDOWS_CHANGED_NOTIFICATION_MAX_DURATION_TIMES_MS = 450;
private static final float[] sTempFloats = new float[9];
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 59f37c2..7386a19 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -86,6 +86,7 @@
import com.android.internal.app.AssistUtils;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.Watchdog;
import com.android.server.pm.KnownPackages;
@@ -454,6 +455,39 @@
finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY;
if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY
|| (finishWithRootActivity && r == rootR)) {
+ ActivityRecord topActivity =
+ r.getTask().getTopNonFinishingActivity();
+ boolean passesAsmChecks = topActivity != null
+ && topActivity.getUid() == r.getUid();
+ if (!passesAsmChecks) {
+ Slog.i(TAG, "Finishing task from background. r: " + r);
+ FrameworkStatsLog.write(FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED,
+ /* caller_uid */
+ r.getUid(),
+ /* caller_activity_class_name */
+ r.info.name,
+ /* target_task_top_activity_uid */
+ topActivity == null ? -1 : topActivity.getUid(),
+ /* target_task_top_activity_class_name */
+ topActivity == null ? null : topActivity.info.name,
+ /* target_task_is_different */
+ false,
+ /* target_activity_uid */
+ -1,
+ /* target_activity_class_name */
+ null,
+ /* target_intent_action */
+ null,
+ /* target_intent_flags */
+ 0,
+ /* action */
+ FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED__ACTION__FINISH_TASK,
+ /* version */
+ 1,
+ /* multi_window */
+ false
+ );
+ }
// If requested, remove the task that is associated to this activity only if it
// was the root activity in the task. The result code and data is ignored
// because we don't support returning them across task boundaries. Also, to
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 0eee8a3..e5401f5 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1747,6 +1747,7 @@
}
prevDc.mClosingApps.remove(this);
+ prevDc.getDisplayPolicy().removeRelaunchingApp(this);
if (prevDc.mFocusedApp == this) {
prevDc.setFocusedApp(null);
@@ -3959,6 +3960,9 @@
void startRelaunching() {
if (mPendingRelaunchCount == 0) {
mRelaunchStartTime = SystemClock.elapsedRealtime();
+ if (mVisibleRequested) {
+ mDisplayContent.getDisplayPolicy().addRelaunchingApp(this);
+ }
}
clearAllDrawn();
@@ -3972,7 +3976,7 @@
mPendingRelaunchCount--;
if (mPendingRelaunchCount == 0 && !isClientVisible()) {
// Don't count if the client won't report drawn.
- mRelaunchStartTime = 0;
+ finishOrAbortReplacingWindow();
}
} else {
// Update keyguard flags upon finishing relaunch.
@@ -3993,7 +3997,12 @@
return;
}
mPendingRelaunchCount = 0;
+ finishOrAbortReplacingWindow();
+ }
+
+ void finishOrAbortReplacingWindow() {
mRelaunchStartTime = 0;
+ mDisplayContent.getDisplayPolicy().removeRelaunchingApp(this);
}
/**
@@ -5102,6 +5111,9 @@
mTaskSupervisor.onProcessActivityStateChanged(app, false /* forceBatch */);
}
logAppCompatState();
+ if (!visible) {
+ finishOrAbortReplacingWindow();
+ }
}
/**
@@ -7901,8 +7913,8 @@
}
@Override
- float getSizeCompatScale() {
- return hasSizeCompatBounds() ? mSizeCompatScale : super.getSizeCompatScale();
+ float getCompatScale() {
+ return hasSizeCompatBounds() ? mSizeCompatScale : super.getCompatScale();
}
@Override
@@ -9204,10 +9216,10 @@
+ " preserveWindow=" + preserveWindow);
if (andResume) {
EventLogTags.writeWmRelaunchResumeActivity(mUserId, System.identityHashCode(this),
- task.mTaskId, shortComponentName);
+ task.mTaskId, shortComponentName, Integer.toHexString(configChangeFlags));
} else {
EventLogTags.writeWmRelaunchActivity(mUserId, System.identityHashCode(this),
- task.mTaskId, shortComponentName);
+ task.mTaskId, shortComponentName, Integer.toHexString(configChangeFlags));
}
startFreezingScreenLocked(0);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index f070064..e6d9492 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1663,7 +1663,8 @@
}
final Task startedTask = mStartActivity.getTask();
if (newTask) {
- EventLogTags.writeWmCreateTask(mStartActivity.mUserId, startedTask.mTaskId);
+ EventLogTags.writeWmCreateTask(mStartActivity.mUserId, startedTask.mTaskId,
+ startedTask.getRootTaskId(), startedTask.getDisplayId());
}
mStartActivity.logStartActivity(EventLogTags.WM_CREATE_ACTIVITY, startedTask);
@@ -1856,6 +1857,11 @@
+ " from background: " + mSourceRecord
+ ". New task: " + newTask);
boolean newOrEmptyTask = newTask || (targetTopActivity == null);
+ int action = newTask
+ ? FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED__ACTION__ACTIVITY_START_NEW_TASK
+ : (mSourceRecord.getTask().equals(targetTask)
+ ? FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED__ACTION__ACTIVITY_START_SAME_TASK
+ : FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED__ACTION__ACTIVITY_START_DIFFERENT_TASK);
FrameworkStatsLog.write(FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED,
/* caller_uid */
callerUid,
@@ -1874,7 +1880,14 @@
/* target_intent_action */
r.intent.getAction(),
/* target_intent_flags */
- r.intent.getFlags()
+ r.intent.getFlags(),
+ /* action */
+ action,
+ /* version */
+ 1,
+ /* multi_window */
+ targetTask != null && !targetTask.equals(mSourceRecord.getTask())
+ && targetTask.isVisible()
);
}
}
@@ -2732,7 +2745,12 @@
newParent = candidateTf;
}
}
- newParent.mTransitionController.collect(newParent);
+ if (newParent.asTask() == null) {
+ // only collect task-fragments.
+ // TODO(b/258095975): we probably shouldn't ever collect the parent here since it isn't
+ // changing. The logic that changes it should collect it.
+ newParent.mTransitionController.collect(newParent);
+ }
if (mStartActivity.getTaskFragment() == null
|| mStartActivity.getTaskFragment() == newParent) {
newParent.addChild(mStartActivity, POSITION_TOP);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 4d970f0..ec48643 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -287,8 +287,10 @@
/**
* Called when the device changes its dreaming state.
+ *
+ * @param activeDreamComponent The currently active dream. If null, the device is not dreaming.
*/
- public abstract void notifyDreamStateChanged(boolean dreaming);
+ public abstract void notifyActiveDreamChanged(@Nullable ComponentName activeDreamComponent);
/**
* Set a uid that is allowed to bypass stopped app switches, launching an app
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index b153a85..7dd8770 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -209,7 +209,6 @@
import android.os.WorkSource;
import android.provider.Settings;
import android.service.dreams.DreamActivity;
-import android.service.dreams.DreamManagerInternal;
import android.service.voice.IVoiceInteractionSession;
import android.service.voice.VoiceInteractionManagerInternal;
import android.sysprop.DisplayProperties;
@@ -669,11 +668,12 @@
private volatile boolean mSleeping;
/**
- * The mDreaming state is set by the {@link DreamManagerService} when it receives a request to
- * start/stop the dream. It is set to true shortly before the {@link DreamService} is started.
- * It is set to false after the {@link DreamService} is stopped.
+ * The mActiveDreamComponent state is set by the {@link DreamManagerService} when it receives a
+ * request to start/stop the dream. It is set to the active dream shortly before the
+ * {@link DreamService} is started. It is set to null after the {@link DreamService} is stopped.
*/
- private volatile boolean mDreaming;
+ @Nullable
+ private volatile ComponentName mActiveDreamComponent;
/**
* The process state used for processes that are running the top activities.
@@ -1439,31 +1439,21 @@
}
boolean isDreaming() {
- return mDreaming;
+ return mActiveDreamComponent != null;
}
boolean canLaunchDreamActivity(String packageName) {
- if (!mDreaming || packageName == null) {
+ if (mActiveDreamComponent == null || packageName == null) {
ProtoLog.e(WM_DEBUG_DREAM, "Cannot launch dream activity due to invalid state. "
- + "dreaming: %b packageName: %s", mDreaming, packageName);
+ + "dream component: %s packageName: %s", mActiveDreamComponent, packageName);
return false;
}
- final DreamManagerInternal dreamManager =
- LocalServices.getService(DreamManagerInternal.class);
- // Verify that the package is the current active dream or doze component. The
- // getActiveDreamComponent() call path does not acquire the DreamManager lock and thus
- // is safe to use.
- final ComponentName activeDream = dreamManager.getActiveDreamComponent(false /* doze */);
- if (activeDream != null && packageName.equals(activeDream.getPackageName())) {
- return true;
- }
- final ComponentName activeDoze = dreamManager.getActiveDreamComponent(true /* doze */);
- if (activeDoze != null && packageName.equals(activeDoze.getPackageName())) {
+ if (packageName.equals(mActiveDreamComponent.getPackageName())) {
return true;
}
ProtoLog.e(WM_DEBUG_DREAM,
- "Dream packageName does not match active dream. Package %s does not match %s or %s",
- packageName, String.valueOf(activeDream), String.valueOf(activeDoze));
+ "Dream packageName does not match active dream. Package %s does not match %s",
+ packageName, String.valueOf(mActiveDreamComponent));
return false;
}
@@ -5680,9 +5670,9 @@
}
@Override
- public void notifyDreamStateChanged(boolean dreaming) {
+ public void notifyActiveDreamChanged(@Nullable ComponentName dreamComponent) {
synchronized (mGlobalLock) {
- mDreaming = dreaming;
+ mActiveDreamComponent = dreamComponent;
}
}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 92d19b0..bd22b32 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -76,7 +76,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.IntDef;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Rect;
import android.os.Trace;
@@ -167,16 +166,6 @@
? null : wallpaperTarget;
}
- @NonNull
- private static ArraySet<ActivityRecord> getAppsForAnimation(
- @NonNull ArraySet<ActivityRecord> apps, boolean excludeLauncherFromAnimation) {
- final ArraySet<ActivityRecord> appsForAnimation = new ArraySet<>(apps);
- if (excludeLauncherFromAnimation) {
- appsForAnimation.removeIf(ConfigurationContainer::isActivityTypeHome);
- }
- return appsForAnimation;
- }
-
/**
* Handle application transition for given display.
*/
@@ -226,45 +215,42 @@
mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(
mDisplayContent.mOpeningApps);
- // Remove launcher from app transition animation while recents is running. Recents animation
- // is managed outside of app transition framework, so we just need to commit visibility.
- final boolean excludeLauncherFromAnimation =
- mDisplayContent.mOpeningApps.stream().anyMatch(
- (app) -> app.isAnimating(PARENTS, ANIMATION_TYPE_RECENTS))
- || mDisplayContent.mClosingApps.stream().anyMatch(
- (app) -> app.isAnimating(PARENTS, ANIMATION_TYPE_RECENTS));
- final ArraySet<ActivityRecord> openingAppsForAnimation = getAppsForAnimation(
- mDisplayContent.mOpeningApps, excludeLauncherFromAnimation);
- final ArraySet<ActivityRecord> closingAppsForAnimation = getAppsForAnimation(
- mDisplayContent.mClosingApps, excludeLauncherFromAnimation);
+ ArraySet<ActivityRecord> tmpOpenApps = mDisplayContent.mOpeningApps;
+ ArraySet<ActivityRecord> tmpCloseApps = mDisplayContent.mClosingApps;
+ if (mDisplayContent.mAtmService.mBackNavigationController.isWaitBackTransition()) {
+ tmpOpenApps = new ArraySet<>(mDisplayContent.mOpeningApps);
+ tmpCloseApps = new ArraySet<>(mDisplayContent.mClosingApps);
+ if (mDisplayContent.mAtmService.mBackNavigationController
+ .removeIfContainsBackAnimationTargets(tmpOpenApps, tmpCloseApps)) {
+ mDisplayContent.mAtmService.mBackNavigationController.clearBackAnimations(null);
+ }
+ }
@TransitionOldType final int transit = getTransitCompatType(
- mDisplayContent.mAppTransition, openingAppsForAnimation, closingAppsForAnimation,
- mDisplayContent.mChangingContainers,
+ mDisplayContent.mAppTransition, tmpOpenApps,
+ tmpCloseApps, mDisplayContent.mChangingContainers,
mWallpaperControllerLocked.getWallpaperTarget(), getOldWallpaper(),
mDisplayContent.mSkipAppTransitionAnimation);
mDisplayContent.mSkipAppTransitionAnimation = false;
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"handleAppTransitionReady: displayId=%d appTransition={%s}"
- + " excludeLauncherFromAnimation=%b openingApps=[%s] closingApps=[%s] transit=%s",
- mDisplayContent.mDisplayId, appTransition.toString(), excludeLauncherFromAnimation,
- mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
- AppTransition.appTransitionOldToString(transit));
+ + " openingApps=[%s] closingApps=[%s] transit=%s",
+ mDisplayContent.mDisplayId, appTransition.toString(), tmpOpenApps,
+ tmpCloseApps, AppTransition.appTransitionOldToString(transit));
// Find the layout params of the top-most application window in the tokens, which is
// what will control the animation theme. If all closing windows are obscured, then there is
// no need to do an animation. This is the case, for example, when this transition is being
// done behind a dream window.
- final ArraySet<Integer> activityTypes = collectActivityTypes(openingAppsForAnimation,
- closingAppsForAnimation, mDisplayContent.mChangingContainers);
+ final ArraySet<Integer> activityTypes = collectActivityTypes(tmpOpenApps,
+ tmpCloseApps, mDisplayContent.mChangingContainers);
final ActivityRecord animLpActivity = findAnimLayoutParamsToken(transit, activityTypes,
- openingAppsForAnimation, closingAppsForAnimation,
- mDisplayContent.mChangingContainers);
+ tmpOpenApps, tmpCloseApps, mDisplayContent.mChangingContainers);
final ActivityRecord topOpeningApp =
- getTopApp(openingAppsForAnimation, false /* ignoreHidden */);
+ getTopApp(tmpOpenApps, false /* ignoreHidden */);
final ActivityRecord topClosingApp =
- getTopApp(closingAppsForAnimation, false /* ignoreHidden */);
+ getTopApp(tmpCloseApps, false /* ignoreHidden */);
final ActivityRecord topChangingApp =
getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */);
final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity);
@@ -276,14 +262,13 @@
overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);
}
- final boolean voiceInteraction = containsVoiceInteraction(closingAppsForAnimation)
- || containsVoiceInteraction(openingAppsForAnimation);
+ final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mClosingApps)
+ || containsVoiceInteraction(mDisplayContent.mOpeningApps);
final int layoutRedo;
mService.mSurfaceAnimationRunner.deferStartingAnimations();
try {
- applyAnimations(openingAppsForAnimation, closingAppsForAnimation, transit, animLp,
- voiceInteraction);
+ applyAnimations(tmpOpenApps, tmpCloseApps, transit, animLp, voiceInteraction);
handleClosingApps();
handleOpeningApps();
handleChangingApps(transit);
@@ -1200,6 +1185,11 @@
if (activity == null) {
continue;
}
+ if (activity.isAnimating(PARENTS, ANIMATION_TYPE_RECENTS)) {
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+ "Delaying app transition for recents animation to finish");
+ return false;
+ }
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"Check opening app=%s: allDrawn=%b startingDisplayed=%b "
+ "startingMoved=%b isRelaunching()=%b startingWindow=%s",
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index d1d39af..9398bbe 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -19,12 +19,13 @@
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.ComponentName;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
@@ -33,6 +34,7 @@
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.SystemProperties;
+import android.util.ArraySet;
import android.util.Slog;
import android.view.IWindowFocusObserver;
import android.view.RemoteAnimationTarget;
@@ -41,9 +43,7 @@
import android.window.BackNavigationInfo;
import android.window.IBackAnimationFinishedCallback;
import android.window.OnBackInvokedCallbackInfo;
-import android.window.ScreenCapture;
import android.window.TaskSnapshot;
-import android.window.WindowContainerToken;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
@@ -62,9 +62,9 @@
private boolean mShowWallpaper;
private Runnable mPendingAnimation;
- // TODO (b/241808055) Find a appropriate time to remove during refactor
- // Execute back animation with legacy transition system. Temporary flag for easier debugging.
- static final boolean ENABLE_SHELL_TRANSITIONS = WindowManagerService.sEnableShellTransitions;
+ private final AnimationTargets mAnimationTargets = new AnimationTargets();
+ private final ArrayList<WindowContainer> mTmpOpenApps = new ArrayList<>();
+ private final ArrayList<WindowContainer> mTmpCloseApps = new ArrayList<>();
/**
* true if the back predictability feature is enabled
@@ -269,10 +269,11 @@
removedWindowContainer,
BackNavigationInfo.typeToString(backType));
- // For now, we only animate when going home and cross task.
+ // For now, we only animate when going home, cross task or cross-activity.
boolean prepareAnimation =
(backType == BackNavigationInfo.TYPE_RETURN_TO_HOME
- || backType == BackNavigationInfo.TYPE_CROSS_TASK)
+ || backType == BackNavigationInfo.TYPE_CROSS_TASK
+ || backType == BackNavigationInfo.TYPE_CROSS_ACTIVITY)
&& adapter != null;
// Only prepare animation if no leash has been created (no animation is running).
@@ -285,7 +286,6 @@
}
if (prepareAnimation) {
- infoBuilder.setDepartingWCT(toWindowContainerToken(currentTask));
prepareAnimationIfNeeded(currentTask, prevTask, prevActivity,
removedWindowContainer, backType, adapter);
}
@@ -304,11 +304,233 @@
return infoBuilder.build();
}
- private static WindowContainerToken toWindowContainerToken(WindowContainer<?> windowContainer) {
- if (windowContainer == null || windowContainer.mRemoteToken == null) {
- return null;
+ boolean isWaitBackTransition() {
+ return mAnimationTargets.mComposed && mAnimationTargets.mWaitTransition;
+ }
+
+ // For legacy transition.
+ /**
+ * Once we find the transition targets match back animation targets, remove the target from
+ * list, so that transition won't count them in since the close animation was finished.
+ *
+ * @return {@code true} if the participants of this transition was animated by back gesture
+ * animations, and shouldn't join next transition.
+ */
+ boolean removeIfContainsBackAnimationTargets(ArraySet<ActivityRecord> openApps,
+ ArraySet<ActivityRecord> closeApps) {
+ if (!isWaitBackTransition()) {
+ return false;
}
- return windowContainer.mRemoteToken.toWindowContainerToken();
+ mTmpCloseApps.addAll(closeApps);
+ boolean result = false;
+ // Note: TmpOpenApps is empty. Unlike shell transition, the open apps will be removed from
+ // mOpeningApps if there is no visibility change.
+ if (mAnimationTargets.containsBackAnimationTargets(mTmpOpenApps, mTmpCloseApps)) {
+ // remove close target from close list, open target from open list;
+ // but the open target can be in close list.
+ for (int i = openApps.size() - 1; i >= 0; --i) {
+ final ActivityRecord ar = openApps.valueAt(i);
+ if (mAnimationTargets.isTarget(ar, true /* open */)) {
+ openApps.removeAt(i);
+ }
+ }
+ for (int i = closeApps.size() - 1; i >= 0; --i) {
+ final ActivityRecord ar = closeApps.valueAt(i);
+ if (mAnimationTargets.isTarget(ar, false /* open */)) {
+ closeApps.removeAt(i);
+ }
+ }
+ result = true;
+ }
+ mTmpCloseApps.clear();
+ return result;
+ }
+
+ // For shell transition
+ /**
+ * Check whether the transition targets was animated by back gesture animation.
+ * Because the opening target could request to do other stuff at onResume, so it could become
+ * close target for a transition. So the condition here is
+ * The closing target should only exist in close list, but the opening target can be either in
+ * open or close list.
+ * @return {@code true} if the participants of this transition was animated by back gesture
+ * animations, and shouldn't join next transition.
+ */
+ boolean containsBackAnimationTargets(Transition transition) {
+ if (!mAnimationTargets.mComposed
+ || (transition.mType != TRANSIT_CLOSE && transition.mType != TRANSIT_TO_BACK)) {
+ return false;
+ }
+ final ArraySet<WindowContainer> targets = transition.mParticipants;
+ for (int i = targets.size() - 1; i >= 0; --i) {
+ final WindowContainer wc = targets.valueAt(i);
+ if (wc.asActivityRecord() == null && wc.asTask() == null) {
+ continue;
+ }
+ // WC can be visible due to setLaunchBehind
+ if (wc.isVisibleRequested()) {
+ mTmpOpenApps.add(wc);
+ } else {
+ mTmpCloseApps.add(wc);
+ }
+ }
+ final boolean result = mAnimationTargets.containsBackAnimationTargets(
+ mTmpOpenApps, mTmpCloseApps);
+ mTmpOpenApps.clear();
+ mTmpCloseApps.clear();
+ return result;
+ }
+
+ boolean isMonitorTransitionTarget(WindowContainer wc) {
+ if (!mAnimationTargets.mComposed || !mAnimationTargets.mWaitTransition) {
+ return false;
+ }
+ return mAnimationTargets.isTarget(wc, wc.isVisibleRequested() /* open */);
+ }
+
+ /**
+ * Cleanup animation, this can either happen when transition ready or finish.
+ * @param cleanupTransaction The transaction which the caller want to apply the internal
+ * cleanup together.
+ */
+ void clearBackAnimations(SurfaceControl.Transaction cleanupTransaction) {
+ mAnimationTargets.clearBackAnimateTarget(cleanupTransaction);
+ }
+
+ /**
+ * TODO: Animation composer
+ * prepareAnimationIfNeeded will become too complicated in order to support
+ * ActivityRecord/WindowState, using a factory class to create the RemoteAnimationTargets for
+ * different scenario.
+ */
+ private static class AnimationTargets {
+ ActivityRecord mCloseTarget; // Must be activity
+ WindowContainer mOpenTarget; // Can be activity or task if activity was removed
+ private boolean mComposed;
+ private boolean mWaitTransition;
+ private int mSwitchType = UNKNOWN;
+ private SurfaceControl.Transaction mFinishedTransaction;
+
+ private static final int UNKNOWN = 0;
+ private static final int TASK_SWITCH = 1;
+ private static final int ACTIVITY_SWITCH = 2;
+
+ void reset(@NonNull WindowContainer close, @NonNull WindowContainer open) {
+ clearBackAnimateTarget(null);
+ if (close.asActivityRecord() != null && open.asActivityRecord() != null
+ && (close.asActivityRecord().getTask() == open.asActivityRecord().getTask())) {
+ mSwitchType = ACTIVITY_SWITCH;
+ mCloseTarget = close.asActivityRecord();
+ } else if (close.asTask() != null && open.asTask() != null
+ && close.asTask() != open.asTask()) {
+ mSwitchType = TASK_SWITCH;
+ mCloseTarget = close.asTask().getTopNonFinishingActivity();
+ } else {
+ mSwitchType = UNKNOWN;
+ return;
+ }
+
+ mOpenTarget = open;
+ mComposed = false;
+ mWaitTransition = false;
+ }
+
+ void composeNewAnimations(@NonNull WindowContainer close, @NonNull WindowContainer open) {
+ reset(close, open);
+ if (mSwitchType == UNKNOWN || mComposed || mCloseTarget == mOpenTarget
+ || mCloseTarget == null || mOpenTarget == null) {
+ return;
+ }
+ mComposed = true;
+ mWaitTransition = false;
+ }
+
+ boolean containTarget(ArrayList<WindowContainer> wcs, boolean open) {
+ for (int i = wcs.size() - 1; i >= 0; --i) {
+ if (isTarget(wcs.get(i), open)) {
+ return true;
+ }
+ }
+ return wcs.isEmpty();
+ }
+
+ boolean isTarget(WindowContainer wc, boolean open) {
+ if (open) {
+ return wc == mOpenTarget || mOpenTarget.hasChild(wc);
+ }
+ if (mSwitchType == TASK_SWITCH) {
+ return wc == mCloseTarget
+ || (wc.asTask() != null && wc.hasChild(mCloseTarget));
+ } else if (mSwitchType == ACTIVITY_SWITCH) {
+ return wc == mCloseTarget;
+ }
+ return false;
+ }
+
+ boolean setFinishTransaction(SurfaceControl.Transaction finishTransaction) {
+ if (!mComposed) {
+ return false;
+ }
+ mFinishedTransaction = finishTransaction;
+ return true;
+ }
+
+ void finishPresentAnimations(SurfaceControl.Transaction t) {
+ if (!mComposed) {
+ return;
+ }
+ final SurfaceControl.Transaction pt = t != null ? t
+ : mOpenTarget.getPendingTransaction();
+ if (mFinishedTransaction != null) {
+ pt.merge(mFinishedTransaction);
+ mFinishedTransaction = null;
+ }
+ }
+
+ void clearBackAnimateTarget(SurfaceControl.Transaction cleanupTransaction) {
+ finishPresentAnimations(cleanupTransaction);
+ mCloseTarget = null;
+ mOpenTarget = null;
+ mComposed = false;
+ mWaitTransition = false;
+ mSwitchType = UNKNOWN;
+ if (mFinishedTransaction != null) {
+ Slog.w(TAG, "Clear back animation, found un-processed finished transaction");
+ if (cleanupTransaction != null) {
+ cleanupTransaction.merge(mFinishedTransaction);
+ } else {
+ mFinishedTransaction.apply();
+ }
+ mFinishedTransaction = null;
+ }
+ }
+
+ // The close target must in close list
+ // The open target can either in close or open list
+ boolean containsBackAnimationTargets(ArrayList<WindowContainer> openApps,
+ ArrayList<WindowContainer> closeApps) {
+ return containTarget(closeApps, false /* open */)
+ && (containTarget(openApps, true /* open */)
+ || containTarget(openApps, false /* open */));
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder(128);
+ sb.append("AnimationTargets{");
+ sb.append(" mOpenTarget= ");
+ sb.append(mOpenTarget);
+ sb.append(" mCloseTarget= ");
+ sb.append(mCloseTarget);
+ sb.append(" mSwitchType= ");
+ sb.append(mSwitchType);
+ sb.append(" mComposed= ");
+ sb.append(mComposed);
+ sb.append(" mWaitTransition= ");
+ sb.append(mWaitTransition);
+ sb.append('}');
+ return sb.toString();
+ }
}
private void prepareAnimationIfNeeded(Task currentTask,
@@ -336,6 +558,7 @@
RemoteAnimationTarget behindAppTarget = null;
if (needsScreenshot(backType)) {
HardwareBuffer screenshotBuffer = null;
+ Task backTargetTask = prevTask;
switch(backType) {
case BackNavigationInfo.TYPE_CROSS_TASK:
int prevTaskId = prevTask != null ? prevTask.mTaskId : 0;
@@ -343,14 +566,10 @@
screenshotBuffer = getTaskSnapshot(prevTaskId, prevUserId);
break;
case BackNavigationInfo.TYPE_CROSS_ACTIVITY:
- //TODO(207481538) Remove once the infrastructure to support per-activity
- // screenshot is implemented. For now we simply have the mBackScreenshots hash
- // map that dumbly saves the screenshots.
- if (prevActivity != null
- && prevActivity.mActivityComponent != null) {
- screenshotBuffer =
- getActivitySnapshot(currentTask, prevActivity.mActivityComponent);
+ if (prevActivity != null && prevActivity.mActivityComponent != null) {
+ screenshotBuffer = getActivitySnapshot(currentTask, prevActivity);
}
+ backTargetTask = currentTask;
break;
}
@@ -369,17 +588,14 @@
// leash needs to be added before to be in the synchronized block.
startedTransaction.setLayer(topAppTarget.leash, 1);
- behindAppTarget = createRemoteAnimationTargetLocked(
- prevTask, screenshotSurface, MODE_OPENING);
+ behindAppTarget =
+ createRemoteAnimationTargetLocked(
+ backTargetTask, screenshotSurface, MODE_OPENING);
// reset leash after animation finished.
leashes.add(screenshotSurface);
}
} else if (prevTask != null) {
- if (!ENABLE_SHELL_TRANSITIONS) {
- // Special handling for preventing next transition.
- currentTask.mBackGestureStarted = true;
- }
prevActivity = prevTask.getTopNonFinishingActivity();
if (prevActivity != null) {
// Make previous task show from behind by marking its top activity as visible
@@ -420,35 +636,36 @@
for (SurfaceControl sc: leashes) {
finishedTransaction.remove(sc);
}
-
synchronized (mWindowManagerService.mGlobalLock) {
- if (ENABLE_SHELL_TRANSITIONS) {
- if (!triggerBack) {
- if (!needsScreenshot(backType)) {
- restoreLaunchBehind(finalPrevActivity);
- }
+ if (triggerBack) {
+ final SurfaceControl surfaceControl =
+ removedWindowContainer.getSurfaceControl();
+ if (surfaceControl != null && surfaceControl.isValid()) {
+ // The animation is finish and start waiting for transition,
+ // hide the task surface before it re-parented to avoid flicker.
+ finishedTransaction.hide(surfaceControl);
}
+ } else if (!needsScreenshot(backType)) {
+ restoreLaunchBehind(finalPrevActivity);
+ }
+ if (!mAnimationTargets.setFinishTransaction(finishedTransaction)) {
+ finishedTransaction.apply();
+ }
+ if (!triggerBack) {
+ mAnimationTargets.clearBackAnimateTarget(null);
} else {
- if (triggerBack) {
- final SurfaceControl surfaceControl =
- removedWindowContainer.getSurfaceControl();
- if (surfaceControl != null && surfaceControl.isValid()) {
- // When going back to home, hide the task surface before it
- // re-parented to avoid flicker.
- finishedTransaction.hide(surfaceControl);
- }
- } else {
- currentTask.mBackGestureStarted = false;
- if (!needsScreenshot(backType)) {
- restoreLaunchBehind(finalPrevActivity);
- }
- }
+ mAnimationTargets.mWaitTransition = true;
}
}
- finishedTransaction.apply();
+ // TODO Add timeout monitor if transition didn't happen
}
};
-
+ if (backType == BackNavigationInfo.TYPE_CROSS_ACTIVITY) {
+ mAnimationTargets.composeNewAnimations(removedWindowContainer, prevActivity);
+ } else if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME
+ || backType == BackNavigationInfo.TYPE_CROSS_TASK) {
+ mAnimationTargets.composeNewAnimations(removedWindowContainer, prevTask);
+ }
scheduleAnimationLocked(backType, targets, adapter, callback);
}
@@ -543,14 +760,8 @@
mShowWallpaper = false;
}
- private HardwareBuffer getActivitySnapshot(@NonNull Task task,
- ComponentName activityComponent) {
- // Check if we have a screenshot of the previous activity, indexed by its
- // component name.
- ScreenCapture.ScreenshotHardwareBuffer backBuffer = task.mBackScreenshots
- .get(activityComponent.flattenToString());
- return backBuffer != null ? backBuffer.getHardwareBuffer() : null;
-
+ private HardwareBuffer getActivitySnapshot(@NonNull Task task, ActivityRecord r) {
+ return task.getSnapshotForActivityRecord(r);
}
private HardwareBuffer getTaskSnapshot(int taskId, int userId) {
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 7bcea36..2589148 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -270,6 +270,12 @@
private final ArraySet<WindowState> mInsetsSourceWindowsExceptIme = new ArraySet<>();
+ /** Apps which are controlling the appearance of system bars */
+ private final ArraySet<ActivityRecord> mSystemBarColorApps = new ArraySet<>();
+
+ /** Apps which are relaunching and were controlling the appearance of system bars */
+ private final ArraySet<ActivityRecord> mRelaunchingSystemBarColorApps = new ArraySet<>();
+
private boolean mIsFreeformWindowOverlappingWithNavBar;
private boolean mLastImmersiveMode;
@@ -1449,6 +1455,7 @@
mStatusBarBackgroundWindows.clear();
mStatusBarColorCheckedBounds.setEmpty();
mStatusBarBackgroundCheckedBounds.setEmpty();
+ mSystemBarColorApps.clear();
mAllowLockscreenWhenOn = false;
mShowingDream = false;
@@ -1525,6 +1532,7 @@
win.mAttrs.insetsFlags.appearance & APPEARANCE_LIGHT_STATUS_BARS,
new Rect(win.getFrame())));
mStatusBarColorCheckedBounds.union(sTmpRect);
+ addSystemBarColorApp(win);
}
}
@@ -1537,6 +1545,7 @@
if (isOverlappingWithNavBar) {
if (mNavBarColorWindowCandidate == null) {
mNavBarColorWindowCandidate = win;
+ addSystemBarColorApp(win);
}
if (mNavBarBackgroundWindow == null) {
mNavBarBackgroundWindow = win;
@@ -1555,9 +1564,11 @@
}
} else if (win.isDimming()) {
if (mStatusBar != null) {
- addStatusBarAppearanceRegionsForDimmingWindow(
+ if (addStatusBarAppearanceRegionsForDimmingWindow(
win.mAttrs.insetsFlags.appearance & APPEARANCE_LIGHT_STATUS_BARS,
- mStatusBar.getFrame(), win.getBounds(), win.getFrame());
+ mStatusBar.getFrame(), win.getBounds(), win.getFrame())) {
+ addSystemBarColorApp(win);
+ }
}
if (isOverlappingWithNavBar && mNavBarColorWindowCandidate == null) {
mNavBarColorWindowCandidate = win;
@@ -1565,18 +1576,21 @@
}
}
- private void addStatusBarAppearanceRegionsForDimmingWindow(int appearance, Rect statusBarFrame,
- Rect winBounds, Rect winFrame) {
+ /**
+ * Returns true if mStatusBarAppearanceRegionList is changed.
+ */
+ private boolean addStatusBarAppearanceRegionsForDimmingWindow(
+ int appearance, Rect statusBarFrame, Rect winBounds, Rect winFrame) {
if (!sTmpRect.setIntersect(winBounds, statusBarFrame)) {
- return;
+ return false;
}
if (mStatusBarColorCheckedBounds.contains(sTmpRect)) {
- return;
+ return false;
}
if (appearance == 0 || !sTmpRect2.setIntersect(winFrame, statusBarFrame)) {
mStatusBarAppearanceRegionList.add(new AppearanceRegion(0, new Rect(winBounds)));
mStatusBarColorCheckedBounds.union(sTmpRect);
- return;
+ return true;
}
// A dimming window can divide status bar into different appearance regions (up to 3).
// +---------+-------------+---------+
@@ -1605,6 +1619,14 @@
// We don't have vertical status bar yet, so we don't handle the other orientation.
}
mStatusBarColorCheckedBounds.union(sTmpRect);
+ return true;
+ }
+
+ private void addSystemBarColorApp(WindowState win) {
+ final ActivityRecord app = win.mActivityRecord;
+ if (app != null) {
+ mSystemBarColorApps.add(app);
+ }
}
/**
@@ -1638,7 +1660,16 @@
*/
private void applyKeyguardPolicy(WindowState win, WindowState imeTarget) {
if (win.canBeHiddenByKeyguard()) {
- if (shouldBeHiddenByKeyguard(win, imeTarget)) {
+ final boolean shouldBeHiddenByKeyguard = shouldBeHiddenByKeyguard(win, imeTarget);
+ if (win.mIsImWindow) {
+ // Notify IME insets provider to freeze the IME insets. In case when turning off
+ // the screen, the IME insets source window will be hidden because of keyguard
+ // policy change and affects the system to freeze the last insets state. (And
+ // unfreeze when the IME is going to show)
+ mDisplayContent.getInsetsStateController().getImeSourceProvider().setFrozen(
+ shouldBeHiddenByKeyguard);
+ }
+ if (shouldBeHiddenByKeyguard) {
win.hide(false /* doAnimation */, true /* requestAnim */);
} else {
win.show(false /* doAnimation */, true /* requestAnim */);
@@ -2077,6 +2108,25 @@
return mDisplayContent.getInsetsPolicy();
}
+ /**
+ * Called when an app has started replacing its main window.
+ */
+ void addRelaunchingApp(ActivityRecord app) {
+ if (mSystemBarColorApps.contains(app)) {
+ mRelaunchingSystemBarColorApps.add(app);
+ }
+ }
+
+ /**
+ * Called when an app has finished replacing its main window or aborted.
+ */
+ void removeRelaunchingApp(ActivityRecord app) {
+ final boolean removed = mRelaunchingSystemBarColorApps.remove(app);
+ if (removed & mRelaunchingSystemBarColorApps.isEmpty()) {
+ updateSystemBarAttributes();
+ }
+ }
+
void resetSystemBarAttributes() {
mLastDisableFlags = 0;
updateSystemBarAttributes();
@@ -2119,6 +2169,11 @@
final int displayId = getDisplayId();
final int disableFlags = win.getDisableFlags();
final int opaqueAppearance = updateSystemBarsLw(win, disableFlags);
+ if (!mRelaunchingSystemBarColorApps.isEmpty()) {
+ // The appearance of system bars might change while relaunching apps. We don't report
+ // the intermediate state to system UI. Otherwise, it might trigger redundant effects.
+ return;
+ }
final WindowState navColorWin = chooseNavigationColorWindowLw(mNavBarColorWindowCandidate,
mDisplayContent.mInputMethodWindow, mNavigationBarPosition);
final boolean isNavbarColorManagedByIme =
@@ -2581,6 +2636,14 @@
pw.print(prefix); pw.print("mTopFullscreenOpaqueWindowState=");
pw.println(mTopFullscreenOpaqueWindowState);
}
+ if (!mSystemBarColorApps.isEmpty()) {
+ pw.print(prefix); pw.print("mSystemBarColorApps=");
+ pw.println(mSystemBarColorApps);
+ }
+ if (!mRelaunchingSystemBarColorApps.isEmpty()) {
+ pw.print(prefix); pw.print("mRelaunchingSystemBarColorApps=");
+ pw.println(mRelaunchingSystemBarColorApps);
+ }
if (mNavBarColorWindowCandidate != null) {
pw.print(prefix); pw.print("mNavBarColorWindowCandidate=");
pw.println(mNavBarColorWindowCandidate);
diff --git a/services/core/java/com/android/server/wm/EventLogTags.logtags b/services/core/java/com/android/server/wm/EventLogTags.logtags
index 1e5a219..d94bf4b 100644
--- a/services/core/java/com/android/server/wm/EventLogTags.logtags
+++ b/services/core/java/com/android/server/wm/EventLogTags.logtags
@@ -8,11 +8,11 @@
# An activity is being finished:
30001 wm_finish_activity (User|1|5),(Token|1|5),(Task ID|1|5),(Component Name|3),(Reason|3)
# A task is being brought to the front of the screen:
-30002 wm_task_to_front (User|1|5),(Task|1|5)
+30002 wm_task_to_front (User|1|5),(Task|1|5),(Display Id|1|5)
# An existing activity is being given a new intent:
30003 wm_new_intent (User|1|5),(Token|1|5),(Task ID|1|5),(Component Name|3),(Action|3),(MIME Type|3),(URI|3),(Flags|1|5)
# A new task is being created:
-30004 wm_create_task (User|1|5),(Task ID|1|5)
+30004 wm_create_task (User|1|5),(Task ID|1|5),(Root Task ID|1|5),(Display Id|1|5)
# A new activity is being created in an existing task:
30005 wm_create_activity (User|1|5),(Token|1|5),(Task ID|1|5),(Component Name|3),(Action|3),(MIME Type|3),(URI|3),(Flags|1|5)
# An activity has been resumed into the foreground but was not already running:
@@ -32,9 +32,9 @@
# An activity is being destroyed:
30018 wm_destroy_activity (User|1|5),(Token|1|5),(Task ID|1|5),(Component Name|3),(Reason|3)
# An activity has been relaunched, resumed, and is now in the foreground:
-30019 wm_relaunch_resume_activity (User|1|5),(Token|1|5),(Task ID|1|5),(Component Name|3)
+30019 wm_relaunch_resume_activity (User|1|5),(Token|1|5),(Task ID|1|5),(Component Name|3),(config mask|3)
# An activity has been relaunched:
-30020 wm_relaunch_activity (User|1|5),(Token|1|5),(Task ID|1|5),(Component Name|3)
+30020 wm_relaunch_activity (User|1|5),(Token|1|5),(Task ID|1|5),(Component Name|3),(config mask|3)
# Activity set to resumed
30043 wm_set_resumed_activity (User|1|5),(Component Name|3),(Reason|3)
@@ -45,9 +45,6 @@
# Attempting to stop an activity
30048 wm_stop_activity (User|1|5),(Token|1|5),(Component Name|3)
-# The task is being removed from its parent task
-30061 wm_remove_task (Task ID|1|5), (Root Task ID|1|5)
-
# An activity been add into stopping list
30066 wm_add_to_stopping (User|1|5),(Token|1|5),(Component Name|3),(Reason|3)
@@ -57,11 +54,11 @@
# Out of memory for surfaces.
31000 wm_no_surface_memory (Window|3),(PID|1|5),(Operation|3)
# Task created.
-31001 wm_task_created (TaskId|1|5),(RootTaskId|1|5)
+31001 wm_task_created (TaskId|1|5)
# Task moved to top (1) or bottom (0).
-31002 wm_task_moved (TaskId|1|5),(ToTop|1),(Index|1)
+31002 wm_task_moved (TaskId|1|5),(Root Task ID|1|5),(Display Id|1|5),(ToTop|1),(Index|1)
# Task removed with source explanation.
-31003 wm_task_removed (TaskId|1|5),(Reason|3)
+31003 wm_task_removed (TaskId|1|5),(Root Task ID|1|5),(Display Id|1|5),(Reason|3)
# bootanim finished:
31007 wm_boot_animation_done (time|2|3)
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 38eca35..554791a 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -55,6 +55,12 @@
private boolean mImeShowing;
private final InsetsSource mLastSource = new InsetsSource(ITYPE_IME);
+ /** @see #setFrozen(boolean) */
+ private boolean mFrozen;
+
+ /** @see #setServerVisible(boolean) */
+ private boolean mServerVisible;
+
ImeInsetsSourceProvider(InsetsSource source,
InsetsStateController stateController, DisplayContent displayContent) {
super(source, stateController, displayContent);
@@ -81,6 +87,32 @@
}
@Override
+ void setServerVisible(boolean serverVisible) {
+ mServerVisible = serverVisible;
+ if (!mFrozen) {
+ super.setServerVisible(serverVisible);
+ }
+ }
+
+ /**
+ * Freeze IME insets source state when required.
+ *
+ * When setting {@param frozen} as {@code true}, the IME insets provider will freeze the
+ * current IME insets state and pending the IME insets state update until setting
+ * {@param frozen} as {@code false}.
+ */
+ void setFrozen(boolean frozen) {
+ if (mFrozen == frozen) {
+ return;
+ }
+ mFrozen = frozen;
+ if (!frozen) {
+ // Unfreeze and process the pending IME insets states.
+ super.setServerVisible(mServerVisible);
+ }
+ }
+
+ @Override
void updateSourceFrame(Rect frame) {
super.updateSourceFrame(frame);
onSourceChanged();
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 7e56dbf..b9fa80c 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -26,6 +26,7 @@
import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN;
import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
+import static android.view.InsetsState.ITYPE_CAPTION_BAR;
import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_IME;
@@ -288,8 +289,9 @@
final boolean alwaysOnTop = token != null && token.isAlwaysOnTop();
// Always use windowing mode fullscreen when get insets for window metrics to make sure it
// contains all insets types.
- final InsetsState originalState = enforceInsetsPolicyForTarget(WINDOWING_MODE_FULLSCREEN,
- alwaysOnTop, attrs, mStateController.getRawInsetsState());
+ final InsetsState originalState = mDisplayContent.getInsetsPolicy()
+ .enforceInsetsPolicyForTarget(type, WINDOWING_MODE_FULLSCREEN, alwaysOnTop,
+ attrs.type, mStateController.getRawInsetsState());
InsetsState state = adjustVisibilityForTransientTypes(originalState);
return adjustInsetsForRoundedCorners(token, state, state == originalState);
}
@@ -341,42 +343,56 @@
/**
- * Modifies the given {@code state} according to the target's window state.
- * When performing layout of the target or dispatching insets to the target, we need to adjust
- * sources based on the target. e.g., the floating window will not receive system bars other
- * than caption, and some insets provider may request to override sizes for given window types.
- * Since the window type and the insets types provided by the window shall not change at
- * runtime, rotation doesn't matter in the layout params.
+ * Modifies the given {@code state} according to the {@code type} (Inset type) provided by
+ * the target.
+ * When performing layout of the target or dispatching insets to the target, we need to exclude
+ * sources which should not be visible to the target. e.g., the source which represents the
+ * target window itself, and the IME source when the target is above IME. We also need to
+ * exclude certain types of insets source for client within specific windowing modes.
*
+ * @param type the inset type provided by the target
* @param windowingMode the windowing mode of the target
* @param isAlwaysOnTop is the target always on top
- * @param attrs the layout params of the target
+ * @param windowType the type of the target
* @param state the input inset state containing all the sources
* @return The state stripped of the necessary information.
*/
- InsetsState enforceInsetsPolicyForTarget(@WindowConfiguration.WindowingMode int windowingMode,
- boolean isAlwaysOnTop, WindowManager.LayoutParams attrs, InsetsState state) {
+ InsetsState enforceInsetsPolicyForTarget(@InternalInsetsType int type,
+ @WindowConfiguration.WindowingMode int windowingMode, boolean isAlwaysOnTop,
+ int windowType, InsetsState state) {
boolean stateCopied = false;
- if (attrs.providedInsets != null && attrs.providedInsets.length > 0) {
+ if (type != ITYPE_INVALID) {
state = new InsetsState(state);
stateCopied = true;
- for (int i = attrs.providedInsets.length - 1; i >= 0; i--) {
- state.removeSource(attrs.providedInsets[i].type);
+ state.removeSource(type);
+
+ // Navigation bar doesn't get influenced by anything else
+ if (type == ITYPE_NAVIGATION_BAR || type == ITYPE_EXTRA_NAVIGATION_BAR) {
+ state.removeSource(ITYPE_STATUS_BAR);
+ state.removeSource(ITYPE_CLIMATE_BAR);
+ state.removeSource(ITYPE_CAPTION_BAR);
+ state.removeSource(ITYPE_NAVIGATION_BAR);
+ state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR);
+ }
+
+ // Status bar doesn't get influenced by caption bar
+ if (type == ITYPE_STATUS_BAR || type == ITYPE_CLIMATE_BAR) {
+ state.removeSource(ITYPE_CAPTION_BAR);
}
}
ArrayMap<Integer, WindowContainerInsetsSourceProvider> providers = mStateController
.getSourceProviders();
for (int i = providers.size() - 1; i >= 0; i--) {
WindowContainerInsetsSourceProvider otherProvider = providers.valueAt(i);
- if (otherProvider.overridesFrame(attrs.type)) {
+ if (otherProvider.overridesFrame(windowType)) {
if (!stateCopied) {
state = new InsetsState(state);
stateCopied = true;
}
InsetsSource override =
new InsetsSource(state.getSource(otherProvider.getSource().getType()));
- override.setFrame(otherProvider.getOverriddenFrame(attrs.type));
+ override.setFrame(otherProvider.getOverriddenFrame(windowType));
state.addSource(override);
}
}
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index ea82417..2dbccae 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -501,12 +501,16 @@
if (hasVisibleTaskbar(mainWindow)) {
cropBounds = new Rect(mActivityRecord.getBounds());
+
+ // Rounded corners should be displayed above the taskbar.
+ // It is important to call adjustBoundsForTaskbarUnchecked before offsetTo
+ // because taskbar bounds are in screen coordinates
+ adjustBoundsForTaskbarUnchecked(mainWindow, cropBounds);
+
// Activity bounds are in screen coordinates while (0,0) for activity's surface
// control is at the top left corner of an app window so offsetting bounds
// accordingly.
cropBounds.offsetTo(0, 0);
- // Rounded corners should be displayed above the taskbar.
- adjustBoundsForTaskbarUnchecked(mainWindow, cropBounds);
}
transaction
@@ -576,9 +580,8 @@
// Rounded corners should be displayed above the taskbar.
bounds.bottom =
Math.min(bounds.bottom, getTaskbarInsetsSource(mainWindow).getFrame().top);
- if (mActivityRecord.inSizeCompatMode()
- && mActivityRecord.getSizeCompatScale() < 1.0f) {
- bounds.scale(1.0f / mActivityRecord.getSizeCompatScale());
+ if (mActivityRecord.inSizeCompatMode() && mActivityRecord.getCompatScale() < 1.0f) {
+ bounds.scale(1.0f / mActivityRecord.getCompatScale());
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index d06f271..cdb3321 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -67,7 +67,6 @@
import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
-import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
@@ -607,12 +606,6 @@
boolean mLastSurfaceShowing = true;
- /**
- * Tracks if a back gesture is in progress.
- * Skips any system transition animations if this is set to {@code true}.
- */
- boolean mBackGestureStarted = false;
-
private Task(ActivityTaskManagerService atmService, int _taskId, Intent _intent,
Intent _affinityIntent, String _affinity, String _rootAffinity,
ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,
@@ -680,7 +673,7 @@
mLaunchCookie = _launchCookie;
mDeferTaskAppear = _deferTaskAppear;
mRemoveWithTaskOrganizer = _removeWithTaskOrganizer;
- EventLogTags.writeWmTaskCreated(mTaskId, isRootTask() ? INVALID_TASK_ID : getRootTaskId());
+ EventLogTags.writeWmTaskCreated(mTaskId);
}
static Task fromWindowContainerToken(WindowContainerToken token) {
@@ -1297,7 +1290,8 @@
}
void updateTaskMovement(boolean toTop, int position) {
- EventLogTags.writeWmTaskMoved(mTaskId, toTop ? 1 : 0, position);
+ EventLogTags.writeWmTaskMoved(mTaskId, getRootTaskId(), getDisplayId(), toTop ? 1 : 0,
+ position);
final TaskDisplayArea taskDisplayArea = getDisplayArea();
if (taskDisplayArea != null && isLeafTask()) {
taskDisplayArea.onLeafTaskMoved(this, toTop);
@@ -2560,7 +2554,7 @@
}
mRemoving = true;
- EventLogTags.writeWmTaskRemoved(mTaskId, reason);
+ EventLogTags.writeWmTaskRemoved(mTaskId, getRootTaskId(), getDisplayId(), reason);
clearPinnedTaskIfNeed();
// If applicable let the TaskOrganizer know the Task is vanishing.
setTaskOrganizer(null);
@@ -2573,7 +2567,8 @@
void reparent(Task rootTask, int position, boolean moveParents, String reason) {
if (DEBUG_ROOT_TASK) Slog.i(TAG, "reParentTask: removing taskId=" + mTaskId
+ " from rootTask=" + getRootTask());
- EventLogTags.writeWmTaskRemoved(mTaskId, "reParentTask:" + reason);
+ EventLogTags.writeWmTaskRemoved(mTaskId, getRootTaskId(), getDisplayId(),
+ "reParentTask:" + reason);
reparent(rootTask, position);
@@ -3331,14 +3326,6 @@
}
});
}
- } else if (mBackGestureStarted) {
- // Cancel playing transitions if a back navigation animation is in progress.
- // This bit is set by {@link BackNavigationController} when a back gesture is started.
- // It is used as a one-off transition overwrite that is cleared when the back gesture
- // is committed and triggers a transition, or when the gesture is cancelled.
- mBackGestureStarted = false;
- mDisplayContent.mSkipAppTransitionAnimation = true;
- ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Skipping app transition animation. task=%s", this);
} else {
super.applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, sources);
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index b6c14bb..6ff91af 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -455,7 +455,7 @@
}
mLastLeafTaskToFrontId = t.mTaskId;
- EventLogTags.writeWmTaskToFront(t.mUserId, t.mTaskId);
+ EventLogTags.writeWmTaskToFront(t.mUserId, t.mTaskId, getDisplayId());
// Notifying only when a leaf task moved to front. Or the listeners would be notified
// couple times from the leaf task all the way up to the root task.
mAtmService.getTaskChangeNotificationController().notifyTaskMovedToFront(t.getTaskInfo());
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 063f0db..872542a 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -79,6 +79,7 @@
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -1859,7 +1860,6 @@
super.addChild(child, index);
if (isAddingActivity && task != null) {
-
// TODO(b/207481538): temporary per-activity screenshoting
if (r != null && BackNavigationController.isScreenshotEnabled()) {
ProtoLog.v(WM_DEBUG_BACK_PREVIEW, "Screenshotting Activity %s",
@@ -2327,6 +2327,11 @@
if (mTaskFragmentOrganizer != null
&& (mLastSurfaceSize.x != 0 || mLastSurfaceSize.y != 0)) {
t.setWindowCrop(mSurfaceControl, 0, 0);
+ final SurfaceControl.Transaction syncTransaction = getSyncTransaction();
+ if (t != syncTransaction) {
+ // Avoid restoring to old window crop if the sync transaction is applied later.
+ syncTransaction.setWindowCrop(mSurfaceControl, 0, 0);
+ }
mLastSurfaceSize.set(0, 0);
}
}
@@ -2528,6 +2533,19 @@
return !mCreatedByOrganizer || mIsRemovalRequested;
}
+ @Nullable
+ HardwareBuffer getSnapshotForActivityRecord(@Nullable ActivityRecord r) {
+ if (!BackNavigationController.isScreenshotEnabled()) {
+ return null;
+ }
+ if (r != null && r.mActivityComponent != null) {
+ ScreenCapture.ScreenshotHardwareBuffer backBuffer =
+ mBackScreenshots.get(r.mActivityComponent.flattenToString());
+ return backBuffer != null ? backBuffer.getHardwareBuffer() : null;
+ }
+ return null;
+ }
+
@Override
void removeChild(WindowContainer child) {
removeChild(child, true /* removeSelfIfPossible */);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index a64bd69..ef68590 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -928,10 +928,16 @@
mFlags |= TRANSIT_FLAG_KEYGUARD_LOCKED;
}
+ // Check whether the participants were animated from back navigation.
+ final boolean markBackAnimated = mController.mAtm.mBackNavigationController
+ .containsBackAnimationTargets(this);
// Resolve the animating targets from the participants
mTargets = calculateTargets(mParticipants, mChanges);
final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, mChanges,
transaction);
+ if (markBackAnimated) {
+ mController.mAtm.mBackNavigationController.clearBackAnimations(mStartTransaction);
+ }
if (mOverrideOptions != null) {
info.setAnimationOptions(mOverrideOptions);
if (mOverrideOptions.getType() == ANIM_OPEN_CROSS_PROFILE_APPS) {
@@ -1935,9 +1941,20 @@
final Task task = wc.asTask();
if (task != null) {
final ActivityRecord topActivity = task.getTopNonFinishingActivity();
- if (topActivity != null && topActivity.mStartingData != null
- && topActivity.mStartingData.hasImeSurface()) {
- flags |= FLAG_WILL_IME_SHOWN;
+ if (topActivity != null) {
+ if (topActivity.mStartingData != null
+ && topActivity.mStartingData.hasImeSurface()) {
+ flags |= FLAG_WILL_IME_SHOWN;
+ }
+ if (topActivity.mAtmService.mBackNavigationController
+ .isMonitorTransitionTarget(topActivity)) {
+ flags |= TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
+ }
+ } else {
+ if (task.mAtmService.mBackNavigationController
+ .isMonitorTransitionTarget(task)) {
+ flags |= TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
+ }
}
if (task.voiceSession != null) {
flags |= FLAG_IS_VOICE_INTERACTION;
@@ -1951,6 +1968,10 @@
flags |= FLAG_IS_VOICE_INTERACTION;
}
flags |= record.mTransitionChangeFlags;
+ if (record.mAtmService.mBackNavigationController
+ .isMonitorTransitionTarget(record)) {
+ flags |= TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
+ }
}
final TaskFragment taskFragment = wc.asTaskFragment();
if (taskFragment != null && task == null) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 73d4496..5087a0b 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -3277,9 +3277,10 @@
void resetSurfacePositionForAnimationLeash(Transaction t) {
t.setPosition(mSurfaceControl, 0, 0);
- if (mSyncState != SYNC_STATE_NONE && t != mSyncTransaction) {
+ final SurfaceControl.Transaction syncTransaction = getSyncTransaction();
+ if (t != syncTransaction) {
// Avoid restoring to old position if the sync transaction is applied later.
- mSyncTransaction.setPosition(mSurfaceControl, 0, 0);
+ syncTransaction.setPosition(mSurfaceControl, 0, 0);
}
mLastSurfacePosition.set(0, 0);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 848c231..4080223 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -425,7 +425,7 @@
* @see #ENABLE_SHELL_TRANSITIONS
*/
public static final boolean sEnableShellTransitions =
- SystemProperties.getBoolean(ENABLE_SHELL_TRANSITIONS, false);
+ SystemProperties.getBoolean(ENABLE_SHELL_TRANSITIONS, true);
/**
* Allows a fullscreen windowing mode activity to launch in its desired orientation directly
@@ -1842,7 +1842,7 @@
// Make this invalid which indicates a null attached frame.
outAttachedFrame.set(0, 0, -1, -1);
}
- outSizeCompatScale[0] = win.getSizeCompatScale();
+ outSizeCompatScale[0] = win.getCompatScaleForClient();
}
Binder.restoreCallingIdentity(origId);
@@ -1928,22 +1928,14 @@
&& attachedWindow.mActivityRecord.mTargetSdk >= Build.VERSION_CODES.O;
} else {
// Otherwise, look at the package
- try {
- ApplicationInfo appInfo = mContext.getPackageManager()
- .getApplicationInfoAsUser(packageName, 0,
- UserHandle.getUserId(callingUid));
- if (appInfo.uid != callingUid) {
- throw new SecurityException("Package " + packageName + " not in UID "
- + callingUid);
- }
- if (appInfo.targetSdkVersion >= Build.VERSION_CODES.O) {
- return true;
- }
- } catch (PackageManager.NameNotFoundException e) {
- /* ignore */
+ final ApplicationInfo appInfo = mPmInternal.getApplicationInfo(
+ packageName, 0 /* flags */, SYSTEM_UID, UserHandle.getUserId(callingUid));
+ if (appInfo == null || appInfo.uid != callingUid) {
+ throw new SecurityException("Package " + packageName + " not in UID "
+ + callingUid);
}
+ return appInfo.targetSdkVersion >= Build.VERSION_CODES.O;
}
- return false;
}
/**
@@ -5295,7 +5287,6 @@
public static final int WINDOW_FREEZE_TIMEOUT = 11;
public static final int PERSIST_ANIMATION_SCALE = 14;
- public static final int FORCE_GC = 15;
public static final int ENABLE_SCREEN = 16;
public static final int APP_FREEZE_TIMEOUT = 17;
public static final int REPORT_WINDOWS_CHANGE = 19;
@@ -5386,26 +5377,6 @@
break;
}
- case FORCE_GC: {
- synchronized (mGlobalLock) {
- // Since we're holding both mWindowMap and mAnimator we don't need to
- // hold mAnimator.mLayoutToAnim.
- if (mAnimator.isAnimationScheduled()) {
- // If we are animating, don't do the gc now but
- // delay a bit so we don't interrupt the animation.
- sendEmptyMessageDelayed(H.FORCE_GC, 2000);
- return;
- }
- // If we are currently rotating the display, it will
- // schedule a new message when done.
- if (mDisplayFrozen) {
- return;
- }
- }
- Runtime.getRuntime().gc();
- break;
- }
-
case ENABLE_SCREEN: {
performEnableScreen();
break;
@@ -6264,14 +6235,6 @@
// now to catch that.
configChanged = displayContent != null && displayContent.updateOrientation();
- // A little kludge: a lot could have happened while the
- // display was frozen, so now that we are coming back we
- // do a gc so that any remote references the system
- // processes holds on others can be released if they are
- // no longer needed.
- mH.removeMessages(H.FORCE_GC);
- mH.sendEmptyMessageDelayed(H.FORCE_GC, 2000);
-
mScreenFrozenLock.release();
if (updateRotation && displayContent != null) {
@@ -8932,7 +8895,7 @@
outInsetsState.set(state, true /* copySources */);
if (WindowState.hasCompatScale(attrs, token, overrideScale)) {
final float compatScale = token != null && token.hasSizeCompatBounds()
- ? token.getSizeCompatScale() * overrideScale
+ ? token.getCompatScale() * overrideScale
: overrideScale;
outInsetsState.scale(1f / compatScale);
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 0168d50..2712cb7 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -25,10 +25,12 @@
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.graphics.GraphicsProtos.dumpPointProto;
import static android.hardware.display.DisplayManager.SWITCHING_TYPE_NONE;
+import static android.hardware.display.DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY;
import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static android.os.PowerManager.DRAW_WAKE_LOCK;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.InsetsState.ITYPE_IME;
+import static android.view.InsetsState.ITYPE_INVALID;
import static android.view.SurfaceControl.Transaction;
import static android.view.SurfaceControl.getGlobalTransaction;
import static android.view.ViewRootImpl.LOCAL_LAYOUT;
@@ -201,6 +203,7 @@
import android.graphics.RectF;
import android.graphics.Region;
import android.gui.TouchOcclusionMode;
+import android.hardware.display.DisplayManager;
import android.os.Binder;
import android.os.Build;
import android.os.Debug;
@@ -472,7 +475,7 @@
// Current transformation being applied.
float mGlobalScale = 1f;
float mInvGlobalScale = 1f;
- float mSizeCompatScale = 1f;
+ float mCompatScale = 1f;
final float mOverrideScale;
float mHScale = 1f, mVScale = 1f;
float mLastHScale = 1f, mLastVScale = 1f;
@@ -1253,19 +1256,21 @@
void updateGlobalScale() {
if (hasCompatScale()) {
- mSizeCompatScale = (mOverrideScale == 1f || mToken.hasSizeCompatBounds())
- ? mToken.getSizeCompatScale()
+ mCompatScale = (mOverrideScale == 1f || mToken.hasSizeCompatBounds())
+ ? mToken.getCompatScale()
: 1f;
- mGlobalScale = mSizeCompatScale * mOverrideScale;
+ mGlobalScale = mCompatScale * mOverrideScale;
mInvGlobalScale = 1f / mGlobalScale;
return;
}
- mGlobalScale = mInvGlobalScale = mSizeCompatScale = 1f;
+ mGlobalScale = mInvGlobalScale = mCompatScale = 1f;
}
- float getSizeCompatScale() {
- return mSizeCompatScale;
+ float getCompatScaleForClient() {
+ // If this window in the size compat mode. The scaling is fully controlled at the server
+ // side. The client doesn't need to take it into account.
+ return mToken.hasSizeCompatBounds() ? 1f : mCompatScale;
}
/**
@@ -1672,11 +1677,14 @@
if (rotatedState != null) {
return insetsPolicy.adjustInsetsForWindow(this, rotatedState);
}
+ final InsetsSourceProvider provider = getControllableInsetProvider();
+ final @InternalInsetsType int insetTypeProvidedByWindow = provider != null
+ ? provider.getSource().getType() : ITYPE_INVALID;
final InsetsState rawInsetsState =
mFrozenInsetsState != null ? mFrozenInsetsState : getMergedInsetsState();
final InsetsState insetsStateForWindow = insetsPolicy
- .enforceInsetsPolicyForTarget(
- getWindowingMode(), isAlwaysOnTop(), mAttrs, rawInsetsState);
+ .enforceInsetsPolicyForTarget(insetTypeProvidedByWindow,
+ getWindowingMode(), isAlwaysOnTop(), mAttrs.type, rawInsetsState);
return insetsPolicy.adjustInsetsForWindow(this, insetsStateForWindow,
includeTransient);
}
@@ -3859,7 +3867,8 @@
outFrames.attachedFrame.scale(mInvGlobalScale);
}
}
- outFrames.sizeCompatScale = mSizeCompatScale;
+
+ outFrames.compatScale = getCompatScaleForClient();
// Note: in the cases where the window is tied to an activity, we should not send a
// configuration update when the window has requested to be hidden. Doing so can lead to
@@ -5475,8 +5484,10 @@
// If refresh rate switching is disabled there is no point to set the frame rate on the
// surface as the refresh rate will be limited by display manager to a single value
// and SurfaceFlinger wouldn't be able to change it anyways.
- if (mWmService.mDisplayManagerInternal.getRefreshRateSwitchingType()
- != SWITCHING_TYPE_NONE) {
+ @DisplayManager.SwitchingType int refreshRateSwitchingType =
+ mWmService.mDisplayManagerInternal.getRefreshRateSwitchingType();
+ if (refreshRateSwitchingType != SWITCHING_TYPE_NONE
+ && refreshRateSwitchingType != SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY) {
final float refreshRate = refreshRatePolicy.getPreferredRefreshRate(this);
if (mAppPreferredFrameRate != refreshRate) {
mAppPreferredFrameRate = refreshRate;
@@ -6007,7 +6018,7 @@
final long duration =
SystemClock.elapsedRealtime() - mActivityRecord.mRelaunchStartTime;
Slog.i(TAG, "finishDrawing of relaunch: " + this + " " + duration + "ms");
- mActivityRecord.mRelaunchStartTime = 0;
+ mActivityRecord.finishOrAbortReplacingWindow();
}
if (mActivityRecord != null && mAttrs.type == TYPE_APPLICATION_STARTING) {
mWmService.mAtmService.mTaskSupervisor.getActivityMetricsLogger()
@@ -6099,8 +6110,7 @@
if (mRedrawForSyncReported) {
return false;
}
- // TODO(b/233286785): Remove mIsWallpaper once WallpaperService handles syncId of relayout.
- if (mInRelayout && !mIsWallpaper) {
+ if (mInRelayout && mPrepareSyncSeqId > 0) {
// The last sync seq id will return to the client, so there is no need to request the
// client to redraw.
return false;
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 7c481f5..f2527b6 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -258,7 +258,7 @@
* @return The scale for applications running in compatibility mode. Multiply the size in the
* application by this scale will be the size in the screen.
*/
- float getSizeCompatScale() {
+ float getCompatScale() {
return mDisplayContent.mCompatibleScreenScale;
}
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index efcca5d..416d042 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -355,7 +355,7 @@
ProtoOutputStream proto = new ProtoOutputStream();
proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
long timeOffsetNs =
- TimeUnit.NANOSECONDS.convert(System.currentTimeMillis(), TimeUnit.NANOSECONDS)
+ TimeUnit.MILLISECONDS.toNanos(System.currentTimeMillis())
- SystemClock.elapsedRealtimeNanos();
proto.write(REAL_TO_ELAPSED_TIME_OFFSET_NANOS, timeOffsetNs);
mBuffer.writeTraceToFile(mTraceFile, proto);
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 5fa8dcc..57b977c 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -152,7 +152,7 @@
"android.hardware.broadcastradio@1.0",
"android.hardware.broadcastradio@1.1",
"android.hardware.contexthub@1.0",
- "android.hardware.gnss-V2-cpp",
+ "android.hardware.gnss-V3-cpp",
"android.hardware.gnss@1.0",
"android.hardware.gnss@1.1",
"android.hardware.gnss@2.0",
@@ -170,7 +170,7 @@
"android.hardware.power@1.1",
"android.hardware.power@1.2",
"android.hardware.power@1.3",
- "android.hardware.power-V3-cpp",
+ "android.hardware.power-V4-cpp",
"android.hardware.power.stats@1.0",
"android.hardware.power.stats-V1-ndk",
"android.hardware.thermal@1.0",
diff --git a/services/core/jni/com_android_server_hint_HintManagerService.cpp b/services/core/jni/com_android_server_hint_HintManagerService.cpp
index 000cb83..d975760 100644
--- a/services/core/jni/com_android_server_hint_HintManagerService.cpp
+++ b/services/core/jni/com_android_server_hint_HintManagerService.cpp
@@ -34,6 +34,7 @@
#include "jni.h"
using android::hardware::power::IPowerHintSession;
+using android::hardware::power::SessionHint;
using android::hardware::power::WorkDuration;
using android::base::StringPrintf;
@@ -81,6 +82,11 @@
appSession->reportActualWorkDuration(actualDurations);
}
+static void sendHint(int64_t session_ptr, SessionHint hint) {
+ sp<IPowerHintSession> appSession = reinterpret_cast<IPowerHintSession*>(session_ptr);
+ appSession->sendHint(hint);
+}
+
static int64_t getHintSessionPreferredRate() {
int64_t rate = -1;
auto result = gPowerHalController.getHintSessionPreferredRate();
@@ -139,6 +145,10 @@
reportActualWorkDuration(session_ptr, actualList);
}
+static void nativeSendHint(JNIEnv* env, jclass /* clazz */, jlong session_ptr, jint hint) {
+ sendHint(session_ptr, static_cast<SessionHint>(hint));
+}
+
static jlong nativeGetHintSessionPreferredRate(JNIEnv* /* env */, jclass /* clazz */) {
return static_cast<jlong>(getHintSessionPreferredRate());
}
@@ -153,6 +163,7 @@
{"nativeCloseHintSession", "(J)V", (void*)nativeCloseHintSession},
{"nativeUpdateTargetWorkDuration", "(JJ)V", (void*)nativeUpdateTargetWorkDuration},
{"nativeReportActualWorkDuration", "(J[J[J)V", (void*)nativeReportActualWorkDuration},
+ {"nativeSendHint", "(JI)V", (void*)nativeSendHint},
{"nativeGetHintSessionPreferredRate", "()J", (void*)nativeGetHintSessionPreferredRate},
};
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 0d87237..3197124 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -450,6 +450,10 @@
dump += StringPrintf(INDENT "Pointer Capture: %s, seq=%" PRIu32 "\n",
mLocked.pointerCaptureRequest.enable ? "Enabled" : "Disabled",
mLocked.pointerCaptureRequest.seq);
+ auto pointerController = mLocked.pointerController.lock();
+ if (pointerController != nullptr) {
+ pointerController->dump(dump);
+ }
}
dump += "\n";
diff --git a/services/core/jni/gnss/Android.bp b/services/core/jni/gnss/Android.bp
index 0531ae2..f3ba484f62 100644
--- a/services/core/jni/gnss/Android.bp
+++ b/services/core/jni/gnss/Android.bp
@@ -61,7 +61,7 @@
"libnativehelper",
"libhardware_legacy",
"libutils",
- "android.hardware.gnss-V2-cpp",
+ "android.hardware.gnss-V3-cpp",
"android.hardware.gnss@1.0",
"android.hardware.gnss@1.1",
"android.hardware.gnss@2.0",
diff --git a/services/core/jni/gnss/GnssCallback.cpp b/services/core/jni/gnss/GnssCallback.cpp
index b931e91..3c1ac1e 100644
--- a/services/core/jni/gnss/GnssCallback.cpp
+++ b/services/core/jni/gnss/GnssCallback.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "GnssCallbckJni"
+#define LOG_TAG "GnssCallbackJni"
#include "GnssCallback.h"
@@ -31,6 +31,7 @@
using hardware::Void;
using GnssLocationAidl = android::hardware::gnss::GnssLocation;
+using GnssSignalType = android::hardware::gnss::GnssSignalType;
using GnssLocation_V1_0 = android::hardware::gnss::V1_0::GnssLocation;
using GnssLocation_V2_0 = android::hardware::gnss::V2_0::GnssLocation;
using IGnssCallbackAidl = android::hardware::gnss::IGnssCallback;
@@ -42,11 +43,18 @@
namespace {
+jclass class_arrayList;
+jclass class_gnssSignalType;
+
+jmethodID method_arrayListAdd;
+jmethodID method_arrayListCtor;
+jmethodID method_gnssSignalTypeCreate;
jmethodID method_reportLocation;
jmethodID method_reportStatus;
jmethodID method_reportSvStatus;
jmethodID method_reportNmea;
jmethodID method_setTopHalCapabilities;
+jmethodID method_setSignalTypeCapabilities;
jmethodID method_setGnssYearOfHardware;
jmethodID method_setGnssHardwareModelName;
jmethodID method_requestLocation;
@@ -88,6 +96,8 @@
method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(J)V");
method_setTopHalCapabilities = env->GetMethodID(clazz, "setTopHalCapabilities", "(I)V");
+ method_setSignalTypeCapabilities =
+ env->GetMethodID(clazz, "setSignalTypeCapabilities", "(Ljava/util/List;)V");
method_setGnssYearOfHardware = env->GetMethodID(clazz, "setGnssYearOfHardware", "(I)V");
method_setGnssHardwareModelName =
env->GetMethodID(clazz, "setGnssHardwareModelName", "(Ljava/lang/String;)V");
@@ -95,16 +105,58 @@
method_requestLocation = env->GetMethodID(clazz, "requestLocation", "(ZZ)V");
method_requestUtcTime = env->GetMethodID(clazz, "requestUtcTime", "()V");
method_reportGnssServiceDied = env->GetMethodID(clazz, "reportGnssServiceDied", "()V");
+
+ jclass arrayListClass = env->FindClass("java/util/ArrayList");
+ class_arrayList = (jclass)env->NewGlobalRef(arrayListClass);
+ method_arrayListCtor = env->GetMethodID(class_arrayList, "<init>", "()V");
+ method_arrayListAdd = env->GetMethodID(class_arrayList, "add", "(Ljava/lang/Object;)Z");
+
+ jclass gnssSignalTypeClass = env->FindClass("android/location/GnssSignalType");
+ class_gnssSignalType = (jclass)env->NewGlobalRef(gnssSignalTypeClass);
+ method_gnssSignalTypeCreate =
+ env->GetStaticMethodID(class_gnssSignalType, "create",
+ "(IDLjava/lang/String;)Landroid/location/GnssSignalType;");
}
Status GnssCallbackAidl::gnssSetCapabilitiesCb(const int capabilities) {
- ALOGD("GnssCallbackAidl::%s: %du\n", __func__, capabilities);
+ ALOGD("%s: %du\n", __func__, capabilities);
JNIEnv* env = getJniEnv();
env->CallVoidMethod(mCallbacksObj, method_setTopHalCapabilities, capabilities);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
return Status::ok();
}
+namespace {
+
+jobject translateSingleSignalType(JNIEnv* env, const GnssSignalType& signalType) {
+ jstring jstringCodeType = env->NewStringUTF(signalType.codeType.c_str());
+ jobject signalTypeObject =
+ env->CallStaticObjectMethod(class_gnssSignalType, method_gnssSignalTypeCreate,
+ signalType.constellation, signalType.carrierFrequencyHz,
+ jstringCodeType);
+ env->DeleteLocalRef(jstringCodeType);
+ return signalTypeObject;
+}
+
+} // anonymous namespace
+
+Status GnssCallbackAidl::gnssSetSignalTypeCapabilitiesCb(
+ const std::vector<GnssSignalType>& signalTypes) {
+ ALOGD("%s: %d signal types", __func__, (int)signalTypes.size());
+ JNIEnv* env = getJniEnv();
+ jobject arrayList = env->NewObject(class_arrayList, method_arrayListCtor);
+ for (auto& signalType : signalTypes) {
+ jobject signalTypeObject = translateSingleSignalType(env, signalType);
+ env->CallBooleanMethod(arrayList, method_arrayListAdd, signalTypeObject);
+ // Delete Local Refs
+ env->DeleteLocalRef(signalTypeObject);
+ }
+ env->CallVoidMethod(mCallbacksObj, method_setSignalTypeCapabilities, arrayList);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ env->DeleteLocalRef(arrayList);
+ return Status::ok();
+}
+
Status GnssCallbackAidl::gnssStatusCb(const GnssStatusValue status) {
JNIEnv* env = getJniEnv();
env->CallVoidMethod(mCallbacksObj, method_reportStatus, status);
diff --git a/services/core/jni/gnss/GnssCallback.h b/services/core/jni/gnss/GnssCallback.h
index a7f96fb..33acec8 100644
--- a/services/core/jni/gnss/GnssCallback.h
+++ b/services/core/jni/gnss/GnssCallback.h
@@ -61,6 +61,8 @@
class GnssCallbackAidl : public hardware::gnss::BnGnssCallback {
public:
binder::Status gnssSetCapabilitiesCb(const int capabilities) override;
+ binder::Status gnssSetSignalTypeCapabilitiesCb(
+ const std::vector<android::hardware::gnss::GnssSignalType>& signalTypes) override;
binder::Status gnssStatusCb(const GnssStatusValue status) override;
binder::Status gnssSvStatusCb(const std::vector<GnssSvInfo>& svInfoList) override;
binder::Status gnssLocationCb(const hardware::gnss::GnssLocation& location) override;
@@ -180,4 +182,4 @@
} // namespace android::gnss
-#endif // _ANDROID_SERVER_GNSS_GNSSCALLBACK_H
\ No newline at end of file
+#endif // _ANDROID_SERVER_GNSS_GNSSCALLBACK_H
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 70422bb..316c736 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -259,6 +259,7 @@
import android.content.pm.Signature;
import android.content.pm.StringParceledListSlice;
import android.content.pm.UserInfo;
+import android.content.pm.UserPackage;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.database.Cursor;
@@ -710,8 +711,7 @@
* Contains (package-user) pairs to remove. An entry (p, u) implies that removal of package p
* is requested for user u.
*/
- private final Set<Pair<String, Integer>> mPackagesToRemove =
- new ArraySet<Pair<String, Integer>>();
+ private final Set<UserPackage> mPackagesToRemove = new ArraySet<>();
final LocalService mLocalService;
@@ -13982,7 +13982,6 @@
@Override
public boolean isProvisioningAllowed(String action, String packageName) {
Objects.requireNonNull(packageName);
-
final CallerIdentity caller = getCallerIdentity();
final long ident = mInjector.binderClearCallingIdentity();
try {
@@ -13994,21 +13993,21 @@
mInjector.binderRestoreCallingIdentity(ident);
}
- return checkProvisioningPreconditionSkipPermission(action, packageName) == STATUS_OK;
+ return checkProvisioningPreconditionSkipPermission(action, packageName, caller.getUserId())
+ == STATUS_OK;
}
@Override
public int checkProvisioningPrecondition(String action, String packageName) {
Objects.requireNonNull(packageName, "packageName is null");
-
+ final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(
hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
- return checkProvisioningPreconditionSkipPermission(action, packageName);
+ return checkProvisioningPreconditionSkipPermission(action, packageName, caller.getUserId());
}
-
private int checkProvisioningPreconditionSkipPermission(String action,
- String packageName) {
+ String packageName, int userId) {
if (!mHasFeature) {
logMissingFeatureAction("Cannot check provisioning for action " + action);
return STATUS_DEVICE_ADMIN_NOT_SUPPORTED;
@@ -14016,7 +14015,8 @@
if (!isProvisioningAllowed()) {
return STATUS_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS;
}
- final int code = checkProvisioningPreConditionSkipPermissionNoLog(action, packageName);
+ final int code = checkProvisioningPreConditionSkipPermissionNoLog(
+ action, packageName, userId);
if (code != STATUS_OK) {
Slogf.d(LOG_TAG, "checkProvisioningPreCondition(" + action + ", " + packageName
+ ") failed: "
@@ -14043,15 +14043,14 @@
}
private int checkProvisioningPreConditionSkipPermissionNoLog(String action,
- String packageName) {
- final int callingUserId = mInjector.userHandleGetCallingUserId();
+ String packageName, int userId) {
if (action != null) {
switch (action) {
case DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE:
- return checkManagedProfileProvisioningPreCondition(packageName, callingUserId);
+ return checkManagedProfileProvisioningPreCondition(packageName, userId);
case DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE:
case DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE:
- return checkDeviceOwnerProvisioningPreCondition(callingUserId);
+ return checkDeviceOwnerProvisioningPreCondition(userId);
}
}
throw new IllegalArgumentException("Unknown provisioning action " + action);
@@ -15044,7 +15043,7 @@
Preconditions.checkCallAuthorization(
hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
- Pair<String, Integer> packageUserPair = new Pair<>(packageName, caller.getUserId());
+ UserPackage packageUserPair = UserPackage.of(caller.getUserId(), packageName);
synchronized (getLockObject()) {
return mPackagesToRemove.contains(packageUserPair);
}
@@ -15072,7 +15071,7 @@
throw new IllegalArgumentException("Cannot uninstall a package with a device owner");
}
- final Pair<String, Integer> packageUserPair = new Pair<>(packageName, userId);
+ final UserPackage packageUserPair = UserPackage.of(userId, packageName);
synchronized (getLockObject()) {
mPackagesToRemove.add(packageUserPair);
}
@@ -15132,7 +15131,7 @@
}
private void startUninstallIntent(final String packageName, final int userId) {
- final Pair<String, Integer> packageUserPair = new Pair<>(packageName, userId);
+ final UserPackage packageUserPair = UserPackage.of(userId, packageName);
synchronized (getLockObject()) {
if (!mPackagesToRemove.contains(packageUserPair)) {
// Do nothing if uninstall was not requested or was already started.
@@ -17622,7 +17621,7 @@
final long identity = Binder.clearCallingIdentity();
try {
final int result = checkProvisioningPreconditionSkipPermission(
- ACTION_PROVISION_MANAGED_PROFILE, admin.getPackageName());
+ ACTION_PROVISION_MANAGED_PROFILE, admin.getPackageName(), caller.getUserId());
if (result != STATUS_OK) {
throw new ServiceSpecificException(
ERROR_PRE_CONDITION_FAILED,
@@ -18032,7 +18031,8 @@
final long identity = Binder.clearCallingIdentity();
try {
int result = checkProvisioningPreconditionSkipPermission(
- ACTION_PROVISION_MANAGED_DEVICE, deviceAdmin.getPackageName());
+ ACTION_PROVISION_MANAGED_DEVICE, deviceAdmin.getPackageName(),
+ caller.getUserId());
if (result != STATUS_OK) {
throw new ServiceSpecificException(
ERROR_PRE_CONDITION_FAILED,
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 593e648..fe2d0be 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -318,8 +318,6 @@
"com.android.clockwork.displayoffload.DisplayOffloadService";
private static final String WEAR_DISPLAY_SERVICE_CLASS =
"com.android.clockwork.display.WearDisplayService";
- private static final String WEAR_LEFTY_SERVICE_CLASS =
- "com.google.android.clockwork.lefty.WearLeftyService";
private static final String WEAR_TIME_SERVICE_CLASS =
"com.android.clockwork.time.WearTimeService";
private static final String WEAR_GLOBAL_ACTIONS_SERVICE_CLASS =
@@ -1404,7 +1402,6 @@
false);
boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice",
false);
- boolean enableLeftyService = SystemProperties.getBoolean("config.enable_lefty", false);
boolean isEmulator = SystemProperties.get("ro.boot.qemu").equals("1");
@@ -1668,7 +1665,18 @@
// Bring up services needed for UI.
if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
t.traceBegin("StartInputMethodManagerLifecycle");
- mSystemServiceManager.startService(InputMethodManagerService.Lifecycle.class);
+ String immsClassName = context.getResources().getString(
+ R.string.config_deviceSpecificInputMethodManagerService);
+ if (immsClassName.isEmpty()) {
+ mSystemServiceManager.startService(InputMethodManagerService.Lifecycle.class);
+ } else {
+ try {
+ Slog.i(TAG, "Starting custom IMMS: " + immsClassName);
+ mSystemServiceManager.startService(immsClassName);
+ } catch (Throwable e) {
+ reportWtf("starting " + immsClassName, e);
+ }
+ }
t.traceEnd();
t.traceBegin("StartAccessibilityManagerService");
@@ -2502,12 +2510,6 @@
mSystemServiceManager.startService(WEAR_TIME_SERVICE_CLASS);
t.traceEnd();
- if (enableLeftyService) {
- t.traceBegin("StartWearLeftyService");
- mSystemServiceManager.startService(WEAR_LEFTY_SERVICE_CLASS);
- t.traceEnd();
- }
-
t.traceBegin("StartWearGlobalActionsService");
mSystemServiceManager.startService(WEAR_GLOBAL_ACTIONS_SERVICE_CLASS);
t.traceEnd();
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
index e4d124e..55645d7 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
@@ -219,6 +219,20 @@
printState(mock(Computer::class.java), mock(IndentingPrintWriter::class.java),
null, null)
},
+ service(Type.QUERENT, "printOwnersForPackage") {
+ printOwnersForPackage(
+ mock(IndentingPrintWriter::class.java),
+ it.targetPackageName,
+ it.userId
+ )
+ },
+ service(Type.QUERENT, "printOwnersForDomains") {
+ printOwnersForDomains(
+ mock(IndentingPrintWriter::class.java),
+ listOf("example.com"),
+ it.userId
+ )
+ },
service(Type.VERIFIER, "setStatus") {
setDomainVerificationStatus(
it.targetDomainSetId,
diff --git a/services/tests/mockingservicestests/src/com/android/server/DumpableDumperRule.java b/services/tests/mockingservicestests/src/com/android/server/DumpableDumperRule.java
new file mode 100644
index 0000000..33275bd
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/DumpableDumperRule.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server;
+
+import android.util.Dumpable;
+import android.util.Log;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@code JUnit} rule that logs (using tag {@value #TAG} the contents of
+ * {@link Dumpable dumpables} in case of failure.
+ */
+public final class DumpableDumperRule implements TestRule {
+
+ private static final String TAG = DumpableDumperRule.class.getSimpleName();
+
+ private static final String[] NO_ARGS = {};
+
+ private final List<Dumpable> mDumpables = new ArrayList<>();
+
+ /**
+ * Adds a {@link Dumpable} to be logged if the test case fails.
+ */
+ public void addDumpable(Dumpable dumpable) {
+ mDumpables.add(dumpable);
+ }
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ try {
+ base.evaluate();
+ } catch (Throwable t) {
+ dumpOnFailure(description.getMethodName());
+ throw t;
+ }
+ }
+ };
+ }
+
+ private void dumpOnFailure(String testName) throws IOException {
+ if (mDumpables.isEmpty()) {
+ return;
+ }
+ Log.w(TAG, "Dumping " + mDumpables.size() + " dumpables on failure of " + testName);
+ mDumpables.forEach(d -> logDumpable(d));
+ }
+
+ private void logDumpable(Dumpable dumpable) {
+ try {
+ try (StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw)) {
+ dumpable.dump(pw, NO_ARGS);
+ String[] dump = sw.toString().split(System.lineSeparator());
+ Log.w(TAG, "Dumping " + dumpable.getDumpableName() + " (" + dump.length
+ + " lines):");
+ for (String line : dump) {
+ Log.w(TAG, line);
+ }
+
+ } catch (RuntimeException e) {
+ Log.e(TAG, "RuntimeException dumping " + dumpable.getDumpableName(), e);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "IOException dumping " + dumpable.getDumpableName(), e);
+ }
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/ExtendedMockitoTestCase.java b/services/tests/mockingservicestests/src/com/android/server/ExtendedMockitoTestCase.java
index 9aa28ce..c0b5070 100644
--- a/services/tests/mockingservicestests/src/com/android/server/ExtendedMockitoTestCase.java
+++ b/services/tests/mockingservicestests/src/com/android/server/ExtendedMockitoTestCase.java
@@ -23,6 +23,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
@@ -38,6 +39,9 @@
private MockitoSession mSession;
+ @Rule
+ public final DumpableDumperRule mDumpableDumperRule = new DumpableDumperRule();
+
@Before
public void startSession() {
if (DEBUG) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
index 4d92b7f..0f7c0d7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -16,6 +16,13 @@
package com.android.server.am;
+import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_ALARM;
+import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_FOREGROUND;
+import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_INTERACTIVE;
+import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_MANIFEST;
+import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_ORDERED;
+import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_PRIORITIZED;
+import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_RESULT_TO;
import static com.android.server.am.BroadcastProcessQueue.insertIntoRunnableList;
import static com.android.server.am.BroadcastProcessQueue.removeFromRunnableList;
import static com.android.server.am.BroadcastQueueTest.CLASS_GREEN;
@@ -36,13 +43,16 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import android.annotation.NonNull;
import android.app.Activity;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
+import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ResolveInfo;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.BundleMerger;
@@ -140,6 +150,18 @@
}
}
+ private static Intent makeMockIntent() {
+ return mock(Intent.class);
+ }
+
+ private static ResolveInfo makeMockManifestReceiver() {
+ return mock(ResolveInfo.class);
+ }
+
+ private static BroadcastFilter makeMockRegisteredReceiver() {
+ return mock(BroadcastFilter.class);
+ }
+
private BroadcastRecord makeBroadcastRecord(Intent intent) {
return makeBroadcastRecord(intent, BroadcastOptions.makeBasic(),
List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)), false);
@@ -150,6 +172,10 @@
List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)), true);
}
+ private BroadcastRecord makeBroadcastRecord(Intent intent, List receivers) {
+ return makeBroadcastRecord(intent, BroadcastOptions.makeBasic(), receivers, false);
+ }
+
private BroadcastRecord makeBroadcastRecord(Intent intent, BroadcastOptions options) {
return makeBroadcastRecord(intent, options,
List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)), false);
@@ -157,8 +183,13 @@
private BroadcastRecord makeBroadcastRecord(Intent intent, BroadcastOptions options,
List receivers, boolean ordered) {
+ return makeBroadcastRecord(intent, options, receivers, null, ordered);
+ }
+
+ private BroadcastRecord makeBroadcastRecord(Intent intent, BroadcastOptions options,
+ List receivers, IIntentReceiver resultTo, boolean ordered) {
return new BroadcastRecord(mImpl, intent, mProcess, PACKAGE_RED, null, 21, 42, false, null,
- null, null, null, AppOpsManager.OP_NONE, options, receivers, null, null,
+ null, null, null, AppOpsManager.OP_NONE, options, receivers, null, resultTo,
Activity.RESULT_OK, null, null, ordered, false, false, UserHandle.USER_SYSTEM,
false, null, false, null);
}
@@ -287,7 +318,8 @@
PACKAGE_GREEN, getUidForPackage(PACKAGE_GREEN));
final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane);
+ final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane,
+ List.of(makeMockRegisteredReceiver()));
queue.enqueueOrReplaceBroadcast(airplaneRecord, 0);
queue.setProcessCached(false);
@@ -367,7 +399,8 @@
PACKAGE_GREEN, getUidForPackage(PACKAGE_GREEN));
final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane);
+ final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane,
+ List.of(makeMockRegisteredReceiver()));
queue.enqueueOrReplaceBroadcast(airplaneRecord, 0);
mConstants.MAX_PENDING_BROADCASTS = 128;
@@ -382,6 +415,80 @@
}
/**
+ * Verify that a cached process that would normally be delayed becomes
+ * immediately runnable when the given broadcast is enqueued.
+ */
+ private void doRunnableAt_Cached(BroadcastRecord testRecord, int testRunnableAtReason) {
+ final BroadcastProcessQueue queue = new BroadcastProcessQueue(mConstants,
+ PACKAGE_GREEN, getUidForPackage(PACKAGE_GREEN));
+ queue.setProcessCached(true);
+
+ final BroadcastRecord lazyRecord = makeBroadcastRecord(
+ new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED),
+ List.of(makeMockRegisteredReceiver()));
+
+ queue.enqueueOrReplaceBroadcast(lazyRecord, 0);
+ assertThat(queue.getRunnableAt()).isGreaterThan(lazyRecord.enqueueTime);
+ assertThat(queue.getRunnableAtReason()).isNotEqualTo(testRunnableAtReason);
+
+ queue.enqueueOrReplaceBroadcast(testRecord, 0);
+ assertThat(queue.getRunnableAt()).isAtMost(testRecord.enqueueTime);
+ assertThat(queue.getRunnableAtReason()).isEqualTo(testRunnableAtReason);
+ }
+
+ @Test
+ public void testRunnableAt_Cached_Manifest() {
+ doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), null,
+ List.of(makeMockManifestReceiver()), null, false), REASON_CONTAINS_MANIFEST);
+ }
+
+ @Test
+ public void testRunnableAt_Cached_Ordered() {
+ doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), null,
+ List.of(makeMockRegisteredReceiver()), null, true), REASON_CONTAINS_ORDERED);
+ }
+
+ @Test
+ public void testRunnableAt_Cached_ResultTo() {
+ final IIntentReceiver resultTo = mock(IIntentReceiver.class);
+ doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), null,
+ List.of(makeMockRegisteredReceiver()), resultTo, false), REASON_CONTAINS_RESULT_TO);
+ }
+
+ @Test
+ public void testRunnableAt_Cached_Foreground() {
+ final Intent foregroundIntent = new Intent();
+ foregroundIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ doRunnableAt_Cached(makeBroadcastRecord(foregroundIntent, null,
+ List.of(makeMockRegisteredReceiver()), null, false), REASON_CONTAINS_FOREGROUND);
+ }
+
+ @Test
+ public void testRunnableAt_Cached_Interactive() {
+ final BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setInteractiveBroadcast(true);
+ doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), options,
+ List.of(makeMockRegisteredReceiver()), null, false), REASON_CONTAINS_INTERACTIVE);
+ }
+
+ @Test
+ public void testRunnableAt_Cached_Alarm() {
+ final BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setAlarmBroadcast(true);
+ doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), options,
+ List.of(makeMockRegisteredReceiver()), null, false), REASON_CONTAINS_ALARM);
+ }
+
+ @Test
+ public void testRunnableAt_Cached_Prioritized() {
+ final List receivers = List.of(
+ withPriority(makeManifestReceiver(PACKAGE_RED, PACKAGE_RED), 10),
+ withPriority(makeManifestReceiver(PACKAGE_GREEN, PACKAGE_GREEN), -10));
+ doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), null,
+ receivers, null, false), REASON_CONTAINS_PRIORITIZED);
+ }
+
+ /**
* Confirm that we always prefer running pending items marked as "urgent",
* then "normal", then "offload", dispatching by the relative ordering
* within each of those clustering groups.
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
index 20af02e..dc49a94 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -33,6 +33,7 @@
import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.os.Handler;
+import android.os.Looper;
import android.os.PowerManager;
import android.os.test.TestLooper;
import android.util.FloatProperty;
@@ -135,6 +136,16 @@
DisplayPowerCallbacks displayPowerCallbacks) {
return mWakelockController;
}
+
+ @Override
+ DisplayPowerProximityStateController getDisplayPowerProximityStateController(
+ WakelockController wakelockController, DisplayDeviceConfig displayDeviceConfig,
+ Looper looper, Runnable nudgeUpdatePowerState, int displayId,
+ SensorManager sensorManager) {
+ return new DisplayPowerProximityStateController(wakelockController,
+ displayDeviceConfig, looper, nudgeUpdatePowerState, displayId,
+ sensorManager);
+ }
};
addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock);
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java
index 2547347..6279b87 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java
@@ -34,6 +34,8 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
@@ -51,6 +53,7 @@
import android.os.Looper;
import android.os.UserHandle;
import android.provider.DeviceConfig;
+import android.util.ArrayMap;
import android.util.ArraySet;
import androidx.test.filters.SmallTest;
@@ -98,12 +101,23 @@
@Mock
private IPackageManager mIPackageManager;
- static class InjectorForTest extends JobConcurrencyManager.Injector {
+ private static class InjectorForTest extends JobConcurrencyManager.Injector {
+ public final ArrayMap<JobServiceContext, JobStatus> contexts = new ArrayMap<>();
+
@Override
JobServiceContext createJobServiceContext(JobSchedulerService service,
JobConcurrencyManager concurrencyManager, IBatteryStats batteryStats,
JobPackageTracker tracker, Looper looper) {
- return mock(JobServiceContext.class);
+ final JobServiceContext context = mock(JobServiceContext.class);
+ doAnswer((Answer<Boolean>) invocationOnMock -> {
+ Object[] args = invocationOnMock.getArguments();
+ final JobStatus job = (JobStatus) args[0];
+ contexts.put(context, job);
+ doReturn(job).when(context).getRunningJobLocked();
+ return true;
+ }).when(context).executeRunnableJob(any(), anyInt());
+ contexts.put(context, null);
+ return context;
}
}
@@ -142,6 +156,13 @@
doReturn(mPendingJobQueue).when(jobSchedulerService).getPendingJobQueue();
doReturn(mIPackageManager).when(AppGlobals::getPackageManager);
mInjector = new InjectorForTest();
+ doAnswer((Answer<Long>) invocationOnMock -> {
+ Object[] args = invocationOnMock.getArguments();
+ final JobStatus job = (JobStatus) args[0];
+ return job.shouldTreatAsExpeditedJob()
+ ? JobSchedulerService.Constants.DEFAULT_RUNTIME_MIN_EJ_GUARANTEE_MS
+ : JobSchedulerService.Constants.DEFAULT_RUNTIME_MIN_GUARANTEE_MS;
+ }).when(jobSchedulerService).getMinJobExecutionGuaranteeMs(any());
mJobConcurrencyManager = new JobConcurrencyManager(jobSchedulerService, mInjector);
mGracePeriodObserver = mock(GracePeriodObserver.class);
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
@@ -167,32 +188,55 @@
final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
- mJobConcurrencyManager
+ final long minPreferredUidOnlyWaitingTimeMs = mJobConcurrencyManager
.prepareForAssignmentDeterminationLocked(idle, preferredUidOnly, stoppable);
assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, idle.size());
assertEquals(0, preferredUidOnly.size());
assertEquals(0, stoppable.size());
+ assertEquals(0, minPreferredUidOnlyWaitingTimeMs);
}
@Test
public void testPrepareForAssignmentDetermination_onlyPendingJobs() {
- final ArraySet<JobStatus> jobs = new ArraySet<>();
for (int i = 0; i < JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT; ++i) {
JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i);
mPendingJobQueue.add(job);
- jobs.add(job);
}
final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
- mJobConcurrencyManager
+ final long minPreferredUidOnlyWaitingTimeMs = mJobConcurrencyManager
.prepareForAssignmentDeterminationLocked(idle, preferredUidOnly, stoppable);
assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, idle.size());
assertEquals(0, preferredUidOnly.size());
assertEquals(0, stoppable.size());
+ assertEquals(0, minPreferredUidOnlyWaitingTimeMs);
+ }
+
+ @Test
+ public void testPrepareForAssignmentDetermination_onlyPreferredUidOnly() {
+ for (int i = 0; i < JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT; ++i) {
+ JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i);
+ mJobConcurrencyManager.addRunningJobForTesting(job);
+ }
+
+ for (int i = 0; i < mInjector.contexts.size(); ++i) {
+ doReturn(true).when(mInjector.contexts.keyAt(i)).isWithinExecutionGuaranteeTime();
+ }
+
+ final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
+ final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
+ final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
+ final long minPreferredUidOnlyWaitingTimeMs = mJobConcurrencyManager
+ .prepareForAssignmentDeterminationLocked(idle, preferredUidOnly, stoppable);
+
+ assertEquals(0, idle.size());
+ assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, preferredUidOnly.size());
+ assertEquals(0, stoppable.size());
+ assertEquals(0, minPreferredUidOnlyWaitingTimeMs);
}
@Test
@@ -216,7 +260,8 @@
mJobConcurrencyManager
.prepareForAssignmentDeterminationLocked(idle, preferredUidOnly, stoppable);
mJobConcurrencyManager
- .determineAssignmentsLocked(changed, idle, preferredUidOnly, stoppable);
+ .determineAssignmentsLocked(changed, idle, preferredUidOnly, stoppable,
+ Long.MAX_VALUE);
assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, changed.size());
for (int i = changed.size() - 1; i >= 0; --i) {
@@ -226,6 +271,169 @@
}
@Test
+ public void testDetermineAssignments_allPreferredUidOnly_shortTimeLeft() throws Exception {
+ mConfigBuilder.setBoolean(JobConcurrencyManager.KEY_ENABLE_MAX_WAIT_TIME_BYPASS, true);
+ setConcurrencyConfig(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT,
+ new TypeConfig(WORK_TYPE_BG, 0, JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT));
+ for (int i = 0; i < JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT * 2; ++i) {
+ final int uid = mDefaultUserId * UserHandle.PER_USER_RANGE + i;
+ final String sourcePkgName = "com.source.package." + UserHandle.getAppId(uid);
+ setPackageUid(sourcePkgName, uid);
+ final JobStatus job = createJob(uid, sourcePkgName);
+ spyOn(job);
+ doReturn(i % 2 == 0).when(job).shouldTreatAsExpeditedJob();
+ if (i < JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT) {
+ mJobConcurrencyManager.addRunningJobForTesting(job);
+ } else {
+ mPendingJobQueue.add(job);
+ }
+ }
+
+ // Waiting time is too short, so we shouldn't create any extra contexts.
+ final long remainingTimeMs = JobConcurrencyManager.DEFAULT_MAX_WAIT_EJ_MS / 2;
+ for (int i = 0; i < mInjector.contexts.size(); ++i) {
+ doReturn(true).when(mInjector.contexts.keyAt(i)).isWithinExecutionGuaranteeTime();
+ doReturn(remainingTimeMs)
+ .when(mInjector.contexts.keyAt(i)).getRemainingGuaranteedTimeMs(anyLong());
+ }
+
+ final ArraySet<JobConcurrencyManager.ContextAssignment> changed = new ArraySet<>();
+ final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
+ final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
+ final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
+
+ long minPreferredUidOnlyWaitingTimeMs = mJobConcurrencyManager
+ .prepareForAssignmentDeterminationLocked(idle, preferredUidOnly, stoppable);
+ assertEquals(remainingTimeMs, minPreferredUidOnlyWaitingTimeMs);
+ assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, preferredUidOnly.size());
+
+ mJobConcurrencyManager
+ .determineAssignmentsLocked(changed, idle, preferredUidOnly, stoppable,
+ minPreferredUidOnlyWaitingTimeMs);
+
+ assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, preferredUidOnly.size());
+ assertEquals(0, changed.size());
+ }
+
+ @Test
+ public void testDetermineAssignments_allPreferredUidOnly_mediumTimeLeft() throws Exception {
+ mConfigBuilder.setBoolean(JobConcurrencyManager.KEY_ENABLE_MAX_WAIT_TIME_BYPASS, true);
+ setConcurrencyConfig(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT,
+ new TypeConfig(WORK_TYPE_BG, 0, JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT));
+ final ArraySet<JobStatus> jobs = new ArraySet<>();
+ for (int i = 0; i < JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT * 2; ++i) {
+ final int uid = mDefaultUserId * UserHandle.PER_USER_RANGE + i;
+ final String sourcePkgName = "com.source.package." + UserHandle.getAppId(uid);
+ setPackageUid(sourcePkgName, uid);
+ final JobStatus job = createJob(uid, sourcePkgName);
+ spyOn(job);
+ doReturn(i % 2 == 0).when(job).shouldTreatAsExpeditedJob();
+ if (i < JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT) {
+ mJobConcurrencyManager.addRunningJobForTesting(job);
+ } else {
+ mPendingJobQueue.add(job);
+ jobs.add(job);
+ }
+ }
+
+ // Waiting time is longer than the EJ waiting time, but shorter than regular job waiting
+ // time, so we should only create an extra context for an EJ.
+ final long remainingTimeMs = (JobConcurrencyManager.DEFAULT_MAX_WAIT_EJ_MS
+ + JobConcurrencyManager.DEFAULT_MAX_WAIT_REGULAR_MS) / 2;
+ for (int i = 0; i < mInjector.contexts.size(); ++i) {
+ doReturn(true).when(mInjector.contexts.keyAt(i)).isWithinExecutionGuaranteeTime();
+ doReturn(remainingTimeMs)
+ .when(mInjector.contexts.keyAt(i)).getRemainingGuaranteedTimeMs(anyLong());
+ }
+
+ final ArraySet<JobConcurrencyManager.ContextAssignment> changed = new ArraySet<>();
+ final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
+ final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
+ final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
+
+ long minPreferredUidOnlyWaitingTimeMs = mJobConcurrencyManager
+ .prepareForAssignmentDeterminationLocked(idle, preferredUidOnly, stoppable);
+ assertEquals(remainingTimeMs, minPreferredUidOnlyWaitingTimeMs);
+ assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, preferredUidOnly.size());
+
+ mJobConcurrencyManager
+ .determineAssignmentsLocked(changed, idle, preferredUidOnly, stoppable,
+ minPreferredUidOnlyWaitingTimeMs);
+
+ assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, preferredUidOnly.size());
+ for (int i = changed.size() - 1; i >= 0; --i) {
+ jobs.remove(changed.valueAt(i).newJob);
+ }
+ assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT - 1, jobs.size());
+ assertEquals(1, changed.size());
+ JobStatus assignedJob = changed.valueAt(0).newJob;
+ assertTrue(assignedJob.shouldTreatAsExpeditedJob());
+ }
+
+ @Test
+ public void testDetermineAssignments_allPreferredUidOnly_longTimeLeft() throws Exception {
+ mConfigBuilder.setBoolean(JobConcurrencyManager.KEY_ENABLE_MAX_WAIT_TIME_BYPASS, true);
+ setConcurrencyConfig(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT,
+ new TypeConfig(WORK_TYPE_BG, 0, JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT));
+ final ArraySet<JobStatus> jobs = new ArraySet<>();
+ for (int i = 0; i < JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT * 2; ++i) {
+ final int uid = mDefaultUserId * UserHandle.PER_USER_RANGE + i;
+ final String sourcePkgName = "com.source.package." + UserHandle.getAppId(uid);
+ setPackageUid(sourcePkgName, uid);
+ final JobStatus job = createJob(uid, sourcePkgName);
+ spyOn(job);
+ doReturn(i % 2 == 0).when(job).shouldTreatAsExpeditedJob();
+ if (i < JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT) {
+ mJobConcurrencyManager.addRunningJobForTesting(job);
+ } else {
+ mPendingJobQueue.add(job);
+ jobs.add(job);
+ }
+ }
+
+ // Waiting time is longer than even the regular job waiting time, so we should
+ // create an extra context for an EJ, and potentially one for a regular job.
+ final long remainingTimeMs = 2 * JobConcurrencyManager.DEFAULT_MAX_WAIT_REGULAR_MS;
+ for (int i = 0; i < mInjector.contexts.size(); ++i) {
+ doReturn(true).when(mInjector.contexts.keyAt(i)).isWithinExecutionGuaranteeTime();
+ doReturn(remainingTimeMs)
+ .when(mInjector.contexts.keyAt(i)).getRemainingGuaranteedTimeMs(anyLong());
+ }
+
+ final ArraySet<JobConcurrencyManager.ContextAssignment> changed = new ArraySet<>();
+ final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
+ final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
+ final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
+
+ long minPreferredUidOnlyWaitingTimeMs = mJobConcurrencyManager
+ .prepareForAssignmentDeterminationLocked(idle, preferredUidOnly, stoppable);
+ assertEquals(remainingTimeMs, minPreferredUidOnlyWaitingTimeMs);
+ assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, preferredUidOnly.size());
+
+ mJobConcurrencyManager
+ .determineAssignmentsLocked(changed, idle, preferredUidOnly, stoppable,
+ minPreferredUidOnlyWaitingTimeMs);
+
+ assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, preferredUidOnly.size());
+ // Depending on iteration order, we may create 1 or 2 contexts.
+ final long numAssignedJobs = changed.size();
+ assertTrue(numAssignedJobs > 0);
+ assertTrue(numAssignedJobs <= 2);
+ for (int i = 0; i < numAssignedJobs; ++i) {
+ jobs.remove(changed.valueAt(i).newJob);
+ }
+ assertEquals(numAssignedJobs,
+ JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT - jobs.size());
+ JobStatus firstAssignedJob = changed.valueAt(0).newJob;
+ if (!firstAssignedJob.shouldTreatAsExpeditedJob()) {
+ assertEquals(2, numAssignedJobs);
+ assertTrue(changed.valueAt(1).newJob.shouldTreatAsExpeditedJob());
+ } else if (numAssignedJobs == 2) {
+ assertFalse(changed.valueAt(1).newJob.shouldTreatAsExpeditedJob());
+ }
+ }
+
+ @Test
public void testIsPkgConcurrencyLimited_top() {
final JobStatus topJob = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE, 0);
topJob.lastEvaluatedBias = JobInfo.BIAS_TOP_APP;
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUMDTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUMDTest.java
index 21f541f..923c3e3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUMDTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUMDTest.java
@@ -44,14 +44,14 @@
@Test
public void testAssignUserToDisplay_systemUser() {
- assertThrows(IllegalArgumentException.class,
- () -> mMediator.assignUserToDisplay(USER_SYSTEM, SECONDARY_DISPLAY_ID));
+ assertThrows(IllegalArgumentException.class, () -> mMediator
+ .assignUserToDisplay(USER_SYSTEM, USER_SYSTEM, SECONDARY_DISPLAY_ID));
}
@Test
public void testAssignUserToDisplay_invalidDisplay() {
assertThrows(IllegalArgumentException.class,
- () -> mMediator.assignUserToDisplay(USER_ID, INVALID_DISPLAY));
+ () -> mMediator.assignUserToDisplay(USER_ID, USER_ID, INVALID_DISPLAY));
}
@Test
@@ -59,7 +59,7 @@
mockCurrentUser(USER_ID);
assertThrows(IllegalArgumentException.class,
- () -> mMediator.assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID));
+ () -> mMediator.assignUserToDisplay(USER_ID, USER_ID, SECONDARY_DISPLAY_ID));
assertNoUserAssignedToDisplay();
}
@@ -67,11 +67,10 @@
@Test
public void testAssignUserToDisplay_startedProfileOfCurrentUser() {
mockCurrentUser(PARENT_USER_ID);
- addDefaultProfileAndParent();
startDefaultProfile();
- IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
- () -> mMediator.assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID));
+ IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> mMediator
+ .assignUserToDisplay(PROFILE_USER_ID, PARENT_USER_ID, SECONDARY_DISPLAY_ID));
Log.v(TAG, "Exception: " + e);
assertNoUserAssignedToDisplay();
@@ -80,11 +79,10 @@
@Test
public void testAssignUserToDisplay_stoppedProfileOfCurrentUser() {
mockCurrentUser(PARENT_USER_ID);
- addDefaultProfileAndParent();
stopDefaultProfile();
- IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
- () -> mMediator.assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID));
+ IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> mMediator
+ .assignUserToDisplay(PROFILE_USER_ID, PARENT_USER_ID, SECONDARY_DISPLAY_ID));
Log.v(TAG, "Exception: " + e);
assertNoUserAssignedToDisplay();
@@ -92,17 +90,17 @@
@Test
public void testAssignUserToDisplay_displayAvailable() {
- mMediator.assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
+ mMediator.assignUserToDisplay(USER_ID, USER_ID, SECONDARY_DISPLAY_ID);
assertUserAssignedToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
}
@Test
public void testAssignUserToDisplay_displayAlreadyAssigned() {
- mMediator.assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
+ mMediator.assignUserToDisplay(USER_ID, USER_ID, SECONDARY_DISPLAY_ID);
- IllegalStateException e = assertThrows(IllegalStateException.class,
- () -> mMediator.assignUserToDisplay(OTHER_USER_ID, SECONDARY_DISPLAY_ID));
+ IllegalStateException e = assertThrows(IllegalStateException.class, () -> mMediator
+ .assignUserToDisplay(OTHER_USER_ID, OTHER_USER_ID, SECONDARY_DISPLAY_ID));
Log.v(TAG, "Exception: " + e);
assertWithMessage("exception (%s) message", e).that(e).hasMessageThat()
@@ -112,10 +110,10 @@
@Test
public void testAssignUserToDisplay_userAlreadyAssigned() {
- mMediator.assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
+ mMediator.assignUserToDisplay(USER_ID, USER_ID, SECONDARY_DISPLAY_ID);
IllegalStateException e = assertThrows(IllegalStateException.class,
- () -> mMediator.assignUserToDisplay(USER_ID, OTHER_SECONDARY_DISPLAY_ID));
+ () -> mMediator.assignUserToDisplay(USER_ID, USER_ID, OTHER_SECONDARY_DISPLAY_ID));
Log.v(TAG, "Exception: " + e);
assertWithMessage("exception (%s) message", e).that(e).hasMessageThat()
@@ -127,11 +125,9 @@
@Test
public void testAssignUserToDisplay_profileOnSameDisplayAsParent() {
- addDefaultProfileAndParent();
-
- mMediator.assignUserToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID);
- IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
- () -> mMediator.assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID));
+ mMediator.assignUserToDisplay(PARENT_USER_ID, PARENT_USER_ID, SECONDARY_DISPLAY_ID);
+ IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> mMediator
+ .assignUserToDisplay(PROFILE_USER_ID, PARENT_USER_ID, SECONDARY_DISPLAY_ID));
Log.v(TAG, "Exception: " + e);
assertUserAssignedToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID);
@@ -139,11 +135,9 @@
@Test
public void testAssignUserToDisplay_profileOnDifferentDisplayAsParent() {
- addDefaultProfileAndParent();
-
- mMediator.assignUserToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID);
- IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
- () -> mMediator.assignUserToDisplay(PROFILE_USER_ID, OTHER_SECONDARY_DISPLAY_ID));
+ mMediator.assignUserToDisplay(PARENT_USER_ID, PARENT_USER_ID, SECONDARY_DISPLAY_ID);
+ IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> mMediator
+ .assignUserToDisplay(PROFILE_USER_ID, PARENT_USER_ID, OTHER_SECONDARY_DISPLAY_ID));
Log.v(TAG, "Exception: " + e);
assertUserAssignedToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID);
@@ -151,11 +145,9 @@
@Test
public void testAssignUserToDisplay_profileDefaultDisplayParentOnSecondaryDisplay() {
- addDefaultProfileAndParent();
-
- mMediator.assignUserToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID);
- IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
- () -> mMediator.assignUserToDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY));
+ mMediator.assignUserToDisplay(PARENT_USER_ID, PARENT_USER_ID, SECONDARY_DISPLAY_ID);
+ IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> mMediator
+ .assignUserToDisplay(PROFILE_USER_ID, PARENT_USER_ID, DEFAULT_DISPLAY));
Log.v(TAG, "Exception: " + e);
assertUserAssignedToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID);
@@ -201,7 +193,6 @@
@Test
public void testIsUserVisibleOnDisplay_startedProfileOfCurrentUserSecondaryDisplayAssignedToAnotherUser() {
- addDefaultProfileAndParent();
startDefaultProfile();
mockCurrentUser(PARENT_USER_ID);
assignUserToDisplay(OTHER_USER_ID, SECONDARY_DISPLAY_ID);
@@ -212,7 +203,6 @@
@Test
public void testIsUserVisibleOnDisplay_stoppedProfileOfCurrentUserSecondaryDisplayAssignedToAnotherUser() {
- addDefaultProfileAndParent();
stopDefaultProfile();
mockCurrentUser(PARENT_USER_ID);
assignUserToDisplay(OTHER_USER_ID, SECONDARY_DISPLAY_ID);
@@ -223,7 +213,6 @@
@Test
public void testIsUserVisibleOnDisplay_startedProfileOfCurrentUserOnUnassignedSecondaryDisplay() {
- addDefaultProfileAndParent();
startDefaultProfile();
mockCurrentUser(PARENT_USER_ID);
@@ -285,19 +274,6 @@
.that(mMediator.getUserAssignedToDisplay(SECONDARY_DISPLAY_ID)).isEqualTo(USER_ID);
}
- // TODO(b/244644281): scenario below shouldn't happen on "real life", as the profile cannot be
- // started on secondary display if its parent isn't, so we might need to remove (or refactor
- // this test) if/when the underlying logic changes
- @Test
- public void testGetUserAssignedToDisplay_profileOnSecondaryDisplay() {
- addDefaultProfileAndParent();
- mockCurrentUser(USER_ID);
- assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID);
-
- assertWithMessage("getUserAssignedToDisplay(%s)", SECONDARY_DISPLAY_ID)
- .that(mMediator.getUserAssignedToDisplay(SECONDARY_DISPLAY_ID)).isEqualTo(USER_ID);
- }
-
// NOTE: we don't need to add tests for profiles (started / stopped profiles of bg user), as
// getUserAssignedToDisplay() for bg users relies only on the user / display assignments
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorSUSDTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorSUSDTest.java
index 7ae8117..7af5f5d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorSUSDTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorSUSDTest.java
@@ -39,27 +39,25 @@
mockCurrentUser(USER_ID);
assertThrows(UnsupportedOperationException.class,
- () -> mMediator.assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID));
+ () -> mMediator.assignUserToDisplay(USER_ID, USER_ID, SECONDARY_DISPLAY_ID));
}
@Test
public void testAssignUserToDisplay_otherDisplay_startProfileOfcurrentUser() {
mockCurrentUser(PARENT_USER_ID);
- addDefaultProfileAndParent();
startDefaultProfile();
- assertThrows(UnsupportedOperationException.class,
- () -> mMediator.assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID));
+ assertThrows(UnsupportedOperationException.class, () -> mMediator
+ .assignUserToDisplay(PROFILE_USER_ID, PARENT_USER_ID, SECONDARY_DISPLAY_ID));
}
@Test
public void testAssignUserToDisplay_otherDisplay_stoppedProfileOfcurrentUser() {
mockCurrentUser(PARENT_USER_ID);
- addDefaultProfileAndParent();
stopDefaultProfile();
- assertThrows(UnsupportedOperationException.class,
- () -> mMediator.assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID));
+ assertThrows(UnsupportedOperationException.class, () -> mMediator
+ .assignUserToDisplay(PROFILE_USER_ID, PARENT_USER_ID, SECONDARY_DISPLAY_ID));
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java
index 22e6e0d..7b20092 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java
@@ -15,23 +15,27 @@
*/
package com.android.server.pm;
+import static android.content.pm.UserInfo.NO_PROFILE_GROUP_ID;
import static android.os.UserHandle.USER_NULL;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
-import static com.android.server.am.UserState.STATE_RUNNING_UNLOCKED;
+import static com.android.server.pm.UserVisibilityMediator.INITIAL_CURRENT_USER_ID;
+import static com.android.server.pm.UserVisibilityMediator.START_USER_RESULT_FAILURE;
+import static com.android.server.pm.UserVisibilityMediator.START_USER_RESULT_SUCCESS_INVISIBLE;
+import static com.android.server.pm.UserVisibilityMediator.START_USER_RESULT_SUCCESS_VISIBLE;
+import static com.android.server.pm.UserVisibilityMediator.startUserResultToString;
import static com.google.common.truth.Truth.assertWithMessage;
import android.annotation.UserIdInt;
-import android.util.SparseIntArray;
+import android.util.Log;
+
+import com.android.server.ExtendedMockitoTestCase;
import org.junit.Before;
import org.junit.Test;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
/**
* Base class for {@link UserVisibilityMediator} tests.
*
@@ -39,7 +43,33 @@
* device mode (for example, whether the device supports concurrent multiple users on multiple
* displays or not).
*/
-abstract class UserVisibilityMediatorTestCase extends UserManagerServiceOrInternalTestCase {
+abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase {
+
+ private static final String TAG = UserVisibilityMediatorTestCase.class.getSimpleName();
+
+ /**
+ * Id for a simple user (that doesn't have profiles).
+ */
+ protected static final int USER_ID = 600;
+
+ /**
+ * Id for another simple user.
+ */
+ protected static final int OTHER_USER_ID = 666;
+
+ /**
+ * Id for a user that has one profile (whose id is {@link #PROFILE_USER_ID}.
+ *
+ * <p>You can use {@link #addDefaultProfileAndParent()} to add both of this user to the service.
+ */
+ protected static final int PARENT_USER_ID = 642;
+
+ /**
+ * Id for a profile whose parent is {@link #PARENTUSER_ID}.
+ *
+ * <p>You can use {@link #addDefaultProfileAndParent()} to add both of this user to the service.
+ */
+ protected static final int PROFILE_USER_ID = 643;
/**
* Id of a secondary display (i.e, not {@link android.view.Display.DEFAULT_DISPLAY}).
@@ -51,12 +81,10 @@
*/
protected static final int OTHER_SECONDARY_DISPLAY_ID = 108;
- private final boolean mUsersOnSecondaryDisplaysEnabled;
+ private static final boolean FG = true;
+ private static final boolean BG = false;
- // TODO(b/244644281): manipulating mUsersOnSecondaryDisplays directly leaks implementation
- // details into the unit test, but it's fine for now as the tests were copied "as is" - it
- // would be better to use a geter() instead
- protected final SparseIntArray mUsersOnSecondaryDisplays = new SparseIntArray();
+ private final boolean mUsersOnSecondaryDisplaysEnabled;
protected UserVisibilityMediator mMediator;
@@ -66,13 +94,93 @@
@Before
public final void setMediator() {
- mMediator = new UserVisibilityMediator(mUms, mUsersOnSecondaryDisplaysEnabled,
- mUsersOnSecondaryDisplays);
+ mMediator = new UserVisibilityMediator(mUsersOnSecondaryDisplaysEnabled);
+ mDumpableDumperRule.addDumpable(mMediator);
+ }
+
+ @Test
+ public final void testStartUser_currentUser() {
+ int result = mMediator.startUser(USER_ID, USER_ID, FG, DEFAULT_DISPLAY);
+ assertStartUserResult(result, START_USER_RESULT_SUCCESS_VISIBLE);
+
+ assertCurrentUser(USER_ID);
+ assertIsCurrentUserOrRunningProfileOfCurrentUser(USER_ID);
+ assertStartedProfileGroupIdOf(USER_ID, USER_ID);
+ }
+
+ @Test
+ public final void testStartUser_currentUserSecondaryDisplay() {
+ int result = mMediator.startUser(USER_ID, USER_ID, FG, SECONDARY_DISPLAY_ID);
+ assertStartUserResult(result, START_USER_RESULT_FAILURE);
+
+ assertCurrentUser(INITIAL_CURRENT_USER_ID);
+ assertIsNotCurrentUserOrRunningProfileOfCurrentUser(USER_ID);
+ assertStartedProfileGroupIdOf(USER_ID, NO_PROFILE_GROUP_ID);
+ }
+
+ @Test
+ public final void testStartUser_profileBg_parentStarted() {
+ mockCurrentUser(PARENT_USER_ID);
+
+ int result = mMediator.startUser(PROFILE_USER_ID, PARENT_USER_ID, BG, DEFAULT_DISPLAY);
+ assertStartUserResult(result, START_USER_RESULT_SUCCESS_VISIBLE);
+
+ assertCurrentUser(PARENT_USER_ID);
+ assertIsCurrentUserOrRunningProfileOfCurrentUser(PROFILE_USER_ID);
+ assertStartedProfileGroupIdOf(PROFILE_USER_ID, PARENT_USER_ID);
+ assertIsStartedProfile(PROFILE_USER_ID);
+ }
+
+ @Test
+ public final void testStartUser_profileBg_parentNotStarted() {
+ int result = mMediator.startUser(PROFILE_USER_ID, PARENT_USER_ID, BG, DEFAULT_DISPLAY);
+ assertStartUserResult(result, START_USER_RESULT_SUCCESS_INVISIBLE);
+
+ assertCurrentUser(INITIAL_CURRENT_USER_ID);
+ assertIsNotCurrentUserOrRunningProfileOfCurrentUser(PROFILE_USER_ID);
+ assertStartedProfileGroupIdOf(PROFILE_USER_ID, PARENT_USER_ID);
+ assertIsStartedProfile(PROFILE_USER_ID);
+ }
+
+ @Test
+ public final void testStartUser_profileBg_secondaryDisplay() {
+ int result = mMediator.startUser(PROFILE_USER_ID, PARENT_USER_ID, BG, SECONDARY_DISPLAY_ID);
+ assertStartUserResult(result, START_USER_RESULT_FAILURE);
+
+ assertCurrentUser(INITIAL_CURRENT_USER_ID);
+ assertIsNotCurrentUserOrRunningProfileOfCurrentUser(PROFILE_USER_ID);
+ }
+
+ @Test
+ public final void testStartUser_profileFg() {
+ int result = mMediator.startUser(PROFILE_USER_ID, PARENT_USER_ID, FG, DEFAULT_DISPLAY);
+ assertStartUserResult(result, START_USER_RESULT_FAILURE);
+
+ assertCurrentUser(INITIAL_CURRENT_USER_ID);
+ assertIsNotCurrentUserOrRunningProfileOfCurrentUser(PROFILE_USER_ID);
+ assertStartedProfileGroupIdOf(PROFILE_USER_ID, NO_PROFILE_GROUP_ID);
+ }
+
+ @Test
+ public final void testStartUser_profileFgSecondaryDisplay() {
+ int result = mMediator.startUser(PROFILE_USER_ID, PARENT_USER_ID, FG, SECONDARY_DISPLAY_ID);
+
+ assertStartUserResult(result, START_USER_RESULT_FAILURE);
+ assertCurrentUser(INITIAL_CURRENT_USER_ID);
+ }
+
+ @Test
+ public final void testGetStartedProfileGroupId_whenStartedWithNoProfileGroupId() {
+ int result = mMediator.startUser(USER_ID, NO_PROFILE_GROUP_ID, FG, DEFAULT_DISPLAY);
+ assertStartUserResult(result, START_USER_RESULT_SUCCESS_VISIBLE);
+
+ assertWithMessage("shit").that(mMediator.getStartedProfileGroupId(USER_ID))
+ .isEqualTo(USER_ID);
}
@Test
public final void testAssignUserToDisplay_defaultDisplayIgnored() {
- mMediator.assignUserToDisplay(USER_ID, DEFAULT_DISPLAY);
+ mMediator.assignUserToDisplay(USER_ID, USER_ID, DEFAULT_DISPLAY);
assertNoUserAssignedToDisplay();
}
@@ -103,18 +211,14 @@
@Test
public final void testIsUserVisible_startedProfileOfcurrentUser() {
- addDefaultProfileAndParent();
mockCurrentUser(PARENT_USER_ID);
startDefaultProfile();
- setUserState(PROFILE_USER_ID, STATE_RUNNING_UNLOCKED);
-
assertWithMessage("isUserVisible(%s)", PROFILE_USER_ID)
.that(mMediator.isUserVisible(PROFILE_USER_ID)).isTrue();
}
@Test
public final void testIsUserVisible_stoppedProfileOfcurrentUser() {
- addDefaultProfileAndParent();
mockCurrentUser(PARENT_USER_ID);
stopDefaultProfile();
@@ -164,7 +268,6 @@
@Test
public final void testIsUserVisibleOnDisplay_startedProfileOfcurrentUserInvalidDisplay() {
- addDefaultProfileAndParent();
mockCurrentUser(PARENT_USER_ID);
startDefaultProfile();
@@ -174,7 +277,6 @@
@Test
public final void testIsUserVisibleOnDisplay_stoppedProfileOfcurrentUserInvalidDisplay() {
- addDefaultProfileAndParent();
mockCurrentUser(PARENT_USER_ID);
stopDefaultProfile();
@@ -184,18 +286,14 @@
@Test
public final void testIsUserVisibleOnDisplay_startedProfileOfcurrentUserDefaultDisplay() {
- addDefaultProfileAndParent();
mockCurrentUser(PARENT_USER_ID);
startDefaultProfile();
- setUserState(PROFILE_USER_ID, STATE_RUNNING_UNLOCKED);
-
assertWithMessage("isUserVisible(%s, %s)", PROFILE_USER_ID, DEFAULT_DISPLAY)
.that(mMediator.isUserVisible(PROFILE_USER_ID, DEFAULT_DISPLAY)).isTrue();
}
@Test
public final void testIsUserVisibleOnDisplay_stoppedProfileOfcurrentUserDefaultDisplay() {
- addDefaultProfileAndParent();
mockCurrentUser(PARENT_USER_ID);
stopDefaultProfile();
@@ -205,18 +303,14 @@
@Test
public final void testIsUserVisibleOnDisplay_startedProfileOfCurrentUserSecondaryDisplay() {
- addDefaultProfileAndParent();
mockCurrentUser(PARENT_USER_ID);
startDefaultProfile();
- setUserState(PROFILE_USER_ID, STATE_RUNNING_UNLOCKED);
-
assertWithMessage("isUserVisible(%s, %s)", PROFILE_USER_ID, SECONDARY_DISPLAY_ID)
.that(mMediator.isUserVisible(PROFILE_USER_ID, SECONDARY_DISPLAY_ID)).isTrue();
}
@Test
public void testIsUserVisibleOnDisplay_stoppedProfileOfcurrentUserSecondaryDisplay() {
- addDefaultProfileAndParent();
mockCurrentUser(PARENT_USER_ID);
stopDefaultProfile();
@@ -250,11 +344,8 @@
@Test
public final void testGetDisplayAssignedToUser_startedProfileOfcurrentUser() {
- addDefaultProfileAndParent();
mockCurrentUser(PARENT_USER_ID);
startDefaultProfile();
- setUserState(PROFILE_USER_ID, STATE_RUNNING_UNLOCKED);
-
assertWithMessage("getDisplayAssignedToUser(%s)", PROFILE_USER_ID)
.that(mMediator.getDisplayAssignedToUser(PROFILE_USER_ID))
.isEqualTo(DEFAULT_DISPLAY);
@@ -262,7 +353,6 @@
@Test
public final void testGetDisplayAssignedToUser_stoppedProfileOfcurrentUser() {
- addDefaultProfileAndParent();
mockCurrentUser(PARENT_USER_ID);
stopDefaultProfile();
@@ -296,28 +386,96 @@
.isEqualTo(USER_ID);
}
- // NOTE: should only called by tests that indirectly needs to check user assignments (like
- // isUserVisible), not by tests for the user assignment methods per se.
+ // TODO(b/244644281): remove if start & assign are merged; if they aren't, add a note explaining
+ // it's not meant to be used to test startUser() itself.
+ protected void mockCurrentUser(@UserIdInt int userId) {
+ Log.d(TAG, "mockCurrentUser(" + userId + ")");
+ int result = mMediator.startUser(userId, userId, FG, DEFAULT_DISPLAY);
+ if (result != START_USER_RESULT_SUCCESS_VISIBLE) {
+ throw new IllegalStateException("Failed to mock current user " + userId
+ + ": mediator returned " + startUserResultToString(result));
+ }
+ }
+
+ // TODO(b/244644281): remove if start & assign are merged; if they aren't, add a note explaining
+ // it's not meant to be used to test startUser() itself.
+ protected void startDefaultProfile() {
+ mockCurrentUser(PARENT_USER_ID);
+ Log.d(TAG, "starting default profile (" + PROFILE_USER_ID + ") in background after starting"
+ + " its parent (" + PARENT_USER_ID + ") on foreground");
+
+ int result = mMediator.startUser(PROFILE_USER_ID, PARENT_USER_ID, BG, DEFAULT_DISPLAY);
+ if (result != START_USER_RESULT_SUCCESS_VISIBLE) {
+ throw new IllegalStateException("Failed to start profile user " + PROFILE_USER_ID
+ + ": mediator returned " + startUserResultToString(result));
+ }
+ }
+
+ // TODO(b/244644281): remove if start & assign are merged; if they aren't, add a note explaining
+ // it's not meant to be used to test stopUser() itself.
+ protected void stopDefaultProfile() {
+ Log.d(TAG, "stopping default profile");
+ mMediator.stopUser(PROFILE_USER_ID);
+ }
+
+ // TODO(b/244644281): remove if start & assign are merged; if they aren't, add a note explaining
+ // it's not meant to be used to test assignUserToDisplay() itself.
protected final void assignUserToDisplay(@UserIdInt int userId, int displayId) {
- mUsersOnSecondaryDisplays.put(userId, displayId);
+ Log.d(TAG, "assignUserToDisplay(" + userId + ", " + displayId + ")");
+ int result = mMediator.startUser(userId, userId, BG, displayId);
+ if (result != START_USER_RESULT_SUCCESS_INVISIBLE) {
+ throw new IllegalStateException("Failed to startuser " + userId
+ + " on background: mediator returned " + startUserResultToString(result));
+ }
+ mMediator.assignUserToDisplay(userId, userId, displayId);
+
}
protected final void assertNoUserAssignedToDisplay() {
- assertWithMessage("mUsersOnSecondaryDisplays()").that(usersOnSecondaryDisplaysAsMap())
+ assertWithMessage("uses on secondary displays")
+ .that(mMediator.getUsersOnSecondaryDisplays())
.isEmpty();
}
protected final void assertUserAssignedToDisplay(@UserIdInt int userId, int displayId) {
- assertWithMessage("mUsersOnSecondaryDisplays()").that(usersOnSecondaryDisplaysAsMap())
+ assertWithMessage("uses on secondary displays")
+ .that(mMediator.getUsersOnSecondaryDisplays())
.containsExactly(userId, displayId);
}
- private Map<Integer, Integer> usersOnSecondaryDisplaysAsMap() {
- int size = mUsersOnSecondaryDisplays.size();
- Map<Integer, Integer> map = new LinkedHashMap<>(size);
- for (int i = 0; i < size; i++) {
- map.put(mUsersOnSecondaryDisplays.keyAt(i), mUsersOnSecondaryDisplays.valueAt(i));
- }
- return map;
+ private void assertCurrentUser(@UserIdInt int userId) {
+ assertWithMessage("mediator.getCurrentUserId()").that(mMediator.getCurrentUserId())
+ .isEqualTo(userId);
+ }
+
+ private void assertIsStartedProfile(@UserIdInt int userId) {
+ assertWithMessage("mediator.isStartedProfile(%s)", userId)
+ .that(mMediator.isStartedProfile(userId))
+ .isTrue();
+ }
+
+ private void assertStartedProfileGroupIdOf(@UserIdInt int profileId, @UserIdInt int parentId) {
+ assertWithMessage("mediator.getStartedProfileGroupId(%s)", profileId)
+ .that(mMediator.getStartedProfileGroupId(profileId))
+ .isEqualTo(parentId);
+ }
+
+ private void assertIsCurrentUserOrRunningProfileOfCurrentUser(int userId) {
+ assertWithMessage("mediator.isCurrentUserOrRunningProfileOfCurrentUser(%s)", userId)
+ .that(mMediator.isCurrentUserOrRunningProfileOfCurrentUser(userId))
+ .isTrue();
+ }
+
+ private void assertIsNotCurrentUserOrRunningProfileOfCurrentUser(int userId) {
+ assertWithMessage("mediator.isCurrentUserOrRunningProfileOfCurrentUser(%s)", userId)
+ .that(mMediator.isCurrentUserOrRunningProfileOfCurrentUser(userId))
+ .isFalse();
+ }
+
+ private void assertStartUserResult(int actualResult, int expectedResult) {
+ assertWithMessage("startUser() result (where %s=%s and %s=%s)",
+ actualResult, startUserResultToString(actualResult),
+ expectedResult, startUserResultToString(expectedResult))
+ .that(actualResult).isEqualTo(expectedResult);
}
}
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 61bb57e..9386a23 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -204,10 +204,10 @@
":FrameworksServicesTests_install_uses_sdk_q0",
":FrameworksServicesTests_install_uses_sdk_q0_r0",
":FrameworksServicesTests_install_uses_sdk_r0",
- ":FrameworksServicesTests_install_uses_sdk_r5",
+ ":FrameworksServicesTests_install_uses_sdk_r1000",
":FrameworksServicesTests_install_uses_sdk_r_none",
":FrameworksServicesTests_install_uses_sdk_r0_s0",
- ":FrameworksServicesTests_install_uses_sdk_r0_s5",
+ ":FrameworksServicesTests_install_uses_sdk_r0_s1000",
":FrameworksServicesTests_keyset_permdef_sa_unone",
":FrameworksServicesTests_keyset_permuse_sa_ua_ub",
":FrameworksServicesTests_keyset_permuse_sb_ua_ub",
diff --git a/services/tests/servicestests/apks/install_uses_sdk/Android.bp b/services/tests/servicestests/apks/install_uses_sdk/Android.bp
index a51293d..2894395 100644
--- a/services/tests/servicestests/apks/install_uses_sdk/Android.bp
+++ b/services/tests/servicestests/apks/install_uses_sdk/Android.bp
@@ -32,9 +32,9 @@
}
android_test_helper_app {
- name: "FrameworksServicesTests_install_uses_sdk_r5",
+ name: "FrameworksServicesTests_install_uses_sdk_r1000",
defaults: ["FrameworksServicesTests_apks_defaults"],
- manifest: "AndroidManifest-r5.xml",
+ manifest: "AndroidManifest-r1000.xml",
}
android_test_helper_app {
@@ -44,9 +44,9 @@
}
android_test_helper_app {
- name: "FrameworksServicesTests_install_uses_sdk_r0_s5",
+ name: "FrameworksServicesTests_install_uses_sdk_r0_s1000",
defaults: ["FrameworksServicesTests_apks_defaults"],
- manifest: "AndroidManifest-r0-s5.xml",
+ manifest: "AndroidManifest-r0-s1000.xml",
}
android_test_helper_app {
diff --git a/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r0-s5.xml b/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r0-s1000.xml
similarity index 97%
rename from services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r0-s5.xml
rename to services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r0-s1000.xml
index bafe4c4..25743b8 100644
--- a/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r0-s5.xml
+++ b/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r0-s1000.xml
@@ -19,7 +19,7 @@
<uses-sdk android:minSdkVersion="4" android:targetSdkVersion="29">
<!-- This fails because 31 is not version 5 -->
<extension-sdk android:sdkVersion="30" android:minExtensionVersion="0" />
- <extension-sdk android:sdkVersion="31" android:minExtensionVersion="5" />
+ <extension-sdk android:sdkVersion="31" android:minExtensionVersion="1000" />
</uses-sdk>
<application>
diff --git a/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r5.xml b/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r1000.xml
similarity index 97%
rename from services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r5.xml
rename to services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r1000.xml
index 7723d05..9bf9254 100644
--- a/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r5.xml
+++ b/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r1000.xml
@@ -18,7 +18,7 @@
<uses-sdk android:minSdkVersion="4" android:targetSdkVersion="29">
<!-- This will fail to install, because minExtensionVersion is not met -->
- <extension-sdk android:sdkVersion="30" android:minExtensionVersion="5" />
+ <extension-sdk android:sdkVersion="30" android:minExtensionVersion="1000" />
</uses-sdk>
<application>
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
index c15f6a9..bbcf77b 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
@@ -326,7 +326,7 @@
void register(Context context) {
if (!mRegistered) {
- context.registerReceiver(this, mFilter);
+ context.registerReceiver(this, mFilter, Context.RECEIVER_EXPORTED_UNAUDITED);
mRegistered = true;
}
}
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
index f28ad79..e54a48b 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
@@ -3470,7 +3470,8 @@
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
- return mMockContext.registerReceiver(receiver, filter);
+ return mMockContext.registerReceiver(receiver, filter,
+ Context.RECEIVER_EXPORTED_UNAUDITED);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 935d1d8..80cee50 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -38,6 +38,7 @@
import static com.android.server.am.UserController.USER_CURRENT_MSG;
import static com.android.server.am.UserController.USER_START_MSG;
import static com.android.server.am.UserController.USER_SWITCH_TIMEOUT_MSG;
+import static com.android.server.am.UserController.USER_VISIBILITY_CHANGED_MSG;
import static com.google.android.collect.Lists.newArrayList;
import static com.google.android.collect.Sets.newHashSet;
@@ -158,6 +159,7 @@
REPORT_USER_SWITCH_MSG,
USER_SWITCH_TIMEOUT_MSG,
USER_START_MSG,
+ USER_VISIBILITY_CHANGED_MSG,
USER_CURRENT_MSG);
private static final Set<Integer> START_BACKGROUND_USER_MESSAGE_CODES = newHashSet(
@@ -283,7 +285,7 @@
assertWithMessage("wrong binder message calls").that(mInjector.mHandler.getMessageCodes())
.containsExactly(USER_START_MSG);
- verifyUserAssignedToDisplay(TEST_PRE_CREATED_USER_ID, Display.DEFAULT_DISPLAY);
+ verifyUserNeverAssignedToDisplay();
}
private void startUserAssertions(
@@ -948,11 +950,13 @@
}
private void verifyUserAssignedToDisplay(@UserIdInt int userId, int displayId) {
- verify(mInjector.getUserManagerInternal()).assignUserToDisplay(userId, displayId);
+ verify(mInjector.getUserManagerInternal()).assignUserToDisplay(eq(userId), anyInt(),
+ anyBoolean(), eq(displayId));
}
private void verifyUserNeverAssignedToDisplay() {
- verify(mInjector.getUserManagerInternal(), never()).assignUserToDisplay(anyInt(), anyInt());
+ verify(mInjector.getUserManagerInternal(), never()).assignUserToDisplay(anyInt(), anyInt(),
+ anyBoolean(), anyInt());
}
private void verifyUserUnassignedFromDisplay(@UserIdInt int userId) {
@@ -964,7 +968,7 @@
}
private void verifySystemUserVisibilityChangedNotified(boolean visible) {
- verify(mInjector).notifySystemUserVisibilityChanged(visible);
+ verify(mInjector).onUserVisibilityChanged(UserHandle.USER_SYSTEM, visible);
}
// Should be public to allow mocking
@@ -1104,13 +1108,13 @@
}
@Override
- void onUserStarting(@UserIdInt int userId, boolean visible) {
- Log.i(TAG, "onUserStarting(" + userId + ", " + visible + ")");
+ void onUserStarting(@UserIdInt int userId) {
+ Log.i(TAG, "onUserStarting(" + userId + ")");
}
@Override
- void notifySystemUserVisibilityChanged(boolean visible) {
- Log.i(TAG, "notifySystemUserVisibilityChanged(" + visible + ")");
+ void onUserVisibilityChanged(@UserIdInt int userId, boolean visible) {
+ Log.i(TAG, "onUserVisibilityChanged(" + userId + ", " + visible + ")");
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 85d8aba..0d6d1a2 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -64,6 +64,7 @@
import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
+import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Binder;
import android.os.IBinder;
@@ -75,7 +76,10 @@
import androidx.test.filters.SmallTest;
import com.android.internal.R;
+import com.android.internal.statusbar.ISessionListener;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.server.biometrics.log.BiometricContextProvider;
+import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.LockoutTracker;
import org.junit.Before;
@@ -129,6 +133,16 @@
ITrustManager mTrustManager;
@Mock
DevicePolicyManager mDevicePolicyManager;
+ @Mock
+ private IStatusBarService mStatusBarService;
+ @Mock
+ private ISessionListener mSessionListener;
+ @Mock
+ private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
+ @Mock
+ private AuthSessionCoordinator mAuthSessionCoordinator;
+
+ BiometricContextProvider mBiometricContextProvider;
@Before
public void setUp() {
@@ -160,6 +174,11 @@
when(mResources.getString(R.string.biometric_error_user_canceled))
.thenReturn(ERROR_USER_CANCELED);
+ when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
+ mBiometricContextProvider = new BiometricContextProvider(mAmbientDisplayConfiguration,
+ mStatusBarService, null /* handler */, mAuthSessionCoordinator);
+ when(mInjector.getBiometricContext(any())).thenReturn(mBiometricContextProvider);
+
final String[] config = {
"0:2:15", // ID0:Fingerprint:Strong
"1:8:15", // ID1:Face:Strong
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthResultCoordinatorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthResultCoordinatorTest.java
index c5a8557..ebf7fd8 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthResultCoordinatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthResultCoordinatorTest.java
@@ -61,11 +61,11 @@
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_WEAK)).isEqualTo(
AUTHENTICATOR_DEFAULT);
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)).isEqualTo(
- AUTHENTICATOR_UNLOCKED);
+ AUTHENTICATOR_DEFAULT);
}
@Test
- public void testLockout() {
+ public void testConvenientLockout() {
mAuthResultCoordinator.lockedOutFor(
BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE);
@@ -80,7 +80,7 @@
}
@Test
- public void testConvenientLockout() {
+ public void testConvenientUnlock() {
mAuthResultCoordinator.authenticatedFor(
BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE);
@@ -91,11 +91,26 @@
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_WEAK)).isEqualTo(
AUTHENTICATOR_DEFAULT);
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)).isEqualTo(
- AUTHENTICATOR_UNLOCKED);
+ AUTHENTICATOR_DEFAULT);
}
@Test
public void testWeakLockout() {
+ mAuthResultCoordinator.lockedOutFor(
+ BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE);
+
+ Map<Integer, Integer> authMap = mAuthResultCoordinator.getResult();
+
+ assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_STRONG)).isEqualTo(
+ AUTHENTICATOR_DEFAULT);
+ assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_WEAK)).isEqualTo(
+ AUTHENTICATOR_DEFAULT);
+ assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)).isEqualTo(
+ AUTHENTICATOR_LOCKED);
+ }
+
+ @Test
+ public void testWeakUnlock() {
mAuthResultCoordinator.authenticatedFor(
BiometricManager.Authenticators.BIOMETRIC_WEAK);
@@ -104,13 +119,29 @@
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_STRONG)).isEqualTo(
AUTHENTICATOR_DEFAULT);
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_WEAK)).isEqualTo(
- AUTHENTICATOR_UNLOCKED);
+ AUTHENTICATOR_DEFAULT);
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)).isEqualTo(
- AUTHENTICATOR_UNLOCKED);
+ AUTHENTICATOR_DEFAULT);
}
@Test
public void testStrongLockout() {
+ mAuthResultCoordinator.lockedOutFor(
+ BiometricManager.Authenticators.BIOMETRIC_STRONG);
+
+ Map<Integer, Integer> authMap = mAuthResultCoordinator.getResult();
+
+ assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_STRONG)).isEqualTo(
+ AUTHENTICATOR_LOCKED);
+ assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_WEAK)).isEqualTo(
+ AUTHENTICATOR_LOCKED);
+ assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)).isEqualTo(
+ AUTHENTICATOR_LOCKED);
+ }
+
+
+ @Test
+ public void testStrongUnlock() {
mAuthResultCoordinator.authenticatedFor(
BiometricManager.Authenticators.BIOMETRIC_STRONG);
@@ -136,9 +167,9 @@
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_STRONG)).isEqualTo(
AUTHENTICATOR_DEFAULT);
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_WEAK)).isEqualTo(
- AUTHENTICATOR_UNLOCKED | AUTHENTICATOR_LOCKED);
+ AUTHENTICATOR_LOCKED);
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)).isEqualTo(
- AUTHENTICATOR_UNLOCKED | AUTHENTICATOR_LOCKED);
+ AUTHENTICATOR_LOCKED);
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthSessionCoordinatorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthSessionCoordinatorTest.java
index 6e44875..c3b9cb1 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthSessionCoordinatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthSessionCoordinatorTest.java
@@ -53,22 +53,28 @@
}
@Test
- public void testUserUnlocked() {
+ public void testUserUnlockedWithWeak() {
mCoordinator.authStartedFor(PRIMARY_USER, 1 /* sensorId */, 0 /* requestId */);
mCoordinator.lockedOutFor(PRIMARY_USER, BIOMETRIC_STRONG, 1 /* sensorId */,
0 /* requestId */);
- assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isFalse();
- assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isFalse();
- assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_STRONG)).isFalse();
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
mCoordinator.authStartedFor(PRIMARY_USER, 1 /* sensorId */, 0 /* requestId */);
- mCoordinator.authenticatedFor(PRIMARY_USER, BIOMETRIC_WEAK, 1 /* sensorId */,
- 0 /* requestId */);
+ mCoordinator.authEndedFor(PRIMARY_USER, BIOMETRIC_WEAK, 1 /* sensorId */,
+ 0 /* requestId */, true /* wasSuccessful */);
- assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
- assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
- assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_STRONG)).isFalse();
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
}
@Test
@@ -77,38 +83,79 @@
mCoordinator.authStartedFor(PRIMARY_USER, 2 /* sensorId */, 0 /* requestId */);
mCoordinator.lockedOutFor(PRIMARY_USER, BIOMETRIC_STRONG, 1 /* sensorId */,
0 /* requestId */);
- mCoordinator.authenticatedFor(PRIMARY_USER, BIOMETRIC_WEAK, 2 /* sensorId */,
- 0 /* requestId */);
+ mCoordinator.authEndedFor(PRIMARY_USER, BIOMETRIC_WEAK, 2 /* sensorId */,
+ 0 /* requestId */, false /* wasSuccessful */);
- assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isFalse();
- assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isFalse();
- assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_STRONG)).isFalse();
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
mCoordinator.authStartedFor(PRIMARY_USER, 1 /* sensorId */, 0 /* requestId */);
- mCoordinator.authenticatedFor(PRIMARY_USER, BIOMETRIC_WEAK, 1 /* sensorId */,
- 0 /* requestId */);
+ mCoordinator.authEndedFor(PRIMARY_USER, BIOMETRIC_WEAK, 1 /* sensorId */,
+ 0 /* requestId */, false /* wasSuccessful */);
- assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
- assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
- assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_STRONG)).isFalse();
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+ }
+
+ @Test
+ public void testWeakAndConvenientCannotResetLockout() {
+ mCoordinator.authStartedFor(PRIMARY_USER, 1 /* sensorId */, 0 /* requestId */);
+ mCoordinator.lockedOutFor(PRIMARY_USER, BIOMETRIC_STRONG, 1 /* sensorId */,
+ 0 /* requestId */);
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+
+ mCoordinator.resetLockoutFor(PRIMARY_USER, BIOMETRIC_WEAK, 0 /* requestId */);
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+
+ mCoordinator.resetLockoutFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE, 0 /* requestId */);
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
}
@Test
public void testUserCanAuthDuringLockoutOfSameSession() {
mCoordinator.resetLockoutFor(PRIMARY_USER, BIOMETRIC_STRONG, 0 /* requestId */);
- assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
- assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
- assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_STRONG)).isTrue();
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
mCoordinator.authStartedFor(PRIMARY_USER, 1 /* sensorId */, 0 /* requestId */);
mCoordinator.authStartedFor(PRIMARY_USER, 2 /* sensorId */, 0 /* requestId */);
mCoordinator.lockedOutFor(PRIMARY_USER, BIOMETRIC_WEAK, 2 /* sensorId */,
0 /* requestId */);
- assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
- assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
- assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
}
@Test
@@ -123,25 +170,39 @@
mCoordinator.resetLockoutFor(PRIMARY_USER, BIOMETRIC_STRONG, 0 /* requestId */);
- assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
- assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
- assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_STRONG)).isTrue();
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
- assertThat(mCoordinator.getCanAuthFor(SECONDARY_USER, BIOMETRIC_CONVENIENCE)).isFalse();
- assertThat(mCoordinator.getCanAuthFor(SECONDARY_USER, BIOMETRIC_WEAK)).isFalse();
- assertThat(mCoordinator.getCanAuthFor(SECONDARY_USER, BIOMETRIC_STRONG)).isFalse();
+ assertThat(
+ mCoordinator.getLockoutStateFor(SECONDARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+ assertThat(mCoordinator.getLockoutStateFor(SECONDARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+ assertThat(mCoordinator.getLockoutStateFor(SECONDARY_USER, BIOMETRIC_STRONG)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
mCoordinator.authStartedFor(PRIMARY_USER, 1 /* sensorId */, 0 /* requestId */);
mCoordinator.authStartedFor(PRIMARY_USER, 2 /* sensorId */, 0 /* requestId */);
mCoordinator.lockedOutFor(PRIMARY_USER, BIOMETRIC_WEAK, 2 /* sensorId */,
0 /* requestId */);
- assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
- assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
- assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
- assertThat(mCoordinator.getCanAuthFor(SECONDARY_USER, BIOMETRIC_CONVENIENCE)).isFalse();
- assertThat(mCoordinator.getCanAuthFor(SECONDARY_USER, BIOMETRIC_WEAK)).isFalse();
- assertThat(mCoordinator.getCanAuthFor(SECONDARY_USER, BIOMETRIC_STRONG)).isFalse();
+ assertThat(
+ mCoordinator.getLockoutStateFor(SECONDARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+ assertThat(mCoordinator.getLockoutStateFor(SECONDARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+ assertThat(mCoordinator.getLockoutStateFor(SECONDARY_USER, BIOMETRIC_STRONG)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index 9f30c75..4c898b0 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -17,6 +17,7 @@
package com.android.server.biometrics.sensors;
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_CANCELED;
+import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_SUCCESS;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
@@ -78,17 +79,24 @@
private static final int TEST_SENSOR_ID = 1;
private static final int LOG_NUM_RECENT_OPERATIONS = 2;
@Rule
- public final TestableContext mContext =
- new TestableContext(InstrumentationRegistry.getContext(), null);
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getContext(), null);
private BiometricScheduler mScheduler;
private IBinder mToken;
@Mock
private IBiometricService mBiometricService;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private AuthSessionCoordinator mAuthSessionCoordinator;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mToken = new Binder();
+ when(mAuthSessionCoordinator.getLockoutStateFor(anyInt(), anyInt())).thenReturn(
+ BIOMETRIC_SUCCESS);
+ when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator);
mScheduler = new BiometricScheduler(TAG, new Handler(TestableLooper.get(this).getLooper()),
BiometricScheduler.SENSOR_TYPE_UNKNOWN, null /* gestureAvailabilityTracker */,
mBiometricService, LOG_NUM_RECENT_OPERATIONS);
@@ -98,10 +106,10 @@
public void testClientDuplicateFinish_ignoredBySchedulerAndDoesNotCrash() {
final Supplier<Object> nonNullDaemon = () -> mock(Object.class);
- final HalClientMonitor<Object> client1 =
- new TestHalClientMonitor(mContext, mToken, nonNullDaemon);
- final HalClientMonitor<Object> client2 =
- new TestHalClientMonitor(mContext, mToken, nonNullDaemon);
+ final HalClientMonitor<Object> client1 = new TestHalClientMonitor(mContext, mToken,
+ nonNullDaemon);
+ final HalClientMonitor<Object> client2 = new TestHalClientMonitor(mContext, mToken,
+ nonNullDaemon);
mScheduler.scheduleClientMonitor(client1);
mScheduler.scheduleClientMonitor(client2);
@@ -112,10 +120,9 @@
@Test
public void testRemovesPendingOperations_whenNullHal_andNotBiometricPrompt() {
// Even if second client has a non-null daemon, it needs to be canceled.
- final TestHalClientMonitor client1 = new TestHalClientMonitor(
- mContext, mToken, () -> null);
- final TestHalClientMonitor client2 = new TestHalClientMonitor(
- mContext, mToken, () -> mock(Object.class));
+ final TestHalClientMonitor client1 = new TestHalClientMonitor(mContext, mToken, () -> null);
+ final TestHalClientMonitor client2 = new TestHalClientMonitor(mContext, mToken,
+ () -> mock(Object.class));
final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class);
final ClientMonitorCallback callback2 = mock(ClientMonitorCallback.class);
@@ -150,10 +157,10 @@
final ClientMonitorCallbackConverter listener1 = mock(ClientMonitorCallbackConverter.class);
- final TestAuthenticationClient client1 =
- new TestAuthenticationClient(mContext, () -> null, mToken, listener1);
- final TestHalClientMonitor client2 =
- new TestHalClientMonitor(mContext, mToken, () -> daemon2);
+ final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext, () -> null,
+ mToken, listener1, mBiometricContext);
+ final TestHalClientMonitor client2 = new TestHalClientMonitor(mContext, mToken,
+ () -> daemon2);
final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class);
final ClientMonitorCallback callback2 = mock(ClientMonitorCallback.class);
@@ -188,15 +195,15 @@
@Test
public void testCancelNotInvoked_whenOperationWaitingForCookie() {
final Supplier<Object> lazyDaemon1 = () -> mock(Object.class);
- final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext,
- lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class));
+ final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext, lazyDaemon1,
+ mToken, mock(ClientMonitorCallbackConverter.class), mBiometricContext);
final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class);
// Schedule a BiometricPrompt authentication request
mScheduler.scheduleClientMonitor(client1, callback1);
- assertNotEquals(0, mScheduler.mCurrentOperation.isReadyToStart(
- mock(ClientMonitorCallback.class)));
+ assertNotEquals(0,
+ mScheduler.mCurrentOperation.isReadyToStart(mock(ClientMonitorCallback.class)));
assertEquals(client1, mScheduler.mCurrentOperation.getClientMonitor());
assertEquals(0, mScheduler.mPendingOperations.size());
@@ -304,7 +311,7 @@
final TestHalClientMonitor client1 = new TestHalClientMonitor(mContext, mToken, lazyDaemon);
final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
final TestAuthenticationClient client2 = new TestAuthenticationClient(mContext, lazyDaemon,
- mToken, callback);
+ mToken, callback, mBiometricContext);
// Add a non-cancellable client, then add the auth client
mScheduler.scheduleClientMonitor(client1);
@@ -367,7 +374,8 @@
final Supplier<Object> lazyDaemon = () -> mock(Object.class);
final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
testCancelsWhenRequestId(requestId, cancelRequestId, started,
- new TestAuthenticationClient(mContext, lazyDaemon, mToken, callback));
+ new TestAuthenticationClient(mContext, lazyDaemon, mToken, callback,
+ mBiometricContext));
}
@Test
@@ -448,11 +456,11 @@
final long requestId2 = 20;
final Supplier<Object> lazyDaemon = () -> mock(Object.class);
final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
- final TestAuthenticationClient client1 = new TestAuthenticationClient(
- mContext, lazyDaemon, mToken, callback);
+ final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext, lazyDaemon,
+ mToken, callback, mBiometricContext);
client1.setRequestId(requestId1);
- final TestAuthenticationClient client2 = new TestAuthenticationClient(
- mContext, lazyDaemon, mToken, callback);
+ final TestAuthenticationClient client2 = new TestAuthenticationClient(mContext, lazyDaemon,
+ mToken, callback, mBiometricContext);
client2.setRequestId(requestId2);
mScheduler.scheduleClientMonitor(client1);
@@ -506,8 +514,8 @@
@Test
public void testClientDestroyed_afterFinish() {
final Supplier<Object> nonNullDaemon = () -> mock(Object.class);
- final TestHalClientMonitor client =
- new TestHalClientMonitor(mContext, mToken, nonNullDaemon);
+ final TestHalClientMonitor client = new TestHalClientMonitor(mContext, mToken,
+ nonNullDaemon);
mScheduler.scheduleClientMonitor(client);
client.mCallback.onClientFinished(client, true /* success */);
waitForIdle();
@@ -520,7 +528,8 @@
final TestableLooper looper = TestableLooper.get(this);
final Supplier<Object> lazyDaemon1 = () -> mock(Object.class);
final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext,
- lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class), 0 /* cookie */);
+ lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class), 0 /* cookie */,
+ mBiometricContext);
final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class);
mScheduler.scheduleClientMonitor(client1, callback1);
@@ -555,7 +564,8 @@
final TestableLooper looper = TestableLooper.get(this);
final Supplier<Object> lazyDaemon1 = () -> mock(Object.class);
final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext,
- lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class), 0 /* cookie */);
+ lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class), 0 /* cookie */,
+ mBiometricContext);
final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class);
mScheduler.scheduleClientMonitor(client1, callback1);
@@ -589,7 +599,8 @@
//Run additional auth client
final TestAuthenticationClient client2 = new TestAuthenticationClient(mContext,
- lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class), 0 /* cookie */);
+ lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class), 0 /* cookie */,
+ mBiometricContext);
final ClientMonitorCallback callback2 = mock(ClientMonitorCallback.class);
mScheduler.scheduleClientMonitor(client2, callback2);
@@ -626,7 +637,8 @@
final TestableLooper looper = TestableLooper.get(this);
final Supplier<Object> lazyDaemon1 = () -> mock(Object.class);
final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext,
- lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class), 0 /* cookie */);
+ lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class), 0 /* cookie */,
+ mBiometricContext);
final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class);
mScheduler.scheduleClientMonitor(client1, callback1);
@@ -679,19 +691,22 @@
TestAuthenticationClient(@NonNull Context context,
@NonNull Supplier<Object> lazyDaemon, @NonNull IBinder token,
- @NonNull ClientMonitorCallbackConverter listener) {
- this(context, lazyDaemon, token, listener, 1 /* cookie */);
+ @NonNull ClientMonitorCallbackConverter listener,
+ BiometricContext biometricContext) {
+ this(context, lazyDaemon, token, listener, 1 /* cookie */, biometricContext);
}
TestAuthenticationClient(@NonNull Context context,
@NonNull Supplier<Object> lazyDaemon, @NonNull IBinder token,
- @NonNull ClientMonitorCallbackConverter listener, int cookie) {
+ @NonNull ClientMonitorCallbackConverter listener, int cookie,
+ @NonNull BiometricContext biometricContext) {
super(context, lazyDaemon, token, listener, 0 /* targetUserId */, 0 /* operationId */,
false /* restricted */, TAG, cookie, false /* requireConfirmation */,
- TEST_SENSOR_ID, mock(BiometricLogger.class), mock(BiometricContext.class),
+ TEST_SENSOR_ID, mock(BiometricLogger.class), biometricContext,
true /* isStrongBiometric */, null /* taskStackListener */,
- mock(LockoutTracker.class), false /* isKeyguard */,
- true /* shouldVibrate */, false /* isKeyguardBypassEnabled */);
+ null /* lockoutTracker */, false /* isKeyguard */,
+ true /* shouldVibrate */, false /* isKeyguardBypassEnabled */,
+ 0 /* sensorStrength */);
}
@Override
@@ -742,14 +757,12 @@
boolean mStoppedHal = false;
int mNumCancels = 0;
- TestEnrollClient(@NonNull Context context,
- @NonNull Supplier<Object> lazyDaemon, @NonNull IBinder token,
- @NonNull ClientMonitorCallbackConverter listener) {
+ TestEnrollClient(@NonNull Context context, @NonNull Supplier<Object> lazyDaemon,
+ @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener) {
super(context, lazyDaemon, token, listener, 0 /* userId */, new byte[69],
- "test" /* owner */, mock(BiometricUtils.class),
- 5 /* timeoutSec */, TEST_SENSOR_ID,
- true /* shouldVibrate */,
- mock(BiometricLogger.class), mock(BiometricContext.class));
+ "test" /* owner */, mock(BiometricUtils.class), 5 /* timeoutSec */,
+ TEST_SENSOR_ID, true /* shouldVibrate */, mock(BiometricLogger.class),
+ mock(BiometricContext.class));
}
@Override
@@ -787,9 +800,9 @@
TestHalClientMonitor(@NonNull Context context, @NonNull IBinder token,
@NonNull Supplier<Object> lazyDaemon, int cookie, int protoEnum) {
- super(context, lazyDaemon, token /* token */, null /* listener */, 0 /* userId */,
- TAG, cookie, TEST_SENSOR_ID,
- mock(BiometricLogger.class), mock(BiometricContext.class));
+ super(context, lazyDaemon, token /* token */, null /* listener */, 0 /* userId */, TAG,
+ cookie, TEST_SENSOR_ID, mock(BiometricLogger.class),
+ mock(BiometricContext.class));
mProtoEnum = protoEnum;
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/MultiBiometricLockoutStateTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/MultiBiometricLockoutStateTest.java
index 0b10a7b..968844e4c 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/MultiBiometricLockoutStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/MultiBiometricLockoutStateTest.java
@@ -50,16 +50,22 @@
private static void unlockAllBiometrics(MultiBiometricLockoutState lockoutState, int userId) {
lockoutState.setAuthenticatorTo(userId, BIOMETRIC_STRONG, true /* canAuthenticate */);
- assertThat(lockoutState.canUserAuthenticate(userId, BIOMETRIC_STRONG)).isTrue();
- assertThat(lockoutState.canUserAuthenticate(userId, BIOMETRIC_WEAK)).isTrue();
- assertThat(lockoutState.canUserAuthenticate(userId, BIOMETRIC_CONVENIENCE)).isTrue();
+ assertThat(lockoutState.getLockoutState(userId, BIOMETRIC_STRONG)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
+ assertThat(lockoutState.getLockoutState(userId, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
+ assertThat(lockoutState.getLockoutState(userId, BIOMETRIC_CONVENIENCE)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
}
private static void lockoutAllBiometrics(MultiBiometricLockoutState lockoutState, int userId) {
lockoutState.setAuthenticatorTo(userId, BIOMETRIC_STRONG, false /* canAuthenticate */);
- assertThat(lockoutState.canUserAuthenticate(userId, BIOMETRIC_STRONG)).isFalse();
- assertThat(lockoutState.canUserAuthenticate(userId, BIOMETRIC_WEAK)).isFalse();
- assertThat(lockoutState.canUserAuthenticate(userId, BIOMETRIC_CONVENIENCE)).isFalse();
+ assertThat(lockoutState.getLockoutState(userId, BIOMETRIC_STRONG)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+ assertThat(lockoutState.getLockoutState(userId, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+ assertThat(lockoutState.getLockoutState(userId, BIOMETRIC_CONVENIENCE)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
}
private void unlockAllBiometrics() {
@@ -79,9 +85,12 @@
@Test
public void testInitialStateLockedOut() {
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isTrue();
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
}
@Test
@@ -89,20 +98,26 @@
unlockAllBiometrics();
mLockoutState.setAuthenticatorTo(PRIMARY_USER, BIOMETRIC_CONVENIENCE,
false /* canAuthenticate */);
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isTrue();
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
assertThat(
- mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isFalse();
+ mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
}
@Test
public void testWeakLockout() {
unlockAllBiometrics();
mLockoutState.setAuthenticatorTo(PRIMARY_USER, BIOMETRIC_WEAK, false /* canAuthenticate */);
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isTrue();
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isFalse();
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
assertThat(
- mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isFalse();
+ mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
}
@Test
@@ -110,10 +125,13 @@
lockoutAllBiometrics();
mLockoutState.setAuthenticatorTo(PRIMARY_USER, BIOMETRIC_STRONG,
false /* canAuthenticate */);
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isFalse();
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isFalse();
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
assertThat(
- mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isFalse();
+ mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
}
@Test
@@ -121,18 +139,24 @@
lockoutAllBiometrics();
mLockoutState.setAuthenticatorTo(PRIMARY_USER, BIOMETRIC_CONVENIENCE,
true /* canAuthenticate */);
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isFalse();
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isFalse();
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
}
@Test
public void testWeakUnlock() {
lockoutAllBiometrics();
mLockoutState.setAuthenticatorTo(PRIMARY_USER, BIOMETRIC_WEAK, true /* canAuthenticate */);
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isFalse();
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
}
@Test
@@ -140,9 +164,12 @@
lockoutAllBiometrics();
mLockoutState.setAuthenticatorTo(PRIMARY_USER, BIOMETRIC_STRONG,
true /* canAuthenticate */);
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isTrue();
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
}
@Test
@@ -154,45 +181,66 @@
lockoutAllBiometrics(lockoutState, userTwo);
lockoutState.setAuthenticatorTo(userOne, BIOMETRIC_WEAK, true /* canAuthenticate */);
- assertThat(lockoutState.canUserAuthenticate(userOne, BIOMETRIC_STRONG)).isFalse();
- assertThat(lockoutState.canUserAuthenticate(userOne, BIOMETRIC_WEAK)).isTrue();
- assertThat(lockoutState.canUserAuthenticate(userOne, BIOMETRIC_CONVENIENCE)).isTrue();
+ assertThat(lockoutState.getLockoutState(userOne, BIOMETRIC_STRONG)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+ assertThat(lockoutState.getLockoutState(userOne, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
+ assertThat(lockoutState.getLockoutState(userOne, BIOMETRIC_CONVENIENCE)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
- assertThat(lockoutState.canUserAuthenticate(userTwo, BIOMETRIC_STRONG)).isFalse();
- assertThat(lockoutState.canUserAuthenticate(userTwo, BIOMETRIC_WEAK)).isFalse();
- assertThat(lockoutState.canUserAuthenticate(userTwo, BIOMETRIC_CONVENIENCE)).isFalse();
+ assertThat(lockoutState.getLockoutState(userTwo, BIOMETRIC_STRONG)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+ assertThat(lockoutState.getLockoutState(userTwo, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
+ assertThat(lockoutState.getLockoutState(userTwo, BIOMETRIC_CONVENIENCE)).isEqualTo(
+ LockoutTracker.LOCKOUT_PERMANENT);
}
@Test
public void testTimedLockout() {
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isTrue();
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
mLockoutState.increaseLockoutTime(PRIMARY_USER, BIOMETRIC_STRONG,
System.currentTimeMillis() + 1);
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isFalse();
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isFalse();
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
+ LockoutTracker.LOCKOUT_TIMED);
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_TIMED);
assertThat(
- mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isFalse();
+ mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
+ LockoutTracker.LOCKOUT_TIMED);
}
@Test
public void testTimedLockoutAfterDuration() {
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isTrue();
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
when(mClock.millis()).thenReturn(0L);
mLockoutState.increaseLockoutTime(PRIMARY_USER, BIOMETRIC_STRONG, 1);
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isFalse();
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isFalse();
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
+ LockoutTracker.LOCKOUT_TIMED);
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_TIMED);
assertThat(
- mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isFalse();
+ mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
+ LockoutTracker.LOCKOUT_TIMED);
when(mClock.millis()).thenReturn(2L);
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isTrue();
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
- assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
+ assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
+ LockoutTracker.LOCKOUT_NONE);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
index 2dc3583..d10d7d4 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
@@ -47,7 +47,6 @@
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
-import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.face.UsageStats;
import org.junit.Before;
@@ -85,8 +84,6 @@
@Mock
private BiometricContext mBiometricContext;
@Mock
- private LockoutCache mLockoutCache;
- @Mock
private UsageStats mUsageStats;
@Mock
private ClientMonitorCallback mCallback;
@@ -161,7 +158,7 @@
false /* restricted */, "test-owner", 4 /* cookie */,
false /* requireConfirmation */, 9 /* sensorId */,
mBiometricLogger, mBiometricContext, true /* isStrongBiometric */,
- mUsageStats, mLockoutCache, false /* allowBackgroundAuthentication */,
+ mUsageStats, null /* mLockoutCache */, false /* allowBackgroundAuthentication */,
false /* isKeyguardBypassEnabled */, null /* sensorPrivacyManager */,
0 /* biometricStrength */) {
@Override
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
index 2afc4d7..1f29bec 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
@@ -17,6 +17,7 @@
package com.android.server.biometrics.sensors.face.aidl;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
@@ -25,7 +26,10 @@
import android.content.Context;
import android.hardware.biometrics.IBiometricService;
+import android.hardware.biometrics.common.CommonProps;
+import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession;
+import android.hardware.biometrics.face.SensorProps;
import android.os.Handler;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
@@ -36,6 +40,7 @@
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.BiometricStateCallback;
import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
@@ -74,12 +79,18 @@
private BiometricContext mBiometricContext;
@Mock
private AuthSessionCoordinator mAuthSessionCoordinator;
+ @Mock
+ private IFace mDaemon;
+ @Mock
+ private BiometricStateCallback mBiometricStateCallback;
private final TestLooper mLooper = new TestLooper();
private final LockoutCache mLockoutCache = new LockoutCache();
private UserAwareBiometricScheduler mScheduler;
private Sensor.HalSessionCallback mHalCallback;
+ private FaceProvider mFaceProvider;
+ private SensorProps[] mSensorProps;
@Before
public void setUp() {
@@ -99,6 +110,16 @@
mHalCallback = new Sensor.HalSessionCallback(mContext, new Handler(mLooper.getLooper()),
TAG, mScheduler, SENSOR_ID,
USER_ID, mLockoutCache, mLockoutResetDispatcher, mHalSessionCallback);
+
+ final SensorProps sensor1 = new SensorProps();
+ sensor1.commonProps = new CommonProps();
+ sensor1.commonProps.sensorId = 0;
+ final SensorProps sensor2 = new SensorProps();
+ sensor2.commonProps = new CommonProps();
+ sensor2.commonProps.sensorId = 1;
+ mSensorProps = new SensorProps[]{sensor1, sensor2};
+ mFaceProvider = new FaceProvider(mContext, mBiometricStateCallback,
+ mSensorProps, TAG, mLockoutResetDispatcher, mBiometricContext);
}
@Test
@@ -128,6 +149,18 @@
verifyNotLocked();
}
+ @Test
+ public void onBinderDied_noErrorOnNullClient() {
+ mScheduler.reset();
+ assertNull(mScheduler.getCurrentClient());
+ mFaceProvider.binderDied();
+
+ for (int i = 0; i < mFaceProvider.mSensors.size(); i++) {
+ final Sensor sensor = mFaceProvider.mSensors.valueAt(i);
+ assertNull(sensor.getSessionForUser(USER_ID));
+ }
+ }
+
private void verifyNotLocked() {
assertEquals(LockoutTracker.LOCKOUT_NONE, mLockoutCache.getLockoutModeForUser(USER_ID));
verify(mLockoutResetDispatcher).notifyLockoutResetCallbacks(eq(SENSOR_ID));
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
index 608eeec..5e5b48d 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -63,7 +63,6 @@
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
-import com.android.server.biometrics.sensors.LockoutCache;
import org.junit.Before;
import org.junit.Rule;
@@ -114,8 +113,6 @@
@Mock
private BiometricManager mBiometricManager;
@Mock
- private LockoutCache mLockoutCache;
- @Mock
private IUdfpsOverlayController mUdfpsOverlayController;
@Mock
private ISidefpsController mSideFpsController;
@@ -376,6 +373,7 @@
@Test
public void fingerprintPowerIgnoresAuthInWindow() throws Exception {
when(mSensorProps.isAnySidefpsType()).thenReturn(true);
+ when(mHal.authenticate(anyLong())).thenReturn(mCancellationSignal);
final FingerprintAuthenticationClient client = createClient(1);
client.start(mCallback);
@@ -386,11 +384,13 @@
mLooper.dispatchAll();
verify(mCallback).onClientFinished(any(), eq(false));
+ verify(mCancellationSignal).cancel();
}
@Test
public void fingerprintAuthIgnoredWaitingForPower() throws Exception {
when(mSensorProps.isAnySidefpsType()).thenReturn(true);
+ when(mHal.authenticate(anyLong())).thenReturn(mCancellationSignal);
final FingerprintAuthenticationClient client = createClient(1);
client.start(mCallback);
@@ -401,11 +401,13 @@
mLooper.dispatchAll();
verify(mCallback).onClientFinished(any(), eq(false));
+ verify(mCancellationSignal).cancel();
}
@Test
- public void fingerprintAuthSucceedsAfterPowerWindow() throws Exception {
+ public void fingerprintAuthFailsWhenAuthAfterPower() throws Exception {
when(mSensorProps.isAnySidefpsType()).thenReturn(true);
+ when(mHal.authenticate(anyLong())).thenReturn(mCancellationSignal);
final FingerprintAuthenticationClient client = createClient(1);
client.start(mCallback);
@@ -419,7 +421,9 @@
mLooper.moveTimeForward(1000);
mLooper.dispatchAll();
- verify(mCallback).onClientFinished(any(), eq(true));
+ verify(mCallback, never()).onClientFinished(any(), eq(true));
+ verify(mCallback).onClientFinished(any(), eq(false));
+ when(mHal.authenticateWithContext(anyLong(), any())).thenReturn(mCancellationSignal);
}
@Test
@@ -658,7 +662,7 @@
false /* requireConfirmation */,
9 /* sensorId */, mBiometricLogger, mBiometricContext,
true /* isStrongBiometric */,
- null /* taskStackListener */, mLockoutCache,
+ null /* taskStackListener */, null /* lockoutCache */,
mUdfpsOverlayController, mSideFpsController, null, allowBackgroundAuthentication,
mSensorProps,
new Handler(mLooper.getLooper()), 0 /* biometricStrength */, mClock) {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index e991ec6..ac1667d 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -442,7 +442,8 @@
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
mMockSystemServices.registerReceiver(receiver, filter, null);
- return spiedContext.registerReceiver(receiver, filter);
+ return spiedContext.registerReceiver(receiver, filter,
+ Context.RECEIVER_EXPORTED_UNAUDITED);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 1fe267f..9e61cab 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -795,6 +795,71 @@
}
@Test
+ @Parameters({
+ "true",
+ "false"
+ })
+ public void testVotingWithSwitchingTypeRenderFrameRateOnly(boolean frameRateIsRefreshRate) {
+ when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ DisplayModeDirector director = createDirectorFromFpsRange(0, 90);
+ SparseArray<Vote> votes = new SparseArray<>();
+ SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
+ votesByDisplay.put(DISPLAY_ID, votes);
+ votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
+ Vote.forRenderFrameRates(30, 90));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+
+ director.injectVotesByDisplay(votesByDisplay);
+ assertThat(director.getModeSwitchingType())
+ .isNotEqualTo(DisplayManager.SWITCHING_TYPE_NONE);
+ DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+
+ assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(30);
+ if (frameRateIsRefreshRate) {
+ assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
+ } else {
+ assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
+ }
+ assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(30);
+ assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
+ if (frameRateIsRefreshRate) {
+ assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(
+ 60);
+ } else {
+ assertThat(desiredSpecs.appRequest.physical.max).isPositiveInfinity();
+ }
+ assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(0);
+ assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(30);
+
+ director.setModeSwitchingType(DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY);
+ assertThat(director.getModeSwitchingType())
+ .isEqualTo(DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY);
+
+ desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(30);
+ assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(30);
+ assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(30);
+ if (frameRateIsRefreshRate) {
+ assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(30);
+ } else {
+ assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
+ }
+ assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(30);
+ assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(30);
+ if (frameRateIsRefreshRate) {
+ assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(30);
+ assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(30);
+ } else {
+ assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(0);
+ assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
+ }
+
+ assertThat(desiredSpecs.baseModeId).isEqualTo(30);
+ }
+
+ @Test
public void testVotingWithSwitchingTypeWithinGroups() {
DisplayModeDirector director = createDirectorFromFpsRange(0, 90);
diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
index f9b8373..9672085 100644
--- a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
+++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
@@ -108,7 +108,7 @@
}
@Override
- public boolean hasFsverity(String path) {
+ public boolean isFromTrustedProvider(String path, byte[] signature) {
return mHasFsverityPaths.contains(path);
}
@@ -291,6 +291,32 @@
}
@Test
+ public void construct_missingSignatureFile() throws Exception {
+ UpdatableFontDir dirForPreparation = new UpdatableFontDir(
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
+ mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
+ dirForPreparation.loadFontFileMap();
+ dirForPreparation.update(Arrays.asList(
+ newFontUpdateRequest("foo.ttf,1,foo", GOOD_SIGNATURE)));
+ assertThat(mUpdatableFontFilesDir.list()).hasLength(1);
+
+ // Remove signature file next to the font file.
+ File fontDir = dirForPreparation.getPostScriptMap().get("foo");
+ File sigFile = new File(fontDir.getParentFile(), "font.fsv_sig");
+ assertThat(sigFile.exists()).isTrue();
+ sigFile.delete();
+
+ UpdatableFontDir dir = new UpdatableFontDir(
+ mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
+ mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
+ dir.loadFontFileMap();
+ // The font file should be removed and should not be loaded.
+ assertThat(dir.getPostScriptMap()).isEmpty();
+ assertThat(mUpdatableFontFilesDir.list()).hasLength(0);
+ assertThat(dir.getFontFamilyMap()).isEmpty();
+ }
+
+ @Test
public void construct_olderThanPreinstalledFont() throws Exception {
Function<Map<String, File>, FontConfig> configSupplier = (map) -> {
FontConfig.Font fooFont = new FontConfig.Font(
@@ -782,8 +808,8 @@
UpdatableFontDir.FsverityUtil fakeFsverityUtil = new UpdatableFontDir.FsverityUtil() {
@Override
- public boolean hasFsverity(String path) {
- return mFakeFsverityUtil.hasFsverity(path);
+ public boolean isFromTrustedProvider(String path, byte[] signature) {
+ return mFakeFsverityUtil.isFromTrustedProvider(path, signature);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
index d91a748..9c8e72c 100644
--- a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
@@ -115,7 +115,8 @@
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_JOB_STARTED);
intentFilter.addAction(ACTION_JOB_STOPPED);
- mContext.registerReceiver(mJobStateChangeReceiver, intentFilter);
+ mContext.registerReceiver(mJobStateChangeReceiver, intentFilter,
+ Context.RECEIVER_EXPORTED_UNAUDITED);
setAppOpsModeAllowed(true);
setPowerExemption(false);
}
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 f138311..164161e 100644
--- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
@@ -155,7 +155,18 @@
}
@Test
- public void testWritingTwoFilesToDisk() throws Exception {
+ public void testWritingTwoJobsToDisk_singleFile() throws Exception {
+ mTaskStoreUnderTest.setUseSplitFiles(false);
+ runWritingTwoJobsToDisk();
+ }
+
+ @Test
+ public void testWritingTwoJobsToDisk_splitFiles() throws Exception {
+ mTaskStoreUnderTest.setUseSplitFiles(true);
+ runWritingTwoJobsToDisk();
+ }
+
+ private void runWritingTwoJobsToDisk() throws Exception {
final JobInfo task1 = new Builder(8, mComponent)
.setRequiresDeviceIdle(true)
.setPeriodic(10000L)
@@ -169,8 +180,10 @@
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.setPersisted(true)
.build();
- final JobStatus taskStatus1 = JobStatus.createFromJobInfo(task1, SOME_UID, null, -1, null);
- final JobStatus taskStatus2 = JobStatus.createFromJobInfo(task2, SOME_UID, null, -1, null);
+ final int uid1 = SOME_UID;
+ final int uid2 = uid1 + 1;
+ final JobStatus taskStatus1 = JobStatus.createFromJobInfo(task1, uid1, null, -1, null);
+ final JobStatus taskStatus2 = JobStatus.createFromJobInfo(task2, uid2, null, -1, null);
mTaskStoreUnderTest.add(taskStatus1);
mTaskStoreUnderTest.add(taskStatus2);
waitForPendingIo();
@@ -414,6 +427,35 @@
}
@Test
+ public void testEstimatedNetworkBytes() throws Exception {
+ assertPersistedEquals(new JobInfo.Builder(0, mComponent)
+ .setPersisted(true)
+ .setRequiredNetwork(new NetworkRequest.Builder().build())
+ .setEstimatedNetworkBytes(
+ JobInfo.NETWORK_BYTES_UNKNOWN, JobInfo.NETWORK_BYTES_UNKNOWN)
+ .build());
+ assertPersistedEquals(new JobInfo.Builder(0, mComponent)
+ .setPersisted(true)
+ .setRequiredNetwork(new NetworkRequest.Builder().build())
+ .setEstimatedNetworkBytes(5, 15)
+ .build());
+ }
+
+ @Test
+ public void testMinimumNetworkChunkBytes() throws Exception {
+ assertPersistedEquals(new JobInfo.Builder(0, mComponent)
+ .setPersisted(true)
+ .setRequiredNetwork(new NetworkRequest.Builder().build())
+ .setMinimumNetworkChunkBytes(JobInfo.NETWORK_BYTES_UNKNOWN)
+ .build());
+ assertPersistedEquals(new JobInfo.Builder(0, mComponent)
+ .setPersisted(true)
+ .setRequiredNetwork(new NetworkRequest.Builder().build())
+ .setMinimumNetworkChunkBytes(42)
+ .build());
+ }
+
+ @Test
public void testPersistedIdleConstraint() throws Exception {
JobInfo.Builder b = new Builder(8, mComponent)
.setRequiresDeviceIdle(true)
@@ -528,6 +570,15 @@
first.getNetworkType(), second.getNetworkType());
assertEquals("Invalid network.",
first.getRequiredNetwork(), second.getRequiredNetwork());
+ assertEquals("Download bytes don't match",
+ first.getEstimatedNetworkDownloadBytes(),
+ second.getEstimatedNetworkDownloadBytes());
+ assertEquals("Upload bytes don't match",
+ first.getEstimatedNetworkUploadBytes(),
+ second.getEstimatedNetworkUploadBytes());
+ assertEquals("Minimum chunk bytes don't match",
+ first.getMinimumNetworkChunkBytes(),
+ second.getMinimumNetworkChunkBytes());
assertEquals("Invalid deadline constraint.",
first.hasLateConstraint(),
second.hasLateConstraint());
diff --git a/services/tests/servicestests/src/com/android/server/locales/AppUpdateTrackerTest.java b/services/tests/servicestests/src/com/android/server/locales/AppUpdateTrackerTest.java
new file mode 100644
index 0000000..2c5d97d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locales/AppUpdateTrackerTest.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locales;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Binder;
+import android.os.LocaleList;
+import android.util.ArraySet;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.util.Arrays;
+import java.util.Set;
+
+/**
+ * Unit tests for {@link AppUpdateTracker}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class AppUpdateTrackerTest {
+ private static final String DEFAULT_PACKAGE_NAME = "com.android.myapp";
+ private static final int DEFAULT_UID = Binder.getCallingUid() + 100;
+ private static final int DEFAULT_USER_ID = 0;
+ private static final String DEFAULT_LOCALE_TAGS = "en-XC,ar-XB";
+ private static final LocaleList DEFAULT_LOCALES = LocaleList.forLanguageTags(
+ DEFAULT_LOCALE_TAGS);
+ private AppUpdateTracker mAppUpdateTracker;
+
+ @Mock
+ private Context mMockContext;
+ @Mock
+ private LocaleManagerService mMockLocaleManagerService;
+ @Mock
+ private ShadowLocaleManagerBackupHelper mMockBackupHelper;
+
+ @Before
+ public void setUp() throws Exception {
+ mMockContext = mock(Context.class);
+ mMockLocaleManagerService = mock(LocaleManagerService.class);
+ mMockBackupHelper = mock(ShadowLocaleManagerBackupHelper.class);
+ mAppUpdateTracker = spy(
+ new AppUpdateTracker(mMockContext, mMockLocaleManagerService, mMockBackupHelper));
+ }
+
+ @Test
+ public void testPackageUpgraded_localeEmpty_doNothing() throws Exception {
+ setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, LocaleList.getEmptyLocaleList());
+ setUpPackageNamesForSp(new ArraySet<>(Arrays.asList(DEFAULT_PACKAGE_NAME)));
+ setUpPackageLocaleConfig(null, DEFAULT_PACKAGE_NAME);
+ setUpAppLocalesOptIn(true);
+
+ mAppUpdateTracker.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME, DEFAULT_UID);
+ verifyNoLocalesCleared();
+ }
+
+ @Test
+ public void testPackageUpgraded_pkgNotInSp_doNothing() throws Exception {
+ setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, DEFAULT_LOCALES);
+ String pkgNameA = "com.android.myAppA";
+ String pkgNameB = "com.android.myAppB";
+ setUpPackageNamesForSp(new ArraySet<>(Arrays.asList(pkgNameA, pkgNameB)));
+ setUpPackageLocaleConfig(null, DEFAULT_PACKAGE_NAME);
+ setUpAppLocalesOptIn(true);
+
+ mAppUpdateTracker.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME, DEFAULT_UID);
+ verifyNoLocalesCleared();
+ }
+
+ @Test
+ public void testPackageUpgraded_appLocalesSupported_doNothing() throws Exception {
+ setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, DEFAULT_LOCALES);
+ setUpPackageNamesForSp(new ArraySet<>(Arrays.asList(DEFAULT_PACKAGE_NAME)));
+ setUpPackageLocaleConfig(DEFAULT_LOCALES, DEFAULT_PACKAGE_NAME);
+
+ setUpAppLocalesOptIn(true);
+ mAppUpdateTracker.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME, DEFAULT_UID);
+ verifyNoLocalesCleared();
+
+ setUpAppLocalesOptIn(false);
+ mAppUpdateTracker.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME, DEFAULT_UID);
+ verifyNoLocalesCleared();
+
+ setUpAppLocalesOptIn(false);
+ setUpPackageLocaleConfig(null, DEFAULT_PACKAGE_NAME);
+ mAppUpdateTracker.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME, DEFAULT_UID);
+ verifyNoLocalesCleared();
+ }
+
+ @Test
+ public void testPackageUpgraded_appLocalesNotSupported_clearAppLocale() throws Exception {
+ setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, DEFAULT_LOCALES);
+ setUpPackageNamesForSp(new ArraySet<>(Arrays.asList(DEFAULT_PACKAGE_NAME)));
+ setUpPackageLocaleConfig(null, DEFAULT_PACKAGE_NAME);
+ setUpAppLocalesOptIn(true);
+
+ mAppUpdateTracker.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME, DEFAULT_UID);
+ verify(mMockLocaleManagerService, times(1)).setApplicationLocales(DEFAULT_PACKAGE_NAME,
+ DEFAULT_USER_ID, LocaleList.forLanguageTags(""), false);
+
+ setUpPackageLocaleConfig(LocaleList.getEmptyLocaleList(), DEFAULT_PACKAGE_NAME);
+
+ mAppUpdateTracker.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME, DEFAULT_UID);
+ verify(mMockLocaleManagerService, times(2)).setApplicationLocales(DEFAULT_PACKAGE_NAME,
+ DEFAULT_USER_ID, LocaleList.forLanguageTags(""), false);
+
+ setUpAppLocalesOptIn(false);
+
+ mAppUpdateTracker.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME, DEFAULT_UID);
+ verify(mMockLocaleManagerService, times(3)).setApplicationLocales(DEFAULT_PACKAGE_NAME,
+ DEFAULT_USER_ID, LocaleList.forLanguageTags(""), false);
+ }
+
+ @Test
+ public void testPackageUpgraded_appLocalesNotInLocaleConfig_clearAppLocale() throws Exception {
+ setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, DEFAULT_LOCALES);
+ setUpPackageNamesForSp(new ArraySet<>(Arrays.asList(DEFAULT_PACKAGE_NAME)));
+ setUpPackageLocaleConfig(LocaleList.forLanguageTags("hi,fr"), DEFAULT_PACKAGE_NAME);
+ setUpAppLocalesOptIn(true);
+
+ mAppUpdateTracker.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME, DEFAULT_UID);
+ verify(mMockLocaleManagerService, times(1)).setApplicationLocales(DEFAULT_PACKAGE_NAME,
+ DEFAULT_USER_ID, LocaleList.forLanguageTags(""), false);
+
+ setUpAppLocalesOptIn(false);
+
+ mAppUpdateTracker.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME, DEFAULT_UID);
+ verify(mMockLocaleManagerService, times(2)).setApplicationLocales(DEFAULT_PACKAGE_NAME,
+ DEFAULT_USER_ID, LocaleList.forLanguageTags(""), false);
+ }
+
+ private void setUpLocalesForPackage(String packageName, LocaleList locales) throws Exception {
+ doReturn(locales).when(mMockLocaleManagerService).getApplicationLocales(eq(packageName),
+ anyInt());
+ }
+
+ private void setUpPackageNamesForSp(Set<String> packageNames) {
+ SharedPreferences mockSharedPreference = mock(SharedPreferences.class);
+ doReturn(mockSharedPreference).when(mMockBackupHelper).getPersistedInfo();
+ doReturn(packageNames).when(mockSharedPreference).getStringSet(anyString(), any());
+ }
+
+ private void setUpPackageLocaleConfig(LocaleList locales, String packageName) {
+ doReturn(locales).when(mAppUpdateTracker).getPackageLocales(eq(packageName), anyInt());
+ }
+
+ private void setUpAppLocalesOptIn(boolean optIn) {
+ doReturn(optIn).when(mAppUpdateTracker).isSettingsAppLocalesOptIn();
+ }
+
+ /**
+ * Verifies that no app locales needs to be cleared for any package.
+ *
+ * <p>If {@link LocaleManagerService#setApplicationLocales} is not invoked when receiving the
+ * callback of package upgraded, we can conclude that no app locales needs to be cleared.
+ */
+ private void verifyNoLocalesCleared() throws Exception {
+ verify(mMockLocaleManagerService, times(0)).setApplicationLocales(anyString(), anyInt(),
+ any(), anyBoolean());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java
index 8f0fb0b..4d42afa 100644
--- a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java
@@ -18,9 +18,11 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -35,6 +37,7 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -44,6 +47,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.SimpleClock;
+import android.util.ArraySet;
import android.util.SparseArray;
import android.util.Xml;
@@ -53,6 +57,7 @@
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
+import com.android.server.locales.LocaleManagerBackupHelper.LocalesInfo;
import org.junit.After;
import org.junit.Before;
@@ -69,9 +74,12 @@
import java.time.Clock;
import java.time.Duration;
import java.time.ZoneOffset;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* Unit tests for the {@link LocaleManagerInternal}.
@@ -89,8 +97,8 @@
private static final Duration RETENTION_PERIOD = Duration.ofDays(3);
private static final LocaleList DEFAULT_LOCALES =
LocaleList.forLanguageTags(DEFAULT_LOCALE_TAGS);
- private static final Map<String, String> DEFAULT_PACKAGE_LOCALES_MAP = Map.of(
- DEFAULT_PACKAGE_NAME, DEFAULT_LOCALE_TAGS);
+ private static final Map<String, LocalesInfo> DEFAULT_PACKAGE_LOCALES_INFO_MAP = Map.of(
+ DEFAULT_PACKAGE_NAME, new LocalesInfo(DEFAULT_LOCALE_TAGS, false));
private static final SparseArray<LocaleManagerBackupHelper.StagedData> STAGE_DATA =
new SparseArray<>();
@@ -103,6 +111,10 @@
private PackageManager mMockPackageManager;
@Mock
private LocaleManagerService mMockLocaleManagerService;
+ @Mock
+ private SharedPreferences mMockDelegateAppLocalePackages;
+ @Mock
+ private SharedPreferences.Editor mMockSpEditor;
BroadcastReceiver mUserMonitor;
PackageMonitor mPackageMonitor;
@@ -127,9 +139,13 @@
mMockContext = mock(Context.class);
mMockPackageManager = mock(PackageManager.class);
mMockLocaleManagerService = mock(LocaleManagerService.class);
+ mMockDelegateAppLocalePackages = mock(SharedPreferences.class);
+ mMockSpEditor = mock(SharedPreferences.Editor.class);
SystemAppUpdateTracker systemAppUpdateTracker = mock(SystemAppUpdateTracker.class);
+ AppUpdateTracker appUpdateTracker = mock(AppUpdateTracker.class);
doReturn(mMockPackageManager).when(mMockContext).getPackageManager();
+ doReturn(mMockSpEditor).when(mMockDelegateAppLocalePackages).edit();
HandlerThread broadcastHandlerThread = new HandlerThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND);
@@ -137,12 +153,12 @@
mBackupHelper = spy(new ShadowLocaleManagerBackupHelper(mMockContext,
mMockLocaleManagerService, mMockPackageManager, mClock, STAGE_DATA,
- broadcastHandlerThread));
+ broadcastHandlerThread, mMockDelegateAppLocalePackages));
doNothing().when(mBackupHelper).notifyBackupManager();
mUserMonitor = mBackupHelper.getUserMonitor();
mPackageMonitor = new LocaleManagerServicePackageMonitor(mBackupHelper,
- systemAppUpdateTracker);
+ systemAppUpdateTracker, appUpdateTracker);
setCurrentTimeMillis(DEFAULT_CREATION_TIME_MILLIS);
}
@@ -171,9 +187,23 @@
public void testBackupPayload_appLocalesSet_returnsNonNullBlob() throws Exception {
setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, DEFAULT_LOCALES);
setUpDummyAppForPackageManager(DEFAULT_PACKAGE_NAME);
+ setUpPackageNamesForSp(Collections.<String>emptySet());
byte[] payload = mBackupHelper.getBackupPayload(DEFAULT_USER_ID);
- verifyPayloadForAppLocales(DEFAULT_PACKAGE_LOCALES_MAP, payload);
+ verifyPayloadForAppLocales(DEFAULT_PACKAGE_LOCALES_INFO_MAP, payload);
+ }
+
+ @Test
+ public void testBackupPayload_appLocalesSet_fromDelegateSelector() throws Exception {
+ setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, DEFAULT_LOCALES);
+ setUpDummyAppForPackageManager(DEFAULT_PACKAGE_NAME);
+ setUpPackageNamesForSp(new ArraySet<>(Arrays.asList(DEFAULT_PACKAGE_NAME)));
+ Map<String, LocalesInfo> expectPackageLocalePack = Map.of(DEFAULT_PACKAGE_NAME,
+ new LocalesInfo(DEFAULT_LOCALE_TAGS, true));
+
+ byte[] payload = mBackupHelper.getBackupPayload(DEFAULT_USER_ID);
+
+ verifyPayloadForAppLocales(expectPackageLocalePack, payload);
}
@Test
@@ -195,14 +225,15 @@
anotherAppInfo.packageName = "com.android.anotherapp";
doReturn(List.of(defaultAppInfo, anotherAppInfo)).when(mMockPackageManager)
.getInstalledApplicationsAsUser(any(), anyInt());
-
setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, DEFAULT_LOCALES);
+ setUpPackageNamesForSp(Collections.<String>emptySet());
// Exception when getting locales for anotherApp.
doThrow(new RemoteException("mock")).when(mMockLocaleManagerService).getApplicationLocales(
eq(anotherAppInfo.packageName), anyInt());
byte[] payload = mBackupHelper.getBackupPayload(DEFAULT_USER_ID);
- verifyPayloadForAppLocales(DEFAULT_PACKAGE_LOCALES_MAP, payload);
+
+ verifyPayloadForAppLocales(DEFAULT_PACKAGE_LOCALES_INFO_MAP, payload);
}
@Test
@@ -226,8 +257,7 @@
@Test
public void testRestore_allAppsInstalled_noStageDataCreated() throws Exception {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
- writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP);
-
+ writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_INFO_MAP);
setUpPackageInstalled(DEFAULT_PACKAGE_NAME);
setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, LocaleList.getEmptyLocaleList());
@@ -235,38 +265,104 @@
// Locales were restored
verify(mMockLocaleManagerService, times(1)).setApplicationLocales(DEFAULT_PACKAGE_NAME,
- DEFAULT_USER_ID, DEFAULT_LOCALES);
-
+ DEFAULT_USER_ID, DEFAULT_LOCALES, false);
checkStageDataDoesNotExist(DEFAULT_USER_ID);
}
@Test
+ public void testRestore_allAppsInstalled_nothingToSp() throws Exception {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_INFO_MAP);
+ setUpPackageInstalled(DEFAULT_PACKAGE_NAME);
+ setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, LocaleList.getEmptyLocaleList());
+ setUpPackageNamesForSp(new ArraySet<>());
+
+ mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
+
+ // Locales were restored
+ verify(mMockLocaleManagerService, times(1)).setApplicationLocales(DEFAULT_PACKAGE_NAME,
+ DEFAULT_USER_ID, DEFAULT_LOCALES, false);
+ checkStageDataDoesNotExist(DEFAULT_USER_ID);
+
+ mBackupHelper.persistLocalesModificationInfo(DEFAULT_USER_ID, DEFAULT_PACKAGE_NAME, false,
+ false);
+
+ verify(mMockSpEditor, times(0)).putStringSet(anyString(), any());
+ }
+
+ @Test
+ public void testRestore_allAppsInstalled_storeInfoToSp() throws Exception {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ Map<String, LocalesInfo> pkgLocalesMap = Map.of(DEFAULT_PACKAGE_NAME,
+ new LocalesInfo(DEFAULT_LOCALE_TAGS, true));
+ writeTestPayload(out, pkgLocalesMap);
+ setUpPackageInstalled(DEFAULT_PACKAGE_NAME);
+ setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, LocaleList.getEmptyLocaleList());
+ setUpPackageNamesForSp(new ArraySet<>());
+
+ mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
+
+ // Locales were restored
+ verify(mMockLocaleManagerService, times(1)).setApplicationLocales(DEFAULT_PACKAGE_NAME,
+ DEFAULT_USER_ID, DEFAULT_LOCALES, true);
+ checkStageDataDoesNotExist(DEFAULT_USER_ID);
+
+ mBackupHelper.persistLocalesModificationInfo(DEFAULT_USER_ID, DEFAULT_PACKAGE_NAME, true,
+ false);
+
+ verify(mMockSpEditor, times(1)).putStringSet(Integer.toString(DEFAULT_USER_ID),
+ new ArraySet<>(Arrays.asList(DEFAULT_PACKAGE_NAME)));
+ }
+
+ @Test
+ public void testRestore_allAppsInstalled_InfoHasExistedInSp() throws Exception {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ Map<String, LocalesInfo> pkgLocalesMap = Map.of(DEFAULT_PACKAGE_NAME,
+ new LocalesInfo(DEFAULT_LOCALE_TAGS, true));
+ writeTestPayload(out, pkgLocalesMap);
+ setUpPackageInstalled(DEFAULT_PACKAGE_NAME);
+ setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, LocaleList.getEmptyLocaleList());
+ setUpPackageNamesForSp(new ArraySet<>(Arrays.asList(DEFAULT_PACKAGE_NAME)));
+
+ mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
+
+ // Locales were restored
+ verify(mMockLocaleManagerService, times(1)).setApplicationLocales(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID, DEFAULT_LOCALES, true);
+ checkStageDataDoesNotExist(DEFAULT_USER_ID);
+
+ mBackupHelper.persistLocalesModificationInfo(DEFAULT_USER_ID, DEFAULT_PACKAGE_NAME, true,
+ false);
+
+ verify(mMockSpEditor, times(0)).putStringSet(anyString(), any());
+ }
+
+ @Test
public void testRestore_noAppsInstalled_everythingStaged() throws Exception {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
- writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP);
-
+ writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_INFO_MAP);
setUpPackageNotInstalled(DEFAULT_PACKAGE_NAME);
mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
verifyNothingRestored();
- verifyStageDataForUser(DEFAULT_PACKAGE_LOCALES_MAP,
+ verifyStageDataForUser(DEFAULT_PACKAGE_LOCALES_INFO_MAP,
DEFAULT_CREATION_TIME_MILLIS, DEFAULT_USER_ID);
}
@Test
public void testRestore_someAppsInstalled_partiallyStaged() throws Exception {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
- HashMap<String, String> pkgLocalesMap = new HashMap<>();
-
+ HashMap<String, LocalesInfo> pkgLocalesMap = new HashMap<>();
String pkgNameA = "com.android.myAppA";
String pkgNameB = "com.android.myAppB";
String langTagsA = "ru";
String langTagsB = "hi,fr";
- pkgLocalesMap.put(pkgNameA, langTagsA);
- pkgLocalesMap.put(pkgNameB, langTagsB);
+ LocalesInfo localesInfoA = new LocalesInfo(langTagsA, true);
+ LocalesInfo localesInfoB = new LocalesInfo(langTagsB, true);
+ pkgLocalesMap.put(pkgNameA, localesInfoA);
+ pkgLocalesMap.put(pkgNameB, localesInfoB);
writeTestPayload(out, pkgLocalesMap);
-
setUpPackageInstalled(pkgNameA);
setUpPackageNotInstalled(pkgNameB);
setUpLocalesForPackage(pkgNameA, LocaleList.getEmptyLocaleList());
@@ -274,9 +370,10 @@
mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
verify(mMockLocaleManagerService, times(1)).setApplicationLocales(pkgNameA, DEFAULT_USER_ID,
- LocaleList.forLanguageTags(langTagsA));
+ LocaleList.forLanguageTags(langTagsA), true);
pkgLocalesMap.remove(pkgNameA);
+
verifyStageDataForUser(pkgLocalesMap,
DEFAULT_CREATION_TIME_MILLIS, DEFAULT_USER_ID);
}
@@ -284,8 +381,7 @@
@Test
public void testRestore_appLocalesAlreadySet_nothingRestoredAndNoStageData() throws Exception {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
- writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP);
-
+ writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_INFO_MAP);
setUpPackageInstalled(DEFAULT_PACKAGE_NAME);
setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, LocaleList.forLanguageTags("hi,mr"));
@@ -300,19 +396,20 @@
public void testRestore_appLocalesSetForSomeApps_restoresOnlyForAppsHavingNoLocalesSet()
throws Exception {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
- HashMap<String, String> pkgLocalesMap = new HashMap<>();
-
+ HashMap<String, LocalesInfo> pkgLocalesMap = new HashMap<>();
String pkgNameA = "com.android.myAppA";
String pkgNameB = "com.android.myAppB";
String pkgNameC = "com.android.myAppC";
String langTagsA = "ru";
String langTagsB = "hi,fr";
String langTagsC = "zh,es";
- pkgLocalesMap.put(pkgNameA, langTagsA);
- pkgLocalesMap.put(pkgNameB, langTagsB);
- pkgLocalesMap.put(pkgNameC, langTagsC);
+ LocalesInfo localesInfoA = new LocalesInfo(langTagsA, true);
+ LocalesInfo localesInfoB = new LocalesInfo(langTagsB, true);
+ LocalesInfo localesInfoC = new LocalesInfo(langTagsC, true);
+ pkgLocalesMap.put(pkgNameA, localesInfoA);
+ pkgLocalesMap.put(pkgNameB, localesInfoB);
+ pkgLocalesMap.put(pkgNameC, localesInfoC);
writeTestPayload(out, pkgLocalesMap);
-
// Both app A & B are installed on the device but A has locales already set.
setUpPackageInstalled(pkgNameA);
setUpPackageInstalled(pkgNameB);
@@ -320,20 +417,22 @@
setUpLocalesForPackage(pkgNameA, LocaleList.forLanguageTags("mr,fr"));
setUpLocalesForPackage(pkgNameB, LocaleList.getEmptyLocaleList());
setUpLocalesForPackage(pkgNameC, LocaleList.getEmptyLocaleList());
+ setUpPackageNamesForSp(new ArraySet<>(Arrays.asList(pkgNameA, pkgNameB, pkgNameC)));
mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
// Restore locales only for myAppB.
verify(mMockLocaleManagerService, times(0)).setApplicationLocales(eq(pkgNameA), anyInt(),
- any());
+ any(), anyBoolean());
verify(mMockLocaleManagerService, times(1)).setApplicationLocales(pkgNameB, DEFAULT_USER_ID,
- LocaleList.forLanguageTags(langTagsB));
+ LocaleList.forLanguageTags(langTagsB), true);
verify(mMockLocaleManagerService, times(0)).setApplicationLocales(eq(pkgNameC), anyInt(),
- any());
+ any(), anyBoolean());
// App C is staged.
pkgLocalesMap.remove(pkgNameA);
pkgLocalesMap.remove(pkgNameB);
+
verifyStageDataForUser(pkgLocalesMap,
DEFAULT_CREATION_TIME_MILLIS, DEFAULT_USER_ID);
}
@@ -341,40 +440,41 @@
@Test
public void testRestore_restoreInvokedAgain_creationTimeChanged() throws Exception {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
- writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP);
-
+ writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_INFO_MAP);
setUpPackageNotInstalled(DEFAULT_PACKAGE_NAME);
mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
- verifyStageDataForUser(DEFAULT_PACKAGE_LOCALES_MAP,
+ verifyStageDataForUser(DEFAULT_PACKAGE_LOCALES_INFO_MAP,
DEFAULT_CREATION_TIME_MILLIS, DEFAULT_USER_ID);
final long newCreationTime = DEFAULT_CREATION_TIME_MILLIS + 100;
setCurrentTimeMillis(newCreationTime);
+
mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
- verifyStageDataForUser(DEFAULT_PACKAGE_LOCALES_MAP,
+ verifyStageDataForUser(DEFAULT_PACKAGE_LOCALES_INFO_MAP,
newCreationTime, DEFAULT_USER_ID);
}
@Test
public void testRestore_appInstalledAfterSUW_restoresFromStage() throws Exception {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
- HashMap<String, String> pkgLocalesMap = new HashMap<>();
-
+ HashMap<String, LocalesInfo> pkgLocalesMap = new HashMap<>();
String pkgNameA = "com.android.myAppA";
String pkgNameB = "com.android.myAppB";
String langTagsA = "ru";
String langTagsB = "hi,fr";
- pkgLocalesMap.put(pkgNameA, langTagsA);
- pkgLocalesMap.put(pkgNameB, langTagsB);
+ LocalesInfo localesInfoA = new LocalesInfo(langTagsA, false);
+ LocalesInfo localesInfoB = new LocalesInfo(langTagsB, true);
+ pkgLocalesMap.put(pkgNameA, localesInfoA);
+ pkgLocalesMap.put(pkgNameB, localesInfoB);
writeTestPayload(out, pkgLocalesMap);
-
setUpPackageNotInstalled(pkgNameA);
setUpPackageNotInstalled(pkgNameB);
setUpLocalesForPackage(pkgNameA, LocaleList.getEmptyLocaleList());
setUpLocalesForPackage(pkgNameB, LocaleList.getEmptyLocaleList());
+ setUpPackageNamesForSp(new ArraySet<>());
mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
@@ -385,9 +485,14 @@
mPackageMonitor.onPackageAdded(pkgNameA, DEFAULT_UID);
verify(mMockLocaleManagerService, times(1)).setApplicationLocales(pkgNameA, DEFAULT_USER_ID,
- LocaleList.forLanguageTags(langTagsA));
+ LocaleList.forLanguageTags(langTagsA), false);
+
+ mBackupHelper.persistLocalesModificationInfo(DEFAULT_USER_ID, pkgNameA, false, false);
+
+ verify(mMockSpEditor, times(0)).putStringSet(anyString(), any());
pkgLocalesMap.remove(pkgNameA);
+
verifyStageDataForUser(pkgLocalesMap, DEFAULT_CREATION_TIME_MILLIS, DEFAULT_USER_ID);
setUpPackageInstalled(pkgNameB);
@@ -395,7 +500,12 @@
mPackageMonitor.onPackageAdded(pkgNameB, DEFAULT_UID);
verify(mMockLocaleManagerService, times(1)).setApplicationLocales(pkgNameB, DEFAULT_USER_ID,
- LocaleList.forLanguageTags(langTagsB));
+ LocaleList.forLanguageTags(langTagsB), true);
+
+ mBackupHelper.persistLocalesModificationInfo(DEFAULT_USER_ID, pkgNameB, true, false);
+
+ verify(mMockSpEditor, times(1)).putStringSet(Integer.toString(DEFAULT_USER_ID),
+ new ArraySet<>(Arrays.asList(pkgNameB)));
checkStageDataDoesNotExist(DEFAULT_USER_ID);
}
@@ -403,15 +513,14 @@
public void testRestore_appInstalledAfterSUWAndLocalesAlreadySet_restoresNothing()
throws Exception {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
- writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP);
-
+ writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_INFO_MAP);
// Package is not present on the device when the SUW restore is going on.
setUpPackageNotInstalled(DEFAULT_PACKAGE_NAME);
mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
verifyNothingRestored();
- verifyStageDataForUser(DEFAULT_PACKAGE_LOCALES_MAP,
+ verifyStageDataForUser(DEFAULT_PACKAGE_LOCALES_INFO_MAP,
DEFAULT_CREATION_TIME_MILLIS, DEFAULT_USER_ID);
// App is installed later (post SUW).
@@ -429,14 +538,13 @@
public void testStageDataDeletion_backupPassRunAfterRetentionPeriod_stageDataDeleted()
throws Exception {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
- writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP);
-
+ writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_INFO_MAP);
setUpPackageNotInstalled(DEFAULT_PACKAGE_NAME);
mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
verifyNothingRestored();
- verifyStageDataForUser(DEFAULT_PACKAGE_LOCALES_MAP,
+ verifyStageDataForUser(DEFAULT_PACKAGE_LOCALES_INFO_MAP,
DEFAULT_CREATION_TIME_MILLIS, DEFAULT_USER_ID);
// Retention period has not elapsed.
@@ -444,8 +552,8 @@
DEFAULT_CREATION_TIME_MILLIS + RETENTION_PERIOD.minusHours(1).toMillis());
doReturn(List.of()).when(mMockPackageManager)
.getInstalledApplicationsAsUser(any(), anyInt());
- assertNull(mBackupHelper.getBackupPayload(DEFAULT_USER_ID));
+ assertNull(mBackupHelper.getBackupPayload(DEFAULT_USER_ID));
checkStageDataExists(DEFAULT_USER_ID);
// Exactly RETENTION_PERIOD amount of time has passed so stage data should still not be
@@ -453,8 +561,8 @@
setCurrentTimeMillis(DEFAULT_CREATION_TIME_MILLIS + RETENTION_PERIOD.toMillis());
doReturn(List.of()).when(mMockPackageManager)
.getInstalledApplicationsAsUser(any(), anyInt());
- assertNull(mBackupHelper.getBackupPayload(DEFAULT_USER_ID));
+ assertNull(mBackupHelper.getBackupPayload(DEFAULT_USER_ID));
checkStageDataExists(DEFAULT_USER_ID);
// Retention period has now expired, stage data should be deleted.
@@ -462,8 +570,8 @@
DEFAULT_CREATION_TIME_MILLIS + RETENTION_PERIOD.plusSeconds(1).toMillis());
doReturn(List.of()).when(mMockPackageManager)
.getInstalledApplicationsAsUser(any(), anyInt());
- assertNull(mBackupHelper.getBackupPayload(DEFAULT_USER_ID));
+ assertNull(mBackupHelper.getBackupPayload(DEFAULT_USER_ID));
checkStageDataDoesNotExist(DEFAULT_USER_ID);
}
@@ -471,16 +579,16 @@
public void testStageDataDeletion_lazyRestoreAfterRetentionPeriod_stageDataDeleted()
throws Exception {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
- HashMap<String, String> pkgLocalesMap = new HashMap<>();
-
+ HashMap<String, LocalesInfo> pkgLocalesMap = new HashMap<>();
String pkgNameA = "com.android.myAppA";
String pkgNameB = "com.android.myAppB";
String langTagsA = "ru";
String langTagsB = "hi,fr";
- pkgLocalesMap.put(pkgNameA, langTagsA);
- pkgLocalesMap.put(pkgNameB, langTagsB);
+ LocalesInfo localesInfoA = new LocalesInfo(langTagsA, false);
+ LocalesInfo localesInfoB = new LocalesInfo(langTagsB, false);
+ pkgLocalesMap.put(pkgNameA, localesInfoA);
+ pkgLocalesMap.put(pkgNameB, localesInfoB);
writeTestPayload(out, pkgLocalesMap);
-
setUpPackageNotInstalled(pkgNameA);
setUpPackageNotInstalled(pkgNameB);
setUpLocalesForPackage(pkgNameA, LocaleList.getEmptyLocaleList());
@@ -494,40 +602,40 @@
// Retention period has not elapsed.
setCurrentTimeMillis(
DEFAULT_CREATION_TIME_MILLIS + RETENTION_PERIOD.minusHours(1).toMillis());
-
setUpPackageInstalled(pkgNameA);
+
mPackageMonitor.onPackageAdded(pkgNameA, DEFAULT_UID);
- verify(mMockLocaleManagerService, times(1)).setApplicationLocales(pkgNameA, DEFAULT_USER_ID,
- LocaleList.forLanguageTags(langTagsA));
+ verify(mMockLocaleManagerService, times(1)).setApplicationLocales(
+ pkgNameA, DEFAULT_USER_ID, LocaleList.forLanguageTags(langTagsA), false);
pkgLocalesMap.remove(pkgNameA);
+
verifyStageDataForUser(pkgLocalesMap, DEFAULT_CREATION_TIME_MILLIS, DEFAULT_USER_ID);
// Retention period has now expired, stage data should be deleted.
setCurrentTimeMillis(
DEFAULT_CREATION_TIME_MILLIS + RETENTION_PERIOD.plusSeconds(1).toMillis());
setUpPackageInstalled(pkgNameB);
+
mPackageMonitor.onPackageAdded(pkgNameB, DEFAULT_UID);
verify(mMockLocaleManagerService, times(0)).setApplicationLocales(eq(pkgNameB), anyInt(),
- any());
-
+ any(), anyBoolean());
checkStageDataDoesNotExist(DEFAULT_USER_ID);
}
@Test
public void testUserRemoval_userRemoved_stageDataDeleted() throws Exception {
final ByteArrayOutputStream outDefault = new ByteArrayOutputStream();
- writeTestPayload(outDefault, DEFAULT_PACKAGE_LOCALES_MAP);
-
+ writeTestPayload(outDefault, DEFAULT_PACKAGE_LOCALES_INFO_MAP);
final ByteArrayOutputStream outWorkProfile = new ByteArrayOutputStream();
String anotherPackage = "com.android.anotherapp";
String anotherLangTags = "mr,zh";
- HashMap<String, String> pkgLocalesMapWorkProfile = new HashMap<>();
- pkgLocalesMapWorkProfile.put(anotherPackage, anotherLangTags);
+ LocalesInfo localesInfo = new LocalesInfo(anotherLangTags, false);
+ HashMap<String, LocalesInfo> pkgLocalesMapWorkProfile = new HashMap<>();
+ pkgLocalesMapWorkProfile.put(anotherPackage, localesInfo);
writeTestPayload(outWorkProfile, pkgLocalesMapWorkProfile);
-
// DEFAULT_PACKAGE_NAME is NOT installed on the device.
setUpPackageNotInstalled(DEFAULT_PACKAGE_NAME);
setUpPackageNotInstalled(anotherPackage);
@@ -537,8 +645,7 @@
WORK_PROFILE_USER_ID);
verifyNothingRestored();
-
- verifyStageDataForUser(DEFAULT_PACKAGE_LOCALES_MAP,
+ verifyStageDataForUser(DEFAULT_PACKAGE_LOCALES_INFO_MAP,
DEFAULT_CREATION_TIME_MILLIS, DEFAULT_USER_ID);
verifyStageDataForUser(pkgLocalesMapWorkProfile,
DEFAULT_CREATION_TIME_MILLIS, WORK_PROFILE_USER_ID);
@@ -554,6 +661,45 @@
DEFAULT_CREATION_TIME_MILLIS, WORK_PROFILE_USER_ID);
}
+ @Test
+ public void testPackageRemoved_noInfoInSp() throws Exception {
+ String pkgNameA = "com.android.myAppA";
+ String pkgNameB = "com.android.myAppB";
+ setUpPackageNamesForSp(new ArraySet<>(Arrays.asList(pkgNameA, pkgNameB)));
+
+ mPackageMonitor.onPackageRemoved(DEFAULT_PACKAGE_NAME, DEFAULT_UID);
+
+ verify(mMockSpEditor, times(0)).putStringSet(anyString(), any());
+ }
+
+ @Test
+ public void testPackageRemoved_removeInfoFromSp() throws Exception {
+ String pkgNameA = "com.android.myAppA";
+ String pkgNameB = "com.android.myAppB";
+ Set<String> pkgNames = new ArraySet<>(Arrays.asList(pkgNameA, pkgNameB));
+ setUpPackageNamesForSp(pkgNames);
+
+ mPackageMonitor.onPackageRemoved(pkgNameA, DEFAULT_UID);
+ pkgNames.remove(pkgNameA);
+
+ verify(mMockSpEditor, times(1)).putStringSet(
+ Integer.toString(DEFAULT_USER_ID), pkgNames);
+ }
+
+ @Test
+ public void testPackageDataCleared_removeInfoFromSp() throws Exception {
+ String pkgNameA = "com.android.myAppA";
+ String pkgNameB = "com.android.myAppB";
+ Set<String> pkgNames = new ArraySet<>(Arrays.asList(pkgNameA, pkgNameB));
+ setUpPackageNamesForSp(pkgNames);
+
+ mPackageMonitor.onPackageDataCleared(pkgNameB, DEFAULT_UID);
+ pkgNames.remove(pkgNameB);
+
+ verify(mMockSpEditor, times(1)).putStringSet(
+ Integer.toString(DEFAULT_USER_ID), pkgNames);
+ }
+
private void setUpPackageInstalled(String packageName) throws Exception {
doReturn(new PackageInfo()).when(mMockPackageManager).getPackageInfoAsUser(
eq(packageName), anyInt(), anyInt());
@@ -576,6 +722,11 @@
.getInstalledApplicationsAsUser(any(), anyInt());
}
+ private void setUpPackageNamesForSp(Set<String> packageNames) {
+ doReturn(packageNames).when(mMockDelegateAppLocalePackages).getStringSet(anyString(),
+ any());
+ }
+
/**
* Verifies that nothing was restored for any package.
*
@@ -584,31 +735,34 @@
*/
private void verifyNothingRestored() throws Exception {
verify(mMockLocaleManagerService, times(0)).setApplicationLocales(anyString(), anyInt(),
- any());
+ any(), anyBoolean());
}
- private static void verifyPayloadForAppLocales(Map<String, String> expectedPkgLocalesMap,
+ private static void verifyPayloadForAppLocales(Map<String, LocalesInfo> expectedPkgLocalesMap,
byte[] payload)
throws IOException, XmlPullParserException {
final ByteArrayInputStream stream = new ByteArrayInputStream(payload);
final TypedXmlPullParser parser = Xml.newFastPullParser();
parser.setInput(stream, StandardCharsets.UTF_8.name());
- Map<String, String> backupDataMap = new HashMap<>();
+ Map<String, LocalesInfo> backupDataMap = new HashMap<>();
XmlUtils.beginDocument(parser, TEST_LOCALES_XML_TAG);
int depth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, depth)) {
if (parser.getName().equals("package")) {
String packageName = parser.getAttributeValue(null, "name");
String languageTags = parser.getAttributeValue(null, "locales");
- backupDataMap.put(packageName, languageTags);
+ boolean delegateSelector = parser.getAttributeBoolean(null, "delegate_selector");
+ LocalesInfo localesInfo = new LocalesInfo(languageTags, delegateSelector);
+ backupDataMap.put(packageName, localesInfo);
}
}
- assertEquals(expectedPkgLocalesMap, backupDataMap);
+ verifyStageData(expectedPkgLocalesMap, backupDataMap);
}
- private static void writeTestPayload(OutputStream stream, Map<String, String> pkgLocalesMap)
+ private static void writeTestPayload(OutputStream stream,
+ Map<String, LocalesInfo> pkgLocalesMap)
throws IOException {
if (pkgLocalesMap.isEmpty()) {
return;
@@ -622,7 +776,9 @@
for (String pkg : pkgLocalesMap.keySet()) {
out.startTag(/* namespace= */ null, "package");
out.attribute(/* namespace= */ null, "name", pkg);
- out.attribute(/* namespace= */ null, "locales", pkgLocalesMap.get(pkg));
+ out.attribute(/* namespace= */ null, "locales", pkgLocalesMap.get(pkg).mLocales);
+ out.attributeBoolean(/* namespace= */ null, "delegate_selector",
+ pkgLocalesMap.get(pkg).mSetFromDelegate);
out.endTag(/*namespace= */ null, "package");
}
@@ -630,12 +786,23 @@
out.endDocument();
}
- private void verifyStageDataForUser(Map<String, String> expectedPkgLocalesMap,
+ private void verifyStageDataForUser(Map<String, LocalesInfo> expectedPkgLocalesMap,
long expectedCreationTimeMillis, int userId) {
LocaleManagerBackupHelper.StagedData stagedDataForUser = STAGE_DATA.get(userId);
assertNotNull(stagedDataForUser);
assertEquals(expectedCreationTimeMillis, stagedDataForUser.mCreationTimeMillis);
- assertEquals(expectedPkgLocalesMap, stagedDataForUser.mPackageStates);
+ verifyStageData(expectedPkgLocalesMap, stagedDataForUser.mPackageStates);
+ }
+
+ private static void verifyStageData(Map<String, LocalesInfo> expectedPkgLocalesMap,
+ Map<String, LocalesInfo> stageData) {
+ assertEquals(expectedPkgLocalesMap.size(), stageData.size());
+ for (String pkg : expectedPkgLocalesMap.keySet()) {
+ assertTrue(stageData.containsKey(pkg));
+ assertEquals(expectedPkgLocalesMap.get(pkg).mLocales, stageData.get(pkg).mLocales);
+ assertEquals(expectedPkgLocalesMap.get(pkg).mSetFromDelegate,
+ stageData.get(pkg).mSetFromDelegate);
+ }
}
private static void checkStageDataExists(int userId) {
diff --git a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
index dc9f907..79ed7d1 100644
--- a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
@@ -133,7 +133,7 @@
try {
mLocaleManagerService.setApplicationLocales(DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID,
- LocaleList.getEmptyLocaleList());
+ LocaleList.getEmptyLocaleList(), false);
fail("Expected SecurityException");
} finally {
verify(mMockContext).enforceCallingOrSelfPermission(
@@ -148,7 +148,7 @@
public void testSetApplicationLocales_nullPackageName_fails() throws Exception {
try {
mLocaleManagerService.setApplicationLocales(/* appPackageName = */ null,
- DEFAULT_USER_ID, LocaleList.getEmptyLocaleList());
+ DEFAULT_USER_ID, LocaleList.getEmptyLocaleList(), false);
fail("Expected NullPointerException");
} finally {
verify(mMockBackupHelper, times(0)).notifyBackupManager();
@@ -162,7 +162,7 @@
try {
mLocaleManagerService.setApplicationLocales(DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID,
- /* locales = */ null);
+ /* locales = */ null, false);
fail("Expected NullPointerException");
} finally {
verify(mMockBackupHelper, times(0)).notifyBackupManager();
@@ -180,7 +180,7 @@
setUpPassingPermissionCheckFor(Manifest.permission.CHANGE_CONFIGURATION);
mLocaleManagerService.setApplicationLocales(DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID,
- DEFAULT_LOCALES);
+ DEFAULT_LOCALES, true);
assertEquals(DEFAULT_LOCALES, mFakePackageConfigurationUpdater.getStoredLocales());
verify(mMockBackupHelper, times(1)).notifyBackupManager();
@@ -193,7 +193,7 @@
.when(mMockPackageManager).getPackageUidAsUser(anyString(), any(), anyInt());
mLocaleManagerService.setApplicationLocales(DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID,
- DEFAULT_LOCALES);
+ DEFAULT_LOCALES, false);
assertEquals(DEFAULT_LOCALES, mFakePackageConfigurationUpdater.getStoredLocales());
verify(mMockBackupHelper, times(1)).notifyBackupManager();
@@ -205,7 +205,7 @@
.when(mMockPackageManager).getPackageUidAsUser(anyString(), any(), anyInt());
try {
mLocaleManagerService.setApplicationLocales(DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID,
- LocaleList.getEmptyLocaleList());
+ LocaleList.getEmptyLocaleList(), false);
fail("Expected IllegalArgumentException");
} finally {
assertNoLocalesStored(mFakePackageConfigurationUpdater.getStoredLocales());
diff --git a/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java b/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java
index e403c87..9f7cbe3 100644
--- a/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java
+++ b/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java
@@ -17,6 +17,7 @@
package com.android.server.locales;
import android.content.Context;
+import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.HandlerThread;
import android.util.SparseArray;
@@ -33,8 +34,8 @@
LocaleManagerService localeManagerService,
PackageManager packageManager, Clock clock,
SparseArray<LocaleManagerBackupHelper.StagedData> stagedData,
- HandlerThread broadcastHandlerThread) {
+ HandlerThread broadcastHandlerThread, SharedPreferences delegateAppLocalePackages) {
super(context, localeManagerService, packageManager, clock, stagedData,
- broadcastHandlerThread);
+ broadcastHandlerThread, delegateAppLocalePackages);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java b/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java
index ee97466..dc0740a 100644
--- a/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java
@@ -135,8 +135,9 @@
mSystemAppUpdateTracker = new SystemAppUpdateTracker(mMockContext,
mLocaleManagerService, mStoragefile);
- mPackageMonitor = new LocaleManagerServicePackageMonitor(
- mockLocaleManagerBackupHelper, mSystemAppUpdateTracker);
+ AppUpdateTracker appUpdateTracker = mock(AppUpdateTracker.class);
+ mPackageMonitor = new LocaleManagerServicePackageMonitor(mockLocaleManagerBackupHelper,
+ mSystemAppUpdateTracker, appUpdateTracker);
}
@After
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverySnapshotListenersStorageTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverySnapshotListenersStorageTest.java
index ea3c5fa..d9ebb4c 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverySnapshotListenersStorageTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverySnapshotListenersStorageTest.java
@@ -62,7 +62,7 @@
context.unregisterReceiver(this);
latch.countDown();
}
- }, new IntentFilter(TEST_INTENT_ACTION));
+ }, new IntentFilter(TEST_INTENT_ACTION), Context.RECEIVER_EXPORTED_UNAUDITED);
mStorage.setSnapshotListener(recoveryAgentUid, intent);
@@ -83,7 +83,8 @@
latch.countDown();
}
};
- context.registerReceiver(broadcastReceiver, new IntentFilter(TEST_INTENT_ACTION));
+ context.registerReceiver(broadcastReceiver, new IntentFilter(TEST_INTENT_ACTION),
+ Context.RECEIVER_EXPORTED_UNAUDITED);
mStorage.setSnapshotListener(recoveryAgentUid, intent);
mStorage.setSnapshotListener(recoveryAgentUid, intent);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
index 8d2cffa..5ece871 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
@@ -60,10 +60,12 @@
.setShowInLauncher(21)
.setStartWithParent(false)
.setShowInSettings(45)
+ .setInheritDevicePolicy(67)
.build();
final UserProperties actualProps = new UserProperties(defaultProps);
actualProps.setShowInLauncher(14);
actualProps.setShowInSettings(32);
+ actualProps.setInheritDevicePolicy(51);
// Write the properties to xml.
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -102,11 +104,13 @@
.setShowInLauncher(2145)
.setStartWithParent(true)
.setShowInSettings(3452)
+ .setInheritDevicePolicy(1732)
.build();
final UserProperties orig = new UserProperties(defaultProps);
orig.setShowInLauncher(2841);
orig.setStartWithParent(false);
orig.setShowInSettings(1437);
+ orig.setInheritDevicePolicy(9456);
// Test every permission level. (Currently, it's linear so it's easy.)
for (int permLevel = 0; permLevel < 4; permLevel++) {
@@ -142,6 +146,8 @@
// Items requiring exposeAll.
assertEqualGetterOrThrows(orig::getStartWithParent, copy::getStartWithParent, exposeAll);
+ assertEqualGetterOrThrows(orig::getInheritDevicePolicy,
+ copy::getInheritDevicePolicy, exposeAll);
// Items requiring hasManagePermission - put them here using hasManagePermission.
assertEqualGetterOrThrows(orig::getShowInSettings, copy::getShowInSettings,
@@ -190,5 +196,7 @@
assertThat(expected.getShowInLauncher()).isEqualTo(actual.getShowInLauncher());
assertThat(expected.getStartWithParent()).isEqualTo(actual.getStartWithParent());
assertThat(expected.getShowInSettings()).isEqualTo(actual.getShowInSettings());
+ assertThat(expected.getInheritDevicePolicy()).isEqualTo(
+ actual.getInheritDevicePolicy());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index df2c5dd..a3c45b7 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -601,6 +601,7 @@
assertThat(userProps.getShowInLauncher()).isEqualTo(typeProps.getShowInLauncher());
assertThat(userProps.getShowInSettings()).isEqualTo(typeProps.getShowInSettings());
assertThrows(SecurityException.class, userProps::getStartWithParent);
+ assertThrows(SecurityException.class, userProps::getInheritDevicePolicy);
}
// Make sure only max managed profiles can be created
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
index 699601b..8e6c014 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
@@ -575,10 +575,11 @@
assertEquals(0, minExtVers.get(31, -1));
Map<Pair<String, Integer>, Integer> appToError = new HashMap<>();
- appToError.put(Pair.create("install_uses_sdk.apk_r5", R.raw.install_uses_sdk_r5),
+ appToError.put(Pair.create("install_uses_sdk.apk_r1000", R.raw.install_uses_sdk_r1000),
PackageManager.INSTALL_FAILED_OLDER_SDK);
- appToError.put(Pair.create("install_uses_sdk.apk_r0_s5", R.raw.install_uses_sdk_r0_s5),
- PackageManager.INSTALL_FAILED_OLDER_SDK);
+ appToError.put(
+ Pair.create("install_uses_sdk.apk_r0_s1000", R.raw.install_uses_sdk_r0_s1000),
+ PackageManager.INSTALL_FAILED_OLDER_SDK);
appToError.put(Pair.create("install_uses_sdk.apk_q0", R.raw.install_uses_sdk_q0),
PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED);
@@ -595,7 +596,7 @@
int result = entry.getValue();
try {
parsePackage(filename, resId, x -> x);
- expect.withMessage("Expected parsing error %d from %s", result, filename).fail();
+ expect.withMessage("Expected parsing error %s from %s", result, filename).fail();
} catch (PackageManagerException expected) {
expect.that(expected.error).isEqualTo(result);
}
diff --git a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
index 397770b..dcbdcdc 100644
--- a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
@@ -42,6 +42,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.IHintSession;
+import android.os.PerformanceHintManager;
import android.os.Process;
import com.android.server.FgThread;
@@ -250,6 +251,32 @@
}
@Test
+ public void testSendHint() throws Exception {
+ HintManagerService service = createService();
+ IBinder token = new Binder();
+
+ AppHintSession a = (AppHintSession) service.getBinderServiceInstance()
+ .createHintSession(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION);
+
+ a.sendHint(PerformanceHintManager.Session.CPU_LOAD_RESET);
+ verify(mNativeWrapperMock, times(1)).halSendHint(anyLong(),
+ eq(PerformanceHintManager.Session.CPU_LOAD_RESET));
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ a.sendHint(-1);
+ });
+
+ reset(mNativeWrapperMock);
+ // Set session to background, then the duration would not be updated.
+ service.mUidObserver.onUidStateChanged(
+ a.mUid, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+ FgThread.getHandler().runWithScissors(() -> { }, 500);
+ assertFalse(a.updateHintAllowed());
+ a.sendHint(PerformanceHintManager.Session.CPU_LOAD_RESET);
+ verify(mNativeWrapperMock, never()).halSendHint(anyLong(), anyInt());
+ }
+
+ @Test
public void testDoHintInBackground() throws Exception {
HintManagerService service = createService();
IBinder token = new Binder();
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/FakeTimeZoneProviderEventPreProcessor.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/FakeTimeZoneProviderEventPreProcessor.java
index 34d0082..f8d169b 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/FakeTimeZoneProviderEventPreProcessor.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/FakeTimeZoneProviderEventPreProcessor.java
@@ -31,9 +31,9 @@
@Override
public TimeZoneProviderEvent preProcess(TimeZoneProviderEvent timeZoneProviderEvent) {
if (mIsUncertain) {
+ TimeZoneProviderStatus timeZoneProviderStatus = null;
return TimeZoneProviderEvent.createUncertainEvent(
- timeZoneProviderEvent.getCreationElapsedMillis(),
- TimeZoneProviderStatus.UNKNOWN);
+ timeZoneProviderEvent.getCreationElapsedMillis(), timeZoneProviderStatus);
}
return timeZoneProviderEvent;
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java
index ed426cd..c18acd2 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java
@@ -16,10 +16,10 @@
package com.android.server.timezonedetector.location;
import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_NOT_APPLICABLE;
+import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_OK;
import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_TEMPORARILY_UNAVAILABLE;
-import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_WORKING;
+import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_OK;
import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_UNKNOWN;
-import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_WORKING;
import static com.android.server.timezonedetector.ConfigurationInternal.DETECTION_MODE_MANUAL;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
@@ -87,9 +87,9 @@
createSuggestionEvent(asList("Europe/Paris"));
private static final TimeZoneProviderStatus UNCERTAIN_PROVIDER_STATUS =
new TimeZoneProviderStatus.Builder()
- .setLocationDetectionStatus(DEPENDENCY_STATUS_TEMPORARILY_UNAVAILABLE)
- .setConnectivityStatus(DEPENDENCY_STATUS_WORKING)
- .setTimeZoneResolutionStatus(OPERATION_STATUS_UNKNOWN)
+ .setLocationDetectionDependencyStatus(DEPENDENCY_STATUS_TEMPORARILY_UNAVAILABLE)
+ .setConnectivityDependencyStatus(DEPENDENCY_STATUS_OK)
+ .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_UNKNOWN)
.build();
private static final TimeZoneProviderEvent USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT =
TimeZoneProviderEvent.createUncertainEvent(
@@ -1405,9 +1405,9 @@
private static TimeZoneProviderEvent createSuggestionEvent(@NonNull List<String> timeZoneIds) {
TimeZoneProviderStatus providerStatus = new TimeZoneProviderStatus.Builder()
- .setLocationDetectionStatus(DEPENDENCY_STATUS_NOT_APPLICABLE)
- .setConnectivityStatus(DEPENDENCY_STATUS_NOT_APPLICABLE)
- .setTimeZoneResolutionStatus(OPERATION_STATUS_WORKING)
+ .setLocationDetectionDependencyStatus(DEPENDENCY_STATUS_NOT_APPLICABLE)
+ .setConnectivityDependencyStatus(DEPENDENCY_STATUS_NOT_APPLICABLE)
+ .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_OK)
.build();
TimeZoneProviderSuggestion suggestion = new TimeZoneProviderSuggestion.Builder()
.setElapsedRealtimeMillis(ARBITRARY_TIME_MILLIS)
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
index 8429fa4..2bee7e6 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
@@ -15,6 +15,9 @@
*/
package com.android.server.timezonedetector.location;
+import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_OK;
+import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_OK;
+
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
@@ -57,6 +60,12 @@
public class LocationTimeZoneProviderTest {
private static final long ARBITRARY_ELAPSED_REALTIME_MILLIS = 123456789L;
+ private static final TimeZoneProviderStatus ARBITRARY_PROVIDER_STATUS =
+ new TimeZoneProviderStatus.Builder()
+ .setConnectivityDependencyStatus(DEPENDENCY_STATUS_OK)
+ .setLocationDetectionDependencyStatus(DEPENDENCY_STATUS_OK)
+ .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_OK)
+ .build();
private TestThreadingDomain mTestThreadingDomain;
private TestProviderListener mProviderListener;
@@ -80,91 +89,108 @@
mTimeZoneProviderEventPreProcessor);
// initialize()
- provider.initialize(mProviderListener);
- provider.assertOnInitializeCalled();
+ {
+ provider.initialize(mProviderListener);
+ provider.assertOnInitializeCalled();
- ProviderState currentState = assertAndReturnProviderState(
- provider, providerMetricsLogger, PROVIDER_STATE_STOPPED);
- assertNull(currentState.currentUserConfiguration);
- assertSame(provider, currentState.provider);
- mTestThreadingDomain.assertQueueEmpty();
+ ProviderState currentState = assertAndReturnProviderState(
+ provider, providerMetricsLogger, PROVIDER_STATE_STOPPED,
+ /*expectedReportedStatus=*/null);
+ assertNull(currentState.currentUserConfiguration);
+ assertSame(provider, currentState.provider);
+ mTestThreadingDomain.assertQueueEmpty();
+ }
+
+ ConfigurationInternal config = USER1_CONFIG_GEO_DETECTION_ENABLED;
// startUpdates()
- ConfigurationInternal config = USER1_CONFIG_GEO_DETECTION_ENABLED;
- Duration arbitraryInitializationTimeout = Duration.ofMinutes(5);
- Duration arbitraryInitializationTimeoutFuzz = Duration.ofMinutes(2);
- Duration arbitraryEventFilteringAgeThreshold = Duration.ofMinutes(3);
- provider.startUpdates(config, arbitraryInitializationTimeout,
- arbitraryInitializationTimeoutFuzz, arbitraryEventFilteringAgeThreshold);
+ {
+ Duration arbitraryInitializationTimeout = Duration.ofMinutes(5);
+ Duration arbitraryInitializationTimeoutFuzz = Duration.ofMinutes(2);
+ Duration arbitraryEventFilteringAgeThreshold = Duration.ofMinutes(3);
+ provider.startUpdates(config, arbitraryInitializationTimeout,
+ arbitraryInitializationTimeoutFuzz, arbitraryEventFilteringAgeThreshold);
- provider.assertOnStartCalled(
- arbitraryInitializationTimeout, arbitraryEventFilteringAgeThreshold);
+ provider.assertOnStartCalled(
+ arbitraryInitializationTimeout, arbitraryEventFilteringAgeThreshold);
- currentState = assertAndReturnProviderState(
- provider, providerMetricsLogger, PROVIDER_STATE_STARTED_INITIALIZING);
- assertSame(provider, currentState.provider);
- assertEquals(config, currentState.currentUserConfiguration);
- assertNull(currentState.event);
- // The initialization timeout should be queued.
- Duration expectedInitializationTimeout =
- arbitraryInitializationTimeout.plus(arbitraryInitializationTimeoutFuzz);
- mTestThreadingDomain.assertSingleDelayedQueueItem(expectedInitializationTimeout);
- // We don't intend to trigger the timeout, so clear it.
- mTestThreadingDomain.removeAllQueuedRunnables();
+ ProviderState currentState = assertAndReturnProviderState(
+ provider, providerMetricsLogger, PROVIDER_STATE_STARTED_INITIALIZING,
+ /*expectedReportedStatus=*/null);
+ assertSame(provider, currentState.provider);
+ assertEquals(config, currentState.currentUserConfiguration);
+ assertNull(currentState.event);
+ // The initialization timeout should be queued.
+ Duration expectedInitializationTimeout =
+ arbitraryInitializationTimeout.plus(arbitraryInitializationTimeoutFuzz);
+ mTestThreadingDomain.assertSingleDelayedQueueItem(expectedInitializationTimeout);
+ // We don't intend to trigger the timeout, so clear it.
+ mTestThreadingDomain.removeAllQueuedRunnables();
- // Entering started does not trigger an onProviderStateChanged() as it is requested by the
- // controller.
- mProviderListener.assertProviderChangeNotReported();
+ // Entering started does not trigger an onProviderStateChanged() as it is requested by
+ // the controller.
+ mProviderListener.assertProviderChangeNotReported();
+ }
// Simulate a suggestion event being received.
- TimeZoneProviderSuggestion suggestion = new TimeZoneProviderSuggestion.Builder()
- .setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS)
- .setTimeZoneIds(Arrays.asList("Europe/London"))
- .build();
- TimeZoneProviderStatus providerStatus = TimeZoneProviderStatus.UNKNOWN;
- TimeZoneProviderEvent event = TimeZoneProviderEvent.createSuggestionEvent(
- ARBITRARY_ELAPSED_REALTIME_MILLIS, suggestion, providerStatus);
- provider.simulateProviderEventReceived(event);
+ {
+ TimeZoneProviderSuggestion suggestion = new TimeZoneProviderSuggestion.Builder()
+ .setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS)
+ .setTimeZoneIds(Arrays.asList("Europe/London"))
+ .build();
+ TimeZoneProviderEvent event = TimeZoneProviderEvent.createSuggestionEvent(
+ ARBITRARY_ELAPSED_REALTIME_MILLIS, suggestion, ARBITRARY_PROVIDER_STATUS);
+ provider.simulateProviderEventReceived(event);
- currentState = assertAndReturnProviderState(
- provider, providerMetricsLogger, PROVIDER_STATE_STARTED_CERTAIN);
- assertSame(provider, currentState.provider);
- assertEquals(event, currentState.event);
- assertEquals(config, currentState.currentUserConfiguration);
- mTestThreadingDomain.assertQueueEmpty();
- mProviderListener.assertProviderChangeReported(PROVIDER_STATE_STARTED_CERTAIN);
+ ProviderState currentState = assertAndReturnProviderState(
+ provider, providerMetricsLogger, PROVIDER_STATE_STARTED_CERTAIN,
+ ARBITRARY_PROVIDER_STATUS);
+ assertSame(provider, currentState.provider);
+ assertEquals(event, currentState.event);
+ assertEquals(config, currentState.currentUserConfiguration);
+ mTestThreadingDomain.assertQueueEmpty();
+ mProviderListener.assertProviderChangeReported(PROVIDER_STATE_STARTED_CERTAIN);
+ }
// Simulate an uncertain event being received.
- event = TimeZoneProviderEvent.createUncertainEvent(ARBITRARY_ELAPSED_REALTIME_MILLIS,
- TimeZoneProviderStatus.UNKNOWN);
- provider.simulateProviderEventReceived(event);
+ {
+ TimeZoneProviderEvent event = TimeZoneProviderEvent.createUncertainEvent(
+ ARBITRARY_ELAPSED_REALTIME_MILLIS, ARBITRARY_PROVIDER_STATUS);
+ provider.simulateProviderEventReceived(event);
- currentState = assertAndReturnProviderState(
- provider, providerMetricsLogger, PROVIDER_STATE_STARTED_UNCERTAIN);
- assertSame(provider, currentState.provider);
- assertEquals(event, currentState.event);
- assertEquals(config, currentState.currentUserConfiguration);
- mTestThreadingDomain.assertQueueEmpty();
- mProviderListener.assertProviderChangeReported(PROVIDER_STATE_STARTED_UNCERTAIN);
+ ProviderState currentState = assertAndReturnProviderState(
+ provider, providerMetricsLogger, PROVIDER_STATE_STARTED_UNCERTAIN,
+ ARBITRARY_PROVIDER_STATUS);
+ assertSame(provider, currentState.provider);
+ assertEquals(event, currentState.event);
+ assertEquals(config, currentState.currentUserConfiguration);
+ mTestThreadingDomain.assertQueueEmpty();
+ mProviderListener.assertProviderChangeReported(PROVIDER_STATE_STARTED_UNCERTAIN);
+ }
// stopUpdates()
- provider.stopUpdates();
- provider.assertOnStopUpdatesCalled();
+ {
+ provider.stopUpdates();
+ provider.assertOnStopUpdatesCalled();
- currentState = assertAndReturnProviderState(
- provider, providerMetricsLogger, PROVIDER_STATE_STOPPED);
- assertSame(provider, currentState.provider);
- assertEquals(PROVIDER_STATE_STOPPED, currentState.stateEnum);
- assertNull(currentState.event);
- assertNull(currentState.currentUserConfiguration);
- mTestThreadingDomain.assertQueueEmpty();
- // Entering stopped does not trigger an onProviderStateChanged() as it is requested by the
- // controller.
- mProviderListener.assertProviderChangeNotReported();
+ ProviderState currentState = assertAndReturnProviderState(
+ provider, providerMetricsLogger, PROVIDER_STATE_STOPPED,
+ /*expectedReportedStatus=*/null);
+ assertSame(provider, currentState.provider);
+ assertEquals(PROVIDER_STATE_STOPPED, currentState.stateEnum);
+ assertNull(currentState.event);
+ assertNull(currentState.currentUserConfiguration);
+ mTestThreadingDomain.assertQueueEmpty();
+ // Entering stopped does not trigger an onProviderStateChanged() as it is requested by
+ // the controller.
+ mProviderListener.assertProviderChangeNotReported();
+ }
// destroy()
- provider.destroy();
- provider.assertOnDestroyCalled();
+ {
+ provider.destroy();
+ provider.assertOnDestroyCalled();
+ }
}
@Test
@@ -196,13 +222,12 @@
.setTimeZoneIds(Arrays.asList("Europe/London"))
.build();
TimeZoneProviderEvent event = TimeZoneProviderEvent.createSuggestionEvent(
- ARBITRARY_ELAPSED_REALTIME_MILLIS, suggestion, TimeZoneProviderStatus.UNKNOWN);
+ ARBITRARY_ELAPSED_REALTIME_MILLIS, suggestion, null);
provider.simulateProviderEventReceived(event);
provider.assertLatestRecordedState(PROVIDER_STATE_STARTED_CERTAIN);
// Simulate an uncertain event being received.
- event = TimeZoneProviderEvent.createUncertainEvent(ARBITRARY_ELAPSED_REALTIME_MILLIS,
- TimeZoneProviderStatus.UNKNOWN);
+ event = TimeZoneProviderEvent.createUncertainEvent(ARBITRARY_ELAPSED_REALTIME_MILLIS, null);
provider.simulateProviderEventReceived(event);
provider.assertLatestRecordedState(PROVIDER_STATE_STARTED_UNCERTAIN);
@@ -239,7 +264,7 @@
.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS)
.setTimeZoneIds(invalidTimeZoneIds)
.build();
- TimeZoneProviderStatus providerStatus = TimeZoneProviderStatus.UNKNOWN;
+ TimeZoneProviderStatus providerStatus = null;
TimeZoneProviderEvent event = TimeZoneProviderEvent.createSuggestionEvent(
ARBITRARY_ELAPSED_REALTIME_MILLIS, invalidIdSuggestion, providerStatus);
provider.simulateProviderEventReceived(event);
@@ -275,9 +300,11 @@
*/
private static ProviderState assertAndReturnProviderState(
TestLocationTimeZoneProvider provider,
- RecordingProviderMetricsLogger providerMetricsLogger, int expectedStateEnum) {
+ RecordingProviderMetricsLogger providerMetricsLogger, int expectedStateEnum,
+ TimeZoneProviderStatus expectedReportedStatus) {
ProviderState currentState = provider.getCurrentState();
assertEquals(expectedStateEnum, currentState.stateEnum);
+ assertEquals(expectedReportedStatus, currentState.getReportedStatus());
providerMetricsLogger.assertChangeLoggedAndRemove(expectedStateEnum);
providerMetricsLogger.assertNoMoreLogEntries();
return currentState;
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java
index c478604..f3440f7 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java
@@ -16,9 +16,9 @@
package com.android.server.timezonedetector.location;
-import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_WORKING;
+import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_OK;
import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_FAILED;
-import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_WORKING;
+import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_OK;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -61,7 +61,7 @@
TimeZoneProviderStatus expectedProviderStatus =
new TimeZoneProviderStatus.Builder(event.getTimeZoneProviderStatus())
- .setTimeZoneResolutionStatus(OPERATION_STATUS_FAILED)
+ .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_FAILED)
.build();
TimeZoneProviderEvent expectedResultEvent =
@@ -75,9 +75,9 @@
private static TimeZoneProviderEvent timeZoneProviderEvent(String... timeZoneIds) {
TimeZoneProviderStatus providerStatus = new TimeZoneProviderStatus.Builder()
- .setLocationDetectionStatus(DEPENDENCY_STATUS_WORKING)
- .setConnectivityStatus(DEPENDENCY_STATUS_WORKING)
- .setTimeZoneResolutionStatus(OPERATION_STATUS_WORKING)
+ .setLocationDetectionDependencyStatus(DEPENDENCY_STATUS_OK)
+ .setConnectivityDependencyStatus(DEPENDENCY_STATUS_OK)
+ .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_OK)
.build();
TimeZoneProviderSuggestion suggestion = new TimeZoneProviderSuggestion.Builder()
.setTimeZoneIds(Arrays.asList(timeZoneIds))
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 2ed8b10..668345d 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -7605,43 +7605,6 @@
}
@Test
- public void testAddAutomaticZenRule_systemCallTakesPackageFromOwner() throws Exception {
- mService.isSystemUid = true;
- ZenModeHelper mockZenModeHelper = mock(ZenModeHelper.class);
- when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
- .thenReturn(true);
- mService.setZenHelper(mockZenModeHelper);
- ComponentName owner = new ComponentName("android", "ProviderName");
- ZenPolicy zenPolicy = new ZenPolicy.Builder().allowAlarms(true).build();
- boolean isEnabled = true;
- AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
- zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled);
- mBinderService.addAutomaticZenRule(rule, "com.android.settings");
-
- // verify that zen mode helper gets passed in a package name of "android"
- verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule), anyString());
- }
-
- @Test
- public void testAddAutomaticZenRule_nonSystemCallTakesPackageFromArg() throws Exception {
- mService.isSystemUid = false;
- ZenModeHelper mockZenModeHelper = mock(ZenModeHelper.class);
- when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
- .thenReturn(true);
- mService.setZenHelper(mockZenModeHelper);
- ComponentName owner = new ComponentName("android", "ProviderName");
- ZenPolicy zenPolicy = new ZenPolicy.Builder().allowAlarms(true).build();
- boolean isEnabled = true;
- AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
- zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled);
- mBinderService.addAutomaticZenRule(rule, "another.package");
-
- // verify that zen mode helper gets passed in the package name from the arg, not the owner
- verify(mockZenModeHelper).addAutomaticZenRule(
- eq("another.package"), eq(rule), anyString());
- }
-
- @Test
public void testAreNotificationsEnabledForPackage() throws Exception {
mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(),
mUid);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 49edde5..ba61980 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -1672,36 +1672,6 @@
}
@Test
- public void testAddAutomaticZenRule_claimedSystemOwner() {
- // Make sure anything that claims to have a "system" owner but not actually part of the
- // system package still gets limited on number of rules
- for (int i = 0; i < RULE_LIMIT_PER_PACKAGE; i++) {
- ScheduleInfo si = new ScheduleInfo();
- si.startHour = i;
- AutomaticZenRule zenRule = new AutomaticZenRule("name" + i,
- new ComponentName("android", "ScheduleConditionProvider" + i),
- null, // configuration activity
- ZenModeConfig.toScheduleConditionId(si),
- new ZenPolicy.Builder().build(),
- NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test");
- assertNotNull(id);
- }
- try {
- AutomaticZenRule zenRule = new AutomaticZenRule("name",
- new ComponentName("android", "ScheduleConditionProviderFinal"),
- null, // configuration activity
- ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
- new ZenPolicy.Builder().build(),
- NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test");
- fail("allowed too many rules to be created");
- } catch (IllegalArgumentException e) {
- // yay
- }
- }
-
- @Test
public void testAddAutomaticZenRule_CA() {
AutomaticZenRule zenRule = new AutomaticZenRule("name",
null,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 053718e..07dba00 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.app.Activity.RESULT_CANCELED;
+import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
import static android.app.ActivityManager.PROCESS_STATE_TOP;
import static android.app.ActivityManager.START_ABORTED;
import static android.app.ActivityManager.START_CANCELED;
@@ -609,10 +610,9 @@
@Test
public void testBackgroundActivityStartsAllowed_noStartsAborted() {
doReturn(true).when(mAtm).isBackgroundActivityStartsEnabled();
-
runAndVerifyBackgroundActivityStartsSubtest("allowed_noStartsAborted", false,
- UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
- UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
false, false, false, false, false);
}
@@ -621,97 +621,220 @@
* disallowed.
*/
@Test
- public void testBackgroundActivityStartsDisallowed_unsupportedStartsAborted() {
+ public void testBackgroundActivityStartsDisallowed_unsupportedUsecaseAborted() {
doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
-
runAndVerifyBackgroundActivityStartsSubtest(
"disallowed_unsupportedUsecase_aborted", true,
- UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
- UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
false, false, false, false, false);
+ }
+
+ /**
+ * This test ensures that unsupported usecases are aborted when background starts are
+ * disallowed.
+ */
+ @Test
+ public void testBackgroundActivityStartsDisallowed_callingUidProcessStateTopAborted() {
+ doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
runAndVerifyBackgroundActivityStartsSubtest(
"disallowed_callingUidProcessStateTop_aborted", true,
UNIMPORTANT_UID, false, PROCESS_STATE_TOP,
- UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
false, false, false, false, false);
+ }
+
+ /**
+ * This test ensures that unsupported usecases are aborted when background starts are
+ * disallowed.
+ */
+ @Test
+ public void testBackgroundActivityStartsDisallowed_realCallingUidProcessStateTopAborted() {
+ doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
runAndVerifyBackgroundActivityStartsSubtest(
"disallowed_realCallingUidProcessStateTop_aborted", true,
- UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
UNIMPORTANT_UID2, false, PROCESS_STATE_TOP,
false, false, false, false, false);
+ }
+
+ /**
+ * This test ensures that unsupported usecases are aborted when background starts are
+ * disallowed.
+ */
+ @Test
+ public void testBackgroundActivityStartsDisallowed_hasForegroundActivitiesAborted() {
+ doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
runAndVerifyBackgroundActivityStartsSubtest(
"disallowed_hasForegroundActivities_aborted", true,
- UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
- UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
true, false, false, false, false);
+ }
+
+ /**
+ * This test ensures that unsupported usecases are aborted when background starts are
+ * disallowed.
+ */
+ @Test
+ public void testBackgroundActivityStartsDisallowed_pinnedSingleInstanceAborted() {
+ doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
runAndVerifyBackgroundActivityStartsSubtest(
"disallowed_pinned_singleinstance_aborted", true,
- UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
- UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
false, false, false, false, false, true);
-
}
/**
* This test ensures that supported usecases aren't aborted when background starts are
- * disallowed.
- * The scenarios each have only one condition that makes them supported.
+ * disallowed. Each scenarios tests one condition that makes them supported in isolation. In
+ * this case the calling process runs as ROOT_UID.
*/
@Test
- public void testBackgroundActivityStartsDisallowed_supportedStartsNotAborted() {
+ public void testBackgroundActivityStartsDisallowed_rootUidNotAborted() {
doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
-
runAndVerifyBackgroundActivityStartsSubtest("disallowed_rootUid_notAborted", false,
- Process.ROOT_UID, false, PROCESS_STATE_TOP + 1,
- UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ Process.ROOT_UID, false, PROCESS_STATE_BOUND_TOP,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
false, false, false, false, false);
+ }
+
+ /**
+ * This test ensures that supported usecases aren't aborted when background starts are
+ * disallowed. Each scenarios tests one condition that makes them supported in isolation. In
+ * this case the calling process is running as SYSTEM_UID.
+ */
+ @Test
+ public void testBackgroundActivityStartsDisallowed_systemUidNotAborted() {
+ doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
runAndVerifyBackgroundActivityStartsSubtest("disallowed_systemUid_notAborted", false,
- Process.SYSTEM_UID, false, PROCESS_STATE_TOP + 1,
- UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ Process.SYSTEM_UID, false, PROCESS_STATE_BOUND_TOP,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
false, false, false, false, false);
+ }
+
+ /**
+ * This test ensures that supported usecases aren't aborted when background starts are
+ * disallowed. Each scenarios tests one condition that makes them supported in isolation. In
+ * this case the calling process is running as NFC_UID.
+ */
+ @Test
+ public void testBackgroundActivityStartsDisallowed_nfcUidNotAborted() {
+ doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
runAndVerifyBackgroundActivityStartsSubtest("disallowed_nfcUid_notAborted", false,
- Process.NFC_UID, false, PROCESS_STATE_TOP + 1,
- UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ Process.NFC_UID, false, PROCESS_STATE_BOUND_TOP,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
false, false, false, false, false);
+ }
+
+ /**
+ * This test ensures that supported usecases aren't aborted when background starts are
+ * disallowed. Each scenarios tests one condition that makes them supported in isolation. In
+ * this case the calling process has a visible window.
+ */
+ @Test
+ public void testBackgroundActivityStartsDisallowed_callingUidHasVisibleWindowNotAborted() {
+ doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
runAndVerifyBackgroundActivityStartsSubtest(
"disallowed_callingUidHasVisibleWindow_notAborted", false,
- UNIMPORTANT_UID, true, PROCESS_STATE_TOP + 1,
- UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID, true, PROCESS_STATE_BOUND_TOP,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
false, false, false, false, false);
+ }
+
+ /**
+ * This test ensures that supported usecases aren't aborted when background starts are
+ * disallowed. Each scenarios tests one condition that makes them supported in isolation. In
+ * this case the real calling process (pending intent) has a visible window.
+ */
+ @Test
+ public void
+ testBackgroundActivityStartsDisallowed_realCallingUidHasVisibleWindowNotAborted() {
+ doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
runAndVerifyBackgroundActivityStartsSubtest(
"disallowed_realCallingUidHasVisibleWindow_notAborted", false,
- UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
- UNIMPORTANT_UID2, true, PROCESS_STATE_TOP + 1,
- false, false, false, false, false);
+ UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
+ UNIMPORTANT_UID2, true, PROCESS_STATE_BOUND_TOP,
+ false, false, false, false, false, false);
+ }
+
+ /**
+ * This test ensures that supported usecases aren't aborted when background starts are
+ * disallowed. Each scenarios tests one condition that makes them supported in isolation. In
+ * this case the caller is in the recent activity list.
+ */
+ @Test
+ public void testBackgroundActivityStartsDisallowed_callerIsRecentsNotAborted() {
+ doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
runAndVerifyBackgroundActivityStartsSubtest(
"disallowed_callerIsRecents_notAborted", false,
- UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
- UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
false, true, false, false, false);
+ }
+
+ /**
+ * This test ensures that supported usecases aren't aborted when background starts are
+ * disallowed. Each scenarios tests one condition that makes them supported in isolation. In
+ * this case the caller is temporarily (10s) allowed to start.
+ */
+ @Test
+ public void testBackgroundActivityStartsDisallowed_callerIsAllowedNotAborted() {
+ doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
runAndVerifyBackgroundActivityStartsSubtest(
"disallowed_callerIsAllowed_notAborted", false,
- UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
- UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
false, false, true, false, false);
+ }
+
+ /**
+ * This test ensures that supported usecases aren't aborted when background starts are
+ * disallowed. Each scenarios tests one condition that makes them supported in isolation. In
+ * this case the caller explicitly has background activity start privilege.
+ */
+ @Test
+ public void testBackgroundActivityStartsDisallowed_callerIsInstrumentingWithBASPnotAborted() {
+ doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
runAndVerifyBackgroundActivityStartsSubtest(
"disallowed_callerIsInstrumentingWithBackgroundActivityStartPrivileges_notAborted",
false,
- UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
- UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
false, false, false, true, false);
+ }
+
+ /**
+ * This test ensures that supported usecases aren't aborted when background starts are
+ * disallowed. Each scenarios tests one condition that makes them supported in isolation. In
+ * this case the caller is a device owner.
+ */
+ @Test
+ public void
+ testBackgroundActivityStartsDisallowed_callingPackageNameIsDeviceOwnerNotAborted() {
+ doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
runAndVerifyBackgroundActivityStartsSubtest(
"disallowed_callingPackageNameIsDeviceOwner_notAborted", false,
- UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
- UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
false, false, false, false, true);
+ }
+ /**
+ * This test ensures that supported usecases aren't aborted when background starts are
+ * disallowed. Each scenarios tests one condition that makes them supported in isolation. In
+ * this case the caller is an IME.
+ */
+ @Test
+ public void testBackgroundActivityStartsDisallowed_callingPackageNameIsImeNotAborted() {
+ doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
setupImeWindow();
runAndVerifyBackgroundActivityStartsSubtest(
"disallowed_callingPackageNameIsIme_notAborted", false,
- CURRENT_IME_UID, false, PROCESS_STATE_TOP + 1,
- UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ CURRENT_IME_UID, false, PROCESS_STATE_BOUND_TOP,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
false, false, false, false, false);
-
}
private void runAndVerifyBackgroundActivityStartsSubtest(String name, boolean shouldHaveAborted,
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index d55e53c..32c95fa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -16,8 +16,6 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_CHANGE;
@@ -27,7 +25,6 @@
import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
@@ -411,50 +408,38 @@
}
@Test
- public void testExcludeLauncher() {
+ public void testDelayWhileRecents() {
final DisplayContent dc = createNewDisplay(Display.STATE_ON);
doReturn(false).when(dc).onDescendantOrientationChanged(any());
final Task task = createTask(dc);
- // Simulate activity1 launches activity2
+ // Simulate activity1 launches activity2.
final ActivityRecord activity1 = createActivityRecord(task);
activity1.setVisible(true);
activity1.mVisibleRequested = false;
activity1.allDrawn = true;
- dc.mClosingApps.add(activity1);
final ActivityRecord activity2 = createActivityRecord(task);
activity2.setVisible(false);
activity2.mVisibleRequested = true;
activity2.allDrawn = true;
+
+ dc.mClosingApps.add(activity1);
dc.mOpeningApps.add(activity2);
dc.prepareAppTransition(TRANSIT_OPEN);
-
- // Simulate start recents
- final ActivityRecord homeActivity = createActivityRecord(dc, WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_HOME);
- homeActivity.setVisible(false);
- homeActivity.mVisibleRequested = true;
- homeActivity.allDrawn = true;
- dc.mOpeningApps.add(homeActivity);
- dc.prepareAppTransition(TRANSIT_NONE);
- doReturn(true).when(task)
- .isSelfAnimating(anyInt(), eq(ANIMATION_TYPE_RECENTS));
+ assertTrue(dc.mAppTransition.containsTransitRequest(TRANSIT_OPEN));
// Wait until everything in animation handler get executed to prevent the exiting window
// from being removed during WindowSurfacePlacer Traversal.
waitUntilHandlersIdle();
+ // Start recents
+ doReturn(true).when(task)
+ .isSelfAnimating(anyInt(), eq(ANIMATION_TYPE_RECENTS));
+
dc.mAppTransitionController.handleAppTransitionReady();
- verify(activity1).commitVisibility(eq(false), anyBoolean(), anyBoolean());
- verify(activity1).applyAnimation(any(), eq(TRANSIT_OLD_ACTIVITY_OPEN), eq(false),
- anyBoolean(), any());
- verify(activity2).commitVisibility(eq(true), anyBoolean(), anyBoolean());
- verify(activity2).applyAnimation(any(), eq(TRANSIT_OLD_ACTIVITY_OPEN), eq(true),
- anyBoolean(), any());
- verify(homeActivity).commitVisibility(eq(true), anyBoolean(), anyBoolean());
- verify(homeActivity, never()).applyAnimation(any(), anyInt(), anyBoolean(), anyBoolean(),
- any());
+ verify(activity1, never()).commitVisibility(anyBoolean(), anyBoolean(), anyBoolean());
+ verify(activity2, never()).commitVisibility(anyBoolean(), anyBoolean(), anyBoolean());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index e3b389b..f3f56e0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -115,18 +115,14 @@
@Test
public void backTypeCrossActivityWhenBackToPreviousActivity() {
- Task task = createTopTaskWithActivity();
- WindowState window = createAppWindow(task, FIRST_APPLICATION_WINDOW, "window");
- addToWindowMap(window, true);
- IOnBackInvokedCallback callback = createOnBackInvokedCallback();
- window.setOnBackInvokedCallbackInfo(
- new OnBackInvokedCallbackInfo(callback, OnBackInvokedDispatcher.PRIORITY_SYSTEM));
+ CrossActivityTestCase testCase = createTopTaskWithTwoActivities();
+ IOnBackInvokedCallback callback = withSystemCallback(testCase.task);
+
BackNavigationInfo backNavigationInfo = startBackNavigation();
assertWithMessage("BackNavigationInfo").that(backNavigationInfo).isNotNull();
+ assertThat(backNavigationInfo.getOnBackInvokedCallback()).isEqualTo(callback);
assertThat(typeToString(backNavigationInfo.getType()))
.isEqualTo(typeToString(BackNavigationInfo.TYPE_CROSS_ACTIVITY));
- assertWithMessage("Activity callback").that(
- backNavigationInfo.getOnBackInvokedCallback()).isEqualTo(callback);
}
@Test
@@ -302,6 +298,34 @@
return task;
}
+ @NonNull
+ private CrossActivityTestCase createTopTaskWithTwoActivities() {
+ Task task = createTask(mDefaultDisplay);
+ ActivityRecord record1 = createActivityRecord(task);
+ ActivityRecord record2 = createActivityRecord(task);
+ // enable OnBackInvokedCallbacks
+ record2.info.applicationInfo.privateFlagsExt |=
+ PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK;
+ WindowState window1 = createWindow(null, FIRST_APPLICATION_WINDOW, record1, "window1");
+ WindowState window2 = createWindow(null, FIRST_APPLICATION_WINDOW, record2, "window2");
+ when(task.mSurfaceControl.isValid()).thenReturn(true);
+ when(record1.mSurfaceControl.isValid()).thenReturn(true);
+ when(record2.mSurfaceControl.isValid()).thenReturn(true);
+ Mockito.doNothing().when(task).reparentSurfaceControl(any(), any());
+ Mockito.doNothing().when(record1).reparentSurfaceControl(any(), any());
+ Mockito.doNothing().when(record2).reparentSurfaceControl(any(), any());
+ mAtm.setFocusedTask(task.mTaskId, record1);
+ mAtm.setFocusedTask(task.mTaskId, record2);
+ addToWindowMap(window1, true);
+ addToWindowMap(window2, true);
+
+ CrossActivityTestCase testCase = new CrossActivityTestCase();
+ testCase.task = task;
+ testCase.recordBack = record1;
+ testCase.recordFront = record2;
+ return testCase;
+ }
+
private void addToWindowMap(WindowState window, boolean focus) {
mWm.mWindowMap.put(window.mClient.asBinder(), window);
if (focus) {
@@ -310,4 +334,10 @@
doReturn(window).when(mWm).getFocusedWindowLocked();
}
}
+
+ private class CrossActivityTestCase {
+ public Task task;
+ public ActivityRecord recordBack;
+ public ActivityRecord recordFront;
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
index ac3d0f0..75c5b6e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
@@ -213,7 +213,7 @@
assertThat(newTaskBounds).isEqualTo(newDagBounds);
// Activity config bounds is unchanged, size compat bounds is (860x[860x860/1200=616])
- assertThat(mFirstActivity.getSizeCompatScale()).isLessThan(1f);
+ assertThat(mFirstActivity.getCompatScale()).isLessThan(1f);
assertThat(activityConfigBounds.width()).isEqualTo(activityBounds.width());
assertThat(activityConfigBounds.height()).isEqualTo(activityBounds.height());
assertThat(activitySizeCompatBounds.height()).isEqualTo(newTaskBounds.height());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
index c839d12..eb8b89d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
@@ -85,4 +85,26 @@
mImeProvider.setImeShowing(false);
assertFalse(mImeProvider.isImeShowing());
}
+
+ @Test
+ public void testSetFrozen() {
+ WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime");
+ makeWindowVisibleAndDrawn(ime);
+ mImeProvider.setWindowContainer(ime, null, null);
+ mImeProvider.setServerVisible(true);
+ mImeProvider.setClientVisible(true);
+ mImeProvider.updateVisibility();
+ assertTrue(mImeProvider.getSource().isVisible());
+
+ // Freezing IME states and set the server visible as false.
+ mImeProvider.setFrozen(true);
+ mImeProvider.setServerVisible(false);
+ // Expect the IME insets visible won't be changed.
+ assertTrue(mImeProvider.getSource().isVisible());
+
+ // Unfreeze IME states and expect the IME insets became invisible due to pending IME
+ // visible state updated.
+ mImeProvider.setFrozen(false);
+ assertFalse(mImeProvider.getSource().isVisible());
+ }
}
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 c906abc..e65610f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -25,6 +25,7 @@
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.InsetsState.ITYPE_TOP_MANDATORY_GESTURES;
import static android.view.InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT;
@@ -71,6 +72,7 @@
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.times;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -87,6 +89,7 @@
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
import android.view.InsetsFrameProvider;
+import android.view.InsetsSource;
import android.view.WindowManager;
import androidx.test.filters.MediumTest;
@@ -105,6 +108,9 @@
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import java.util.List;
/**
* Tests for Size Compatibility mode.
@@ -2368,6 +2374,48 @@
}
@Test
+ public void testLetterboxDetailsForTaskBar_letterboxNotOverlappingTaskBar() {
+ mAtm.mDevEnableNonResizableMultiWindow = true;
+ final int screenHeight = 2200;
+ final int screenWidth = 1400;
+ final int taskbarHeight = 200;
+ setUpDisplaySizeWithApp(screenWidth, screenHeight);
+
+ final TestSplitOrganizer organizer =
+ new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
+
+ // Move first activity to split screen which takes half of the screen.
+ organizer.mPrimary.setBounds(0, screenHeight / 2, screenWidth, screenHeight);
+ organizer.putTaskToPrimary(mTask, true);
+
+ final InsetsSource navSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
+ navSource.setFrame(new Rect(0, screenHeight - taskbarHeight, screenWidth, screenHeight));
+
+ mActivity.mWmService.mLetterboxConfiguration.setLetterboxActivityCornersRadius(15);
+
+ final WindowState w1 = addWindowToActivity(mActivity);
+ w1.mAboveInsetsState.addSource(navSource);
+
+ // Prepare unresizable activity with max aspect ratio
+ prepareUnresizable(mActivity, /* maxAspect */ 1.1f, SCREEN_ORIENTATION_UNSPECIFIED);
+
+ // Refresh the letterboxes
+ mActivity.mRootWindowContainer.performSurfacePlacement();
+
+ final ArgumentCaptor<Rect> cropCapturer = ArgumentCaptor.forClass(Rect.class);
+ verify(mTransaction, times(2)).setWindowCrop(
+ eq(w1.getSurfaceControl()),
+ cropCapturer.capture()
+ );
+ final List<Rect> capturedCrops = cropCapturer.getAllValues();
+
+ final int expectedHeight = screenHeight / 2 - taskbarHeight;
+ assertEquals(2, capturedCrops.size());
+ assertEquals(expectedHeight, capturedCrops.get(0).bottom);
+ assertEquals(expectedHeight, capturedCrops.get(1).bottom);
+ }
+
+ @Test
public void testSplitScreenLetterboxDetailsForStatusBar_twoLetterboxedApps() {
mAtm.mDevEnableNonResizableMultiWindow = true;
setUpDisplaySizeWithApp(2800, 1000);
@@ -3160,7 +3208,7 @@
/** Asserts that the size of activity is larger than its parent so it is scaling. */
private void assertScaled() {
assertTrue(mActivity.inSizeCompatMode());
- assertNotEquals(1f, mActivity.getSizeCompatScale(), 0.0001f /* delta */);
+ assertNotEquals(1f, mActivity.getCompatScale(), 0.0001f /* delta */);
}
/** Asserts that the activity is best fitted in the parent. */
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 7f5beb1..4fd2b78 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -2392,6 +2392,8 @@
@Override
public void setAppStandbyBucket(String packageName, int bucket, int userId) {
+ super.setAppStandbyBucket_enforcePermission();
+
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final long token = Binder.clearCallingIdentity();
@@ -2442,6 +2444,8 @@
@Override
public void setAppStandbyBuckets(ParceledListSlice appBuckets, int userId) {
+ super.setAppStandbyBuckets_enforcePermission();
+
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final long token = Binder.clearCallingIdentity();
@@ -2493,6 +2497,8 @@
public void setEstimatedLaunchTime(String packageName, long estimatedLaunchTime,
int userId) {
+ super.setEstimatedLaunchTime_enforcePermission();
+
final long token = Binder.clearCallingIdentity();
try {
UsageStatsService.this
@@ -2506,6 +2512,8 @@
@Override
public void setEstimatedLaunchTimes(ParceledListSlice estimatedLaunchTimes, int userId) {
+ super.setEstimatedLaunchTimes_enforcePermission();
+
final long token = Binder.clearCallingIdentity();
try {
UsageStatsService.this
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerLogger.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerLogger.java
deleted file mode 100644
index 73b4ce7..0000000
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerLogger.java
+++ /dev/null
@@ -1,136 +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;
-
-import android.util.Log;
-
-import java.io.PrintWriter;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.LinkedList;
-
-/**
-* Constructor SoundTriggerLogger class
-*/
-public class SoundTriggerLogger {
-
- // ring buffer of events to log.
- private final LinkedList<Event> mEvents;
-
- private final String mTitle;
-
- // the maximum number of events to keep in log
- private final int mMemSize;
-
- /**
- * Constructor for Event class.
- */
- public abstract static class Event {
- // formatter for timestamps
- private static final SimpleDateFormat sFormat = new SimpleDateFormat("MM-dd HH:mm:ss:SSS");
-
- private final long mTimestamp;
-
- Event() {
- mTimestamp = System.currentTimeMillis();
- }
-
- /**
- * Convert event to String
- * @return StringBuilder
- */
- public String toString() {
- return (new StringBuilder(sFormat.format(new Date(mTimestamp))))
- .append(" ").append(eventToString()).toString();
- }
-
- /**
- * Causes the string message for the event to appear in the logcat.
- * Here is an example of how to create a new event (a StringEvent), adding it to the logger
- * (an instance of SoundTriggerLogger) while also making it show in the logcat:
- * <pre>
- * myLogger.log(
- * (new StringEvent("something for logcat and logger")).printLog(MyClass.TAG) );
- * </pre>
- * @param tag the tag for the android.util.Log.v
- * @return the same instance of the event
- */
- public Event printLog(String tag) {
- Log.i(tag, eventToString());
- return this;
- }
-
- /**
- * Convert event to String.
- * This method is only called when the logger history is about to the dumped,
- * so this method is where expensive String conversions should be made, not when the Event
- * subclass is created.
- * Timestamp information will be automatically added, do not include it.
- * @return a string representation of the event that occurred.
- */
- public abstract String eventToString();
- }
-
- /**
- * Constructor StringEvent class
- */
- public static class StringEvent extends Event {
- private final String mMsg;
-
- public StringEvent(String msg) {
- mMsg = msg;
- }
-
- @Override
- public String eventToString() {
- return mMsg;
- }
- }
-
- /**
- * Constructor for logger.
- * @param size the maximum number of events to keep in log
- * @param title the string displayed before the recorded log
- */
- public SoundTriggerLogger(int size, String title) {
- mEvents = new LinkedList<Event>();
- mMemSize = size;
- mTitle = title;
- }
-
- /**
- * Constructor for logger.
- * @param evt the maximum number of events to keep in log
- */
- public synchronized void log(Event evt) {
- if (mEvents.size() >= mMemSize) {
- mEvents.removeFirst();
- }
- mEvents.add(evt);
- }
-
- /**
- * Constructor for logger.
- * @param pw the maximum number of events to keep in log
- */
- public synchronized void dump(PrintWriter pw) {
- pw.println("ST Event log: " + mTitle);
- for (Event evt : mEvents) {
- pw.println(evt.toString());
- }
- }
-}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 5183e5b..81717f4 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -84,6 +84,7 @@
import com.android.internal.app.ISoundTriggerService;
import com.android.internal.app.ISoundTriggerSession;
import com.android.server.SystemService;
+import com.android.server.utils.EventLogger;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -309,14 +310,14 @@
Slog.i(TAG, "startRecognition(): Uuid : " + parcelUuid);
}
- sEventLogger.log(new SoundTriggerLogger.StringEvent("startRecognition(): Uuid : "
- + parcelUuid));
+ sEventLogger.enqueue(new EventLogger.StringEvent(
+ "startRecognition(): Uuid : " + parcelUuid));
GenericSoundModel model = getSoundModel(parcelUuid);
if (model == null) {
Slog.w(TAG, "Null model in database for id: " + parcelUuid);
- sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ sEventLogger.enqueue(new EventLogger.StringEvent(
"startRecognition(): Null model in database for id: " + parcelUuid));
return STATUS_ERROR;
@@ -339,7 +340,7 @@
Slog.i(TAG, "stopRecognition(): Uuid : " + parcelUuid);
}
- sEventLogger.log(new SoundTriggerLogger.StringEvent("stopRecognition(): Uuid : "
+ sEventLogger.enqueue(new EventLogger.StringEvent("stopRecognition(): Uuid : "
+ parcelUuid));
int ret = mSoundTriggerHelper.stopGenericRecognition(parcelUuid.getUuid(),
@@ -359,7 +360,7 @@
Slog.i(TAG, "getSoundModel(): id = " + soundModelId);
}
- sEventLogger.log(new SoundTriggerLogger.StringEvent("getSoundModel(): id = "
+ sEventLogger.enqueue(new EventLogger.StringEvent("getSoundModel(): id = "
+ soundModelId));
SoundTrigger.GenericSoundModel model = mDbHelper.getGenericSoundModel(
@@ -376,7 +377,7 @@
Slog.i(TAG, "updateSoundModel(): model = " + soundModel);
}
- sEventLogger.log(new SoundTriggerLogger.StringEvent("updateSoundModel(): model = "
+ sEventLogger.enqueue(new EventLogger.StringEvent("updateSoundModel(): model = "
+ soundModel));
mDbHelper.updateGenericSoundModel(soundModel);
@@ -391,7 +392,7 @@
Slog.i(TAG, "deleteSoundModel(): id = " + soundModelId);
}
- sEventLogger.log(new SoundTriggerLogger.StringEvent("deleteSoundModel(): id = "
+ sEventLogger.enqueue(new EventLogger.StringEvent("deleteSoundModel(): id = "
+ soundModelId));
// Unload the model if it is loaded.
@@ -411,7 +412,7 @@
if (soundModel == null || soundModel.getUuid() == null) {
Slog.w(TAG, "Invalid sound model");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ sEventLogger.enqueue(new EventLogger.StringEvent(
"loadGenericSoundModel(): Invalid sound model"));
return STATUS_ERROR;
@@ -420,7 +421,7 @@
Slog.i(TAG, "loadGenericSoundModel(): id = " + soundModel.getUuid());
}
- sEventLogger.log(new SoundTriggerLogger.StringEvent("loadGenericSoundModel(): id = "
+ sEventLogger.enqueue(new EventLogger.StringEvent("loadGenericSoundModel(): id = "
+ soundModel.getUuid()));
synchronized (mLock) {
@@ -447,7 +448,7 @@
if (soundModel == null || soundModel.getUuid() == null) {
Slog.w(TAG, "Invalid sound model");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ sEventLogger.enqueue(new EventLogger.StringEvent(
"loadKeyphraseSoundModel(): Invalid sound model"));
return STATUS_ERROR;
@@ -455,7 +456,7 @@
if (soundModel.getKeyphrases() == null || soundModel.getKeyphrases().length != 1) {
Slog.w(TAG, "Only one keyphrase per model is currently supported.");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ sEventLogger.enqueue(new EventLogger.StringEvent(
"loadKeyphraseSoundModel(): Only one keyphrase per model"
+ " is currently supported."));
@@ -465,8 +466,8 @@
Slog.i(TAG, "loadKeyphraseSoundModel(): id = " + soundModel.getUuid());
}
- sEventLogger.log(
- new SoundTriggerLogger.StringEvent("loadKeyphraseSoundModel(): id = "
+ sEventLogger.enqueue(
+ new EventLogger.StringEvent("loadKeyphraseSoundModel(): id = "
+ soundModel.getUuid()));
synchronized (mLock) {
@@ -503,7 +504,7 @@
Slog.i(TAG, "startRecognition(): id = " + soundModelId);
}
- sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ sEventLogger.enqueue(new EventLogger.StringEvent(
"startRecognitionForService(): id = " + soundModelId));
IRecognitionStatusCallback callback =
@@ -515,7 +516,7 @@
if (soundModel == null) {
Slog.w(TAG, soundModelId + " is not loaded");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ sEventLogger.enqueue(new EventLogger.StringEvent(
"startRecognitionForService():" + soundModelId + " is not loaded"));
return STATUS_ERROR;
@@ -527,7 +528,7 @@
if (existingCallback != null) {
Slog.w(TAG, soundModelId + " is already running");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ sEventLogger.enqueue(new EventLogger.StringEvent(
"startRecognitionForService():"
+ soundModelId + " is already running"));
@@ -542,7 +543,7 @@
default:
Slog.e(TAG, "Unknown model type");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ sEventLogger.enqueue(new EventLogger.StringEvent(
"startRecognitionForService(): Unknown model type"));
return STATUS_ERROR;
@@ -551,7 +552,7 @@
if (ret != STATUS_OK) {
Slog.e(TAG, "Failed to start model: " + ret);
- sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ sEventLogger.enqueue(new EventLogger.StringEvent(
"startRecognitionForService(): Failed to start model:"));
return ret;
@@ -574,7 +575,7 @@
Slog.i(TAG, "stopRecognition(): id = " + soundModelId);
}
- sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ sEventLogger.enqueue(new EventLogger.StringEvent(
"stopRecognitionForService(): id = " + soundModelId));
synchronized (mLock) {
@@ -582,7 +583,7 @@
if (soundModel == null) {
Slog.w(TAG, soundModelId + " is not loaded");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ sEventLogger.enqueue(new EventLogger.StringEvent(
"stopRecognitionForService(): " + soundModelId
+ " is not loaded"));
@@ -595,7 +596,7 @@
if (callback == null) {
Slog.w(TAG, soundModelId + " is not running");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ sEventLogger.enqueue(new EventLogger.StringEvent(
"stopRecognitionForService(): " + soundModelId
+ " is not running"));
@@ -610,7 +611,7 @@
default:
Slog.e(TAG, "Unknown model type");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ sEventLogger.enqueue(new EventLogger.StringEvent(
"stopRecognitionForService(): Unknown model type"));
return STATUS_ERROR;
@@ -619,7 +620,7 @@
if (ret != STATUS_OK) {
Slog.e(TAG, "Failed to stop model: " + ret);
- sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ sEventLogger.enqueue(new EventLogger.StringEvent(
"stopRecognitionForService(): Failed to stop model: " + ret));
return ret;
@@ -642,7 +643,7 @@
Slog.i(TAG, "unloadSoundModel(): id = " + soundModelId);
}
- sEventLogger.log(new SoundTriggerLogger.StringEvent("unloadSoundModel(): id = "
+ sEventLogger.enqueue(new EventLogger.StringEvent("unloadSoundModel(): id = "
+ soundModelId));
synchronized (mLock) {
@@ -650,7 +651,7 @@
if (soundModel == null) {
Slog.w(TAG, soundModelId + " is not loaded");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ sEventLogger.enqueue(new EventLogger.StringEvent(
"unloadSoundModel(): " + soundModelId + " is not loaded"));
return STATUS_ERROR;
@@ -667,7 +668,7 @@
default:
Slog.e(TAG, "Unknown model type");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ sEventLogger.enqueue(new EventLogger.StringEvent(
"unloadSoundModel(): Unknown model type"));
return STATUS_ERROR;
@@ -675,7 +676,7 @@
if (ret != STATUS_OK) {
Slog.e(TAG, "Failed to unload model");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ sEventLogger.enqueue(new EventLogger.StringEvent(
"unloadSoundModel(): Failed to unload model"));
return ret;
@@ -709,7 +710,7 @@
Slog.i(TAG, "getModelState(): id = " + soundModelId);
}
- sEventLogger.log(new SoundTriggerLogger.StringEvent("getModelState(): id = "
+ sEventLogger.enqueue(new EventLogger.StringEvent("getModelState(): id = "
+ soundModelId));
synchronized (mLock) {
@@ -717,7 +718,7 @@
if (soundModel == null) {
Slog.w(TAG, soundModelId + " is not loaded");
- sEventLogger.log(new SoundTriggerLogger.StringEvent("getModelState(): "
+ sEventLogger.enqueue(new EventLogger.StringEvent("getModelState(): "
+ soundModelId + " is not loaded"));
return ret;
@@ -729,7 +730,7 @@
default:
// SoundModel.TYPE_KEYPHRASE is not supported to increase privacy.
Slog.e(TAG, "Unsupported model type, " + soundModel.getType());
- sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ sEventLogger.enqueue(new EventLogger.StringEvent(
"getModelState(): Unsupported model type, "
+ soundModel.getType()));
break;
@@ -751,7 +752,7 @@
synchronized (mLock) {
ModuleProperties properties = mSoundTriggerHelper.getModuleProperties();
- sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ sEventLogger.enqueue(new EventLogger.StringEvent(
"getModuleProperties(): " + properties));
return properties;
}
@@ -769,7 +770,7 @@
+ ", value=" + value);
}
- sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ sEventLogger.enqueue(new EventLogger.StringEvent(
"setParameter(): id=" + soundModelId
+ ", param=" + modelParam
+ ", value=" + value));
@@ -780,7 +781,7 @@
Slog.w(TAG, soundModelId + " is not loaded. Loaded models: "
+ mLoadedModels.toString());
- sEventLogger.log(new SoundTriggerLogger.StringEvent("setParameter(): "
+ sEventLogger.enqueue(new EventLogger.StringEvent("setParameter(): "
+ soundModelId + " is not loaded"));
return STATUS_BAD_VALUE;
@@ -803,7 +804,7 @@
+ ", param=" + modelParam);
}
- sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ sEventLogger.enqueue(new EventLogger.StringEvent(
"getParameter(): id=" + soundModelId
+ ", param=" + modelParam));
@@ -812,7 +813,7 @@
if (soundModel == null) {
Slog.w(TAG, soundModelId + " is not loaded");
- sEventLogger.log(new SoundTriggerLogger.StringEvent("getParameter(): "
+ sEventLogger.enqueue(new EventLogger.StringEvent("getParameter(): "
+ soundModelId + " is not loaded"));
throw new IllegalArgumentException("sound model is not loaded");
@@ -834,7 +835,7 @@
+ ", param=" + modelParam);
}
- sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ sEventLogger.enqueue(new EventLogger.StringEvent(
"queryParameter(): id=" + soundModelId
+ ", param=" + modelParam));
@@ -843,7 +844,7 @@
if (soundModel == null) {
Slog.w(TAG, soundModelId + " is not loaded");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ sEventLogger.enqueue(new EventLogger.StringEvent(
"queryParameter(): "
+ soundModelId + " is not loaded"));
@@ -857,7 +858,7 @@
private void clientDied() {
Slog.w(TAG, "Client died, cleaning up session.");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ sEventLogger.enqueue(new EventLogger.StringEvent(
"Client died, cleaning up session."));
mSoundTriggerHelper.detach();
}
@@ -1027,7 +1028,7 @@
} catch (Exception e) {
Slog.e(TAG, mPuuid + ": Cannot remove client", e);
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ sEventLogger.enqueue(new EventLogger.StringEvent(mPuuid
+ ": Cannot remove client"));
}
@@ -1052,7 +1053,7 @@
private void destroy() {
if (DEBUG) Slog.v(TAG, mPuuid + ": destroy");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid + ": destroy"));
+ sEventLogger.enqueue(new EventLogger.StringEvent(mPuuid + ": destroy"));
synchronized (mRemoteServiceLock) {
disconnectLocked();
@@ -1086,7 +1087,7 @@
Slog.e(TAG, mPuuid + ": Could not stop operation "
+ mRunningOpIds.valueAt(i), e);
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ sEventLogger.enqueue(new EventLogger.StringEvent(mPuuid
+ ": Could not stop operation " + mRunningOpIds.valueAt(
i)));
@@ -1116,7 +1117,7 @@
if (ri == null) {
Slog.w(TAG, mPuuid + ": " + mServiceName + " not found");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ sEventLogger.enqueue(new EventLogger.StringEvent(mPuuid
+ ": " + mServiceName + " not found"));
return;
@@ -1127,7 +1128,7 @@
Slog.w(TAG, mPuuid + ": " + mServiceName + " does not require "
+ BIND_SOUND_TRIGGER_DETECTION_SERVICE);
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ sEventLogger.enqueue(new EventLogger.StringEvent(mPuuid
+ ": " + mServiceName + " does not require "
+ BIND_SOUND_TRIGGER_DETECTION_SERVICE));
@@ -1143,7 +1144,7 @@
} else {
Slog.w(TAG, mPuuid + ": Could not bind to " + mServiceName);
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ sEventLogger.enqueue(new EventLogger.StringEvent(mPuuid
+ ": Could not bind to " + mServiceName));
}
@@ -1165,7 +1166,7 @@
mPuuid + ": Dropped operation as already destroyed or marked for "
+ "destruction");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ sEventLogger.enqueue(new EventLogger.StringEvent(mPuuid
+ ":Dropped operation as already destroyed or marked for "
+ "destruction"));
@@ -1197,7 +1198,7 @@
mPuuid + ": Dropped operation as too many operations "
+ "were run in last 24 hours");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ sEventLogger.enqueue(new EventLogger.StringEvent(mPuuid
+ ": Dropped operation as too many operations "
+ "were run in last 24 hours"));
@@ -1207,7 +1208,7 @@
} catch (Exception e) {
Slog.e(TAG, mPuuid + ": Could not drop operation", e);
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ sEventLogger.enqueue(new EventLogger.StringEvent(mPuuid
+ ": Could not drop operation"));
}
@@ -1224,7 +1225,7 @@
try {
if (DEBUG) Slog.v(TAG, mPuuid + ": runOp " + opId);
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ sEventLogger.enqueue(new EventLogger.StringEvent(mPuuid
+ ": runOp " + opId));
op.run(opId, mService);
@@ -1232,7 +1233,7 @@
} catch (Exception e) {
Slog.e(TAG, mPuuid + ": Could not run operation " + opId, e);
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ sEventLogger.enqueue(new EventLogger.StringEvent(mPuuid
+ ": Could not run operation " + opId));
}
@@ -1265,7 +1266,7 @@
Slog.w(TAG, mPuuid + "->" + mServiceName + ": IGNORED onKeyphraseDetected(" + event
+ ")");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid + "->" + mServiceName
+ sEventLogger.enqueue(new EventLogger.StringEvent(mPuuid + "->" + mServiceName
+ ": IGNORED onKeyphraseDetected(" + event + ")"));
}
@@ -1282,9 +1283,9 @@
attributesBuilder.setInternalCapturePreset(MediaRecorder.AudioSource.HOTWORD);
AudioAttributes attributes = attributesBuilder.build();
- AudioFormat originalFormat = event.getCaptureFormat();
+ AudioFormat originalFormat = event.getCaptureFormat();
- sEventLogger.log(new SoundTriggerLogger.StringEvent("createAudioRecordForEvent"));
+ sEventLogger.enqueue(new EventLogger.StringEvent("createAudioRecordForEvent"));
return (new AudioRecord.Builder())
.setAudioAttributes(attributes)
@@ -1301,7 +1302,7 @@
public void onGenericSoundTriggerDetected(SoundTrigger.GenericRecognitionEvent event) {
if (DEBUG) Slog.v(TAG, mPuuid + ": Generic sound trigger event: " + event);
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ sEventLogger.enqueue(new EventLogger.StringEvent(mPuuid
+ ": Generic sound trigger event: " + event));
runOrAddOperation(new Operation(
@@ -1336,7 +1337,7 @@
public void onError(int status) {
if (DEBUG) Slog.v(TAG, mPuuid + ": onError: " + status);
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ sEventLogger.enqueue(new EventLogger.StringEvent(mPuuid
+ ": onError: " + status));
runOrAddOperation(
@@ -1359,7 +1360,7 @@
public void onRecognitionPaused() {
Slog.i(TAG, mPuuid + "->" + mServiceName + ": IGNORED onRecognitionPaused");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ sEventLogger.enqueue(new EventLogger.StringEvent(mPuuid
+ "->" + mServiceName + ": IGNORED onRecognitionPaused"));
}
@@ -1368,7 +1369,7 @@
public void onRecognitionResumed() {
Slog.i(TAG, mPuuid + "->" + mServiceName + ": IGNORED onRecognitionResumed");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ sEventLogger.enqueue(new EventLogger.StringEvent(mPuuid
+ "->" + mServiceName + ": IGNORED onRecognitionResumed"));
}
@@ -1377,7 +1378,7 @@
public void onServiceConnected(ComponentName name, IBinder service) {
if (DEBUG) Slog.v(TAG, mPuuid + ": onServiceConnected(" + service + ")");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ sEventLogger.enqueue(new EventLogger.StringEvent(mPuuid
+ ": onServiceConnected(" + service + ")"));
synchronized (mRemoteServiceLock) {
@@ -1400,7 +1401,7 @@
public void onServiceDisconnected(ComponentName name) {
if (DEBUG) Slog.v(TAG, mPuuid + ": onServiceDisconnected");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ sEventLogger.enqueue(new EventLogger.StringEvent(mPuuid
+ ": onServiceDisconnected"));
synchronized (mRemoteServiceLock) {
@@ -1412,7 +1413,7 @@
public void onBindingDied(ComponentName name) {
if (DEBUG) Slog.v(TAG, mPuuid + ": onBindingDied");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ sEventLogger.enqueue(new EventLogger.StringEvent(mPuuid
+ ": onBindingDied"));
synchronized (mRemoteServiceLock) {
@@ -1424,7 +1425,7 @@
public void onNullBinding(ComponentName name) {
Slog.w(TAG, name + " for model " + mPuuid + " returned a null binding");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(name + " for model "
+ sEventLogger.enqueue(new EventLogger.StringEvent(name + " for model "
+ mPuuid + " returned a null binding"));
synchronized (mRemoteServiceLock) {
@@ -1610,7 +1611,7 @@
private void clientDied() {
Slog.w(TAG, "Client died, cleaning up session.");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(
+ sEventLogger.enqueue(new EventLogger.StringEvent(
"Client died, cleaning up session."));
mSoundTriggerHelper.detach();
}
@@ -1637,7 +1638,7 @@
//=================================================================
// For logging
- private static final SoundTriggerLogger sEventLogger = new SoundTriggerLogger(200,
+ private static final EventLogger sEventLogger = new EventLogger(200,
"SoundTrigger activity");
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamManager.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamManager.java
new file mode 100644
index 0000000..f921118
--- /dev/null
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamManager.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.voiceinteraction;
+
+import static android.app.AppOpsManager.MODE_ALLOWED;
+
+import static com.android.server.voiceinteraction.HotwordDetectionConnection.DEBUG;
+
+import android.annotation.NonNull;
+import android.app.AppOpsManager;
+import android.media.permission.Identity;
+import android.os.ParcelFileDescriptor;
+import android.service.voice.HotwordAudioStream;
+import android.service.voice.HotwordDetectedResult;
+import android.util.Pair;
+import android.util.Slog;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+final class HotwordAudioStreamManager {
+
+ private static final String TAG = "HotwordAudioStreamManager";
+ private static final String OP_MESSAGE = "Streaming hotword audio to VoiceInteractionService";
+ private static final String TASK_ID_PREFIX = "HotwordDetectedResult@";
+ private static final String THREAD_NAME_PREFIX = "Copy-";
+
+ private final AppOpsManager mAppOpsManager;
+ private final Identity mVoiceInteractorIdentity;
+ private final ExecutorService mExecutorService = Executors.newCachedThreadPool();
+
+ HotwordAudioStreamManager(@NonNull AppOpsManager appOpsManager,
+ @NonNull Identity voiceInteractorIdentity) {
+ mAppOpsManager = appOpsManager;
+ mVoiceInteractorIdentity = voiceInteractorIdentity;
+ }
+
+ /**
+ * Starts copying the audio streams in the given {@link HotwordDetectedResult}.
+ * <p>
+ * The returned {@link HotwordDetectedResult} is identical the one that was passed in, except
+ * that the {@link ParcelFileDescriptor}s within {@link HotwordDetectedResult#getAudioStreams()}
+ * are replaced with descriptors from pipes managed by {@link HotwordAudioStreamManager}. The
+ * returned value should be passed on to the client (i.e., the voice interactor).
+ * </p>
+ *
+ * @throws IOException If there was an error creating the managed pipe.
+ */
+ @NonNull
+ public HotwordDetectedResult startCopyingAudioStreams(@NonNull HotwordDetectedResult result)
+ throws IOException {
+ List<HotwordAudioStream> audioStreams = result.getAudioStreams();
+ if (audioStreams.isEmpty()) {
+ return result;
+ }
+
+ List<HotwordAudioStream> newAudioStreams = new ArrayList<>(audioStreams.size());
+ List<Pair<ParcelFileDescriptor, ParcelFileDescriptor>> sourcesAndSinks = new ArrayList<>(
+ audioStreams.size());
+ for (HotwordAudioStream audioStream : audioStreams) {
+ ParcelFileDescriptor[] clientPipe = ParcelFileDescriptor.createReliablePipe();
+ ParcelFileDescriptor clientAudioSource = clientPipe[0];
+ ParcelFileDescriptor clientAudioSink = clientPipe[1];
+ HotwordAudioStream newAudioStream =
+ audioStream.buildUpon().setAudioStreamParcelFileDescriptor(
+ clientAudioSource).build();
+ newAudioStreams.add(newAudioStream);
+
+ ParcelFileDescriptor serviceAudioSource =
+ audioStream.getAudioStreamParcelFileDescriptor();
+ sourcesAndSinks.add(new Pair<>(serviceAudioSource, clientAudioSink));
+ }
+
+ String resultTaskId = TASK_ID_PREFIX + System.identityHashCode(result);
+ mExecutorService.execute(new HotwordDetectedResultCopyTask(resultTaskId, sourcesAndSinks));
+
+ return result.buildUpon().setAudioStreams(newAudioStreams).build();
+ }
+
+ private class HotwordDetectedResultCopyTask implements Runnable {
+ private final String mResultTaskId;
+ private final List<Pair<ParcelFileDescriptor, ParcelFileDescriptor>> mSourcesAndSinks;
+ private final ExecutorService mExecutorService = Executors.newCachedThreadPool();
+
+ HotwordDetectedResultCopyTask(String resultTaskId,
+ List<Pair<ParcelFileDescriptor, ParcelFileDescriptor>> sourcesAndSinks) {
+ mResultTaskId = resultTaskId;
+ mSourcesAndSinks = sourcesAndSinks;
+ }
+
+ @Override
+ public void run() {
+ Thread.currentThread().setName(THREAD_NAME_PREFIX + mResultTaskId);
+ int size = mSourcesAndSinks.size();
+ List<SingleAudioStreamCopyTask> tasks = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ Pair<ParcelFileDescriptor, ParcelFileDescriptor> sourceAndSink =
+ mSourcesAndSinks.get(i);
+ ParcelFileDescriptor serviceAudioSource = sourceAndSink.first;
+ ParcelFileDescriptor clientAudioSink = sourceAndSink.second;
+ String streamTaskId = mResultTaskId + "@" + i;
+ tasks.add(new SingleAudioStreamCopyTask(streamTaskId, serviceAudioSource,
+ clientAudioSink));
+ }
+
+ if (mAppOpsManager.startOpNoThrow(AppOpsManager.OPSTR_RECORD_AUDIO_HOTWORD,
+ mVoiceInteractorIdentity.uid, mVoiceInteractorIdentity.packageName,
+ mVoiceInteractorIdentity.attributionTag, OP_MESSAGE) == MODE_ALLOWED) {
+ try {
+ // TODO(b/244599891): Set timeout, close after inactivity
+ mExecutorService.invokeAll(tasks);
+ } catch (InterruptedException e) {
+ Slog.e(TAG, mResultTaskId + ": Task was interrupted", e);
+ bestEffortPropagateError(e.getMessage());
+ } finally {
+ mAppOpsManager.finishOp(AppOpsManager.OPSTR_RECORD_AUDIO_HOTWORD,
+ mVoiceInteractorIdentity.uid, mVoiceInteractorIdentity.packageName,
+ mVoiceInteractorIdentity.attributionTag);
+ }
+ } else {
+ bestEffortPropagateError(
+ "Failed to obtain RECORD_AUDIO_HOTWORD permission for "
+ + SoundTriggerSessionPermissionsDecorator.toString(
+ mVoiceInteractorIdentity));
+ }
+ }
+
+ private void bestEffortPropagateError(@NonNull String errorMessage) {
+ try {
+ for (Pair<ParcelFileDescriptor, ParcelFileDescriptor> sourceAndSink :
+ mSourcesAndSinks) {
+ ParcelFileDescriptor serviceAudioSource = sourceAndSink.first;
+ ParcelFileDescriptor clientAudioSink = sourceAndSink.second;
+ serviceAudioSource.closeWithError(errorMessage);
+ clientAudioSink.closeWithError(errorMessage);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, mResultTaskId + ": Failed to propagate error", e);
+ }
+ }
+ }
+
+ private static class SingleAudioStreamCopyTask implements Callable<Void> {
+ // TODO: Make this buffer size customizable from updateState()
+ private static final int COPY_BUFFER_LENGTH = 1_024;
+
+ private final String mStreamTaskId;
+ private final ParcelFileDescriptor mAudioSource;
+ private final ParcelFileDescriptor mAudioSink;
+
+ SingleAudioStreamCopyTask(String streamTaskId, ParcelFileDescriptor audioSource,
+ ParcelFileDescriptor audioSink) {
+ mStreamTaskId = streamTaskId;
+ mAudioSource = audioSource;
+ mAudioSink = audioSink;
+ }
+
+ @Override
+ public Void call() throws Exception {
+ Thread.currentThread().setName(THREAD_NAME_PREFIX + mStreamTaskId);
+
+ // Note: We are intentionally NOT using try-with-resources here. If we did,
+ // the ParcelFileDescriptors will be automatically closed WITHOUT errors before we go
+ // into the IOException-catch block. We want to propagate the error while closing the
+ // PFDs.
+ InputStream fis = null;
+ OutputStream fos = null;
+ try {
+ fis = new ParcelFileDescriptor.AutoCloseInputStream(mAudioSource);
+ fos = new ParcelFileDescriptor.AutoCloseOutputStream(mAudioSink);
+ byte[] buffer = new byte[COPY_BUFFER_LENGTH];
+ while (true) {
+ if (Thread.interrupted()) {
+ Slog.e(TAG,
+ mStreamTaskId + ": SingleAudioStreamCopyTask task was interrupted");
+ break;
+ }
+
+ int bytesRead = fis.read(buffer);
+ if (bytesRead < 0) {
+ Slog.i(TAG, mStreamTaskId + ": Reached end of audio stream");
+ break;
+ }
+ if (bytesRead > 0) {
+ if (DEBUG) {
+ // TODO(b/244599440): Add proper logging
+ Slog.d(TAG, mStreamTaskId + ": Copied " + bytesRead
+ + " bytes from audio stream. First 20 bytes=" + Arrays.toString(
+ Arrays.copyOfRange(buffer, 0, 20)));
+ }
+ fos.write(buffer, 0, bytesRead);
+ }
+ // TODO(b/244599891): Close PFDs after inactivity
+ }
+ } catch (IOException e) {
+ mAudioSource.closeWithError(e.getMessage());
+ mAudioSink.closeWithError(e.getMessage());
+ Slog.e(TAG, mStreamTaskId + ": Failed to copy audio stream", e);
+ } finally {
+ if (fis != null) {
+ fis.close();
+ }
+ if (fos != null) {
+ fos.close();
+ }
+ }
+
+ return null;
+ }
+ }
+
+}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 372fdaf..1fc54d6 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -35,7 +35,12 @@
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_RESTARTED__REASON__AUDIO_SERVICE_DIED;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_RESTARTED__REASON__SCHEDULE;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__APP_REQUEST_UPDATE_STATE;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_ON_ERROR_EXCEPTION;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_ON_PROCESS_RESTARTED_EXCEPTION;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_ON_REJECTED_EXCEPTION;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_ON_STATUS_REPORTED_EXCEPTION;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_UPDATE_STATE_AFTER_TIMEOUT;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__CALL_UPDATE_STATE_EXCEPTION;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__EXTERNAL_SOURCE_DETECTED;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__EXTERNAL_SOURCE_DETECT_SECURITY_EXCEPTION;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__EXTERNAL_SOURCE_REJECTED;
@@ -144,6 +149,7 @@
private static final int HOTWORD_DETECTION_SERVICE_DIED = -1;
private static final int CALLBACK_ONDETECTED_GOT_SECURITY_EXCEPTION = -2;
private static final int CALLBACK_DETECT_TIMEOUT = -3;
+ private static final int CALLBACK_ONDETECTED_STREAM_COPY_ERROR = -4;
// Hotword metrics
private static final int METRICS_INIT_UNKNOWN_TIMEOUT =
@@ -170,11 +176,15 @@
HOTWORD_DETECTOR_EVENTS__EVENT__EXTERNAL_SOURCE_REJECTED;
private static final int METRICS_EXTERNAL_SOURCE_DETECT_SECURITY_EXCEPTION =
HOTWORD_DETECTOR_EVENTS__EVENT__EXTERNAL_SOURCE_DETECT_SECURITY_EXCEPTION;
+ private static final int METRICS_CALLBACK_ON_STATUS_REPORTED_EXCEPTION =
+ HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_ON_STATUS_REPORTED_EXCEPTION;
private final Executor mAudioCopyExecutor = Executors.newCachedThreadPool();
// TODO: This may need to be a Handler(looper)
private final ScheduledExecutorService mScheduledExecutorService =
Executors.newSingleThreadScheduledExecutor();
+ private final AppOpsManager mAppOpsManager;
+ private final HotwordAudioStreamManager mHotwordAudioStreamManager;
@Nullable private final ScheduledFuture<?> mCancellationTaskFuture;
private final AtomicBoolean mUpdateStateAfterStartFinished = new AtomicBoolean(false);
private final IBinder.DeathRecipient mAudioServerDeathRecipient = this::audioServerDied;
@@ -235,6 +245,9 @@
mContext = context;
mVoiceInteractionServiceUid = voiceInteractionServiceUid;
mVoiceInteractorIdentity = voiceInteractorIdentity;
+ mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
+ mHotwordAudioStreamManager = new HotwordAudioStreamManager(mAppOpsManager,
+ mVoiceInteractorIdentity);
mDetectionComponentName = serviceName;
mUser = userId;
mCallback = callback;
@@ -336,11 +349,10 @@
HotwordMetricsLogger.writeServiceInitResultEvent(mDetectorType,
initResultMetricsResult);
} catch (RemoteException e) {
- // TODO: Add a new atom for RemoteException case, the error doesn't very
- // correct here
Slog.w(TAG, "Failed to report initialization status: " + e);
- HotwordMetricsLogger.writeServiceInitResultEvent(mDetectorType,
- METRICS_INIT_CALLBACK_STATE_ERROR);
+ HotwordMetricsLogger.writeDetectorEvent(mDetectorType,
+ METRICS_CALLBACK_ON_STATUS_REPORTED_EXCEPTION,
+ mVoiceInteractionServiceUid);
}
}
};
@@ -352,6 +364,9 @@
} catch (RemoteException e) {
// TODO: (b/181842909) Report an error to voice interactor
Slog.w(TAG, "Failed to updateState for HotwordDetectionService", e);
+ HotwordMetricsLogger.writeDetectorEvent(mDetectorType,
+ HOTWORD_DETECTOR_EVENTS__EVENT__CALL_UPDATE_STATE_EXCEPTION,
+ mVoiceInteractionServiceUid);
}
return future.orTimeout(MAX_UPDATE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
}).whenComplete((res, err) -> {
@@ -366,8 +381,9 @@
METRICS_INIT_UNKNOWN_TIMEOUT);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to report initialization status UNKNOWN", e);
- HotwordMetricsLogger.writeServiceInitResultEvent(mDetectorType,
- METRICS_INIT_CALLBACK_STATE_ERROR);
+ HotwordMetricsLogger.writeDetectorEvent(mDetectorType,
+ METRICS_CALLBACK_ON_STATUS_REPORTED_EXCEPTION,
+ mVoiceInteractionServiceUid);
}
} else if (err != null) {
Slog.w(TAG, "Failed to update state: " + err);
@@ -489,13 +505,19 @@
return;
}
saveProximityMetersToBundle(result);
- mSoftwareCallback.onDetected(result, null, null);
- if (result != null) {
- Slog.i(TAG, "Egressed " + HotwordDetectedResult.getUsageSize(result)
- + " bits from hotword trusted process");
- if (mDebugHotwordLogging) {
- Slog.i(TAG, "Egressed detected result: " + result);
- }
+ HotwordDetectedResult newResult;
+ try {
+ newResult = mHotwordAudioStreamManager.startCopyingAudioStreams(result);
+ } catch (IOException e) {
+ // TODO: Write event
+ mSoftwareCallback.onError();
+ return;
+ }
+ mSoftwareCallback.onDetected(newResult, null, null);
+ Slog.i(TAG, "Egressed " + HotwordDetectedResult.getUsageSize(newResult)
+ + " bits from hotword trusted process");
+ if (mDebugHotwordLogging) {
+ Slog.i(TAG, "Egressed detected result: " + newResult);
}
}
}
@@ -610,6 +632,7 @@
enforcePermissionsForDataDelivery();
enforceExtraKeyphraseIdNotLeaked(result, recognitionEvent);
} catch (SecurityException e) {
+ Slog.i(TAG, "Ignoring #onDetected due to a SecurityException", e);
HotwordMetricsLogger.writeKeyphraseTriggerEvent(
mDetectorType,
METRICS_KEYPHRASE_TRIGGERED_DETECT_SECURITY_EXCEPTION);
@@ -617,13 +640,19 @@
return;
}
saveProximityMetersToBundle(result);
- externalCallback.onKeyphraseDetected(recognitionEvent, result);
- if (result != null) {
- Slog.i(TAG, "Egressed " + HotwordDetectedResult.getUsageSize(result)
- + " bits from hotword trusted process");
- if (mDebugHotwordLogging) {
- Slog.i(TAG, "Egressed detected result: " + result);
- }
+ HotwordDetectedResult newResult;
+ try {
+ newResult = mHotwordAudioStreamManager.startCopyingAudioStreams(result);
+ } catch (IOException e) {
+ // TODO: Write event
+ externalCallback.onError(CALLBACK_ONDETECTED_STREAM_COPY_ERROR);
+ return;
+ }
+ externalCallback.onKeyphraseDetected(recognitionEvent, newResult);
+ Slog.i(TAG, "Egressed " + HotwordDetectedResult.getUsageSize(newResult)
+ + " bits from hotword trusted process");
+ if (mDebugHotwordLogging) {
+ Slog.i(TAG, "Egressed detected result: " + newResult);
}
}
}
@@ -678,6 +707,9 @@
externalCallback.onError(CALLBACK_DETECT_TIMEOUT);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to report onError status: ", e);
+ HotwordMetricsLogger.writeDetectorEvent(mDetectorType,
+ HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_ON_ERROR_EXCEPTION,
+ mVoiceInteractionServiceUid);
}
},
MAX_VALIDATION_TIMEOUT_MILLIS,
@@ -722,6 +754,7 @@
}
private void restartProcessLocked() {
+ // TODO(b/244598068): Check HotwordAudioStreamManager first
Slog.v(TAG, "Restarting hotword detection process");
ServiceConnection oldConnection = mRemoteHotwordDetectionService;
HotwordDetectionServiceIdentity previousIdentity = mIdentity;
@@ -737,6 +770,9 @@
HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECTED_FROM_RESTART);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to call #rejected");
+ HotwordMetricsLogger.writeDetectorEvent(mDetectorType,
+ HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_ON_REJECTED_EXCEPTION,
+ mVoiceInteractionServiceUid);
}
mValidatingDspTrigger = false;
}
@@ -752,6 +788,9 @@
mCallback.onProcessRestarted();
} catch (RemoteException e) {
Slog.w(TAG, "Failed to communicate #onProcessRestarted", e);
+ HotwordMetricsLogger.writeDetectorEvent(mDetectorType,
+ HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_ON_PROCESS_RESTARTED_EXCEPTION,
+ mVoiceInteractionServiceUid);
}
// Restart listening from microphone if the hotword process has been restarted.
@@ -891,6 +930,9 @@
callback.onError();
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to report onError status: " + ex);
+ HotwordMetricsLogger.writeDetectorEvent(mDetectorType,
+ HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_ON_ERROR_EXCEPTION,
+ mVoiceInteractionServiceUid);
}
} finally {
synchronized (mLock) {
@@ -956,16 +998,24 @@
callback.onError();
return;
}
- callback.onDetected(triggerResult, null /* audioFormat */,
+ HotwordDetectedResult newResult;
+ try {
+ newResult =
+ mHotwordAudioStreamManager.startCopyingAudioStreams(
+ triggerResult);
+ } catch (IOException e) {
+ // TODO: Write event
+ callback.onError();
+ return;
+ }
+ callback.onDetected(newResult, null /* audioFormat */,
null /* audioStream */);
- if (triggerResult != null) {
- Slog.i(TAG, "Egressed "
- + HotwordDetectedResult.getUsageSize(triggerResult)
- + " bits from hotword trusted process");
- if (mDebugHotwordLogging) {
- Slog.i(TAG,
- "Egressed detected result: " + triggerResult);
- }
+ Slog.i(TAG, "Egressed "
+ + HotwordDetectedResult.getUsageSize(newResult)
+ + " bits from hotword trusted process");
+ if (mDebugHotwordLogging) {
+ Slog.i(TAG,
+ "Egressed detected result: " + newResult);
}
}
});
@@ -1070,6 +1120,9 @@
mCallback.onError(HOTWORD_DETECTION_SERVICE_DIED);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to report onError status: " + e);
+ HotwordMetricsLogger.writeDetectorEvent(mDetectorType,
+ HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_ON_ERROR_EXCEPTION,
+ mVoiceInteractionServiceUid);
}
}
}
@@ -1224,7 +1277,7 @@
Binder.withCleanCallingIdentity(() -> {
enforcePermissionForPreflight(mContext, mVoiceInteractorIdentity, RECORD_AUDIO);
int hotwordOp = AppOpsManager.strOpToOp(AppOpsManager.OPSTR_RECORD_AUDIO_HOTWORD);
- mContext.getSystemService(AppOpsManager.class).noteOpNoThrow(hotwordOp,
+ mAppOpsManager.noteOpNoThrow(hotwordOp,
mVoiceInteractorIdentity.uid, mVoiceInteractorIdentity.packageName,
mVoiceInteractorIdentity.attributionTag, OP_MESSAGE);
enforcePermissionForDataDelivery(mContext, mVoiceInteractorIdentity,
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 151ff80..7207e373 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -1174,6 +1174,8 @@
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE)
@Override
public void setDisabled(boolean disabled) {
+ super.setDisabled_enforcePermission();
+
synchronized (this) {
if (mTemporarilyDisabled == disabled) {
if (DEBUG) Slog.d(TAG, "setDisabled(): already " + disabled);
@@ -1244,6 +1246,8 @@
public void updateState(
@Nullable PersistableBundle options,
@Nullable SharedMemory sharedMemory) {
+ super.updateState_enforcePermission();
+
synchronized (this) {
enforceIsCurrentVoiceInteractionService();
@@ -1260,6 +1264,8 @@
@Nullable SharedMemory sharedMemory,
IHotwordRecognitionStatusCallback callback,
int detectorType) {
+ super.initAndVerifyDetector_enforcePermission();
+
synchronized (this) {
enforceIsCurrentVoiceInteractionService();
@@ -1711,6 +1717,8 @@
@Nullable String attributionTag,
@Nullable IVoiceInteractionSessionShowCallback showCallback,
@Nullable IBinder activityToken) {
+ super.showSessionForActiveService_enforcePermission();
+
if (DEBUG_USER) Slog.d(TAG, "showSessionForActiveService()");
synchronized (this) {
@@ -1742,6 +1750,8 @@
@Override
public void hideCurrentSession() throws RemoteException {
+ super.hideCurrentSession_enforcePermission();
+
if (mImpl == null) {
return;
}
@@ -1762,6 +1772,8 @@
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE)
@Override
public void launchVoiceAssistFromKeyguard() {
+ super.launchVoiceAssistFromKeyguard_enforcePermission();
+
synchronized (this) {
if (mImpl == null) {
Slog.w(TAG, "launchVoiceAssistFromKeyguard without running voice interaction"
@@ -1780,6 +1792,8 @@
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE)
@Override
public boolean isSessionRunning() {
+ super.isSessionRunning_enforcePermission();
+
synchronized (this) {
return mImpl != null && mImpl.mActiveSession != null;
}
@@ -1788,6 +1802,8 @@
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE)
@Override
public boolean activeServiceSupportsAssist() {
+ super.activeServiceSupportsAssist_enforcePermission();
+
synchronized (this) {
return mImpl != null && mImpl.mInfo != null && mImpl.mInfo.getSupportsAssist();
}
@@ -1796,6 +1812,8 @@
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE)
@Override
public boolean activeServiceSupportsLaunchFromKeyguard() throws RemoteException {
+ super.activeServiceSupportsLaunchFromKeyguard_enforcePermission();
+
synchronized (this) {
return mImpl != null && mImpl.mInfo != null
&& mImpl.mInfo.getSupportsLaunchFromKeyguard();
@@ -1805,6 +1823,8 @@
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE)
@Override
public void onLockscreenShown() {
+ super.onLockscreenShown_enforcePermission();
+
synchronized (this) {
if (mImpl == null) {
return;
@@ -1828,6 +1848,8 @@
@Override
public void registerVoiceInteractionSessionListener(
IVoiceInteractionSessionListener listener) {
+ super.registerVoiceInteractionSessionListener_enforcePermission();
+
synchronized (this) {
mVoiceInteractionSessionListeners.register(listener);
}
@@ -1837,6 +1859,8 @@
@Override
public void getActiveServiceSupportedActions(List<String> voiceActions,
IVoiceActionCheckCallback callback) {
+ super.getActiveServiceSupportedActions_enforcePermission();
+
synchronized (this) {
if (mImpl == null) {
try {
diff --git a/telecomm/OWNERS b/telecomm/OWNERS
index eb0c432..dcaf858 100644
--- a/telecomm/OWNERS
+++ b/telecomm/OWNERS
@@ -4,3 +4,7 @@
tgunn@google.com
xiaotonj@google.com
rgreenwalt@google.com
+chinmayd@google.com
+grantmenke@google.com
+pmadapurmath@google.com
+tjstuart@google.com
\ No newline at end of file
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 7be40b8..936fad5 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -8615,7 +8615,12 @@
*
* Used to trade privacy/security against potentially reduced carrier coverage for some
* carriers.
+ *
+ * @deprecated Future versions of Android will disallow carriers from hiding this toggle
+ * because disabling 2g is a security feature that users should always have access to at
+ * their discretion.
*/
+ @Deprecated
public static final String KEY_HIDE_ENABLE_2G = "hide_enable_2g_bool";
/**
@@ -8732,7 +8737,8 @@
* {@link TelephonyManager#purchasePremiumCapability(int, Executor, Consumer)}
* returns a failure due to user action or timeout.
* The maximum number of network boost notifications to show the user are defined in
- * {@link #KEY_PREMIUM_CAPABILITY_MAXIMUM_NOTIFICATION_COUNT_INT_ARRAY}.
+ * {@link #KEY_PREMIUM_CAPABILITY_MAXIMUM_DAILY_NOTIFICATION_COUNT_INT} and
+ * {@link #KEY_PREMIUM_CAPABILITY_MAXIMUM_MONTHLY_NOTIFICATION_COUNT_INT}.
*
* The default value is 30 minutes.
*
@@ -8744,20 +8750,32 @@
"premium_capability_notification_backoff_hysteresis_time_millis_long";
/**
- * The maximum number of times that we display the notification for a network boost via premium
- * capabilities when {@link TelephonyManager#purchasePremiumCapability(int, Executor, Consumer)}
+ * The maximum number of times in a day that we display the notification for a network boost
+ * via premium capabilities when
+ * {@link TelephonyManager#purchasePremiumCapability(int, Executor, Consumer)}
* returns a failure due to user action or timeout.
*
- * An int array with 2 values: {max_notifications_per_day, max_notifications_per_month}.
- *
- * The default value is {2, 10}, meaning we display a maximum of 2 network boost notifications
- * per day and 10 notifications per month.
+ * The default value is 2 times.
*
* @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED
* @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT
*/
- public static final String KEY_PREMIUM_CAPABILITY_MAXIMUM_NOTIFICATION_COUNT_INT_ARRAY =
- "premium_capability_maximum_notification_count_int_array";
+ public static final String KEY_PREMIUM_CAPABILITY_MAXIMUM_DAILY_NOTIFICATION_COUNT_INT =
+ "premium_capability_maximum_daily_notification_count_int";
+
+ /**
+ * The maximum number of times in a month that we display the notification for a network boost
+ * via premium capabilities when
+ * {@link TelephonyManager#purchasePremiumCapability(int, Executor, Consumer)}
+ * returns a failure due to user action or timeout.
+ *
+ * The default value is 10 times.
+ *
+ * @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED
+ * @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT
+ */
+ public static final String KEY_PREMIUM_CAPABILITY_MAXIMUM_MONTHLY_NOTIFICATION_COUNT_INT =
+ "premium_capability_maximum_monthly_notification_count_int";
/**
* The amount of time in milliseconds that the purchase request should be throttled when
@@ -8767,7 +8785,7 @@
* The default value is 30 minutes.
*
* @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR
- * @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_CONGESTED
+ * @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED
*/
public static final String
KEY_PREMIUM_CAPABILITY_PURCHASE_CONDITION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG =
@@ -8781,7 +8799,7 @@
* During the setup time, calls to
* {@link TelephonyManager#purchasePremiumCapability(int, Executor, Consumer)} will return
* {@link TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_PENDING_NETWORK_SETUP}.
- * If the network fails set up a slicing configuration for the premium capability within the
+ * If the network fails to set up a slicing configuration for the premium capability within the
* setup time, subsequent purchase requests will be allowed to go through again.
*
* The default value is 5 minutes.
@@ -8808,7 +8826,7 @@
* when connected to {@link TelephonyManager#NETWORK_TYPE_LTE} to purchase and use
* premium capabilities.
* If this is {@code false}, applications can only purchase and use premium capabilities when
- * conencted to {@link TelephonyManager#NETWORK_TYPE_NR}.
+ * connected to {@link TelephonyManager#NETWORK_TYPE_NR}.
*
* This is {@code false} by default.
*/
@@ -9507,8 +9525,8 @@
TimeUnit.MINUTES.toMillis(30));
sDefaults.putLong(KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG,
TimeUnit.MINUTES.toMillis(30));
- sDefaults.putIntArray(KEY_PREMIUM_CAPABILITY_MAXIMUM_NOTIFICATION_COUNT_INT_ARRAY,
- new int[] {2, 10});
+ sDefaults.putInt(KEY_PREMIUM_CAPABILITY_MAXIMUM_DAILY_NOTIFICATION_COUNT_INT, 2);
+ sDefaults.putInt(KEY_PREMIUM_CAPABILITY_MAXIMUM_MONTHLY_NOTIFICATION_COUNT_INT, 10);
sDefaults.putLong(
KEY_PREMIUM_CAPABILITY_PURCHASE_CONDITION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG,
TimeUnit.MINUTES.toMillis(30));
diff --git a/telephony/java/android/telephony/PreciseCallState.java b/telephony/java/android/telephony/PreciseCallState.java
index 98eeacf..d4b912e 100644
--- a/telephony/java/android/telephony/PreciseCallState.java
+++ b/telephony/java/android/telephony/PreciseCallState.java
@@ -66,6 +66,11 @@
public static final int PRECISE_CALL_STATE_DISCONNECTED = 7;
/** Call state: Disconnecting. */
public static final int PRECISE_CALL_STATE_DISCONNECTING = 8;
+ /**
+ * Call state: Incoming in pre-alerting state.
+ * A call will be in this state prior to entering {@link #PRECISE_CALL_STATE_ALERTING}.
+ */
+ public static final int PRECISE_CALL_STATE_INCOMING_SETUP = 9;
private @PreciseCallStates int mRingingCallState = PRECISE_CALL_STATE_NOT_VALID;
private @PreciseCallStates int mForegroundCallState = PRECISE_CALL_STATE_NOT_VALID;
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index bfa60ba..6be2f77 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -138,13 +138,6 @@
*/
public static final int FREQUENCY_RANGE_MMWAVE = 4;
- private static final List<Integer> FREQUENCY_RANGE_ORDER = Arrays.asList(
- FREQUENCY_RANGE_UNKNOWN,
- FREQUENCY_RANGE_LOW,
- FREQUENCY_RANGE_MID,
- FREQUENCY_RANGE_HIGH,
- FREQUENCY_RANGE_MMWAVE);
-
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = "DUPLEX_MODE_",
@@ -2108,15 +2101,6 @@
}
/**
- * @hide
- */
- public static final int getBetterNRFrequencyRange(int range1, int range2) {
- return FREQUENCY_RANGE_ORDER.indexOf(range1) > FREQUENCY_RANGE_ORDER.indexOf(range2)
- ? range1
- : range2;
- }
-
- /**
* Returns a copy of self with location-identifying information removed.
* Always clears the NetworkRegistrationInfo's CellIdentity fields, but if removeCoarseLocation
* is true, clears other info as well.
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 35b2055..e099e69 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2975,7 +2975,11 @@
public static final int NETWORK_TYPE_HSUPA = TelephonyProtoEnums.NETWORK_TYPE_HSUPA; // = 9.
/** Current network is HSPA */
public static final int NETWORK_TYPE_HSPA = TelephonyProtoEnums.NETWORK_TYPE_HSPA; // = 10.
- /** Current network is iDen */
+ /**
+ * Current network is iDen
+ * @deprecated Legacy network type no longer being used.
+ */
+ @Deprecated
public static final int NETWORK_TYPE_IDEN = TelephonyProtoEnums.NETWORK_TYPE_IDEN; // = 11.
/** Current network is EVDO revision B*/
public static final int NETWORK_TYPE_EVDO_B = TelephonyProtoEnums.NETWORK_TYPE_EVDO_B; // = 12.
@@ -13983,6 +13987,7 @@
public static final long NETWORK_TYPE_BITMASK_HSPA = (1 << (NETWORK_TYPE_HSPA -1));
/**
* network type bitmask indicating the support of radio tech iDen.
+ * @hide
*/
public static final long NETWORK_TYPE_BITMASK_IDEN = (1 << (NETWORK_TYPE_IDEN - 1));
/**
@@ -17226,12 +17231,13 @@
* #KEY_PREMIUM_CAPABILITY_PURCHASE_CONDITION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG}.
* If displaying the network boost notification is throttled, it will be for the amount of time
* specified by {@link CarrierConfigManager
- * #KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_INT_ARRAY}.
+ * #KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG}.
* If a foreground application requests premium capabilities, the network boost notification
* will be displayed to the user regardless of the throttled status.
* We will show the network boost notification to the user up to the daily and monthly maximum
- * number of times specified by {@link CarrierConfigManager
- * #KEY_PREMIUM_CAPABILITY_MAXIMUM_NOTIFICATION_COUNT_INT_ARRAY}.
+ * number of times specified by
+ * {@link CarrierConfigManager#KEY_PREMIUM_CAPABILITY_MAXIMUM_DAILY_NOTIFICATION_COUNT_INT} and
+ * {@link CarrierConfigManager#KEY_PREMIUM_CAPABILITY_MAXIMUM_MONTHLY_NOTIFICATION_COUNT_INT}.
* Subsequent attempts will return the same error until the request is no longer throttled
* or throttling conditions change.
*/
@@ -17318,14 +17324,14 @@
public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE = 12;
/**
- * Purchase premium capability failed because the network is congested.
+ * Purchase premium capability failed because the entitlement check failed.
* Subsequent attempts will be throttled for the amount of time specified by
* {@link CarrierConfigManager
* #KEY_PREMIUM_CAPABILITY_PURCHASE_CONDITION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG}
* and return {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED}.
* Throttling will be reevaluated when the network is no longer congested.
*/
- public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_CONGESTED = 13;
+ public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED = 13;
/**
* Purchase premium capability failed because the request was not made on the default data
@@ -17333,7 +17339,7 @@
* Subsequent attempts will return the same error until the request is made on the default
* data subscription.
*/
- public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA = 14;
+ public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUB = 14;
/**
* Purchase premium capability was successful and is waiting for the network to setup the
@@ -17341,7 +17347,7 @@
* {@link CarrierConfigManager#KEY_PREMIUM_CAPABILITY_NETWORK_SETUP_TIME_MILLIS_LONG},
* subsequent requests will return {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED}
* until the purchase expires. If the setup is not complete within the time specified above,
- * applications can reques the premium capability again.
+ * applications can request the premium capability again.
*/
public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_PENDING_NETWORK_SETUP = 15;
@@ -17362,8 +17368,8 @@
PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT,
PURCHASE_PREMIUM_CAPABILITY_RESULT_FEATURE_NOT_SUPPORTED,
PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE,
- PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_CONGESTED,
- PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA,
+ PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED,
+ PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUB,
PURCHASE_PREMIUM_CAPABILITY_RESULT_PENDING_NETWORK_SETUP})
public @interface PurchasePremiumCapabilityResult {}
@@ -17401,10 +17407,10 @@
return "REQUEST_FAILED";
case PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE:
return "NETWORK_NOT_AVAILABLE";
- case PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_CONGESTED:
- return "NETWORK_CONGESTED";
- case PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA:
- return "NOT_DEFAULT_DATA";
+ case PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED:
+ return "ENTITLEMENT_CHECK_FAILED";
+ case PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUB:
+ return "NOT_DEFAULT_DATA_SUB";
case PURCHASE_PREMIUM_CAPABILITY_RESULT_PENDING_NETWORK_SETUP:
return "PENDING_NETWORK_SETUP";
default:
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index 73aff43..a834e2bb 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -468,14 +468,14 @@
final boolean isQosBearerSessionsSame =
(mQosBearerSessions == null || other.mQosBearerSessions == null)
? mQosBearerSessions == other.mQosBearerSessions
- : mQosBearerSessions.size() == other.mQosBearerSessions.size()
- && mQosBearerSessions.containsAll(other.mQosBearerSessions);
+ : (mQosBearerSessions.size() == other.mQosBearerSessions.size()
+ && mQosBearerSessions.containsAll(other.mQosBearerSessions));
final boolean isTrafficDescriptorsSame =
(mTrafficDescriptors == null || other.mTrafficDescriptors == null)
? mTrafficDescriptors == other.mTrafficDescriptors
- : mTrafficDescriptors.size() == other.mTrafficDescriptors.size()
- && mTrafficDescriptors.containsAll(other.mTrafficDescriptors);
+ : (mTrafficDescriptors.size() == other.mTrafficDescriptors.size()
+ && mTrafficDescriptors.containsAll(other.mTrafficDescriptors));
return mCause == other.mCause
&& mSuggestedRetryTime == other.mSuggestedRetryTime
@@ -504,10 +504,35 @@
@Override
public int hashCode() {
+ // Generate order-independent hashes for lists
+ int addressesHash = mAddresses.stream()
+ .map(LinkAddress::hashCode)
+ .mapToInt(Integer::intValue)
+ .sum();
+ int dnsAddressesHash = mDnsAddresses.stream()
+ .map(InetAddress::hashCode)
+ .mapToInt(Integer::intValue)
+ .sum();
+ int gatewayAddressesHash = mGatewayAddresses.stream()
+ .map(InetAddress::hashCode)
+ .mapToInt(Integer::intValue)
+ .sum();
+ int pcscfAddressesHash = mPcscfAddresses.stream()
+ .map(InetAddress::hashCode)
+ .mapToInt(Integer::intValue)
+ .sum();
+ int qosBearerSessionsHash = mQosBearerSessions.stream()
+ .map(QosBearerSession::hashCode)
+ .mapToInt(Integer::intValue)
+ .sum();
+ int trafficDescriptorsHash = mTrafficDescriptors.stream()
+ .map(TrafficDescriptor::hashCode)
+ .mapToInt(Integer::intValue)
+ .sum();
return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType,
- mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses,
- mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, mDefaultQos,
- mQosBearerSessions, mSliceInfo, mTrafficDescriptors);
+ mInterfaceName, addressesHash, dnsAddressesHash, gatewayAddressesHash,
+ pcscfAddressesHash, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId,
+ mDefaultQos, qosBearerSessionsHash, mSliceInfo, trafficDescriptorsHash);
}
@Override
@@ -816,8 +841,8 @@
/**
* Set pdu session id.
* <p/>
- * The id must be between 1 and 15 when linked to a pdu session. If no pdu session
- * exists for the current data call, the id must be set to {@link PDU_SESSION_ID_NOT_SET}.
+ * The id must be between 1 and 15 when linked to a pdu session. If no pdu session
+ * exists for the current data call, the id must be set to {@link #PDU_SESSION_ID_NOT_SET}.
*
* @param pduSessionId Pdu Session Id of the data call.
* @return The same instance of the builder.
@@ -858,6 +883,7 @@
*/
public @NonNull Builder setQosBearerSessions(
@NonNull List<QosBearerSession> qosBearerSessions) {
+ Objects.requireNonNull(qosBearerSessions);
mQosBearerSessions = qosBearerSessions;
return this;
}
@@ -891,6 +917,7 @@
*/
public @NonNull Builder setTrafficDescriptors(
@NonNull List<TrafficDescriptor> trafficDescriptors) {
+ Objects.requireNonNull(trafficDescriptors);
mTrafficDescriptors = trafficDescriptors;
return this;
}
diff --git a/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl b/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl
index ba2b62d..8e27077 100644
--- a/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl
+++ b/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl
@@ -27,4 +27,5 @@
oneway void createNetworkAvailabilityProvider(int slotId, IQualifiedNetworksServiceCallback callback);
oneway void removeNetworkAvailabilityProvider(int slotId);
oneway void reportThrottleStatusChanged(int slotId, in List<ThrottleStatus> statuses);
+ oneway void reportEmergencyDataNetworkPreferredTransportChanged (int slotId, int transportType);
}
diff --git a/telephony/java/android/telephony/data/QualifiedNetworksService.java b/telephony/java/android/telephony/data/QualifiedNetworksService.java
index fb97336..56f0f9f 100644
--- a/telephony/java/android/telephony/data/QualifiedNetworksService.java
+++ b/telephony/java/android/telephony/data/QualifiedNetworksService.java
@@ -68,6 +68,7 @@
private static final int QNS_REMOVE_ALL_NETWORK_AVAILABILITY_PROVIDERS = 3;
private static final int QNS_UPDATE_QUALIFIED_NETWORKS = 4;
private static final int QNS_APN_THROTTLE_STATUS_CHANGED = 5;
+ private static final int QNS_EMERGENCY_DATA_NETWORK_PREFERRED_TRANSPORT_CHANGED = 6;
private final HandlerThread mHandlerThread;
@@ -193,6 +194,20 @@
}
/**
+ * The framework calls this method when the preferred transport type used to set up
+ * emergency data network is changed.
+ *
+ * This method is meant to be overridden.
+ *
+ * @param transportType transport type changed to be preferred
+ */
+ public void reportEmergencyDataNetworkPreferredTransportChanged(
+ @AccessNetworkConstants.TransportType int transportType) {
+ Log.d(TAG, "reportEmergencyDataNetworkPreferredTransportChanged: "
+ + AccessNetworkConstants.transportTypeToString(transportType));
+ }
+
+ /**
* Called when the qualified networks provider is removed. The extended class should
* implement this method to perform cleanup works.
*/
@@ -237,6 +252,13 @@
}
break;
+ case QNS_EMERGENCY_DATA_NETWORK_PREFERRED_TRANSPORT_CHANGED:
+ if (provider != null) {
+ int transportType = (int) message.arg2;
+ provider.reportEmergencyDataNetworkPreferredTransportChanged(transportType);
+ }
+ break;
+
case QNS_REMOVE_NETWORK_AVAILABILITY_PROVIDER:
if (provider != null) {
provider.close();
@@ -332,6 +354,14 @@
mHandler.obtainMessage(QNS_APN_THROTTLE_STATUS_CHANGED, slotIndex, 0, statuses)
.sendToTarget();
}
+
+ @Override
+ public void reportEmergencyDataNetworkPreferredTransportChanged(int slotIndex,
+ @AccessNetworkConstants.TransportType int transportType) {
+ mHandler.obtainMessage(
+ QNS_EMERGENCY_DATA_NETWORK_PREFERRED_TRANSPORT_CHANGED,
+ slotIndex, transportType).sendToTarget();
+ }
}
private void log(String s) {
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java
index d9d5c14..e78a1e1 100644
--- a/telephony/java/android/telephony/emergency/EmergencyNumber.java
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java
@@ -660,9 +660,6 @@
if (!first.getEmergencyUrns().equals(second.getEmergencyUrns())) {
return false;
}
- if (first.getEmergencyCallRouting() != second.getEmergencyCallRouting()) {
- return false;
- }
// Never merge two numbers if one of them is from test mode but the other one is not;
// This supports to remove a number from the test mode.
if (first.isFromSources(EMERGENCY_NUMBER_SOURCE_TEST)
@@ -685,12 +682,18 @@
public static EmergencyNumber mergeSameEmergencyNumbers(@NonNull EmergencyNumber first,
@NonNull EmergencyNumber second) {
if (areSameEmergencyNumbers(first, second)) {
+ int routing = first.getEmergencyCallRouting();
+
+ if (second.isFromSources(EMERGENCY_NUMBER_SOURCE_DATABASE)) {
+ routing = second.getEmergencyCallRouting();
+ }
+
return new EmergencyNumber(first.getNumber(), first.getCountryIso(), first.getMnc(),
first.getEmergencyServiceCategoryBitmask(),
first.getEmergencyUrns(),
first.getEmergencyNumberSourceBitmask()
| second.getEmergencyNumberSourceBitmask(),
- first.getEmergencyCallRouting());
+ routing);
}
return null;
}
diff --git a/telephony/java/android/telephony/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java
index 0a2bb3d..e61d1e6 100644
--- a/telephony/java/android/telephony/euicc/EuiccCardManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java
@@ -118,7 +118,10 @@
/** Resets the default SM-DP+ address. */
public static final int RESET_OPTION_RESET_DEFAULT_SMDP_ADDRESS = 1 << 2;
- /** Result code when the requested profile is not found */
+ /** Result code when the requested profile is not found
+ * @deprecated use {@link #RESULT_PROFILE_DOES_NOT_EXIST}
+ **/
+ @Deprecated
public static final int RESULT_PROFILE_NOT_FOUND = 1;
/** Result code of execution with no error. */
@@ -133,6 +136,9 @@
/** Result code indicating the caller is not the active LPA. */
public static final int RESULT_CALLER_NOT_ALLOWED = -3;
+ /** Result code when the requested profile does not exist */
+ public static final int RESULT_PROFILE_DOES_NOT_EXIST = -4;
+
/**
* Callback to receive the result of an eUICC card API.
*
@@ -224,7 +230,7 @@
/**
* Requests the enabled profile for a given port on an eUicc. Callback with result code
- * {@link RESULT_PROFILE_NOT_FOUND} and {@code NULL} EuiccProfile if there is no enabled
+ * {@link RESULT_PROFILE_DOES_NOT_EXIST} and {@code NULL} EuiccProfile if there is no enabled
* profile on the target port.
*
* @param cardId The Id of the eUICC.
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryEntryProvider.kt b/telephony/java/android/telephony/ims/SrvccCall.aidl
similarity index 72%
rename from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryEntryProvider.kt
rename to telephony/java/android/telephony/ims/SrvccCall.aidl
index 817c209f..0f0a079 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryEntryProvider.kt
+++ b/telephony/java/android/telephony/ims/SrvccCall.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (c) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.gallery
+package android.telephony.ims;
-import com.android.settingslib.spa.framework.EntryProvider
-
-class GalleryEntryProvider : EntryProvider()
+parcelable SrvccCall;
diff --git a/telephony/java/android/telephony/ims/SrvccCall.java b/telephony/java/android/telephony/ims/SrvccCall.java
new file mode 100644
index 0000000..cdc271e
--- /dev/null
+++ b/telephony/java/android/telephony/ims/SrvccCall.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Annotation.PreciseCallStates;
+
+import java.util.Objects;
+
+/**
+ * A Parcelable object to represent the current state of an IMS call that is being tracked
+ * in the ImsService when an SRVCC begins. This information will be delivered to modem.
+ * @see SrvccStartedCallback
+ *
+ * @hide
+ */
+@SystemApi
+public final class SrvccCall implements Parcelable {
+ private static final String TAG = "SrvccCall";
+
+ /** The IMS call profile */
+ private ImsCallProfile mImsCallProfile;
+
+ /** The IMS call id */
+ private String mCallId;
+
+ /** The call state */
+ private @PreciseCallStates int mCallState;
+
+ private SrvccCall(Parcel in) {
+ readFromParcel(in);
+ }
+
+ /**
+ * Constructs an instance of SrvccCall.
+ *
+ * @param callId the call ID associated with the IMS call
+ * @param callState the state of this IMS call
+ * @param imsCallProfile the profile associated with this IMS call
+ * @throws IllegalArgumentException if the callId or the imsCallProfile is null
+ */
+ public SrvccCall(@NonNull String callId, @PreciseCallStates int callState,
+ @NonNull ImsCallProfile imsCallProfile) {
+ if (callId == null) throw new IllegalArgumentException("callId is null");
+ if (imsCallProfile == null) throw new IllegalArgumentException("imsCallProfile is null");
+
+ mCallId = callId;
+ mCallState = callState;
+ mImsCallProfile = imsCallProfile;
+ }
+
+ /**
+ * @return the {@link ImsCallProfile} associated with this IMS call,
+ * which will be used to get the address, the name, and the audio direction
+ * including the call in pre-alerting state.
+ */
+ @NonNull
+ public ImsCallProfile getImsCallProfile() {
+ return mImsCallProfile;
+ }
+
+ /**
+ * @return the call ID associated with this IMS call.
+ *
+ * @see android.telephony.ims.stub.ImsCallSessionImplBase#getCallId().
+ */
+ @NonNull
+ public String getCallId() {
+ return mCallId;
+ }
+
+ /**
+ * @return the call state of the associated IMS call.
+ */
+ public @PreciseCallStates int getPreciseCallState() {
+ return mCallState;
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "{ callId=" + mCallId
+ + ", callState=" + mCallState
+ + ", imsCallProfile=" + mImsCallProfile
+ + " }";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SrvccCall that = (SrvccCall) o;
+ return mImsCallProfile.equals(that.mImsCallProfile)
+ && mCallId.equals(that.mCallId)
+ && mCallState == that.mCallState;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hash(mImsCallProfile, mCallId);
+ result = 31 * result + mCallState;
+ return result;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeString(mCallId);
+ out.writeInt(mCallState);
+ out.writeParcelable(mImsCallProfile, 0);
+ }
+
+ private void readFromParcel(Parcel in) {
+ mCallId = in.readString();
+ mCallState = in.readInt();
+ mImsCallProfile = in.readParcelable(ImsCallProfile.class.getClassLoader(),
+ android.telephony.ims.ImsCallProfile.class);
+ }
+
+ public static final @android.annotation.NonNull Creator<SrvccCall> CREATOR =
+ new Creator<SrvccCall>() {
+ @Override
+ public SrvccCall createFromParcel(Parcel in) {
+ return new SrvccCall(in);
+ }
+
+ @Override
+ public SrvccCall[] newArray(int size) {
+ return new SrvccCall[size];
+ }
+ };
+}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl b/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl
index 801b81c..8519173 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl
@@ -20,6 +20,7 @@
import android.telephony.ims.aidl.IImsMmTelListener;
import android.telephony.ims.aidl.IImsSmsListener;
import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.aidl.ISrvccStartedCallback;
import android.telephony.ims.feature.CapabilityChangeRequest;
import android.telephony.ims.RtpHeaderExtensionType;
@@ -55,6 +56,10 @@
IImsCapabilityCallback c);
oneway void queryCapabilityConfiguration(int capability, int radioTech,
IImsCapabilityCallback c);
+ oneway void notifySrvccStarted(in ISrvccStartedCallback cb);
+ oneway void notifySrvccCompleted();
+ oneway void notifySrvccFailed();
+ oneway void notifySrvccCanceled();
// SMS APIs
void setSmsListener(IImsSmsListener l);
oneway void sendSms(in int token, int messageRef, String format, String smsc, boolean retry,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/IFloatingTasks.aidl b/telephony/java/android/telephony/ims/aidl/ISrvccStartedCallback.aidl
similarity index 67%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/floating/IFloatingTasks.aidl
rename to telephony/java/android/telephony/ims/aidl/ISrvccStartedCallback.aidl
index f79ca10..a173abf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/IFloatingTasks.aidl
+++ b/telephony/java/android/telephony/ims/aidl/ISrvccStartedCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (c) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.floating;
+package android.telephony.ims.aidl;
-import android.content.Intent;
+import android.telephony.ims.SrvccCall;
+
+import java.util.List;
/**
- * Interface that is exposed to remote callers to manipulate floating task features.
+ * {@hide}
*/
-interface IFloatingTasks {
-
- void showTask(in Intent intent) = 1;
-
+oneway interface ISrvccStartedCallback {
+ void onSrvccCallNotified(in List<SrvccCall> profiles);
}
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index c0ff12e..8147759 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -31,10 +31,12 @@
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ImsService;
import android.telephony.ims.RtpHeaderExtensionType;
+import android.telephony.ims.SrvccCall;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsMmTelFeature;
import android.telephony.ims.aidl.IImsMmTelListener;
import android.telephony.ims.aidl.IImsSmsListener;
+import android.telephony.ims.aidl.ISrvccStartedCallback;
import android.telephony.ims.stub.ImsCallSessionImplBase;
import android.telephony.ims.stub.ImsEcbmImplBase;
import android.telephony.ims.stub.ImsMultiEndpointImplBase;
@@ -60,6 +62,7 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
import java.util.function.Supplier;
/**
@@ -289,6 +292,38 @@
"onSmsReady");
}
+ @Override
+ public void notifySrvccStarted(final ISrvccStartedCallback cb) {
+ executeMethodAsyncNoException(
+ () -> MmTelFeature.this.notifySrvccStarted(
+ (profiles) -> {
+ try {
+ cb.onSrvccCallNotified(profiles);
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "onSrvccCallNotified e=" + e);
+ }
+ }),
+ "notifySrvccStarted");
+ }
+
+ @Override
+ public void notifySrvccCompleted() {
+ executeMethodAsyncNoException(
+ () -> MmTelFeature.this.notifySrvccCompleted(), "notifySrvccCompleted");
+ }
+
+ @Override
+ public void notifySrvccFailed() {
+ executeMethodAsyncNoException(
+ () -> MmTelFeature.this.notifySrvccFailed(), "notifySrvccFailed");
+ }
+
+ @Override
+ public void notifySrvccCanceled() {
+ executeMethodAsyncNoException(
+ () -> MmTelFeature.this.notifySrvccCanceled(), "notifySrvccCanceled");
+ }
+
// Call the methods with a clean calling identity on the executor and wait indefinitely for
// the future to return.
private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException {
@@ -969,6 +1004,75 @@
"Not implemented on device.");
}
+ /**
+ * Notifies the MmTelFeature that the network has initiated an SRVCC (Single radio voice
+ * call continuity) for all IMS calls. When the network initiates an SRVCC, calls from
+ * the LTE domain are handed over to the legacy circuit switched domain. The modem requires
+ * knowledge of ongoing calls in the IMS domain in order to complete the SRVCC operation.
+ * <p>
+ * @param consumer The callback used to notify the framework of the list of IMS calls and their
+ * state at the time of the SRVCC.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void notifySrvccStarted(@NonNull Consumer<List<SrvccCall>> consumer) {
+ // Base Implementation - Should be overridden by IMS service
+ }
+
+ /**
+ * Notifies the MmTelFeature that the SRVCC is completed and the calls have been moved
+ * over to the circuit-switched domain.
+ * {@link android.telephony.CarrierConfigManager.ImsVoice#KEY_SRVCC_TYPE_INT_ARRAY}
+ * specifies the calls can be moved. Other calls will be disconnected.
+ * <p>
+ * The MmTelFeature may now release all resources related to the IMS calls.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void notifySrvccCompleted() {
+ // Base Implementation - Should be overridden by IMS service
+ }
+
+ /**
+ * Notifies the MmTelFeature that the SRVCC has failed.
+ *
+ * The handover can fail by encountering a failure at the radio level
+ * or temporary MSC server internal errors in handover procedure.
+ * Refer to 3GPP TS 23.216 section 8 Handover Failure.
+ * <p>
+ * IMS service will recover and continue calls over IMS.
+ * Per TS 24.237 12.2.4.2, UE shall send SIP UPDATE request containing the reason-text
+ * set to "failure to transition to CS domain".
+ *
+ * @hide
+ */
+ @SystemApi
+ public void notifySrvccFailed() {
+ // Base Implementation - Should be overridden by IMS service
+ }
+
+ /**
+ * Notifies the MmTelFeature that the SRVCC has been canceled.
+ *
+ * Since the state of network can be changed, the network can decide to terminate
+ * the handover procedure before its completion and to return to its state before the handover
+ * procedure was triggered.
+ * Refer to 3GPP TS 23.216 section 8.1.3 Handover Cancellation.
+ *
+ * <p>
+ * IMS service will recover and continue calls over IMS.
+ * Per TS 24.237 12.2.4.2, UE shall send SIP UPDATE request containing the reason-text
+ * set to "handover canceled".
+ *
+ * @hide
+ */
+ @SystemApi
+ public void notifySrvccCanceled() {
+ // Base Implementation - Should be overridden by IMS service
+ }
+
private void setSmsListener(IImsSmsListener listener) {
getSmsImplementation().registerSmsListener(listener);
}
diff --git a/tests/JobSchedulerPerfTests/src/com/android/frameworks/perftests/job/JobStorePerfTests.java b/tests/JobSchedulerPerfTests/src/com/android/frameworks/perftests/job/JobStorePerfTests.java
index dd9b294..afaeca1 100644
--- a/tests/JobSchedulerPerfTests/src/com/android/frameworks/perftests/job/JobStorePerfTests.java
+++ b/tests/JobSchedulerPerfTests/src/com/android/frameworks/perftests/job/JobStorePerfTests.java
@@ -15,7 +15,6 @@
*/
package com.android.frameworks.perftests.job;
-
import android.app.job.JobInfo;
import android.content.ComponentName;
import android.content.Context;
@@ -46,7 +45,8 @@
public class JobStorePerfTests {
private static final String SOURCE_PACKAGE = "com.android.frameworks.perftests.job";
private static final int SOURCE_USER_ID = 0;
- private static final int CALLING_UID = 10079;
+ private static final int BASE_CALLING_UID = 10079;
+ private static final int MAX_UID_COUNT = 10;
private static Context sContext;
private static File sTestDir;
@@ -65,10 +65,10 @@
sJobStore = JobStore.initAndGetForTesting(sContext, sTestDir);
for (int i = 0; i < 50; i++) {
- sFewJobs.add(createJobStatus("fewJobs", i));
+ sFewJobs.add(createJobStatus("fewJobs", i, BASE_CALLING_UID + (i % MAX_UID_COUNT)));
}
for (int i = 0; i < 500; i++) {
- sManyJobs.add(createJobStatus("manyJobs", i));
+ sManyJobs.add(createJobStatus("manyJobs", i, BASE_CALLING_UID + (i % MAX_UID_COUNT)));
}
}
@@ -104,6 +104,64 @@
runPersistedJobWriting(sManyJobs);
}
+ private void runPersistedJobWriting_delta(List<JobStatus> jobList,
+ List<JobStatus> jobAdditions, List<JobStatus> jobRemovals) {
+ final ManualBenchmarkState benchmarkState = mPerfManualStatusReporter.getBenchmarkState();
+
+ long elapsedTimeNs = 0;
+ while (benchmarkState.keepRunning(elapsedTimeNs)) {
+ sJobStore.clearForTesting();
+ for (JobStatus job : jobList) {
+ sJobStore.addForTesting(job);
+ }
+ sJobStore.writeStatusToDiskForTesting();
+
+ for (JobStatus job : jobAdditions) {
+ sJobStore.addForTesting(job);
+ }
+ for (JobStatus job : jobRemovals) {
+ sJobStore.removeForTesting(job);
+ }
+
+ final long startTime = SystemClock.elapsedRealtimeNanos();
+ sJobStore.writeStatusToDiskForTesting();
+ final long endTime = SystemClock.elapsedRealtimeNanos();
+ elapsedTimeNs = endTime - startTime;
+ }
+ }
+
+ @Test
+ public void testPersistedJobWriting_delta_fewJobs() {
+ List<JobStatus> additions = new ArrayList<>();
+ List<JobStatus> removals = new ArrayList<>();
+ final int numModifiedUids = MAX_UID_COUNT / 2;
+ for (int i = 0; i < sFewJobs.size() / 3; ++i) {
+ JobStatus job = createJobStatus("fewJobs", i, BASE_CALLING_UID + (i % numModifiedUids));
+ if (i % 2 == 0) {
+ additions.add(job);
+ } else {
+ removals.add(job);
+ }
+ }
+ runPersistedJobWriting_delta(sFewJobs, additions, removals);
+ }
+
+ @Test
+ public void testPersistedJobWriting_delta_manyJobs() {
+ List<JobStatus> additions = new ArrayList<>();
+ List<JobStatus> removals = new ArrayList<>();
+ final int numModifiedUids = MAX_UID_COUNT / 2;
+ for (int i = 0; i < sManyJobs.size() / 3; ++i) {
+ JobStatus job = createJobStatus("fewJobs", i, BASE_CALLING_UID + (i % numModifiedUids));
+ if (i % 2 == 0) {
+ additions.add(job);
+ } else {
+ removals.add(job);
+ }
+ }
+ runPersistedJobWriting_delta(sManyJobs, additions, removals);
+ }
+
private void runPersistedJobReading(List<JobStatus> jobList, boolean rtcIsGood) {
final ManualBenchmarkState benchmarkState = mPerfManualStatusReporter.getBenchmarkState();
@@ -144,12 +202,12 @@
runPersistedJobReading(sManyJobs, false);
}
- private static JobStatus createJobStatus(String testTag, int jobId) {
+ private static JobStatus createJobStatus(String testTag, int jobId, int callingUid) {
JobInfo jobInfo = new JobInfo.Builder(jobId,
new ComponentName(sContext, "JobStorePerfTestJobService"))
.setPersisted(true)
.build();
return JobStatus.createFromJobInfo(
- jobInfo, CALLING_UID, SOURCE_PACKAGE, SOURCE_USER_ID, testTag);
+ jobInfo, callingUid, SOURCE_PACKAGE, SOURCE_USER_ID, testTag);
}
}
diff --git a/tests/TouchLatency/Android.bp b/tests/TouchLatency/Android.bp
index 3a9e240..4ef1ead 100644
--- a/tests/TouchLatency/Android.bp
+++ b/tests/TouchLatency/Android.bp
@@ -12,6 +12,7 @@
manifest: "app/src/main/AndroidManifest.xml",
// omit gradle 'build' dir
srcs: ["app/src/main/java/**/*.java"],
+ static_libs: ["com.google.android.material_material"],
resource_dirs: ["app/src/main/res"],
aaptflags: ["--auto-add-overlay"],
sdk_version: "current",
diff --git a/tests/TouchLatency/app/build.gradle b/tests/TouchLatency/app/build.gradle
index f5ae6f4..129baab 100644
--- a/tests/TouchLatency/app/build.gradle
+++ b/tests/TouchLatency/app/build.gradle
@@ -6,7 +6,7 @@
defaultConfig {
applicationId "com.prefabulated.touchlatency"
- minSdkVersion 28
+ minSdkVersion 30
targetSdkVersion 33
versionCode 1
versionName "1.0"
@@ -17,4 +17,9 @@
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
+
+ dependencies {
+ implementation 'androidx.appcompat:appcompat:1.5.1'
+ implementation 'com.google.android.material:material:1.6.0'
+ }
}
diff --git a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
index 6ab3b3e..2e93c87 100644
--- a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
+++ b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
@@ -16,7 +16,6 @@
package com.prefabulated.touchlatency;
-import android.app.Activity;
import android.app.ActivityOptions;
import android.content.Intent;
import android.hardware.display.DisplayManager;
@@ -30,25 +29,49 @@
import android.view.Window;
import android.view.WindowManager;
-public class TouchLatencyActivity extends Activity {
- private Mode mDisplayModes[];
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.google.android.material.slider.RangeSlider;
+import com.google.android.material.slider.RangeSlider.OnChangeListener;
+
+public class TouchLatencyActivity extends AppCompatActivity {
+ private static final int REFRESH_RATE_SLIDER_MIN = 20;
+ private static final int REFRESH_RATE_SLIDER_STEP = 5;
+
+ private Menu mMenu;
+ private Mode[] mDisplayModes;
private int mCurrentModeIndex;
+ private float mSliderPreferredRefreshRate;
private DisplayManager mDisplayManager;
+
private final DisplayManager.DisplayListener mDisplayListener =
new DisplayManager.DisplayListener() {
@Override
public void onDisplayAdded(int i) {
- invalidateOptionsMenu();
+ updateOptionsMenu();
}
@Override
public void onDisplayRemoved(int i) {
- invalidateOptionsMenu();
+ updateOptionsMenu();
}
@Override
public void onDisplayChanged(int i) {
- invalidateOptionsMenu();
+ updateOptionsMenu();
+ }
+ };
+
+ private final RangeSlider.OnChangeListener mRefreshRateSliderListener = new OnChangeListener() {
+ @Override
+ public void onValueChange(@NonNull RangeSlider slider, float value, boolean fromUser) {
+ if (value == mSliderPreferredRefreshRate) return;
+
+ mSliderPreferredRefreshRate = value;
+ WindowManager.LayoutParams w = getWindow().getAttributes();
+ w.preferredRefreshRate = mSliderPreferredRefreshRate;
+ getWindow().setAttributes(w);
}
};
@@ -75,17 +98,23 @@
Trace.endSection();
}
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- Trace.beginSection("TouchLatencyActivity onCreateOptionsMenu");
- // Inflate the menu; this adds items to the action bar if it is present.
- getMenuInflater().inflate(R.menu.menu_touch_latency, menu);
+ public void updateOptionsMenu() {
if (mDisplayModes.length > 1) {
- MenuItem menuItem = menu.findItem(R.id.display_mode);
+ MenuItem menuItem = mMenu.findItem(R.id.display_mode);
Mode currentMode = getWindowManager().getDefaultDisplay().getMode();
updateDisplayMode(menuItem, currentMode);
}
- updateMultiDisplayMenu(menu.findItem(R.id.multi_display));
+ updateRefreshRateMenu(mMenu.findItem(R.id.frame_rate));
+ updateMultiDisplayMenu(mMenu.findItem(R.id.multi_display));
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ Trace.beginSection("TouchLatencyActivity onCreateOptionsMenu");
+ mMenu = menu;
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.menu_touch_latency, mMenu);
+ updateOptionsMenu();
Trace.endSection();
return true;
}
@@ -96,6 +125,32 @@
menuItem.setVisible(true);
}
+ private float getHighestRefreshRate() {
+ float maxRefreshRate = 0;
+ for (Display.Mode mode : getDisplay().getSupportedModes()) {
+ if (sameSizeMode(mode) && mode.getRefreshRate() > maxRefreshRate) {
+ maxRefreshRate = mode.getRefreshRate();
+ }
+ }
+ return maxRefreshRate;
+ }
+
+ private void updateRefreshRateMenu(MenuItem item) {
+ item.setActionView(R.layout.refresh_rate_layout);
+ RangeSlider slider = item.getActionView().findViewById(R.id.slider_from_layout);
+ slider.addOnChangeListener(mRefreshRateSliderListener);
+
+ float highestRefreshRate = getHighestRefreshRate();
+ slider.setValueFrom(REFRESH_RATE_SLIDER_MIN);
+ slider.setValueTo(highestRefreshRate);
+ slider.setStepSize(REFRESH_RATE_SLIDER_STEP);
+ if (mSliderPreferredRefreshRate < REFRESH_RATE_SLIDER_MIN
+ || mSliderPreferredRefreshRate > highestRefreshRate) {
+ mSliderPreferredRefreshRate = highestRefreshRate;
+ }
+ slider.setValues(mSliderPreferredRefreshRate);
+ }
+
private void updateMultiDisplayMenu(MenuItem item) {
item.setVisible(mDisplayManager.getDisplays().length > 1);
}
@@ -105,6 +160,12 @@
mDisplayManager.registerDisplayListener(mDisplayListener, new Handler());
}
+ private boolean sameSizeMode(Display.Mode mode) {
+ Mode currentMode = mDisplayModes[mCurrentModeIndex];
+ return currentMode.getPhysicalHeight() == mode.getPhysicalHeight()
+ && currentMode.getPhysicalWidth() == mode.getPhysicalWidth();
+ }
+
public void changeDisplayMode(MenuItem item) {
Window w = getWindow();
WindowManager.LayoutParams params = w.getAttributes();
@@ -112,10 +173,7 @@
int modeIndex = (mCurrentModeIndex + 1) % mDisplayModes.length;
while (modeIndex != mCurrentModeIndex) {
// skip modes with different resolutions
- Mode currentMode = mDisplayModes[mCurrentModeIndex];
- Mode nextMode = mDisplayModes[modeIndex];
- if (currentMode.getPhysicalHeight() == nextMode.getPhysicalHeight()
- && currentMode.getPhysicalWidth() == nextMode.getPhysicalWidth()) {
+ if (sameSizeMode(mDisplayModes[modeIndex])) {
break;
}
modeIndex = (modeIndex + 1) % mDisplayModes.length;
diff --git a/tests/TouchLatency/app/src/main/res/layout/refresh_rate_layout.xml b/tests/TouchLatency/app/src/main/res/layout/refresh_rate_layout.xml
new file mode 100644
index 0000000..bb9ce60
--- /dev/null
+++ b/tests/TouchLatency/app/src/main/res/layout/refresh_rate_layout.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <com.google.android.material.slider.RangeSlider
+ android:id="@+id/slider_from_layout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:tickColor="@color/cardview_light_background"
+ app:trackColor="@color/cardview_light_background"
+ app:thumbColor="@color/cardview_dark_background"
+ android:visibility="visible"/>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml b/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml
index abc7fd5..7169021 100644
--- a/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml
+++ b/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml
@@ -14,21 +14,25 @@
limitations under the License.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" tools:context=".TouchLatencyActivity">
<item
android:id="@+id/action_settings"
android:orderInCategory="101"
- android:showAsAction="always"
- android:title="@string/mode"/>
+ android:title="@string/mode"
+ app:showAsAction="always" />
+ <item
+ android:id="@+id/frame_rate"
+ android:title="@string/frame_rate"
+ app:showAsAction="collapseActionView" />
<item
android:id="@+id/display_mode"
- android:showAsAction="ifRoom"
android:title="@string/display_mode"
- android:visible="false"/>
-
+ android:visible="false"
+ app:showAsAction="always" />
<item
android:id="@+id/multi_display"
- android:showAsAction="ifRoom"
android:title="@string/multi_display"
- android:visible="false"/>
+ android:visible="false"
+ app:showAsAction="ifRoom" />
</menu>
diff --git a/tests/TouchLatency/app/src/main/res/values/strings.xml b/tests/TouchLatency/app/src/main/res/values/strings.xml
index 5ee86d8..cad2df7 100644
--- a/tests/TouchLatency/app/src/main/res/values/strings.xml
+++ b/tests/TouchLatency/app/src/main/res/values/strings.xml
@@ -18,5 +18,6 @@
<string name="mode">Touch</string>
<string name="display_mode">Mode</string>
+ <string name="frame_rate">Frame Rate</string>
<string name="multi_display">multi-display</string>
</resources>
diff --git a/tests/TouchLatency/app/src/main/res/values/styles.xml b/tests/TouchLatency/app/src/main/res/values/styles.xml
index 22da7c1..b23a87e 100644
--- a/tests/TouchLatency/app/src/main/res/values/styles.xml
+++ b/tests/TouchLatency/app/src/main/res/values/styles.xml
@@ -16,7 +16,7 @@
<resources>
<!-- Base application theme. -->
- <style name="AppTheme" parent="@android:style/Theme.Material.Light.DarkActionBar">
+ <style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
<!-- Customize your theme here. -->
</style>
diff --git a/tests/TouchLatency/gradle.properties b/tests/TouchLatency/gradle.properties
index 1d3591c..ccd5dda 100644
--- a/tests/TouchLatency/gradle.properties
+++ b/tests/TouchLatency/gradle.properties
@@ -15,4 +15,5 @@
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
-# org.gradle.parallel=true
\ No newline at end of file
+# org.gradle.parallel=true
+android.useAndroidX=true
\ No newline at end of file
diff --git a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
index cbe13d9..650686f 100644
--- a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
+++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
@@ -373,6 +373,10 @@
try (InputStream is = new FileInputStream(certPath)) {
result = runShellCommand("mini-keyctl padd asymmetric fsv_test .fs-verity", is);
}
+ // /data/local/tmp is not readable by system server. Copy a cert file to /data/fonts
+ final String copiedCert = "/data/fonts/debug_cert.der";
+ runShellCommand("cp " + certPath + " " + copiedCert, null);
+ runShellCommand("cmd font install-debug-cert " + copiedCert, null);
// Assert that there are no errors.
assertThat(result.second).isEmpty();
String keyId = result.first.trim();
diff --git a/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java b/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java
index 3da8b46..133c176 100644
--- a/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java
+++ b/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java
@@ -147,12 +147,39 @@
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
- return registerReceiver(receiver, filter, null, null);
+ return registerReceiver(receiver, filter, null, null, 0);
+ }
+
+ /**
+ * Registers the specified {@code receiver} to listen for broadcasts that match the {@code
+ * filter} in the current process.
+ *
+ * <p>Since this method only listens for broadcasts in the current process, the provided {@code
+ * flags} are ignored; this method is primarily intended to allow receivers that register with
+ * flags to register in the current process during tests.
+ */
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, int flags) {
+ return registerReceiver(receiver, filter, null, null, flags);
}
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
String broadcastPermission, Handler scheduler) {
+ return registerReceiver(receiver, filter, broadcastPermission, scheduler, 0);
+ }
+
+ /**
+ * Registers the specified {@code receiver} to listen for broadcasts that match the {@code
+ * filter} to run in the context of the specified {@code scheduler} in the current process.
+ *
+ * <p>Since this method only listens for broadcasts in the current process, the provided {@code
+ * flags} are ignored; this method is primarily intended to allow receivers that register with
+ * flags to register in the current process during tests.
+ */
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+ String broadcastPermission, Handler scheduler, int flags) {
synchronized (mInterceptors) {
mInterceptors.add(new BroadcastInterceptor(receiver, filter, scheduler));
}
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 2a450ba..1d7fd1d 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -46,6 +46,13 @@
string version = 2;
}
+// References to non local resources
+message DynamicRefTable {
+ PackageId package_id = 1;
+ string package_name = 2;
+}
+
+
// Top level message representing a resource table.
message ResourceTable {
// The string pool containing source paths referenced throughout the resource table. This does
@@ -60,6 +67,8 @@
// The version fingerprints of the tools that built the resource table.
repeated ToolFingerprint tool_fingerprint = 4;
+
+ repeated DynamicRefTable dynamic_ref_table = 5;
}
// A package ID in the range [0x00, 0xff].
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 116dcd6..a8d2299 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -1085,6 +1085,10 @@
const auto localeconfig_entry =
ResolveTableEntry(context_, &final_table_, localeconfig_reference);
if (!localeconfig_entry) {
+ // If locale config is resolved from external symbols - skip validation.
+ if (context_->GetExternalSymbols()->FindByReference(*localeconfig_reference)) {
+ return true;
+ }
context_->GetDiagnostics()->Error(
android::DiagMessage(localeConfig->compiled_value->GetSource())
<< "no localeConfig entry");
diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp
index 254f3a5..28fcc1a 100644
--- a/tools/aapt2/cmd/Link_test.cpp
+++ b/tools/aapt2/cmd/Link_test.cpp
@@ -840,6 +840,43 @@
ASSERT_TRUE(Link(link1_args, &diag));
}
+TEST_F(LinkTest, LocaleConfigVerificationExternalSymbol) {
+ StdErrDiagnostics diag;
+ const std::string base_files_dir = GetTestPath("base");
+ ASSERT_TRUE(CompileFile(GetTestPath("res/xml/locales_config.xml"), R"(
+ <locale-config xmlns:android="http://schemas.android.com/apk/res/android">
+ <locale android:name="en-US"/>
+ <locale android:name="pt"/>
+ <locale android:name="es-419"/>
+ <locale android:name="zh-Hans-SG"/>
+ </locale-config>)",
+ base_files_dir, &diag));
+ const std::string base_apk = GetTestPath("base.apk");
+ std::vector<std::string> link_args = {
+ "--manifest",
+ GetDefaultManifest("com.aapt2.app"),
+ "-o",
+ base_apk,
+ };
+ ASSERT_TRUE(Link(link_args, base_files_dir, &diag));
+
+ const std::string localeconfig_manifest = GetTestPath("localeconfig_manifest.xml");
+ const std::string out_apk = GetTestPath("out.apk");
+ WriteFile(localeconfig_manifest, android::base::StringPrintf(R"(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.aapt2.app">
+
+ <application
+ android:localeConfig="@xml/locales_config">
+ </application>
+ </manifest>)"));
+ link_args = LinkCommandBuilder(this)
+ .SetManifestFile(localeconfig_manifest)
+ .AddParameter("-I", base_apk)
+ .Build(out_apk);
+ ASSERT_TRUE(Link(link_args, &diag));
+}
+
TEST_F(LinkTest, LocaleConfigWrongTag) {
StdErrDiagnostics diag;
const std::string compiled_files_dir = GetTestPath("compiled");
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index 6a1e8c1..e39f327 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -562,6 +562,11 @@
}
}
+ for (const pb::DynamicRefTable& dynamic_ref : pb_table.dynamic_ref_table()) {
+ out_table->included_packages_.insert(
+ {dynamic_ref.package_id().id(), dynamic_ref.package_name()});
+ }
+
// Deserialize the overlayable groups of the table
std::vector<std::shared_ptr<Overlayable>> overlayables;
for (const pb::Overlayable& pb_overlayable : pb_table.overlayable()) {
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index 163a60a..a6d58fd 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -345,7 +345,11 @@
pb::ToolFingerprint* pb_fingerprint = out_table->add_tool_fingerprint();
pb_fingerprint->set_tool(util::GetToolName());
pb_fingerprint->set_version(util::GetToolFingerprint());
-
+ for (auto it = table.included_packages_.begin(); it != table.included_packages_.end(); ++it) {
+ pb::DynamicRefTable* pb_dynamic_ref = out_table->add_dynamic_ref_table();
+ pb_dynamic_ref->mutable_package_id()->set_id(it->first);
+ pb_dynamic_ref->set_package_name(it->second);
+ }
std::vector<Overlayable*> overlayables;
auto table_view = table.GetPartitionedView();
for (const auto& package : table_view.packages) {
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index 692fa42..5adc5e6 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -1024,4 +1024,28 @@
EXPECT_THAT(*(custom_layout->path), Eq("res/layout/bar.xml"));
}
+TEST(ProtoSerializeTest, SerializeDynamicRef) {
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder().SetCompilationPackage("app").SetPackageId(0x7f).Build();
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder().Build();
+ table->included_packages_.insert({20, "foobar"});
+ table->included_packages_.insert({30, "barfoo"});
+
+ ResourceTable new_table;
+ pb::ResourceTable pb_table;
+ MockFileCollection files;
+ std::string error;
+ SerializeTableToPb(*table, &pb_table, context->GetDiagnostics());
+ ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error));
+ EXPECT_THAT(error, IsEmpty());
+
+ int result = new_table.included_packages_.size();
+ EXPECT_THAT(result, Eq(2));
+ auto it = new_table.included_packages_.begin();
+ EXPECT_THAT(it->first, Eq(20));
+ EXPECT_THAT(it->second, Eq("foobar"));
+ it++;
+ EXPECT_THAT(it->first, Eq(30));
+ EXPECT_THAT(it->second, Eq("barfoo"));
+}
} // namespace aapt
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index df09e47..d0850b8 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -69,7 +69,9 @@
StringPiece attr_value = attr->value;
const char* startChar = attr_value.begin();
if (attr_name == "pathPattern") {
- if (*startChar == '/' || *startChar == '.' || *startChar == '*') {
+ // pathPattern starts with '.' or '*' does not need leading slash.
+ // Reference starts with @ does not need leading slash.
+ if (*startChar == '/' || *startChar == '.' || *startChar == '*' || *startChar == '@') {
return true;
} else {
diag->Error(android::DiagMessage(data_el->line_number)
@@ -80,7 +82,8 @@
return false;
}
} else {
- if (*startChar == '/') {
+ // Reference starts with @ does not need leading slash.
+ if (*startChar == '/' || *startChar == '@') {
return true;
} else {
diag->Error(android::DiagMessage(data_el->line_number)
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index cec9a1a..8d1a647 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -1408,5 +1408,24 @@
</application>
</manifest>)";
EXPECT_THAT(Verify(input), NotNull());
+
+ // DeepLink with string reference as a path.
+ input = R"(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android">
+ <application>
+ <activity android:name=".MainActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <data android:scheme="http"
+ android:host="www.example.com"
+ android:path="@string/startup_uri" />
+ </intent-filter>
+ </activity>
+ </application>
+ </manifest>)";
+ EXPECT_THAT(Verify(input), NotNull());
}
} // namespace aapt
diff --git a/tools/fonts/fontchain_linter.py b/tools/fonts/fontchain_linter.py
index 35a0ce6..87b4c68 100755
--- a/tools/fonts/fontchain_linter.py
+++ b/tools/fonts/fontchain_linter.py
@@ -381,62 +381,14 @@
return tuple(f"{s:X}" for s in sequence)
return hex(sequence)
-def check_plausible_compat_pua(coverage, all_emoji, equivalent_emoji):
- # A PUA should point to every RGI emoji and that PUA should be unique to the
- # set of equivalent sequences for the emoji.
- problems = []
- for seq in all_emoji:
- # We're looking to match not-PUA with PUA so filter out existing PUA
- if contains_pua(seq):
- continue
-
- # Filter out non-RGI things that end up in all_emoji
- if only_tags(seq) or seq in {ZWJ, COMBINING_KEYCAP, EMPTY_FLAG_SEQUENCE}:
- continue
-
- equivalents = [seq]
- if seq in equivalent_emoji:
- equivalents.append(equivalent_emoji[seq])
-
- # If there are problems the hex code is much more useful
- log_equivalents = [hex_strs(s) for s in equivalents]
-
- # The system compat font should NOT include regional indicators as these have been split out
- if contains_regional_indicator(seq):
- assert not any(s in coverage for s in equivalents), f"Regional indicators not expected in compat font, found {log_equivalents}"
- continue
-
- glyph = {coverage[e] for e in equivalents}
- if len(glyph) != 1:
- problems.append(f"{log_equivalents} should all point to the same glyph")
- continue
- glyph = next(iter(glyph))
-
- pua = {s for s, g in coverage.items() if contains_pua(s) and g == glyph}
- if not pua:
- problems.append(f"Expected PUA for {log_equivalents} but none exist")
- continue
-
- assert not problems, "\n".join(sorted(problems)) + f"\n{len(problems)} PUA problems"
-
-def check_emoji_compat(all_emoji, equivalent_emoji):
+def check_emoji_not_compat(all_emoji, equivalent_emoji):
compat_psnames = set()
for emoji_font in get_emoji_fonts():
ttf = open_font(emoji_font)
psname = get_psname(ttf)
- is_compat_font = "meta" in ttf and 'Emji' in ttf["meta"].data
- if not is_compat_font:
- continue
- compat_psnames.add(psname)
-
- # If the font has compat metadata it should have PUAs for emoji sequences
- coverage = get_emoji_map(emoji_font)
- check_plausible_compat_pua(coverage, all_emoji, equivalent_emoji)
-
-
- # NotoColorEmoji must be a Compat font.
- assert 'NotoColorEmoji' in compat_psnames, 'NotoColorEmoji MUST be a compat font'
+ if "meta" in ttf:
+ assert 'Emji' not in ttf["meta"].data, 'NotoColorEmoji MUST be a compat font'
def check_emoji_font_coverage(emoji_fonts, all_emoji, equivalent_emoji):
@@ -847,7 +799,7 @@
ucd_path = sys.argv[3]
parse_ucd(ucd_path)
all_emoji, default_emoji, equivalent_emoji = compute_expected_emoji()
- check_emoji_compat(all_emoji, equivalent_emoji)
+ check_emoji_not_compat(all_emoji, equivalent_emoji)
check_emoji_coverage(all_emoji, equivalent_emoji)
check_emoji_defaults(default_emoji)
diff --git a/tools/locked_region_code_injection/Android.bp b/tools/locked_region_code_injection/Android.bp
index 6efd1f6..ff1f8e2 100644
--- a/tools/locked_region_code_injection/Android.bp
+++ b/tools/locked_region_code_injection/Android.bp
@@ -16,6 +16,6 @@
"asm-commons-9.2",
"asm-tree-9.2",
"asm-analysis-9.2",
- "guava-21.0",
+ "guava",
],
}
diff --git a/tools/traceinjection/Android.bp b/tools/traceinjection/Android.bp
index 39d1b1c..bb32df6 100644
--- a/tools/traceinjection/Android.bp
+++ b/tools/traceinjection/Android.bp
@@ -16,7 +16,7 @@
"asm-commons-9.2",
"asm-tree-9.2",
"asm-analysis-9.2",
- "guava-21.0",
+ "guava",
],
}