Merge "[bt] Add the Google module in pixel image" into main
diff --git a/apex/jobscheduler/framework/aconfig/job.aconfig b/apex/jobscheduler/framework/aconfig/job.aconfig
index 63624d8..8b1a40c 100644
--- a/apex/jobscheduler/framework/aconfig/job.aconfig
+++ b/apex/jobscheduler/framework/aconfig/job.aconfig
@@ -55,3 +55,14 @@
description: "Introduce a new getPendingJobReasonsHistory() API which returns a limited historical view of getPendingJobReasons()."
bug: "372031023"
}
+
+
+flag {
+ name: "add_type_info_to_wakelock_tag"
+ namespace: "backstage_power"
+ description: "Append the job type info to wakelock tag"
+ bug: "381880530"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
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 4335cae..fe6daa5 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -5766,6 +5766,41 @@
}
// Shell command infrastructure
+ int getJobWakelockTag(PrintWriter pw, String pkgName, int userId, @Nullable String namespace,
+ int jobId) {
+ try {
+ final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
+ userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
+ if (uid < 0) {
+ pw.print("unknown(");
+ pw.print(pkgName);
+ pw.println(")");
+ return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
+ }
+
+ synchronized (mLock) {
+ final JobStatus js = mJobs.getJobByUidAndJobId(uid, namespace, jobId);
+ if (DEBUG) {
+ Slog.d(TAG, "get-job-wakelock-tag " + namespace
+ + "/" + uid + "/" + jobId + ": " + js);
+ }
+ if (js == null) {
+ pw.print("unknown(");
+ UserHandle.formatUid(pw, uid);
+ pw.print("/jid");
+ pw.print(jobId);
+ pw.println(")");
+ return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
+ }
+
+ pw.println(js.getWakelockTag());
+ }
+ } catch (RemoteException e) {
+ // can't happen
+ }
+ return 0;
+ }
+
int getJobState(PrintWriter pw, String pkgName, int userId, @Nullable String namespace,
int jobId) {
try {
@@ -5945,6 +5980,9 @@
pw.print(android.app.job.Flags.FLAG_GET_PENDING_JOB_REASONS_HISTORY_API,
android.app.job.Flags.getPendingJobReasonsHistoryApi());
pw.println();
+ pw.print(android.app.job.Flags.FLAG_ADD_TYPE_INFO_TO_WAKELOCK_TAG,
+ android.app.job.Flags.addTypeInfoToWakelockTag());
+ pw.println();
pw.decreaseIndent();
pw.println();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
index 42c8250..633598e 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
@@ -86,6 +86,8 @@
return getTransferredNetworkBytes(pw, BYTE_OPTION_DOWNLOAD);
case "get-transferred-upload-bytes":
return getTransferredNetworkBytes(pw, BYTE_OPTION_UPLOAD);
+ case "get-job-wakelock-tag":
+ return getJobWakelockTag(pw);
case "get-job-state":
return getJobState(pw);
case "heartbeat":
@@ -424,6 +426,9 @@
case android.app.job.Flags.FLAG_JOB_DEBUG_INFO_APIS:
pw.println(android.app.job.Flags.jobDebugInfoApis());
break;
+ case android.app.job.Flags.FLAG_ADD_TYPE_INFO_TO_WAKELOCK_TAG:
+ pw.println(android.app.job.Flags.addTypeInfoToWakelockTag());
+ break;
case com.android.server.job.Flags.FLAG_BATCH_ACTIVE_BUCKET_JOBS:
pw.println(com.android.server.job.Flags.batchActiveBucketJobs());
break;
@@ -581,6 +586,49 @@
}
}
+ private int getJobWakelockTag(PrintWriter pw) throws Exception {
+ checkPermission("get job wakelock tag");
+
+ int userId = UserHandle.USER_SYSTEM;
+ String namespace = null;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-u":
+ case "--user":
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ break;
+
+ case "-n":
+ case "--namespace":
+ namespace = getNextArgRequired();
+ break;
+
+ default:
+ pw.println("Error: unknown option '" + opt + "'");
+ return -1;
+ }
+ }
+
+ if (userId == UserHandle.USER_CURRENT) {
+ userId = ActivityManager.getCurrentUser();
+ }
+
+ final String pkgName = getNextArgRequired();
+ final String jobIdStr = getNextArgRequired();
+ final int jobId = Integer.parseInt(jobIdStr);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ int ret = mInternal.getJobWakelockTag(pw, pkgName, userId, namespace, jobId);
+ printError(ret, pkgName, userId, namespace, jobId);
+ return ret;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
private int getJobState(PrintWriter pw) throws Exception {
checkPermission("get job state");
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 5a33aa0..4b9d736 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
@@ -1459,7 +1459,12 @@
@NonNull
public String getWakelockTag() {
if (mWakelockTag == null) {
- mWakelockTag = "*job*/" + this.batteryName;
+ mWakelockTag = "*job*";
+ if (android.app.job.Flags.addTypeInfoToWakelockTag()) {
+ mWakelockTag += (isRequestedExpeditedJob()
+ ? "e" : (getJob().isUserInitiated() ? "u" : "r"));
+ }
+ mWakelockTag += "/" + this.batteryName;
}
return mWakelockTag;
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index d5cb6c0..6c5ac8a 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -360,6 +360,7 @@
field @Deprecated public static final String REQUEST_NETWORK_SCORES = "android.permission.REQUEST_NETWORK_SCORES";
field public static final String REQUEST_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE";
field public static final String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
+ field @FlaggedApi("android.content.pm.uid_based_provider_lookup") public static final String RESOLVE_COMPONENT_FOR_UID = "android.permission.RESOLVE_COMPONENT_FOR_UID";
field public static final String RESTART_WIFI_SUBSYSTEM = "android.permission.RESTART_WIFI_SUBSYSTEM";
field @FlaggedApi("android.permission.flags.health_connect_backup_restore_permission_enabled") public static final String RESTORE_HEALTH_CONNECT_DATA_AND_SETTINGS = "android.permission.RESTORE_HEALTH_CONNECT_DATA_AND_SETTINGS";
field public static final String RESTORE_RUNTIME_PERMISSIONS = "android.permission.RESTORE_RUNTIME_PERMISSIONS";
@@ -4241,6 +4242,7 @@
method public abstract void registerDexModule(@NonNull String, @Nullable android.content.pm.PackageManager.DexModuleRegisterCallback);
method @RequiresPermission("android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS") public abstract void removeOnPermissionsChangeListener(@NonNull android.content.pm.PackageManager.OnPermissionsChangedListener);
method public void replacePreferredActivity(@NonNull android.content.IntentFilter, int, @NonNull java.util.List<android.content.ComponentName>, @NonNull android.content.ComponentName);
+ method @FlaggedApi("android.content.pm.uid_based_provider_lookup") @Nullable @RequiresPermission(android.Manifest.permission.RESOLVE_COMPONENT_FOR_UID) public android.content.pm.ProviderInfo resolveContentProviderForUid(@NonNull String, @NonNull android.content.pm.PackageManager.ComponentInfoFlags, int);
method @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public abstract void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle, @NonNull String);
method public void sendDeviceCustomizationReadyBroadcast();
@@ -12676,27 +12678,21 @@
package android.security.advancedprotection {
@FlaggedApi("android.security.aapm_api") public final class AdvancedProtectionFeature implements android.os.Parcelable {
- ctor public AdvancedProtectionFeature(@NonNull String);
+ ctor public AdvancedProtectionFeature(int);
method public int describeContents();
- method @NonNull public String getId();
+ method public int getId();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.security.advancedprotection.AdvancedProtectionFeature> CREATOR;
}
@FlaggedApi("android.security.aapm_api") public final class AdvancedProtectionManager {
- method @NonNull public android.content.Intent createSupportIntent(@NonNull String, @Nullable String);
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE) public java.util.List<android.security.advancedprotection.AdvancedProtectionFeature> getAdvancedProtectionFeatures();
method @RequiresPermission(android.Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE) public void setAdvancedProtectionEnabled(boolean);
- field @FlaggedApi("android.security.aapm_api") public static final String ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG = "android.security.advancedprotection.action.SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG";
- field public static final String EXTRA_SUPPORT_DIALOG_FEATURE = "android.security.advancedprotection.extra.SUPPORT_DIALOG_FEATURE";
- field public static final String EXTRA_SUPPORT_DIALOG_TYPE = "android.security.advancedprotection.extra.SUPPORT_DIALOG_TYPE";
- field public static final String FEATURE_ID_DISALLOW_CELLULAR_2G = "android.security.advancedprotection.feature_disallow_2g";
- field public static final String FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES = "android.security.advancedprotection.feature_disallow_install_unknown_sources";
- field public static final String FEATURE_ID_DISALLOW_USB = "android.security.advancedprotection.feature_disallow_usb";
- field public static final String FEATURE_ID_DISALLOW_WEP = "android.security.advancedprotection.feature_disallow_wep";
- field public static final String FEATURE_ID_ENABLE_MTE = "android.security.advancedprotection.feature_enable_mte";
- field public static final String SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION = "android.security.advancedprotection.type_blocked_interaction";
- field public static final String SUPPORT_DIALOG_TYPE_DISABLED_SETTING = "android.security.advancedprotection.type_disabled_setting";
+ field public static final int FEATURE_ID_DISALLOW_CELLULAR_2G = 0; // 0x0
+ field public static final int FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES = 1; // 0x1
+ field public static final int FEATURE_ID_DISALLOW_USB = 2; // 0x2
+ field public static final int FEATURE_ID_DISALLOW_WEP = 3; // 0x3
+ field public static final int FEATURE_ID_ENABLE_MTE = 4; // 0x4
}
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index abdfb535..999db18 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -485,6 +485,11 @@
*/
public static final int OOM_ADJ_REASON_FOLLOW_UP = 23;
+ /**
+ * Oom Adj Reason: Update after oom adjuster configuration has changed.
+ */
+ public static final int OOM_ADJ_REASON_RECONFIGURATION = 24;
+
@IntDef(prefix = {"OOM_ADJ_REASON_"}, value = {
OOM_ADJ_REASON_NONE,
OOM_ADJ_REASON_ACTIVITY,
@@ -510,6 +515,7 @@
OOM_ADJ_REASON_RESTRICTION_CHANGE,
OOM_ADJ_REASON_COMPONENT_DISABLED,
OOM_ADJ_REASON_FOLLOW_UP,
+ OOM_ADJ_REASON_RECONFIGURATION,
})
@Retention(RetentionPolicy.SOURCE)
public @interface OomAdjReason {}
diff --git a/core/java/android/app/AppOpsManager.aidl b/core/java/android/app/AppOpsManager.aidl
index b4dee2e..56ed290 100644
--- a/core/java/android/app/AppOpsManager.aidl
+++ b/core/java/android/app/AppOpsManager.aidl
@@ -19,6 +19,7 @@
parcelable AppOpsManager.PackageOps;
parcelable AppOpsManager.NoteOpEventProxyInfo;
parcelable AppOpsManager.NoteOpEvent;
+parcelable AppOpsManager.NotedOp;
parcelable AppOpsManager.OpFeatureEntry;
parcelable AppOpsManager.OpEntry;
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 1913812..53b4b54e 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -262,6 +262,23 @@
private static final Object sLock = new Object();
+ // A map that records noted times for each op.
+ private static ArrayMap<NotedOp, Integer> sPendingNotedOps = new ArrayMap<>();
+ private static HandlerThread sHandlerThread;
+ private static final int NOTE_OP_BATCHING_DELAY_MILLIS = 1000;
+
+ private boolean isNoteOpBatchingSupported() {
+ // If noteOp is called from system server no IPC is made, hence we don't need batching.
+ if (Process.myUid() == Process.SYSTEM_UID) {
+ return false;
+ }
+ return Flags.noteOpBatchingEnabled();
+ }
+
+ private static final Object sBatchedNoteOpLock = new Object();
+ @GuardedBy("sBatchedNoteOpLock")
+ private static boolean sIsBatchedNoteOpCallScheduled = false;
+
/** Current {@link OnOpNotedCallback}. Change via {@link #setOnOpNotedCallback} */
@GuardedBy("sLock")
private static @Nullable OnOpNotedCallback sOnOpNotedCallback;
@@ -7466,6 +7483,141 @@
}
/**
+ * A NotedOp is an app op grouped in noteOp API and sent to the system server in a batch
+ *
+ * @hide
+ */
+ public static final class NotedOp implements Parcelable {
+ private final @IntRange(from = 0, to = _NUM_OP - 1) int mOp;
+ private final @IntRange(from = 0) int mUid;
+ private final @Nullable String mPackageName;
+ private final @Nullable String mAttributionTag;
+ private final int mVirtualDeviceId;
+ private final @Nullable String mMessage;
+ private final boolean mShouldCollectAsyncNotedOp;
+ private final boolean mShouldCollectMessage;
+
+ public NotedOp(int op, int uid, @Nullable String packageName,
+ @Nullable String attributionTag, int virtualDeviceId, @Nullable String message,
+ boolean shouldCollectAsyncNotedOp, boolean shouldCollectMessage) {
+ mOp = op;
+ mUid = uid;
+ mPackageName = packageName;
+ mAttributionTag = attributionTag;
+ mVirtualDeviceId = virtualDeviceId;
+ mMessage = message;
+ mShouldCollectAsyncNotedOp = shouldCollectAsyncNotedOp;
+ mShouldCollectMessage = shouldCollectMessage;
+ }
+
+ NotedOp(Parcel source) {
+ mOp = source.readInt();
+ mUid = source.readInt();
+ mPackageName = source.readString();
+ mAttributionTag = source.readString();
+ mVirtualDeviceId = source.readInt();
+ mMessage = source.readString();
+ mShouldCollectAsyncNotedOp = source.readBoolean();
+ mShouldCollectMessage = source.readBoolean();
+ }
+
+ public int getOp() {
+ return mOp;
+ }
+
+ public int getUid() {
+ return mUid;
+ }
+
+ public @Nullable String getPackageName() {
+ return mPackageName;
+ }
+
+ public @Nullable String getAttributionTag() {
+ return mAttributionTag;
+ }
+
+ public int getVirtualDeviceId() {
+ return mVirtualDeviceId;
+ }
+
+ public @Nullable String getMessage() {
+ return mMessage;
+ }
+
+ public boolean getShouldCollectAsyncNotedOp() {
+ return mShouldCollectAsyncNotedOp;
+ }
+
+ public boolean getShouldCollectMessage() {
+ return mShouldCollectMessage;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mOp);
+ dest.writeInt(mUid);
+ dest.writeString(mPackageName);
+ dest.writeString(mAttributionTag);
+ dest.writeInt(mVirtualDeviceId);
+ dest.writeString(mMessage);
+ dest.writeBoolean(mShouldCollectAsyncNotedOp);
+ dest.writeBoolean(mShouldCollectMessage);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ NotedOp that = (NotedOp) o;
+ return mOp == that.mOp
+ && mUid == that.mUid
+ && Objects.equals(mPackageName, that.mPackageName)
+ && Objects.equals(mAttributionTag, that.mAttributionTag)
+ && mVirtualDeviceId == that.mVirtualDeviceId
+ && Objects.equals(mMessage, that.mMessage)
+ && Objects.equals(mShouldCollectAsyncNotedOp, that.mShouldCollectAsyncNotedOp)
+ && Objects.equals(mShouldCollectMessage, that.mShouldCollectMessage);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mOp, mUid, mPackageName, mAttributionTag, mVirtualDeviceId,
+ mMessage, mShouldCollectAsyncNotedOp, mShouldCollectMessage);
+ }
+
+ @Override
+ public String toString() {
+ return "NotedOp{"
+ + "mOp=" + mOp
+ + ", mUid=" + mUid
+ + ", mPackageName=" + mPackageName
+ + ", mAttributionTag=" + mAttributionTag
+ + ", mVirtualDeviceId=" + mVirtualDeviceId
+ + ", mMessage=" + mMessage
+ + ", mShouldCollectAsyncNotedOp=" + mShouldCollectAsyncNotedOp
+ + ", mShouldCollectMessage=" + mShouldCollectMessage
+ + "}";
+ }
+
+ public static final @NonNull Creator<NotedOp> CREATOR =
+ new Creator<>() {
+ @Override public NotedOp createFromParcel(Parcel source) {
+ return new NotedOp(source);
+ }
+
+ @Override public NotedOp[] newArray(int size) {
+ return new NotedOp[size];
+ }
+ };
+ }
+
+ /**
* Computes the sum of the counts for the given flags in between the begin and
* end UID states.
*
@@ -9301,6 +9453,65 @@
message);
}
+ /**
+ * Create a new NotedOp object to represent the note operation. If the note operation is
+ * a duplicate in the buffer, put it in a batch for an async binder call to the system server.
+ *
+ * @return whether this note operation is a duplicate in the buffer. If it's the
+ * first, the noteOp is not batched, the caller should manually call noteOperation.
+ */
+ private boolean batchDuplicateNoteOps(int op, int uid, @Nullable String packageName,
+ @Nullable String attributionTag, int virtualDeviceId, @Nullable String message,
+ boolean collectAsync, boolean shouldCollectMessage) {
+ synchronized (sBatchedNoteOpLock) {
+ NotedOp notedOp = new NotedOp(op, uid, packageName, attributionTag,
+ virtualDeviceId, message, collectAsync, shouldCollectMessage);
+
+ // Batch same noteOp calls and send them with their counters to the system
+ // service asynchronously. The time window for batching is specified in
+ // NOTE_OP_BATCHING_DELAY_MILLIS. Always allow the first noteOp call to go
+ // through binder API. Accumulate subsequent same noteOp calls during the
+ // time window in sPendingNotedOps.
+ boolean isDuplicated = sPendingNotedOps.containsKey(notedOp);
+ if (!isDuplicated) {
+ sPendingNotedOps.put(notedOp, 0);
+ } else {
+ sPendingNotedOps.merge(notedOp, 1, Integer::sum);
+ }
+
+ if (!sIsBatchedNoteOpCallScheduled) {
+ if (sHandlerThread == null) {
+ sHandlerThread = new HandlerThread("AppOpsManagerNoteOpBatching");
+ sHandlerThread.start();
+ }
+
+ sHandlerThread.getThreadHandler().postDelayed(() -> {
+ ArrayMap<NotedOp, Integer> pendingNotedOpsCopy;
+ synchronized(sBatchedNoteOpLock) {
+ sIsBatchedNoteOpCallScheduled = false;
+ pendingNotedOpsCopy = sPendingNotedOps;
+ sPendingNotedOps = new ArrayMap<>();
+ }
+ for (int i = pendingNotedOpsCopy.size() - 1; i >= 0; i--) {
+ if (pendingNotedOpsCopy.valueAt(i) == 0) {
+ pendingNotedOpsCopy.removeAt(i);
+ }
+ }
+ if (!pendingNotedOpsCopy.isEmpty()) {
+ try {
+ mService.noteOperationsInBatch(pendingNotedOpsCopy);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }, NOTE_OP_BATCHING_DELAY_MILLIS);
+
+ sIsBatchedNoteOpCallScheduled = true;
+ }
+ return isDuplicated;
+ }
+ }
+
private int noteOpNoThrow(int op, int uid, @Nullable String packageName,
@Nullable String attributionTag, int virtualDeviceId, @Nullable String message) {
try {
@@ -9315,15 +9526,34 @@
}
}
- SyncNotedAppOp syncOp;
- if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
- syncOp = mService.noteOperation(op, uid, packageName, attributionTag,
- collectionMode == COLLECT_ASYNC, message, shouldCollectMessage);
- } else {
- syncOp = mService.noteOperationForDevice(op, uid, packageName, attributionTag,
- virtualDeviceId, collectionMode == COLLECT_ASYNC, message,
- shouldCollectMessage);
+ SyncNotedAppOp syncOp = null;
+ boolean isNoteOpDuplicated = false;
+ if (isNoteOpBatchingSupported()) {
+ int mode = sAppOpModeCache.query(
+ new AppOpModeQuery(op, uid, packageName, virtualDeviceId, attributionTag,
+ "noteOpNoThrow"));
+ // For FOREGROUND mode, we still need to make a binder call to the system service
+ // to translate it to ALLOWED or IGNORED. So no batching is needed.
+ if (mode != MODE_FOREGROUND) {
+ isNoteOpDuplicated = batchDuplicateNoteOps(op, uid, packageName, attributionTag,
+ virtualDeviceId, message,
+ collectionMode == COLLECT_ASYNC, shouldCollectMessage);
+
+ syncOp = new SyncNotedAppOp(mode, op, attributionTag, packageName);
+ }
}
+
+ if (!isNoteOpDuplicated) {
+ if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
+ syncOp = mService.noteOperation(op, uid, packageName, attributionTag,
+ collectionMode == COLLECT_ASYNC, message, shouldCollectMessage);
+ } else {
+ syncOp = mService.noteOperationForDevice(op, uid, packageName, attributionTag,
+ virtualDeviceId, collectionMode == COLLECT_ASYNC, message,
+ shouldCollectMessage);
+ }
+ }
+
if (syncOp.getOpMode() == MODE_ALLOWED) {
if (collectionMode == COLLECT_SELF) {
collectNotedOpForSelf(syncOp);
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index b21defb..8b7ea0f 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -29,7 +29,7 @@
import com.android.internal.util.function.DodecFunction;
import com.android.internal.util.function.HexConsumer;
import com.android.internal.util.function.HexFunction;
-import com.android.internal.util.function.OctFunction;
+import com.android.internal.util.function.NonaFunction;
import com.android.internal.util.function.QuadFunction;
import com.android.internal.util.function.UndecFunction;
@@ -86,9 +86,9 @@
*/
SyncNotedAppOp noteOperation(int code, int uid, @Nullable String packageName,
@Nullable String featureId, int virtualDeviceId, boolean shouldCollectAsyncNotedOp,
- @Nullable String message, boolean shouldCollectMessage,
- @NonNull OctFunction<Integer, Integer, String, String, Integer, Boolean, String,
- Boolean, SyncNotedAppOp> superImpl);
+ @Nullable String message, boolean shouldCollectMessage, int notedCount,
+ @NonNull NonaFunction<Integer, Integer, String, String, Integer, Boolean, String,
+ Boolean, Integer, SyncNotedAppOp> superImpl);
/**
* Allows overriding note proxy operation behavior.
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index da33847..2dead56 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1751,6 +1751,19 @@
}
}
+ /** @hide **/
+ @Override
+ public ProviderInfo resolveContentProviderForUid(@NonNull String authority,
+ ComponentInfoFlags flags, int callingUid) {
+ try {
+ return mPM.resolveContentProviderForUid(authority,
+ updateFlagsForComponent(flags.getValue(), getUserId(), null), getUserId(),
+ callingUid);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
@Override
public List<ProviderInfo> queryContentProviders(String processName, int uid, int flags) {
return queryContentProviders(processName, uid, ComponentInfoFlags.of(flags));
diff --git a/core/java/android/app/BackgroundStartPrivileges.java b/core/java/android/app/BackgroundStartPrivileges.java
index 20278ea..adea0a8 100644
--- a/core/java/android/app/BackgroundStartPrivileges.java
+++ b/core/java/android/app/BackgroundStartPrivileges.java
@@ -23,12 +23,13 @@
import com.android.internal.util.Preconditions;
import java.util.List;
+import java.util.Objects;
/**
* Privileges granted to a Process that allows it to execute starts from the background.
* @hide
*/
-public class BackgroundStartPrivileges {
+public final class BackgroundStartPrivileges {
/** No privileges. */
public static final BackgroundStartPrivileges NONE = new BackgroundStartPrivileges(
false, false, null);
@@ -190,4 +191,22 @@
+ ", originatingToken=" + mOriginatingToken
+ ']';
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ BackgroundStartPrivileges that = (BackgroundStartPrivileges) o;
+ return mAllowsBackgroundActivityStarts == that.mAllowsBackgroundActivityStarts
+ && mAllowsBackgroundForegroundServiceStarts
+ == that.mAllowsBackgroundForegroundServiceStarts
+ && Objects.equals(mOriginatingToken, that.mOriginatingToken);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mAllowsBackgroundActivityStarts,
+ mAllowsBackgroundForegroundServiceStarts,
+ mOriginatingToken);
+ }
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 9f898b8..e6ddbf4 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -196,6 +196,21 @@
ProviderInfo resolveContentProvider(String name, long flags, int userId);
/**
+ * Resolve content providers with a given authority, for a specific
+ * callingUid.
+ *
+ * @param authority Authority of the content provider
+ * @param flags Additional option flags to modify the data returned.
+ * @param userId Current user ID
+ * @param callingUid UID of the caller who's access to the content provider
+ is to be checked
+ *
+ * @return ProviderInfo of the resolved content provider. May return null
+ */
+ ProviderInfo resolveContentProviderForUid(String authority, long flags,
+ int userId, int callingUid);
+
+ /**
* Retrieve sync information for all content providers.
*
* @param outNames Filled in with a list of the root names of the content
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 438a21b..c16582f 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -8349,6 +8349,25 @@
}
/**
+ * Resolve content providers with a given authority, for a specific callingUid.
+ * @param authority Authority of the content provider
+ * @param flags Additional option flags to modify the data returned.
+ * @param callingUid UID of the caller who's access to the content provider is to be checked
+
+ * @return ProviderInfo of the resolved content provider.
+ * @hide
+ */
+ @Nullable
+ @FlaggedApi(android.content.pm.Flags.FLAG_UID_BASED_PROVIDER_LOOKUP)
+ @RequiresPermission(Manifest.permission.RESOLVE_COMPONENT_FOR_UID)
+ @SystemApi
+ public ProviderInfo resolveContentProviderForUid(@NonNull String authority,
+ @NonNull ComponentInfoFlags flags, int callingUid) {
+ throw new UnsupportedOperationException(
+ "resolveContentProviderForUid not implemented in subclass");
+ }
+
+ /**
* Retrieve content provider information.
* <p>
* <em>Note: unlike most other methods, an empty result set is indicated
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 0d219a9..7bba06c 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -152,7 +152,7 @@
name: "cache_sdk_system_features"
namespace: "system_performance"
description: "Feature flag to enable optimized cache for SDK-defined system feature lookups."
- bug: "375000483"
+ bug: "326623529"
}
flag {
@@ -375,3 +375,11 @@
description: "Feature flag to remove the consumption of the hidden module status (ModuleInfo#IsHidden) in the Android source tree."
bug: "363952383"
}
+
+flag {
+ name: "uid_based_provider_lookup"
+ is_exported: true
+ namespace: "package_manager_service"
+ bug: "334024639"
+ description: "Feature flag to check whether a given UID can access a content provider"
+}
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 4fbdf7f..d88a9d4 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -56,15 +56,18 @@
void stopWifiDisplayScan();
// Requires CONFIGURE_WIFI_DISPLAY permission.
+ @EnforcePermission("CONFIGURE_WIFI_DISPLAY")
void connectWifiDisplay(String address);
// No permissions required.
void disconnectWifiDisplay();
// Requires CONFIGURE_WIFI_DISPLAY permission.
+ @EnforcePermission("CONFIGURE_WIFI_DISPLAY")
void renameWifiDisplay(String address, String alias);
// Requires CONFIGURE_WIFI_DISPLAY permission.
+ @EnforcePermission("CONFIGURE_WIFI_DISPLAY")
void forgetWifiDisplay(String address);
// Requires CONFIGURE_WIFI_DISPLAY permission.
@@ -169,6 +172,7 @@
void setBrightness(int displayId, float brightness);
// Retrieves the display brightness.
+ @EnforcePermission("CONTROL_DISPLAY_BRIGHTNESS")
float getBrightness(int displayId);
// Temporarily sets the auto brightness adjustment factor.
@@ -196,8 +200,7 @@
// Sets the HDR conversion mode for a device.
// Requires MODIFY_HDR_CONVERSION_MODE permission.
- @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
- + ".permission.MODIFY_HDR_CONVERSION_MODE)")
+ @EnforcePermission("MODIFY_HDR_CONVERSION_MODE")
void setHdrConversionMode(in HdrConversionMode hdrConversionMode);
HdrConversionMode getHdrConversionModeSetting();
HdrConversionMode getHdrConversionMode();
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 01222cd..18d2afb 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -687,12 +687,18 @@
return removeFrozenStateChangeCallbackNative(wrappedCallback);
}
+ public static boolean isFrozenStateChangeCallbackSupported() {
+ return isFrozenStateChangeCallbackSupportedNative();
+ }
+
private native void addFrozenStateChangeCallbackNative(FrozenStateChangeCallback callback)
throws RemoteException;
private native boolean removeFrozenStateChangeCallbackNative(
FrozenStateChangeCallback callback);
+ private static native boolean isFrozenStateChangeCallbackSupportedNative();
+
/**
* Perform a dump on the remote object
*
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index f3bb514..727dcba 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -4,6 +4,8 @@
# PowerManager
per-file IPowerManager.aidl = file:/services/core/java/com/android/server/power/OWNERS
+per-file IScreenTimeoutPolicyListener.aidl = file:/services/core/java/com/android/server/power/OWNERS
+per-file IWakeLockCallback.aidl = file:/services/core/java/com/android/server/power/OWNERS
per-file PowerManager.java = file:/services/core/java/com/android/server/power/OWNERS
per-file PowerManagerInternal.java = file:/services/core/java/com/android/server/power/OWNERS
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 132805d..507bcb8 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -940,10 +940,10 @@
/**
* Specifies if a user is disallowed from resetting network settings
- * from Settings. This can only be set by device owners and profile owners on the primary user.
+ * from Settings. This can only be set by device owners and profile owners on the main user.
* The default value is <code>false</code>.
- * <p>This restriction has no effect on secondary users and managed profiles since only the
- * primary user can reset the network settings of the device.
+ * <p>This restriction has no effect on non-Admin users since they cannot reset the network
+ * settings of the device.
*
* <p>Holders of the permission
* {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_MOBILE_NETWORK}
@@ -1077,11 +1077,11 @@
/**
* Specifies if a user is disallowed from configuring cell broadcasts.
*
- * <p>This restriction can only be set by a device owner, a profile owner on the primary
+ * <p>This restriction can only be set by a device owner, a profile owner on the main
* user or a profile owner of an organization-owned managed profile on the parent profile.
* When it is set by a device owner, it applies globally. When it is set by a profile owner
- * on the primary user or by a profile owner of an organization-owned managed profile on
- * the parent profile, it disables the primary user from configuring cell broadcasts.
+ * on the main user or by a profile owner of an organization-owned managed profile on
+ * the parent profile, it disables the user from configuring cell broadcasts.
*
* <p>Holders of the permission
* {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_MOBILE_NETWORK}
@@ -1089,8 +1089,8 @@
*
* <p>The default value is <code>false</code>.
*
- * <p>This restriction has no effect on secondary users and managed profiles since only the
- * primary user can configure cell broadcasts.
+ * <p>This restriction has no effect on non-Admin users since they cannot configure cell
+ * broadcasts.
*
* <p>Key for user restrictions.
* <p>Type: Boolean
@@ -1103,11 +1103,11 @@
/**
* Specifies if a user is disallowed from configuring mobile networks.
*
- * <p>This restriction can only be set by a device owner, a profile owner on the primary
+ * <p>This restriction can only be set by a device owner, a profile owner on the main
* user or a profile owner of an organization-owned managed profile on the parent profile.
* When it is set by a device owner, it applies globally. When it is set by a profile owner
- * on the primary user or by a profile owner of an organization-owned managed profile on
- * the parent profile, it disables the primary user from configuring mobile networks.
+ * on the main user or by a profile owner of an organization-owned managed profile on
+ * the parent profile, it disables the user from configuring mobile networks.
*
* <p>Holders of the permission
* {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_MOBILE_NETWORK}
@@ -1115,8 +1115,8 @@
*
* <p>The default value is <code>false</code>.
*
- * <p>This restriction has no effect on secondary users and managed profiles since only the
- * primary user can configure mobile networks.
+ * <p>This restriction has no effect on non-Admin users since they cannot configure mobile
+ * networks.
*
* <p>Key for user restrictions.
* <p>Type: Boolean
diff --git a/core/java/android/security/advancedprotection/AdvancedProtectionFeature.java b/core/java/android/security/advancedprotection/AdvancedProtectionFeature.java
index a086bf7..d476d96 100644
--- a/core/java/android/security/advancedprotection/AdvancedProtectionFeature.java
+++ b/core/java/android/security/advancedprotection/AdvancedProtectionFeature.java
@@ -30,26 +30,25 @@
@FlaggedApi(Flags.FLAG_AAPM_API)
@SystemApi
public final class AdvancedProtectionFeature implements Parcelable {
- private final String mId;
+ private final int mId;
/**
* Create an object identifying an Advanced Protection feature for AdvancedProtectionManager
- * @param id A unique ID to identify this feature. It is used by Settings screens to display
- * information about this feature.
+ * @param id Feature identifier. It is used by Settings screens to display information about
+ * this feature.
*/
- public AdvancedProtectionFeature(@NonNull String id) {
+ public AdvancedProtectionFeature(@AdvancedProtectionManager.FeatureId int id) {
mId = id;
}
private AdvancedProtectionFeature(Parcel in) {
- mId = in.readString8();
+ mId = in.readInt();
}
/**
* @return the unique ID representing this feature
*/
- @NonNull
- public String getId() {
+ public int getId() {
return mId;
}
@@ -60,7 +59,7 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeString8(mId);
+ dest.writeInt(mId);
}
@NonNull
diff --git a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
index 59628e8..ea01fc9 100644
--- a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
+++ b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
@@ -24,17 +24,18 @@
import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.StringDef;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
+import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.RemoteException;
+import android.os.UserManager;
import android.security.Flags;
import android.util.Log;
@@ -59,54 +60,57 @@
private static final String TAG = "AdvancedProtectionMgr";
/**
- * Advanced Protection's identifier for setting policies or restrictions in DevicePolicyManager.
+ * Advanced Protection's identifier for setting policies or restrictions in
+ * {@link DevicePolicyManager}.
*
* @hide */
public static final String ADVANCED_PROTECTION_SYSTEM_ENTITY =
"android.security.advancedprotection";
/**
- * Feature identifier for disallowing 2G.
+ * Feature identifier for disallowing connections to 2G networks.
*
+ * @see UserManager#DISALLOW_CELLULAR_2G
* @hide */
@SystemApi
- public static final String FEATURE_ID_DISALLOW_CELLULAR_2G =
- "android.security.advancedprotection.feature_disallow_2g";
+ public static final int FEATURE_ID_DISALLOW_CELLULAR_2G = 0;
/**
- * Feature identifier for disallowing install of unknown sources.
+ * Feature identifier for disallowing installs of apps from unknown sources.
*
+ * @see UserManager#DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY
* @hide */
@SystemApi
- public static final String FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES =
- "android.security.advancedprotection.feature_disallow_install_unknown_sources";
+ public static final int FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES = 1;
/**
- * Feature identifier for disallowing USB.
+ * Feature identifier for disallowing USB connections.
*
* @hide */
@SystemApi
- public static final String FEATURE_ID_DISALLOW_USB =
- "android.security.advancedprotection.feature_disallow_usb";
+ public static final int FEATURE_ID_DISALLOW_USB = 2;
/**
- * Feature identifier for disallowing WEP.
+ * Feature identifier for disallowing connections to Wi-Fi Wired Equivalent Privacy (WEP)
+ * networks.
*
+ * @see WifiManager#isWepSupported()
* @hide */
@SystemApi
- public static final String FEATURE_ID_DISALLOW_WEP =
- "android.security.advancedprotection.feature_disallow_wep";
+ public static final int FEATURE_ID_DISALLOW_WEP = 3;
/**
- * Feature identifier for enabling MTE.
+ * Feature identifier for enabling the Memory Tagging Extension (MTE). MTE is a CPU extension
+ * that allows to protect against certain classes of security problems at a small runtime
+ * performance cost overhead.
*
+ * @see DevicePolicyManager#setMtePolicy(int)
* @hide */
@SystemApi
- public static final String FEATURE_ID_ENABLE_MTE =
- "android.security.advancedprotection.feature_enable_mte";
+ public static final int FEATURE_ID_ENABLE_MTE = 4;
/** @hide */
- @StringDef(prefix = { "FEATURE_ID_" }, value = {
+ @IntDef(prefix = { "FEATURE_ID_" }, value = {
FEATURE_ID_DISALLOW_CELLULAR_2G,
FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES,
FEATURE_ID_DISALLOW_USB,
@@ -116,7 +120,7 @@
@Retention(RetentionPolicy.SOURCE)
public @interface FeatureId {}
- private static final Set<String> ALL_FEATURE_IDS = Set.of(
+ private static final Set<Integer> ALL_FEATURE_IDS = Set.of(
FEATURE_ID_DISALLOW_CELLULAR_2G,
FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES,
FEATURE_ID_DISALLOW_USB,
@@ -135,9 +139,6 @@
* Output: Nothing.
*
* @hide */
- @SystemApi
- @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
- @FlaggedApi(android.security.Flags.FLAG_AAPM_API)
public static final String ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG =
"android.security.advancedprotection.action.SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG";
@@ -147,7 +148,6 @@
*
* @hide */
@FeatureId
- @SystemApi
public static final String EXTRA_SUPPORT_DIALOG_FEATURE =
"android.security.advancedprotection.extra.SUPPORT_DIALOG_FEATURE";
@@ -157,37 +157,41 @@
*
* @hide */
@SupportDialogType
- @SystemApi
public static final String EXTRA_SUPPORT_DIALOG_TYPE =
"android.security.advancedprotection.extra.SUPPORT_DIALOG_TYPE";
/**
+ * Type for {@link #EXTRA_SUPPORT_DIALOG_TYPE} indicating an unknown action was blocked by
+ * advanced protection, hence the support dialog should display a default explanation.
+ *
+ * @hide */
+ public static final int SUPPORT_DIALOG_TYPE_UNKNOWN = 0;
+
+ /**
* Type for {@link #EXTRA_SUPPORT_DIALOG_TYPE} indicating a user performed an action that was
* blocked by advanced protection.
*
* @hide */
- @SystemApi
- public static final String SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION =
- "android.security.advancedprotection.type_blocked_interaction";
+ public static final int SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION = 1;
/**
* Type for {@link #EXTRA_SUPPORT_DIALOG_TYPE} indicating a user pressed on a setting toggle
* that was disabled by advanced protection.
*
* @hide */
- @SystemApi
- public static final String SUPPORT_DIALOG_TYPE_DISABLED_SETTING =
- "android.security.advancedprotection.type_disabled_setting";
+ public static final int SUPPORT_DIALOG_TYPE_DISABLED_SETTING = 2;
/** @hide */
- @StringDef(prefix = { "SUPPORT_DIALOG_TYPE_" }, value = {
+ @IntDef(prefix = { "SUPPORT_DIALOG_TYPE_" }, value = {
+ SUPPORT_DIALOG_TYPE_UNKNOWN,
SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION,
SUPPORT_DIALOG_TYPE_DISABLED_SETTING,
})
@Retention(RetentionPolicy.SOURCE)
public @interface SupportDialogType {}
- private static final Set<String> ALL_SUPPORT_DIALOG_TYPES = Set.of(
+ private static final Set<Integer> ALL_SUPPORT_DIALOG_TYPES = Set.of(
+ SUPPORT_DIALOG_TYPE_UNKNOWN,
SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION,
SUPPORT_DIALOG_TYPE_DISABLED_SETTING);
@@ -324,15 +328,13 @@
* disabled by advanced protection.
* @hide
*/
- @SystemApi
- public @NonNull Intent createSupportIntent(@NonNull @FeatureId String featureId,
- @Nullable @SupportDialogType String type) {
- Objects.requireNonNull(featureId);
+ public static @NonNull Intent createSupportIntent(@FeatureId int featureId,
+ @SupportDialogType int type) {
if (!ALL_FEATURE_IDS.contains(featureId)) {
throw new IllegalArgumentException(featureId + " is not a valid feature ID. See"
+ " FEATURE_ID_* APIs.");
}
- if (type != null && !ALL_SUPPORT_DIALOG_TYPES.contains(type)) {
+ if (!ALL_SUPPORT_DIALOG_TYPES.contains(type)) {
throw new IllegalArgumentException(type + " is not a valid type. See"
+ " SUPPORT_DIALOG_TYPE_* APIs.");
}
@@ -340,21 +342,19 @@
Intent intent = new Intent(ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG);
intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(EXTRA_SUPPORT_DIALOG_FEATURE, featureId);
- if (type != null) {
- intent.putExtra(EXTRA_SUPPORT_DIALOG_TYPE, type);
- }
+ intent.putExtra(EXTRA_SUPPORT_DIALOG_TYPE, type);
return intent;
}
/** @hide */
- public @NonNull Intent createSupportIntentForPolicyIdentifierOrRestriction(
- @NonNull String identifier, @Nullable @SupportDialogType String type) {
+ public static @NonNull Intent createSupportIntentForPolicyIdentifierOrRestriction(
+ @NonNull String identifier, @SupportDialogType int type) {
Objects.requireNonNull(identifier);
- if (type != null && !ALL_SUPPORT_DIALOG_TYPES.contains(type)) {
+ if (!ALL_SUPPORT_DIALOG_TYPES.contains(type)) {
throw new IllegalArgumentException(type + " is not a valid type. See"
+ " SUPPORT_DIALOG_TYPE_* APIs.");
}
- final String featureId;
+ final int featureId;
if (DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY.equals(identifier)) {
featureId = FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES;
} else if (DISALLOW_CELLULAR_2G.equals(identifier)) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 8f8bfe2..d88b6d6 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -34199,7 +34199,8 @@
&& viewRootImpl.shouldCheckFrameRateCategory()
&& parent instanceof View
&& ((View) parent).getFrameContentVelocity() <= 0
- && !isInputMethodWindowType) {
+ && !isInputMethodWindowType
+ && viewRootImpl.getFrameRateCompatibility() != FRAME_RATE_COMPATIBILITY_AT_LEAST) {
return FRAME_RATE_CATEGORY_HIGH_HINT | FRAME_RATE_CATEGORY_REASON_BOOST;
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index f82e5f9..d5f471e 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -938,27 +938,6 @@
synchronized (mH) {
if (mCurRootView == viewRootImpl) {
mCurRootViewWindowFocused = false;
-
- if (Flags.refactorInsetsController() && mCurRootView != null) {
- final int softInputMode = mCurRootView.mWindowAttributes.softInputMode;
- final int state =
- softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE;
- if (state == WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN) {
- // when losing focus (e.g., by going to another window), we reset the
- // requestedVisibleTypes of WindowInsetsController by hiding the IME
- final var statsToken = ImeTracker.forLogging().onStart(
- ImeTracker.TYPE_HIDE, ImeTracker.ORIGIN_CLIENT,
- SoftInputShowHideReason.HIDE_WINDOW_LOST_FOCUS,
- false /* fromUser */);
- if (DEBUG) {
- Log.d(TAG, "onWindowLostFocus, hiding IME because "
- + "of STATE_ALWAYS_HIDDEN");
- }
- mCurRootView.getInsetsController().hide(WindowInsets.Type.ime(),
- false /* fromIme */, statsToken);
- }
- }
-
clearCurRootViewIfNeeded();
}
}
@@ -1012,6 +991,26 @@
@GuardedBy("mH")
private void setCurrentRootViewLocked(ViewRootImpl rootView) {
final boolean wasEmpty = mCurRootView == null;
+ if (Flags.refactorInsetsController() && !wasEmpty && mCurRootView != rootView) {
+ final int softInputMode = mCurRootView.mWindowAttributes.softInputMode;
+ final int state =
+ softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE;
+ if (state == WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN) {
+ // when losing input focus (e.g., by going to another window), we reset the
+ // requestedVisibleTypes of WindowInsetsController by hiding the IME
+ final var statsToken = ImeTracker.forLogging().onStart(
+ ImeTracker.TYPE_HIDE, ImeTracker.ORIGIN_CLIENT,
+ SoftInputShowHideReason.HIDE_WINDOW_LOST_FOCUS,
+ false /* fromUser */);
+ if (DEBUG) {
+ Log.d(TAG, "setCurrentRootViewLocked, hiding IME because "
+ + "of STATE_ALWAYS_HIDDEN");
+ }
+ mCurRootView.getInsetsController().hide(WindowInsets.Type.ime(),
+ false /* fromIme */, statsToken);
+ }
+ }
+
mImeDispatcher.switchRootView(mCurRootView, rootView);
mCurRootView = rootView;
if (wasEmpty && mCurRootView != null) {
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
index 0d04961..a42759e 100644
--- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
+++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
@@ -2,10 +2,10 @@
container: "system"
flag {
- name: "disable_thin_letterboxing_policy"
+ name: "ignore_aspect_ratio_restrictions_for_resizeable_freeform_activities"
namespace: "large_screen_experiences_app_compat"
- description: "Whether reachability is disabled in case of thin letterboxing"
- bug: "341027847"
+ description: "If a resizeable activity enters freeform mode, ignore all aspect ratio constraints."
+ bug: "381866902"
metadata {
purpose: PURPOSE_BUGFIX
}
@@ -73,16 +73,6 @@
}
flag {
- name: "immersive_app_repositioning"
- namespace: "large_screen_experiences_app_compat"
- description: "Fix immersive apps changing size when repositioning"
- bug: "334076352"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "camera_compat_for_freeform"
namespace: "large_screen_experiences_app_compat"
description: "Whether to apply Camera Compat treatment to fixed-orientation apps in freeform windowing mode"
@@ -155,4 +145,14 @@
description: "Whether the API for forcing apps to be universal resizable on virtual display is available"
bug: "372848702"
is_exported: true
+}
+
+flag {
+ name: "release_user_aspect_ratio_wm"
+ namespace: "large_screen_experiences_app_compat"
+ description: "Whether to release UserAspectRatioSettingsWindowManager when button is hidden"
+ bug: "385049711"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
\ No newline at end of file
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 9d11d14..c1ed512 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -368,17 +368,6 @@
}
flag {
- name: "disallow_app_progress_embedded_window"
- namespace: "windowing_frontend"
- description: "Pilfer pointers when app transfer input gesture to embedded window."
- bug: "365504126"
- is_fixed_read_only: true
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "predictive_back_system_override_callback"
namespace: "windowing_frontend"
description: "Provide pre-make predictive back API extension"
@@ -430,4 +419,15 @@
description: "Support insets definition and calculation relative to task bounds."
bug: "277292497"
is_fixed_read_only: true
+}
+
+flag {
+ name: "exclude_drawing_app_theme_snapshot_from_lock"
+ namespace: "windowing_frontend"
+ description: "Do not hold wm lock when drawing app theme snapshot."
+ is_fixed_read_only: true
+ bug: "373502791"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
\ No newline at end of file
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 2cfc680..f01aa80 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -163,4 +163,5 @@
void finishOperationForDevice(IBinder clientId, int code, int uid, String packageName,
@nullable String attributionTag, int virtualDeviceId);
List<AppOpsManager.PackageOps> getPackagesForOpsForDevice(in int[] ops, String persistentDeviceId);
+ oneway void noteOperationsInBatch(in Map batchedNoteOps);
}
diff --git a/core/java/com/android/internal/app/ProcessMap.java b/core/java/com/android/internal/app/ProcessMap.java
index 542b6d0..b4945e7 100644
--- a/core/java/com/android/internal/app/ProcessMap.java
+++ b/core/java/com/android/internal/app/ProcessMap.java
@@ -28,6 +28,11 @@
if (uids == null) return null;
return uids.get(uid);
}
+
+ public SparseArray<E> get(String name) {
+ SparseArray<E> uids = mMap.get(name);
+ return uids;
+ }
public E put(String name, int uid, E value) {
SparseArray<E> uids = mMap.get(name);
diff --git a/core/java/com/android/internal/util/LATENCY_TRACKER_OWNERS b/core/java/com/android/internal/util/LATENCY_TRACKER_OWNERS
index 7755000..3ed902f 100644
--- a/core/java/com/android/internal/util/LATENCY_TRACKER_OWNERS
+++ b/core/java/com/android/internal/util/LATENCY_TRACKER_OWNERS
@@ -1,3 +1,5 @@
# TODO(b/274465475): Migrate LatencyTracker testing to its own module
marcinoc@google.com
ilkos@google.com
+jjaggi@google.com
+nicomazz@google.com
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 5acdf32..027113a 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -482,22 +482,11 @@
"libbinder",
"libhidlbase", // libhwbinder is in here
],
- version_script: "platform/linux/libandroid_runtime_export.txt",
- },
- darwin: {
- host_ldlibs: [
- "-framework AppKit",
- ],
- dist: {
- targets: ["layoutlib_jni"],
- dir: "layoutlib_native/darwin",
- },
- exported_symbols_list: "platform/darwin/libandroid_runtime_export.exp",
},
linux_glibc_x86_64: {
ldflags: ["-static-libgcc"],
dist: {
- targets: ["layoutlib_jni"],
+ targets: ["layoutlib"],
dir: "layoutlib_native/linux",
tag: "stripped_all",
},
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 8003bb7..639f5bf 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -1706,6 +1706,10 @@
return res;
}
+static jboolean android_os_BinderProxy_frozenStateChangeCallbackSupported(JNIEnv*, jclass*) {
+ return ProcessState::isDriverFeatureEnabled(ProcessState::DriverFeature::FREEZE_NOTIFICATION);
+}
+
static void BinderProxy_destroy(void* rawNativeData)
{
BinderProxyNativeData * nativeData = (BinderProxyNativeData *) rawNativeData;
@@ -1750,6 +1754,8 @@
"(Landroid/os/IBinder$FrozenStateChangeCallback;)V", (void*)android_os_BinderProxy_addFrozenStateChangeCallback},
{"removeFrozenStateChangeCallbackNative",
"(Landroid/os/IBinder$FrozenStateChangeCallback;)Z", (void*)android_os_BinderProxy_removeFrozenStateChangeCallback},
+ {"isFrozenStateChangeCallbackSupportedNative",
+ "()Z", (void*)android_os_BinderProxy_frozenStateChangeCallbackSupported},
{"getNativeFinalizer", "()J", (void*)android_os_BinderProxy_getNativeFinalizer},
{"getExtension", "()Landroid/os/IBinder;", (void*)android_os_BinderProxy_getExtension},
};
diff --git a/core/jni/platform/darwin/libandroid_runtime_export.exp b/core/jni/platform/darwin/libandroid_runtime_export.exp
deleted file mode 100644
index 00a7585..0000000
--- a/core/jni/platform/darwin/libandroid_runtime_export.exp
+++ /dev/null
@@ -1,38 +0,0 @@
-#
-# Copyright (C) 2024 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# symbols needed for the JNI operations
-_JNI_OnLoad
-_ANativeWindow*
-
-# symbols needed to link with layoutlib_jni
-___android_log*
-__ZNK7android7RefBase*
-__ZN7android4base9SetLogger*
-__ZN7android4base10SetAborter*
-__ZN7android4base11GetProperty*
-__ZN7android4Rect*
-__ZN7android5Fence*
-__ZN7android7RefBase*
-__ZN7android7String*
-__ZN7android10VectorImpl*
-__ZN7android11BufferQueue*
-__ZN7android14AndroidRuntime*
-__ZN7android14sp_report_raceEv*
-__ZN7android15KeyCharacterMap*
-__ZN7android15InputDeviceInfo*
-__ZN7android31android_view_InputDevice_create*
-__ZN7android53android_view_Surface_createFromIGraphicBufferProducer*
diff --git a/core/jni/platform/linux/libandroid_runtime_export.txt b/core/jni/platform/linux/libandroid_runtime_export.txt
deleted file mode 100644
index 19e3478..0000000
--- a/core/jni/platform/linux/libandroid_runtime_export.txt
+++ /dev/null
@@ -1,43 +0,0 @@
-#
-# Copyright (C) 2024 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-{
- global:
- # symbols needed for the JNI operations
- JNI_OnLoad;
- ANativeWindow*;
-
- # symbols needed to link with layoutlib_jni
- __android_log*;
- _ZNK7android7RefBase*;
- _ZN7android4base9SetLogger*;
- _ZN7android4base10SetAborter*;
- _ZN7android4base11GetProperty*;
- _ZN7android4Rect*;
- _ZN7android5Fence*;
- _ZN7android7RefBase*;
- _ZN7android7String*;
- _ZN7android10VectorImpl*;
- _ZN7android11BufferQueue*;
- _ZN7android14AndroidRuntime*;
- _ZN7android14sp_report_raceEv*;
- _ZN7android15KeyCharacterMap*;
- _ZN7android15InputDeviceInfo*;
- _ZN7android31android_view_InputDevice_create*;
- _ZN7android53android_view_Surface_createFromIGraphicBufferProducer*;
- local:
- *;
-};
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index df98952..ed05e6d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -8780,6 +8780,20 @@
android:featureFlag="com.android.art.flags.executable_method_file_offsets" />
<!--
+ @SystemApi
+ @FlaggedApi(android.content.pm.Flags.FLAG_UID_BASED_PROVIDER_LOOKUP)
+ Allows an app to resolve components (e.g ContentProviders) on behalf of
+ other UIDs
+ <p>Protection level: signature|privileged
+ @hide
+ -->
+ <permission
+ android:name="android.permission.RESOLVE_COMPONENT_FOR_UID"
+ android:protectionLevel="signature|privileged"
+ android:featureFlag="android.content.pm.uid_based_provider_lookup" />
+ <uses-permission android:name="android.permission.RESOLVE_COMPONENT_FOR_UID" />
+
+ <!--
@TestApi
Signature permission reserved for testing. This should never be used to
gate any actual functionality.
diff --git a/core/tests/coretests/res/color/color_with_lstar.xml b/core/tests/coretests/res/color/color_with_lstar.xml
index dcc3d6d..7762fc0 100644
--- a/core/tests/coretests/res/color/color_with_lstar.xml
+++ b/core/tests/coretests/res/color/color_with_lstar.xml
@@ -16,5 +16,5 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="#ff0000" android:lStar="50" />
+ <item android:color="@color/testcolor2" android:lStar="50" />
</selector>
diff --git a/core/tests/coretests/res/values/colors.xml b/core/tests/coretests/res/values/colors.xml
index 029aa0d..f01af84 100644
--- a/core/tests/coretests/res/values/colors.xml
+++ b/core/tests/coretests/res/values/colors.xml
@@ -25,6 +25,5 @@
<drawable name="yellow">#ffffff00</drawable>
<color name="testcolor1">#ff00ff00</color>
<color name="testcolor2">#ffff0000</color>
- <color name="testcolor3">#fff00000</color>
<color name="failColor">#ff0000ff</color>
</resources>
diff --git a/core/tests/coretests/src/android/app/BackgroundStartPrivilegesTest.java b/core/tests/coretests/src/android/app/BackgroundStartPrivilegesTest.java
index cf6266c..931d646 100644
--- a/core/tests/coretests/src/android/app/BackgroundStartPrivilegesTest.java
+++ b/core/tests/coretests/src/android/app/BackgroundStartPrivilegesTest.java
@@ -119,4 +119,15 @@
Arrays.asList(BSP_ALLOW_A, BSP_ALLOW_A, BSP_ALLOW_A, BSP_ALLOW_A)))
.isEqualTo(BSP_ALLOW_A);
}
+
+ @Test
+ public void backgroundStartPrivilege_equals_works() {
+ assertThat(NONE).isEqualTo(NONE);
+ assertThat(ALLOW_BAL).isEqualTo(ALLOW_BAL);
+ assertThat(ALLOW_FGS).isEqualTo(ALLOW_FGS);
+ assertThat(BSP_ALLOW_A).isEqualTo(BSP_ALLOW_A);
+ assertThat(NONE).isNotEqualTo(ALLOW_BAL);
+ assertThat(ALLOW_FGS).isNotEqualTo(ALLOW_BAL);
+ assertThat(BSP_ALLOW_A).isNotEqualTo(BSP_ALLOW_B);
+ }
}
diff --git a/core/tests/coretests/src/android/graphics/BitmapFactoryTest.java b/core/tests/coretests/src/android/graphics/BitmapFactoryTest.java
index 564460e..84bdbe0 100644
--- a/core/tests/coretests/src/android/graphics/BitmapFactoryTest.java
+++ b/core/tests/coretests/src/android/graphics/BitmapFactoryTest.java
@@ -16,19 +16,27 @@
package android.graphics;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
import android.os.ParcelFileDescriptor;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import junit.framework.TestCase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.io.ByteArrayOutputStream;
import java.io.FileDescriptor;
-public class BitmapFactoryTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+public class BitmapFactoryTest {
// tests that we can decode bitmaps from MemoryFiles
@SmallTest
+ @Test
public void testBitmapParcelFileDescriptor() throws Exception {
Bitmap bitmap1 = Bitmap.createBitmap(
new int[] { Color.BLUE }, 1, 1, Bitmap.Config.RGB_565);
diff --git a/core/tests/coretests/src/android/graphics/BitmapTest.java b/core/tests/coretests/src/android/graphics/BitmapTest.java
index 2280cf1..0126d36 100644
--- a/core/tests/coretests/src/android/graphics/BitmapTest.java
+++ b/core/tests/coretests/src/android/graphics/BitmapTest.java
@@ -16,19 +16,28 @@
package android.graphics;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
import android.hardware.HardwareBuffer;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import junit.framework.TestCase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
-public class BitmapTest extends TestCase {
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BitmapTest {
- @SmallTest
+ @Test
public void testBasic() throws Exception {
Bitmap bm1 = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
Bitmap bm2 = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
@@ -63,7 +72,7 @@
assertTrue("getConfig", bm3.getConfig() == Bitmap.Config.ARGB_8888);
}
- @SmallTest
+ @Test
public void testMutability() throws Exception {
Bitmap bm1 = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
Bitmap bm2 = Bitmap.createBitmap(new int[100 * 200], 100, 200,
@@ -82,7 +91,7 @@
}
}
- @SmallTest
+ @Test
public void testGetPixelsWithAlpha() throws Exception {
int[] colors = new int[100];
for (int i = 0; i < 100; i++) {
@@ -108,7 +117,7 @@
}
- @SmallTest
+ @Test
public void testGetPixelsWithoutAlpha() throws Exception {
int[] colors = new int[100];
for (int i = 0; i < 100; i++) {
@@ -125,7 +134,7 @@
}
}
- @SmallTest
+ @Test
public void testSetPixelsWithAlpha() throws Exception {
int[] colors = new int[100];
for (int i = 0; i < 100; i++) {
@@ -151,7 +160,7 @@
}
}
- @SmallTest
+ @Test
public void testSetPixelsWithoutAlpha() throws Exception {
int[] colors = new int[100];
for (int i = 0; i < 100; i++) {
@@ -181,7 +190,7 @@
return unpre;
}
- @SmallTest
+ @Test
public void testSetPixelsWithNonOpaqueAlpha() throws Exception {
int[] colors = new int[256];
for (int i = 0; i < 256; i++) {
@@ -238,10 +247,13 @@
}
}
- @SmallTest
+ private static final int GRAPHICS_USAGE =
+ GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_SW_READ_OFTEN
+ | GraphicBuffer.USAGE_SW_WRITE_OFTEN;
+
+ @Test
public void testWrapHardwareBufferWithSrgbColorSpace() {
- GraphicBuffer buffer = GraphicBuffer.create(10, 10, PixelFormat.RGBA_8888,
- GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_SOFTWARE_MASK);
+ GraphicBuffer buffer = GraphicBuffer.create(10, 10, PixelFormat.RGBA_8888, GRAPHICS_USAGE);
Canvas canvas = buffer.lockCanvas();
canvas.drawColor(Color.YELLOW);
buffer.unlockCanvasAndPost(canvas);
@@ -252,10 +264,9 @@
assertEquals(ColorSpace.get(ColorSpace.Named.SRGB), hardwareBitmap.getColorSpace());
}
- @SmallTest
+ @Test
public void testWrapHardwareBufferWithDisplayP3ColorSpace() {
- GraphicBuffer buffer = GraphicBuffer.create(10, 10, PixelFormat.RGBA_8888,
- GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_SOFTWARE_MASK);
+ GraphicBuffer buffer = GraphicBuffer.create(10, 10, PixelFormat.RGBA_8888, GRAPHICS_USAGE);
Canvas canvas = buffer.lockCanvas();
canvas.drawColor(Color.YELLOW);
buffer.unlockCanvasAndPost(canvas);
@@ -267,7 +278,7 @@
assertEquals(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), hardwareBitmap.getColorSpace());
}
- @SmallTest
+ @Test
public void testCopyWithDirectByteBuffer() {
// Initialize Bitmap
final int width = 2;
@@ -305,7 +316,7 @@
assertTrue(bm2.sameAs(bm1));
}
- @SmallTest
+ @Test
public void testCopyWithDirectShortBuffer() {
// Initialize Bitmap
final int width = 2;
@@ -344,7 +355,7 @@
assertTrue(bm2.sameAs(bm1));
}
- @SmallTest
+ @Test
public void testCopyWithDirectIntBuffer() {
// Initialize Bitmap
final int width = 2;
@@ -383,7 +394,7 @@
assertTrue(bm2.sameAs(bm1));
}
- @SmallTest
+ @Test
public void testCopyWithHeapByteBuffer() {
// Initialize Bitmap
final int width = 2;
@@ -420,7 +431,7 @@
assertTrue(bm2.sameAs(bm1));
}
- @SmallTest
+ @Test
public void testCopyWithHeapShortBuffer() {
// Initialize Bitmap
final int width = 2;
@@ -457,7 +468,7 @@
assertTrue(bm2.sameAs(bm1));
}
- @SmallTest
+ @Test
public void testCopyWithHeapIntBuffer() {
// Initialize Bitmap
final int width = 2;
diff --git a/core/tests/coretests/src/android/graphics/ColorStateListTest.java b/core/tests/coretests/src/android/graphics/ColorStateListTest.java
index ab41bd0..5cc915e 100644
--- a/core/tests/coretests/src/android/graphics/ColorStateListTest.java
+++ b/core/tests/coretests/src/android/graphics/ColorStateListTest.java
@@ -16,33 +16,41 @@
package android.graphics;
+import static org.junit.Assert.assertEquals;
+
import android.content.res.ColorStateList;
import android.content.res.Resources;
-import android.test.AndroidTestCase;
import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.frameworks.coretests.R;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
/**
- * Tests of {@link android.graphics.ColorStateList}
+ * Tests of {@link ColorStateList}
*/
-public class ColorStateListTest extends AndroidTestCase {
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ColorStateListTest {
private Resources mResources;
private int mFailureColor;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mResources = mContext.getResources();
+ @Before
+ public void setUp() throws Exception {
+ mResources = InstrumentationRegistry.getInstrumentation().getContext().getResources();
mFailureColor = mResources.getColor(R.color.failColor);
}
- @SmallTest
+ @Test
public void testStateIsInList() throws Exception {
ColorStateList colorStateList = mResources.getColorStateList(R.color.color1);
int[] focusedState = {android.R.attr.state_focused};
@@ -50,7 +58,7 @@
assertEquals(mResources.getColor(R.color.testcolor1), focusColor);
}
- @SmallTest
+ @Test
public void testStateIsInList_proto() throws Exception {
ColorStateList colorStateList = recreateFromProto(
mResources.getColorStateList(R.color.color1));
@@ -59,7 +67,7 @@
assertEquals(mResources.getColor(R.color.testcolor1), focusColor);
}
- @SmallTest
+ @Test
public void testEmptyState() throws Exception {
ColorStateList colorStateList = mResources.getColorStateList(R.color.color1);
int[] emptyState = {};
@@ -67,7 +75,7 @@
assertEquals(mResources.getColor(R.color.testcolor2), defaultColor);
}
- @SmallTest
+ @Test
public void testEmptyState_proto() throws Exception {
ColorStateList colorStateList = recreateFromProto(
mResources.getColorStateList(R.color.color1));
@@ -76,22 +84,23 @@
assertEquals(mResources.getColor(R.color.testcolor2), defaultColor);
}
- @SmallTest
+ @Test
public void testGetColor() throws Exception {
int defaultColor = mResources.getColor(R.color.color1);
assertEquals(mResources.getColor(R.color.testcolor2), defaultColor);
}
- @SmallTest
+ @Test
public void testGetColorWhenListHasNoDefault() throws Exception {
int defaultColor = mResources.getColor(R.color.color_no_default);
assertEquals(mResources.getColor(R.color.testcolor1), defaultColor);
}
- @SmallTest
+ @Test
public void testLstar() throws Exception {
+ var cl = ColorStateList.valueOf(mResources.getColor(R.color.testcolor2)).withLStar(50.0f);
int defaultColor = mResources.getColor(R.color.color_with_lstar);
- assertEquals(mResources.getColor(R.color.testcolor3), defaultColor);
+ assertEquals(cl.getDefaultColor(), defaultColor);
}
private ColorStateList recreateFromProto(ColorStateList colorStateList) throws Exception {
diff --git a/core/tests/coretests/src/android/graphics/FontFileUtilTest.java b/core/tests/coretests/src/android/graphics/FontFileUtilTest.java
index 52cc4ca..063bdf5 100644
--- a/core/tests/coretests/src/android/graphics/FontFileUtilTest.java
+++ b/core/tests/coretests/src/android/graphics/FontFileUtilTest.java
@@ -30,9 +30,11 @@
import android.util.Pair;
import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import org.junit.Test;
+import org.junit.runner.RunWith;
import java.io.File;
import java.io.FileInputStream;
@@ -43,6 +45,7 @@
import java.nio.channels.FileChannel;
@SmallTest
+@RunWith(AndroidJUnit4.class)
public class FontFileUtilTest {
private static final String TAG = "FontFileUtilTest";
private static final String CACHE_FILE_PREFIX = ".font";
diff --git a/core/tests/coretests/src/android/graphics/PaintFontVariationTest.java b/core/tests/coretests/src/android/graphics/PaintFontVariationTest.java
index 8a54e5b..816bde6 100644
--- a/core/tests/coretests/src/android/graphics/PaintFontVariationTest.java
+++ b/core/tests/coretests/src/android/graphics/PaintFontVariationTest.java
@@ -21,10 +21,9 @@
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.test.InstrumentationTestCase;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.text.flags.Flags;
@@ -37,7 +36,7 @@
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class PaintFontVariationTest extends InstrumentationTestCase {
+public class PaintFontVariationTest {
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
diff --git a/core/tests/coretests/src/android/graphics/PaintTest.java b/core/tests/coretests/src/android/graphics/PaintTest.java
index 878ba70..56760d7 100644
--- a/core/tests/coretests/src/android/graphics/PaintTest.java
+++ b/core/tests/coretests/src/android/graphics/PaintTest.java
@@ -18,19 +18,26 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.test.InstrumentationTestCase;
import android.text.TextUtils;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.text.flags.Flags;
import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.Arrays;
import java.util.HashSet;
@@ -38,13 +45,14 @@
/**
* PaintTest tests {@link Paint}.
*/
-public class PaintTest extends InstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class PaintTest {
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
private static final String FONT_PATH = "fonts/HintedAdvanceWidthTest-Regular.ttf";
- static void assertEquals(String message, float[] expected, float[] actual) {
+ static void assertFloatArrayEquals(String message, float[] expected, float[] actual) {
if (expected.length != actual.length) {
fail(message + " expected array length:<" + expected.length + "> but was:<"
+ actual.length + ">");
@@ -88,9 +96,10 @@
};
@SmallTest
+ @Test
public void testHintingWidth() {
final Typeface fontTypeface = Typeface.createFromAsset(
- getInstrumentation().getContext().getAssets(), FONT_PATH);
+ InstrumentationRegistry.getInstrumentation().getContext().getAssets(), FONT_PATH);
Paint paint = new Paint();
paint.setTypeface(fontTypeface);
@@ -103,12 +112,14 @@
paint.setHinting(Paint.HINTING_OFF);
paint.getTextWidths(String.valueOf(testCase.mText), widths);
- assertEquals("Text width of '" + testCase.mText + "' without hinting is not expected.",
+ assertFloatArrayEquals(
+ "Text width of '" + testCase.mText + "' without hinting is not expected.",
testCase.mWidthWithoutHinting, widths);
paint.setHinting(Paint.HINTING_ON);
paint.getTextWidths(String.valueOf(testCase.mText), widths);
- assertEquals("Text width of '" + testCase.mText + "' with hinting is not expected.",
+ assertFloatArrayEquals(
+ "Text width of '" + testCase.mText + "' with hinting is not expected.",
testCase.mWidthWithHinting, widths);
}
}
@@ -131,9 +142,11 @@
return sb.toString();
}
+ @Test
public void testHasGlyph_variationSelectors() {
final Typeface fontTypeface = Typeface.createFromAsset(
- getInstrumentation().getContext().getAssets(), "fonts/hasGlyphTestFont.ttf");
+ InstrumentationRegistry.getInstrumentation().getContext().getAssets(),
+ "fonts/hasGlyphTestFont.ttf");
Paint p = new Paint();
p.setTypeface(fontTypeface);
@@ -175,6 +188,7 @@
}
}
+ @Test
public void testGetTextRunAdvances() {
{
// LTR
@@ -231,6 +245,7 @@
}
}
+ @Test
public void testGetTextRunAdvances_invalid() {
Paint p = new Paint();
char[] text = "test".toCharArray();
@@ -284,6 +299,7 @@
}
}
+ @Test
public void testMeasureTextBidi() {
Paint p = new Paint();
{
@@ -340,18 +356,21 @@
}
}
+ @Test
public void testSetGetWordSpacing() {
Paint p = new Paint();
- assertEquals(0.0f, p.getWordSpacing()); // The default value should be 0.
+ assertEquals(0.0f, p.getWordSpacing(), 0.0f); // The default value should be 0.
p.setWordSpacing(1.0f);
- assertEquals(1.0f, p.getWordSpacing());
+ assertEquals(1.0f, p.getWordSpacing(), 0.0f);
p.setWordSpacing(-2.0f);
- assertEquals(-2.0f, p.getWordSpacing());
+ assertEquals(-2.0f, p.getWordSpacing(), 0.0f);
}
+ @Test
public void testGetUnderlinePositionAndThickness() {
final Typeface fontTypeface = Typeface.createFromAsset(
- getInstrumentation().getContext().getAssets(), "fonts/underlineTestFont.ttf");
+ InstrumentationRegistry.getInstrumentation().getContext().getAssets(),
+ "fonts/underlineTestFont.ttf");
final Paint p = new Paint();
final int textSize = 100;
p.setTextSize(textSize);
@@ -391,6 +410,7 @@
return ccByChars;
}
+ @Test
public void testCluster() {
final Paint p = new Paint();
p.setTextSize(100);
@@ -417,6 +437,7 @@
}
@RequiresFlagsEnabled(Flags.FLAG_TYPEFACE_CACHE_FOR_VAR_SETTINGS)
+ @Test
public void testDerivedFromSameTypeface() {
final Paint p = new Paint();
@@ -432,6 +453,7 @@
}
@RequiresFlagsEnabled(Flags.FLAG_TYPEFACE_CACHE_FOR_VAR_SETTINGS)
+ @Test
public void testDerivedFromChained() {
final Paint p = new Paint();
diff --git a/core/tests/coretests/src/android/graphics/ThreadBitmapTest.java b/core/tests/coretests/src/android/graphics/ThreadBitmapTest.java
index e1ca7df..fbaf502 100644
--- a/core/tests/coretests/src/android/graphics/ThreadBitmapTest.java
+++ b/core/tests/coretests/src/android/graphics/ThreadBitmapTest.java
@@ -16,17 +16,17 @@
package android.graphics;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
-import junit.framework.TestCase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
-public class ThreadBitmapTest extends TestCase {
-
- @Override
- protected void setUp() throws Exception {
- }
+@RunWith(AndroidJUnit4.class)
+public class ThreadBitmapTest {
@LargeTest
+ @Test
public void testCreation() {
for (int i = 0; i < 200; i++) {
@@ -44,4 +44,3 @@
public void run() {}
}
}
-
diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
index 1429272..2b6eda8f 100644
--- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
+++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
@@ -39,6 +39,8 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.text.flags.Flags;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -514,9 +516,14 @@
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
paint.setElegantTextHeight(false);
- assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
- assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f);
- assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+ if (Flags.deprecateElegantTextHeightApi()) {
+ // Calling setElegantTextHeight is no-op.
+ assertTrue(paint.isElegantTextHeight());
+ } else {
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
+ assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+ }
}
@Test
@@ -553,9 +560,14 @@
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
paint.setElegantTextHeight(false);
- assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
- assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
- assertEquals(GLYPH_3EM_WIDTH, paint.measureText("c"), 0.0f);
+ if (Flags.deprecateElegantTextHeightApi()) {
+ // Calling setElegantTextHeight is no-op.
+ assertTrue(paint.isElegantTextHeight());
+ } else {
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
+ assertEquals(GLYPH_3EM_WIDTH, paint.measureText("c"), 0.0f);
+ }
testTypeface = fontMap.get("sans-serif");
assertNotNull(testTypeface);
@@ -566,9 +578,14 @@
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
paint.setElegantTextHeight(false);
- assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
- assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
- assertEquals(GLYPH_3EM_WIDTH, paint.measureText("c"), 0.0f);
+ if (Flags.deprecateElegantTextHeightApi()) {
+ // Calling setElegantTextHeight is no-op.
+ assertTrue(paint.isElegantTextHeight());
+ } else {
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
+ assertEquals(GLYPH_3EM_WIDTH, paint.measureText("c"), 0.0f);
+ }
}
@Test
diff --git a/core/tests/coretests/src/android/security/advancedprotection/AdvancedProtectionManagerTest.java b/core/tests/coretests/src/android/security/advancedprotection/AdvancedProtectionManagerTest.java
new file mode 100644
index 0000000..45864b0
--- /dev/null
+++ b/core/tests/coretests/src/android/security/advancedprotection/AdvancedProtectionManagerTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.security.advancedprotection;
+
+import static android.security.advancedprotection.AdvancedProtectionManager.ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG;
+import static android.security.advancedprotection.AdvancedProtectionManager.EXTRA_SUPPORT_DIALOG_FEATURE;
+import static android.security.advancedprotection.AdvancedProtectionManager.EXTRA_SUPPORT_DIALOG_TYPE;
+import static android.security.advancedprotection.AdvancedProtectionManager.FEATURE_ID_DISALLOW_CELLULAR_2G;
+import static android.security.advancedprotection.AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION;
+import static android.security.advancedprotection.AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_DISABLED_SETTING;
+import static android.security.advancedprotection.AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_UNKNOWN;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import android.content.Intent;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class AdvancedProtectionManagerTest {
+ private static final int FEATURE_ID_INVALID = -1;
+ private static final int SUPPORT_DIALOG_TYPE_INVALID = -1;
+
+ @Test
+ public void testCreateSupportIntent_validFeature_validTypeUnknown_createsIntent() {
+ Intent intent = AdvancedProtectionManager.createSupportIntent(
+ FEATURE_ID_DISALLOW_CELLULAR_2G, SUPPORT_DIALOG_TYPE_UNKNOWN);
+
+ assertEquals(ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG, intent.getAction());
+ assertEquals(FEATURE_ID_DISALLOW_CELLULAR_2G, intent.getIntExtra(
+ EXTRA_SUPPORT_DIALOG_FEATURE, FEATURE_ID_INVALID));
+ assertEquals(SUPPORT_DIALOG_TYPE_UNKNOWN, intent.getIntExtra(EXTRA_SUPPORT_DIALOG_TYPE,
+ SUPPORT_DIALOG_TYPE_INVALID));
+ }
+
+ @Test
+ public void testCreateSupportIntent_validFeature_validTypeBlockedInteraction_createsIntent() {
+ Intent intent = AdvancedProtectionManager.createSupportIntent(
+ FEATURE_ID_DISALLOW_CELLULAR_2G, SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION);
+
+ assertEquals(ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG, intent.getAction());
+ assertEquals(FEATURE_ID_DISALLOW_CELLULAR_2G, intent.getIntExtra(
+ EXTRA_SUPPORT_DIALOG_FEATURE, FEATURE_ID_INVALID));
+ assertEquals(SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION, intent.getIntExtra(
+ EXTRA_SUPPORT_DIALOG_TYPE, SUPPORT_DIALOG_TYPE_INVALID));
+ }
+
+ @Test
+ public void testCreateSupportIntent_validFeature_validTypeDisabledSetting_createsIntent() {
+ Intent intent = AdvancedProtectionManager.createSupportIntent(
+ FEATURE_ID_DISALLOW_CELLULAR_2G, SUPPORT_DIALOG_TYPE_DISABLED_SETTING);
+
+ assertEquals(ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG, intent.getAction());
+ assertEquals(FEATURE_ID_DISALLOW_CELLULAR_2G, intent.getIntExtra(
+ EXTRA_SUPPORT_DIALOG_FEATURE, FEATURE_ID_INVALID));
+ assertEquals(SUPPORT_DIALOG_TYPE_DISABLED_SETTING, intent.getIntExtra(
+ EXTRA_SUPPORT_DIALOG_TYPE, SUPPORT_DIALOG_TYPE_INVALID));
+ }
+
+ @Test
+ public void testCreateSupportIntent_validFeature_invalidType_throwsIllegalArgument() {
+ assertThrows(IllegalArgumentException.class, () ->
+ AdvancedProtectionManager.createSupportIntent(FEATURE_ID_DISALLOW_CELLULAR_2G,
+ SUPPORT_DIALOG_TYPE_INVALID));
+ }
+
+ @Test
+ public void testCreateSupportIntent_invalidFeature_validType_throwsIllegalArgument() {
+ assertThrows(IllegalArgumentException.class, () ->
+ AdvancedProtectionManager.createSupportIntent(FEATURE_ID_INVALID,
+ SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION));
+ }
+
+ @Test
+ public void testCreateSupportIntent_invalidFeature_invalidType_throwsIllegalArgument() {
+ assertThrows(IllegalArgumentException.class, () ->
+ AdvancedProtectionManager.createSupportIntent(FEATURE_ID_INVALID,
+ SUPPORT_DIALOG_TYPE_INVALID));
+ }
+}
diff --git a/core/tests/coretests/src/android/view/ViewFrameRateTest.java b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
index fb1efa8..8b4f714 100644
--- a/core/tests/coretests/src/android/view/ViewFrameRateTest.java
+++ b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
@@ -1171,6 +1171,88 @@
waitForAfterDraw();
}
+ @Test
+ public void ignoreHeuristicWhenFling() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
+
+ waitForFrameRateCategoryToSettle();
+ FrameLayout host = new FrameLayout(mActivity);
+ View childView = new View(mActivity);
+ float velocity = 1000;
+
+ TranslateAnimation translateAnimation = new TranslateAnimation(
+ Animation.RELATIVE_TO_PARENT, 0f, // fromXDelta
+ Animation.RELATIVE_TO_PARENT, 0f, // toXDelta
+ Animation.RELATIVE_TO_PARENT, 1f, // fromYDelta (100%p)
+ Animation.RELATIVE_TO_PARENT, 0f // toYDelta
+ );
+ translateAnimation.setDuration(100);
+
+ mActivityRule.runOnUiThread(() -> {
+ ViewGroup.LayoutParams fullSize = new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT);
+ mActivity.setContentView(host, fullSize);
+ host.setFrameContentVelocity(velocity);
+ ViewGroupOverlay overlay = host.getOverlay();
+ overlay.add(childView);
+ assertEquals(velocity, host.getFrameContentVelocity());
+ assertEquals(host.getFrameContentVelocity(),
+ ((View) childView.getParent()).getFrameContentVelocity());
+
+ mMovingView.startAnimation(translateAnimation);
+
+ // The frame rate should be "Normal" during fling gestures,
+ // even if there's a moving View.
+ assertEquals(FRAME_RATE_CATEGORY_NORMAL,
+ mViewRoot.getLastPreferredFrameRateCategory());
+ });
+ waitForAfterDraw();
+ }
+
+ @Test
+ public void ignoreHeuristicWhenFlingMovementFirst() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
+
+ waitForFrameRateCategoryToSettle();
+ FrameLayout host = new FrameLayout(mActivity);
+ View childView = new View(mActivity);
+ float velocity = 1000;
+
+ TranslateAnimation translateAnimation = new TranslateAnimation(
+ Animation.RELATIVE_TO_PARENT, 0f, // fromXDelta
+ Animation.RELATIVE_TO_PARENT, 0f, // toXDelta
+ Animation.RELATIVE_TO_PARENT, 1f, // fromYDelta (100%p)
+ Animation.RELATIVE_TO_PARENT, 0f // toYDelta
+ );
+ translateAnimation.setDuration(100);
+
+ mActivityRule.runOnUiThread(() -> {
+ mMovingView.startAnimation(translateAnimation);
+
+ ViewGroup.LayoutParams fullSize = new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT);
+ mActivity.setContentView(host, fullSize);
+ host.setFrameContentVelocity(velocity);
+ ViewGroupOverlay overlay = host.getOverlay();
+ overlay.add(childView);
+ assertEquals(velocity, host.getFrameContentVelocity());
+ assertEquals(host.getFrameContentVelocity(),
+ ((View) childView.getParent()).getFrameContentVelocity());
+
+ // The frame rate should be "Normal" during fling gestures,
+ // even if there's a moving View.
+ assertEquals(FRAME_RATE_CATEGORY_NORMAL,
+ mViewRoot.getLastPreferredFrameRateCategory());
+ });
+ waitForAfterDraw();
+ }
+
private void runAfterDraw(@NonNull Runnable runnable) {
Handler handler = new Handler(Looper.getMainLooper());
mAfterDrawLatch = new CountDownLatch(1);
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 2398e71..f136e06 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -125,6 +125,7 @@
<permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
<permission name="android.permission.PACKAGE_USAGE_STATS"/>
<permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
+ <permission name="android.permission.RESOLVE_COMPONENT_FOR_UID"/>
</privapp-permissions>
<privapp-permissions package="com.android.phone">
@@ -609,6 +610,8 @@
<permission name="android.permission.MANAGE_INTRUSION_DETECTION_STATE" />
<!-- Permission required for CTS test - KeyguardLockedStateApiTest -->
<permission name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE" />
+ <!-- Permission required for CTS test - CtsContentProviderMultiUserTest -->
+ <permission name="android.permission.RESOLVE_COMPONENT_FOR_UID"/>
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 5f1fb4b..50054850 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -171,3 +171,10 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "task_view_repository"
+ namespace: "multitasking"
+ description: "Factor task-view state tracking out of taskviewtransitions"
+ bug: "384976265"
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java
index b141beb..1cc58c8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java
@@ -32,6 +32,7 @@
import android.widget.LinearLayout;
import android.widget.TextView;
+import com.android.window.flags.Flags;
import com.android.wm.shell.R;
/**
@@ -172,6 +173,9 @@
@Override
public void onAnimationEnd(Animator animation) {
view.setVisibility(View.GONE);
+ if (Flags.releaseUserAspectRatioWm()) {
+ mWindowManager.release();
+ }
}
});
fadeOut.start();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
index ff6fb59..ceef699 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
@@ -541,6 +541,9 @@
TASK_FINISHED(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__TASK_FINISHED),
SCREEN_OFF(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__SCREEN_OFF),
TASK_MINIMIZED(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__TASK_MINIMIZED),
+ TASK_MOVED_TO_BACK(
+ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__TASK_MOVED_TO_BACK
+ ),
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
index 9a60cfe..ccfbbee 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
@@ -404,12 +404,17 @@
wasPreviousTransitionExitByScreenOff = true
ExitReason.SCREEN_OFF
}
+ // TODO(b/384490301): differentiate back gesture / button exit from clicking the close
+ // button located in the window top corner.
+ transitionInfo.type == WindowManager.TRANSIT_TO_BACK -> ExitReason.TASK_MOVED_TO_BACK
transitionInfo.type == WindowManager.TRANSIT_CLOSE -> ExitReason.TASK_FINISHED
transitionInfo.type == TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG -> ExitReason.DRAG_TO_EXIT
transitionInfo.type == TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON ->
ExitReason.APP_HANDLE_MENU_BUTTON_EXIT
+
transitionInfo.type == TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT ->
ExitReason.KEYBOARD_SHORTCUT_EXIT
+
transitionInfo.isExitToRecentsTransition() -> ExitReason.RETURN_HOME_OR_OVERVIEW
transitionInfo.type == Transitions.TRANSIT_MINIMIZE -> ExitReason.TASK_MINIMIZED
else -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
index 45faba6..0330a5f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
@@ -127,14 +127,20 @@
override fun onTransitionStarting(transition: IBinder) {
val mActiveTaskDetails = activeTransitionTokensAndTasks[transition]
- if (mActiveTaskDetails != null && mActiveTaskDetails.transitionInfo != null) {
- // Begin minimize window CUJ instrumentation.
- interactionJankMonitor.begin(
- mActiveTaskDetails.transitionInfo?.rootLeash,
- context,
- handler,
- CUJ_DESKTOP_MODE_MINIMIZE_WINDOW,
- )
+ val info = mActiveTaskDetails?.transitionInfo ?: return
+ val minimizeChange = getMinimizeChange(info, mActiveTaskDetails.taskId) ?: return
+ // Begin minimize window CUJ instrumentation.
+ interactionJankMonitor.begin(
+ minimizeChange.leash,
+ context,
+ handler,
+ CUJ_DESKTOP_MODE_MINIMIZE_WINDOW,
+ )
+ }
+
+ private fun getMinimizeChange(info: TransitionInfo, taskId: Int): TransitionInfo.Change? {
+ return info.changes.find { change ->
+ change.taskInfo?.taskId == taskId && change.mode == TRANSIT_TO_BACK
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java
new file mode 100644
index 0000000..0d75e65
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.windowdecor;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.content.Context;
+import android.hardware.input.InputManager;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.InputDevice;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
+import com.android.wm.shell.shared.FocusTransitionListener;
+import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
+import com.android.wm.shell.shared.annotations.ShellMainThread;
+import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.FocusTransitionObserver;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
+
+/**
+ * Works with decorations that extend {@link CarWindowDecoration}.
+ */
+public abstract class CarWindowDecorViewModel
+ implements WindowDecorViewModel, FocusTransitionListener {
+ private static final String TAG = "CarWindowDecorViewModel";
+
+ private final ShellTaskOrganizer mTaskOrganizer;
+ private final Context mContext;
+ private final @ShellBackgroundThread ShellExecutor mBgExecutor;
+ private final ShellExecutor mMainExecutor;
+ private final DisplayController mDisplayController;
+ private final FocusTransitionObserver mFocusTransitionObserver;
+ private final SyncTransactionQueue mSyncQueue;
+ private final SparseArray<CarWindowDecoration> mWindowDecorByTaskId = new SparseArray<>();
+ private final WindowDecorViewHostSupplier<WindowDecorViewHost> mWindowDecorViewHostSupplier;
+
+ public CarWindowDecorViewModel(
+ Context context,
+ @ShellBackgroundThread ShellExecutor bgExecutor,
+ @ShellMainThread ShellExecutor shellExecutor,
+ ShellInit shellInit,
+ ShellTaskOrganizer taskOrganizer,
+ DisplayController displayController,
+ SyncTransactionQueue syncQueue,
+ FocusTransitionObserver focusTransitionObserver,
+ WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier) {
+ mContext = context;
+ mMainExecutor = shellExecutor;
+ mBgExecutor = bgExecutor;
+ mTaskOrganizer = taskOrganizer;
+ mDisplayController = displayController;
+ mFocusTransitionObserver = focusTransitionObserver;
+ mSyncQueue = syncQueue;
+ mWindowDecorViewHostSupplier = windowDecorViewHostSupplier;
+
+ shellInit.addInitCallback(this::onInit, this);
+ }
+
+ private void onInit() {
+ mFocusTransitionObserver.setLocalFocusTransitionListener(this, mMainExecutor);
+ }
+
+ @Override
+ public void onFocusedTaskChanged(int taskId, boolean isFocusedOnDisplay,
+ boolean isFocusedGlobally) {
+ // no-op
+ }
+
+ @Override
+ public void setFreeformTaskTransitionStarter(FreeformTaskTransitionStarter transitionStarter) {
+ // no-op
+ }
+
+ @Override
+ public void setSplitScreenController(SplitScreenController splitScreenController) {
+ // no-op
+ }
+
+ @Override
+ public boolean onTaskOpening(
+ RunningTaskInfo taskInfo,
+ SurfaceControl taskSurface,
+ SurfaceControl.Transaction startT,
+ SurfaceControl.Transaction finishT) {
+ if (!shouldShowWindowDecor(taskInfo)) {
+ return false;
+ }
+ createWindowDecoration(taskInfo, taskSurface, startT, finishT);
+ return true;
+ }
+
+ @Override
+ public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
+ final CarWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
+
+ if (decoration == null) {
+ return;
+ }
+
+ if (!shouldShowWindowDecor(taskInfo)) {
+ destroyWindowDecoration(taskInfo);
+ return;
+ }
+
+ decoration.relayout(taskInfo, decoration.mHasGlobalFocus, decoration.mExclusionRegion);
+ }
+
+ @Override
+ public void onTaskVanished(RunningTaskInfo taskInfo) {
+ // A task vanishing doesn't necessarily mean the task was closed, it could also mean its
+ // windowing mode changed. We're only interested in closing tasks so checking whether
+ // its info still exists in the task organizer is one way to disambiguate.
+ final boolean closed = mTaskOrganizer.getRunningTaskInfo(taskInfo.taskId) == null;
+ if (closed) {
+ // Destroying the window decoration is usually handled when a TRANSIT_CLOSE transition
+ // changes happen, but there are certain cases in which closing tasks aren't included
+ // in transitions, such as when a non-visible task is closed. See b/296921167.
+ // Destroy the decoration here in case the lack of transition missed it.
+ destroyWindowDecoration(taskInfo);
+ }
+ }
+
+ @Override
+ public void onTaskChanging(
+ RunningTaskInfo taskInfo,
+ SurfaceControl taskSurface,
+ SurfaceControl.Transaction startT,
+ SurfaceControl.Transaction finishT) {
+ final CarWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
+
+ if (!shouldShowWindowDecor(taskInfo)) {
+ if (decoration != null) {
+ destroyWindowDecoration(taskInfo);
+ }
+ return;
+ }
+
+ if (decoration == null) {
+ createWindowDecoration(taskInfo, taskSurface, startT, finishT);
+ } else {
+ decoration.relayout(taskInfo, startT, finishT);
+ }
+ }
+
+ @Override
+ public void onTaskClosing(
+ RunningTaskInfo taskInfo,
+ SurfaceControl.Transaction startT,
+ SurfaceControl.Transaction finishT) {
+ final CarWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
+ if (decoration == null) {
+ return;
+ }
+ decoration.relayout(taskInfo, startT, finishT);
+ }
+
+ @Override
+ public void destroyWindowDecoration(RunningTaskInfo taskInfo) {
+ final CarWindowDecoration decoration =
+ mWindowDecorByTaskId.removeReturnOld(taskInfo.taskId);
+ if (decoration == null) {
+ return;
+ }
+
+ decoration.close();
+ }
+
+ /**
+ * @return {@code true} if the task/activity associated with {@code taskInfo} should show
+ * window decoration.
+ */
+ protected abstract boolean shouldShowWindowDecor(RunningTaskInfo taskInfo);
+
+ private void createWindowDecoration(
+ RunningTaskInfo taskInfo,
+ SurfaceControl taskSurface,
+ SurfaceControl.Transaction startT,
+ SurfaceControl.Transaction finishT) {
+ final CarWindowDecoration 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 CarWindowDecoration windowDecoration =
+ new CarWindowDecoration(
+ mContext,
+ mContext.createContextAsUser(UserHandle.of(taskInfo.userId), 0 /* flags */),
+ mDisplayController,
+ mTaskOrganizer,
+ taskInfo,
+ taskSurface,
+ mBgExecutor,
+ mWindowDecorViewHostSupplier,
+ new ButtonClickListener(taskInfo));
+ mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
+ windowDecoration.relayout(taskInfo, startT, finishT);
+ }
+
+ private class ButtonClickListener implements View.OnClickListener {
+ private final WindowContainerToken mTaskToken;
+ private final int mDisplayId;
+
+ private ButtonClickListener(RunningTaskInfo taskInfo) {
+ mTaskToken = taskInfo.token;
+ mDisplayId = taskInfo.displayId;
+ }
+
+ @Override
+ public void onClick(View v) {
+ final int id = v.getId();
+ if (id == R.id.close_window) {
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.removeTask(mTaskToken);
+ mSyncQueue.queue(wct);
+ } else if (id == R.id.back_button) {
+ sendBackEvent(KeyEvent.ACTION_DOWN, mDisplayId);
+ sendBackEvent(KeyEvent.ACTION_UP, mDisplayId);
+ }
+ }
+
+ private void sendBackEvent(int action, int displayId) {
+ final long when = SystemClock.uptimeMillis();
+ final KeyEvent ev = new KeyEvent(when, when, action, KeyEvent.KEYCODE_BACK,
+ 0 /* repeat */, 0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD,
+ 0 /* scancode */, KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
+ InputDevice.SOURCE_KEYBOARD);
+
+ ev.setDisplayId(displayId);
+ if (!mContext.getSystemService(InputManager.class)
+ .injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC)) {
+ Log.e(TAG, "Inject input event fail");
+ }
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java
new file mode 100644
index 0000000..1ca82d2
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.windowdecor;
+
+import static android.view.InsetsSource.FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
+
+import android.annotation.SuppressLint;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.view.InsetsState;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.window.WindowContainerTransaction;
+
+import androidx.annotation.NonNull;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
+
+/**
+ * {@link WindowDecoration} to show app controls for windows on automotive.
+ */
+public class CarWindowDecoration extends WindowDecoration<WindowDecorLinearLayout> {
+ private WindowDecorLinearLayout mRootView;
+ private @ShellBackgroundThread final ShellExecutor mBgExecutor;
+ private final View.OnClickListener mClickListener;
+
+ CarWindowDecoration(
+ Context context,
+ @android.annotation.NonNull Context userContext,
+ DisplayController displayController,
+ ShellTaskOrganizer taskOrganizer,
+ ActivityManager.RunningTaskInfo taskInfo,
+ SurfaceControl taskSurface,
+ @ShellBackgroundThread ShellExecutor bgExecutor,
+ WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier,
+ View.OnClickListener clickListener) {
+ super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface,
+ windowDecorViewHostSupplier);
+ mBgExecutor = bgExecutor;
+ mClickListener = clickListener;
+ }
+
+ @Override
+ void relayout(ActivityManager.RunningTaskInfo taskInfo, boolean hasGlobalFocus,
+ @NonNull Region displayExclusionRegion) {
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ relayout(taskInfo, t, t);
+ }
+
+ @SuppressLint("MissingPermission")
+ void relayout(ActivityManager.RunningTaskInfo taskInfo,
+ SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+
+ RelayoutParams relayoutParams = new RelayoutParams();
+ RelayoutResult<WindowDecorLinearLayout> outResult = new RelayoutResult<>();
+
+ updateRelayoutParams(relayoutParams, taskInfo,
+ mDisplayController.getInsetsState(taskInfo.displayId));
+
+ relayout(relayoutParams, startT, finishT, wct, mRootView, outResult);
+ // After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
+ mBgExecutor.execute(() -> mTaskOrganizer.applyTransaction(wct));
+
+ if (outResult.mRootView == null) {
+ // This means something blocks the window decor from showing, e.g. the task is hidden.
+ // Nothing is set up in this case including the decoration surface.
+ return;
+ }
+ if (mRootView != outResult.mRootView) {
+ mRootView = outResult.mRootView;
+ setupRootView(outResult.mRootView, mClickListener);
+ }
+ }
+
+ @Override
+ @NonNull
+ Rect calculateValidDragArea() {
+ return new Rect();
+ }
+
+ @Override
+ int getCaptionViewId() {
+ return R.id.caption;
+ }
+
+ private void updateRelayoutParams(
+ RelayoutParams relayoutParams,
+ ActivityManager.RunningTaskInfo taskInfo,
+ InsetsState displayInsetsState) {
+ relayoutParams.reset();
+ relayoutParams.mRunningTaskInfo = taskInfo;
+ // todo(b/382071404): update to car specific UI
+ relayoutParams.mLayoutResId = R.layout.caption_window_decor;
+ relayoutParams.mCaptionHeightId = R.dimen.freeform_decor_caption_height;
+ relayoutParams.mIsCaptionVisible = mIsStatusBarVisible && !mIsKeyguardVisibleAndOccluded;
+ relayoutParams.mCaptionTopPadding = 0;
+ relayoutParams.mInsetSourceFlags |= FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
+ relayoutParams.mApplyStartTransactionOnDraw = true;
+ }
+
+ /**
+ * Sets up listeners when a new root view is created.
+ */
+ private void setupRootView(View rootView, View.OnClickListener onClickListener) {
+ final View caption = rootView.findViewById(R.id.caption);
+ final View close = caption.findViewById(R.id.close_window);
+ if (close != null) {
+ close.setOnClickListener(onClickListener);
+ }
+ final View back = caption.findViewById(R.id.back_button);
+ if (back != null) {
+ back.setOnClickListener(onClickListener);
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 7928e5e..a7a5f09 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -935,10 +935,12 @@
// back to the decoration using
// {@link DesktopModeWindowDecoration#setOnMaximizeOrRestoreClickListener}, which
// should shared with the maximize menu's maximize/restore actions.
+ final DesktopRepository desktopRepository = mDesktopUserRepositories.getProfile(
+ decoration.mTaskInfo.userId);
if (Flags.enableFullyImmersiveInDesktop()
- && TaskInfoKt.getRequestingImmersive(decoration.mTaskInfo)) {
- // Task is requesting immersive, so it should either enter or exit immersive,
- // depending on immersive state.
+ && desktopRepository.isTaskInFullImmersiveState(
+ decoration.mTaskInfo.taskId)) {
+ // Task is in immersive and should exit.
onEnterOrExitImmersive(decoration.mTaskInfo);
} else {
// Full immersive is disabled or task doesn't request/support it, so just
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
index 0154ff3..9d41013 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
@@ -509,6 +509,20 @@
}
@Test
+ fun transitExitBackGesture_logTaskRemovedAndExitReasonTaskMovedToBack() {
+ // add a freeform task
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
+ transitionObserver.isSessionActive = true
+
+ // task moved to back
+ val change = createChange(TRANSIT_TO_BACK, createTaskInfo(WINDOWING_MODE_FREEFORM))
+ val transitionInfo = TransitionInfoBuilder(TRANSIT_TO_BACK).addChange(change).build()
+ callOnTransitionReady(transitionInfo)
+
+ verifyTaskRemovedAndExitLogging(ExitReason.TASK_MOVED_TO_BACK, DEFAULT_TASK_UPDATE)
+ }
+
+ @Test
fun transitExitDesktopUnknown_logTaskRemovedAndExitReasonUnknown() {
// add a freeform task
transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
index e6f1fcf..52602f2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
@@ -551,7 +551,7 @@
.getTransitionObserver()
.onTransitionReady(
transition,
- TransitionInfoBuilder(TRANSIT_OPEN).build(),
+ TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
StubTransaction() /* startTransaction */,
StubTransaction(), /* finishTransaction */
)
@@ -583,7 +583,7 @@
.getTransitionObserver()
.onTransitionReady(
transition,
- TransitionInfoBuilder(TRANSIT_OPEN).build(),
+ TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
StubTransaction() /* startTransaction */,
StubTransaction(), /* finishTransaction */
)
@@ -616,7 +616,7 @@
.getTransitionObserver()
.onTransitionReady(
mergedTransition,
- TransitionInfoBuilder(TRANSIT_OPEN).build(),
+ TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
StubTransaction() /* startTransaction */,
StubTransaction(), /* finishTransaction */
)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index aead0a7..ffe8e71 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -1054,26 +1054,6 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
- fun testImmersiveButtonClick_entersImmersiveMode() {
- val onClickListenerCaptor = forClass(View.OnClickListener::class.java)
- as ArgumentCaptor<View.OnClickListener>
- val decor = createOpenTaskDecoration(
- windowingMode = WINDOWING_MODE_FREEFORM,
- onCaptionButtonClickListener = onClickListenerCaptor,
- requestingImmersive = true,
- )
- val view = mock(View::class.java)
- whenever(view.id).thenReturn(R.id.maximize_window)
- whenever(mockDesktopRepository.isTaskInFullImmersiveState(decor.mTaskInfo.taskId))
- .thenReturn(false)
-
- onClickListenerCaptor.value.onClick(view)
-
- verify(mockDesktopImmersiveController).moveTaskToImmersive(decor.mTaskInfo)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
fun testImmersiveRestoreButtonClick_exitsImmersiveMode() {
val onClickListenerCaptor = forClass(View.OnClickListener::class.java)
as ArgumentCaptor<View.OnClickListener>
diff --git a/location/api/system-current.txt b/location/api/system-current.txt
index 023bad2..eb19ba8 100644
--- a/location/api/system-current.txt
+++ b/location/api/system-current.txt
@@ -1,6 +1,29 @@
// Signature format: 2.0
package android.location {
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class AuxiliaryInformation implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<android.location.GnssSignalType> getAvailableSignalTypes();
+ method @IntRange(from=0xfffffff9, to=6) public int getFrequencyChannelNumber();
+ method public int getSatType();
+ method @IntRange(from=1) public int getSvid();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int BDS_B1C_ORBIT_TYPE_GEO = 1; // 0x1
+ field public static final int BDS_B1C_ORBIT_TYPE_IGSO = 2; // 0x2
+ field public static final int BDS_B1C_ORBIT_TYPE_MEO = 3; // 0x3
+ field public static final int BDS_B1C_ORBIT_TYPE_UNDEFINED = 0; // 0x0
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.AuxiliaryInformation> CREATOR;
+ }
+
+ public static final class AuxiliaryInformation.Builder {
+ ctor public AuxiliaryInformation.Builder();
+ method @NonNull public android.location.AuxiliaryInformation build();
+ method @NonNull public android.location.AuxiliaryInformation.Builder setAvailableSignalTypes(@NonNull java.util.List<android.location.GnssSignalType>);
+ method @NonNull public android.location.AuxiliaryInformation.Builder setFrequencyChannelNumber(@IntRange(from=0xfffffff9, to=6) int);
+ method @NonNull public android.location.AuxiliaryInformation.Builder setSatType(int);
+ method @NonNull public android.location.AuxiliaryInformation.Builder setSvid(@IntRange(from=1) int);
+ }
+
public abstract class BatchedLocationCallback {
ctor public BatchedLocationCallback();
method public void onLocationBatch(java.util.List<android.location.Location>);
@@ -9,6 +32,7 @@
@FlaggedApi("android.location.flags.gnss_assistance_interface") public final class BeidouAssistance implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.location.GnssAlmanac getAlmanac();
+ method @Nullable public android.location.AuxiliaryInformation getAuxiliaryInformation();
method @Nullable public android.location.KlobucharIonosphericModel getIonosphericModel();
method @Nullable public android.location.LeapSecondsModel getLeapSecondsModel();
method @NonNull public java.util.List<android.location.RealTimeIntegrityModel> getRealTimeIntegrityModels();
@@ -24,12 +48,13 @@
ctor public BeidouAssistance.Builder();
method @NonNull public android.location.BeidouAssistance build();
method @NonNull public android.location.BeidouAssistance.Builder setAlmanac(@Nullable android.location.GnssAlmanac);
+ method @NonNull public android.location.BeidouAssistance.Builder setAuxiliaryInformation(@Nullable android.location.AuxiliaryInformation);
method @NonNull public android.location.BeidouAssistance.Builder setIonosphericModel(@Nullable android.location.KlobucharIonosphericModel);
method @NonNull public android.location.BeidouAssistance.Builder setLeapSecondsModel(@Nullable android.location.LeapSecondsModel);
- method @NonNull public android.location.BeidouAssistance.Builder setRealTimeIntegrityModels(@Nullable java.util.List<android.location.RealTimeIntegrityModel>);
- method @NonNull public android.location.BeidouAssistance.Builder setSatelliteCorrections(@Nullable java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections>);
- method @NonNull public android.location.BeidouAssistance.Builder setSatelliteEphemeris(@Nullable java.util.List<android.location.BeidouSatelliteEphemeris>);
- method @NonNull public android.location.BeidouAssistance.Builder setTimeModels(@Nullable java.util.List<android.location.TimeModel>);
+ method @NonNull public android.location.BeidouAssistance.Builder setRealTimeIntegrityModels(@NonNull java.util.List<android.location.RealTimeIntegrityModel>);
+ method @NonNull public android.location.BeidouAssistance.Builder setSatelliteCorrections(@NonNull java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections>);
+ method @NonNull public android.location.BeidouAssistance.Builder setSatelliteEphemeris(@NonNull java.util.List<android.location.BeidouSatelliteEphemeris>);
+ method @NonNull public android.location.BeidouAssistance.Builder setTimeModels(@NonNull java.util.List<android.location.TimeModel>);
method @NonNull public android.location.BeidouAssistance.Builder setUtcModel(@Nullable android.location.UtcModel);
}
@@ -151,6 +176,7 @@
@FlaggedApi("android.location.flags.gnss_assistance_interface") public final class GalileoAssistance implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.location.GnssAlmanac getAlmanac();
+ method @Nullable public android.location.AuxiliaryInformation getAuxiliaryInformation();
method @Nullable public android.location.KlobucharIonosphericModel getIonosphericModel();
method @Nullable public android.location.LeapSecondsModel getLeapSecondsModel();
method @NonNull public java.util.List<android.location.RealTimeIntegrityModel> getRealTimeIntegrityModels();
@@ -166,12 +192,13 @@
ctor public GalileoAssistance.Builder();
method @NonNull public android.location.GalileoAssistance build();
method @NonNull public android.location.GalileoAssistance.Builder setAlmanac(@Nullable android.location.GnssAlmanac);
+ method @NonNull public android.location.GalileoAssistance.Builder setAuxiliaryInformation(@Nullable android.location.AuxiliaryInformation);
method @NonNull public android.location.GalileoAssistance.Builder setIonosphericModel(@Nullable android.location.KlobucharIonosphericModel);
method @NonNull public android.location.GalileoAssistance.Builder setLeapSecondsModel(@Nullable android.location.LeapSecondsModel);
- method @NonNull public android.location.GalileoAssistance.Builder setRealTimeIntegrityModels(@Nullable java.util.List<android.location.RealTimeIntegrityModel>);
- method @NonNull public android.location.GalileoAssistance.Builder setSatelliteCorrections(@Nullable java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections>);
- method @NonNull public android.location.GalileoAssistance.Builder setSatelliteEphemeris(@Nullable java.util.List<android.location.GalileoSatelliteEphemeris>);
- method @NonNull public android.location.GalileoAssistance.Builder setTimeModels(@Nullable java.util.List<android.location.TimeModel>);
+ method @NonNull public android.location.GalileoAssistance.Builder setRealTimeIntegrityModels(@NonNull java.util.List<android.location.RealTimeIntegrityModel>);
+ method @NonNull public android.location.GalileoAssistance.Builder setSatelliteCorrections(@NonNull java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections>);
+ method @NonNull public android.location.GalileoAssistance.Builder setSatelliteEphemeris(@NonNull java.util.List<android.location.GalileoSatelliteEphemeris>);
+ method @NonNull public android.location.GalileoAssistance.Builder setTimeModels(@NonNull java.util.List<android.location.TimeModel>);
method @NonNull public android.location.GalileoAssistance.Builder setUtcModel(@Nullable android.location.UtcModel);
}
@@ -319,6 +346,7 @@
@FlaggedApi("android.location.flags.gnss_assistance_interface") public final class GlonassAssistance implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.location.GlonassAlmanac getAlmanac();
+ method @Nullable public android.location.AuxiliaryInformation getAuxiliaryInformation();
method @NonNull public java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections> getSatelliteCorrections();
method @NonNull public java.util.List<android.location.GlonassSatelliteEphemeris> getSatelliteEphemeris();
method @NonNull public java.util.List<android.location.TimeModel> getTimeModels();
@@ -331,9 +359,10 @@
ctor public GlonassAssistance.Builder();
method @NonNull public android.location.GlonassAssistance build();
method @NonNull public android.location.GlonassAssistance.Builder setAlmanac(@Nullable android.location.GlonassAlmanac);
- method @NonNull public android.location.GlonassAssistance.Builder setSatelliteCorrections(@Nullable java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections>);
- method @NonNull public android.location.GlonassAssistance.Builder setSatelliteEphemeris(@Nullable java.util.List<android.location.GlonassSatelliteEphemeris>);
- method @NonNull public android.location.GlonassAssistance.Builder setTimeModels(@Nullable java.util.List<android.location.TimeModel>);
+ method @NonNull public android.location.GlonassAssistance.Builder setAuxiliaryInformation(@Nullable android.location.AuxiliaryInformation);
+ method @NonNull public android.location.GlonassAssistance.Builder setSatelliteCorrections(@NonNull java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections>);
+ method @NonNull public android.location.GlonassAssistance.Builder setSatelliteEphemeris(@NonNull java.util.List<android.location.GlonassSatelliteEphemeris>);
+ method @NonNull public android.location.GlonassAssistance.Builder setTimeModels(@NonNull java.util.List<android.location.TimeModel>);
method @NonNull public android.location.GlonassAssistance.Builder setUtcModel(@Nullable android.location.UtcModel);
}
@@ -688,6 +717,7 @@
@FlaggedApi("android.location.flags.gnss_assistance_interface") public final class GpsAssistance implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.location.GnssAlmanac getAlmanac();
+ method @Nullable public android.location.AuxiliaryInformation getAuxiliaryInformation();
method @Nullable public android.location.KlobucharIonosphericModel getIonosphericModel();
method @Nullable public android.location.LeapSecondsModel getLeapSecondsModel();
method @NonNull public java.util.List<android.location.RealTimeIntegrityModel> getRealTimeIntegrityModels();
@@ -703,12 +733,13 @@
ctor public GpsAssistance.Builder();
method @NonNull public android.location.GpsAssistance build();
method @NonNull public android.location.GpsAssistance.Builder setAlmanac(@Nullable android.location.GnssAlmanac);
+ method @NonNull public android.location.GpsAssistance.Builder setAuxiliaryInformation(@Nullable android.location.AuxiliaryInformation);
method @NonNull public android.location.GpsAssistance.Builder setIonosphericModel(@Nullable android.location.KlobucharIonosphericModel);
method @NonNull public android.location.GpsAssistance.Builder setLeapSecondsModel(@Nullable android.location.LeapSecondsModel);
- method @NonNull public android.location.GpsAssistance.Builder setRealTimeIntegrityModels(@Nullable java.util.List<android.location.RealTimeIntegrityModel>);
- method @NonNull public android.location.GpsAssistance.Builder setSatelliteCorrections(@Nullable java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections>);
- method @NonNull public android.location.GpsAssistance.Builder setSatelliteEphemeris(@Nullable java.util.List<android.location.GpsSatelliteEphemeris>);
- method @NonNull public android.location.GpsAssistance.Builder setTimeModels(@Nullable java.util.List<android.location.TimeModel>);
+ method @NonNull public android.location.GpsAssistance.Builder setRealTimeIntegrityModels(@NonNull java.util.List<android.location.RealTimeIntegrityModel>);
+ method @NonNull public android.location.GpsAssistance.Builder setSatelliteCorrections(@NonNull java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections>);
+ method @NonNull public android.location.GpsAssistance.Builder setSatelliteEphemeris(@NonNull java.util.List<android.location.GpsSatelliteEphemeris>);
+ method @NonNull public android.location.GpsAssistance.Builder setTimeModels(@NonNull java.util.List<android.location.TimeModel>);
method @NonNull public android.location.GpsAssistance.Builder setUtcModel(@Nullable android.location.UtcModel);
}
@@ -1222,6 +1253,7 @@
@FlaggedApi("android.location.flags.gnss_assistance_interface") public final class QzssAssistance implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.location.GnssAlmanac getAlmanac();
+ method @Nullable public android.location.AuxiliaryInformation getAuxiliaryInformation();
method @Nullable public android.location.KlobucharIonosphericModel getIonosphericModel();
method @Nullable public android.location.LeapSecondsModel getLeapSecondsModel();
method @NonNull public java.util.List<android.location.RealTimeIntegrityModel> getRealTimeIntegrityModels();
@@ -1237,12 +1269,13 @@
ctor public QzssAssistance.Builder();
method @NonNull public android.location.QzssAssistance build();
method @NonNull public android.location.QzssAssistance.Builder setAlmanac(@Nullable android.location.GnssAlmanac);
+ method @NonNull public android.location.QzssAssistance.Builder setAuxiliaryInformation(@Nullable android.location.AuxiliaryInformation);
method @NonNull public android.location.QzssAssistance.Builder setIonosphericModel(@Nullable android.location.KlobucharIonosphericModel);
method @NonNull public android.location.QzssAssistance.Builder setLeapSecondsModel(@Nullable android.location.LeapSecondsModel);
- method @NonNull public android.location.QzssAssistance.Builder setRealTimeIntegrityModels(@Nullable java.util.List<android.location.RealTimeIntegrityModel>);
- method @NonNull public android.location.QzssAssistance.Builder setSatelliteCorrections(@Nullable java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections>);
- method @NonNull public android.location.QzssAssistance.Builder setSatelliteEphemeris(@Nullable java.util.List<android.location.QzssSatelliteEphemeris>);
- method @NonNull public android.location.QzssAssistance.Builder setTimeModels(@Nullable java.util.List<android.location.TimeModel>);
+ method @NonNull public android.location.QzssAssistance.Builder setRealTimeIntegrityModels(@NonNull java.util.List<android.location.RealTimeIntegrityModel>);
+ method @NonNull public android.location.QzssAssistance.Builder setSatelliteCorrections(@NonNull java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections>);
+ method @NonNull public android.location.QzssAssistance.Builder setSatelliteEphemeris(@NonNull java.util.List<android.location.QzssSatelliteEphemeris>);
+ method @NonNull public android.location.QzssAssistance.Builder setTimeModels(@NonNull java.util.List<android.location.TimeModel>);
method @NonNull public android.location.QzssAssistance.Builder setUtcModel(@Nullable android.location.UtcModel);
}
@@ -1273,11 +1306,11 @@
method public int describeContents();
method @NonNull public String getAdvisoryNumber();
method @NonNull public String getAdvisoryType();
+ method @NonNull public java.util.List<android.location.GnssSignalType> getBadSignalTypes();
+ method @IntRange(from=1, to=206) public int getBadSvid();
method @IntRange(from=0) public long getEndDateSeconds();
method @IntRange(from=0) public long getPublishDateSeconds();
method @IntRange(from=0) public long getStartDateSeconds();
- method @IntRange(from=1, to=206) public int getSvid();
- method public boolean isUsable();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.location.RealTimeIntegrityModel> CREATOR;
}
@@ -1287,11 +1320,11 @@
method @NonNull public android.location.RealTimeIntegrityModel build();
method @NonNull public android.location.RealTimeIntegrityModel.Builder setAdvisoryNumber(@NonNull String);
method @NonNull public android.location.RealTimeIntegrityModel.Builder setAdvisoryType(@NonNull String);
+ method @NonNull public android.location.RealTimeIntegrityModel.Builder setBadSignalTypes(@NonNull java.util.List<android.location.GnssSignalType>);
+ method @NonNull public android.location.RealTimeIntegrityModel.Builder setBadSvid(@IntRange(from=1, to=206) int);
method @NonNull public android.location.RealTimeIntegrityModel.Builder setEndDateSeconds(@IntRange(from=0) long);
method @NonNull public android.location.RealTimeIntegrityModel.Builder setPublishDateSeconds(@IntRange(from=0) long);
method @NonNull public android.location.RealTimeIntegrityModel.Builder setStartDateSeconds(@IntRange(from=0) long);
- method @NonNull public android.location.RealTimeIntegrityModel.Builder setSvid(@IntRange(from=1, to=206) int);
- method @NonNull public android.location.RealTimeIntegrityModel.Builder setUsable(boolean);
}
@FlaggedApi("android.location.flags.gnss_assistance_interface") public final class SatelliteEphemerisTime implements android.os.Parcelable {
diff --git a/location/java/android/location/AuxiliaryInformation.java b/location/java/android/location/AuxiliaryInformation.java
new file mode 100644
index 0000000..601c87e
--- /dev/null
+++ b/location/java/android/location/AuxiliaryInformation.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A class contains parameters to provide additional assistance information dependent on the GNSS
+ * constellation.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class AuxiliaryInformation implements Parcelable {
+
+ /**
+ * BDS B1C Satellite orbit type.
+ *
+ * <p>This is defined in BDS-SIS-ICD-B1I-3.0, section 3.1.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ BDS_B1C_ORBIT_TYPE_UNDEFINED,
+ BDS_B1C_ORBIT_TYPE_GEO,
+ BDS_B1C_ORBIT_TYPE_IGSO,
+ BDS_B1C_ORBIT_TYPE_MEO
+ })
+ public @interface BeidouB1CSatelliteOrbitType {}
+
+ /**
+ * The following enumerations must be in sync with the values declared in
+ * AuxiliaryInformation.aidl.
+ */
+
+ /** The orbit type is undefined. */
+ public static final int BDS_B1C_ORBIT_TYPE_UNDEFINED = 0;
+
+ /** The orbit type is GEO. */
+ public static final int BDS_B1C_ORBIT_TYPE_GEO = 1;
+
+ /** The orbit type is IGSO. */
+ public static final int BDS_B1C_ORBIT_TYPE_IGSO = 2;
+
+ /** The orbit type is MEO. */
+ public static final int BDS_B1C_ORBIT_TYPE_MEO = 3;
+
+ /**
+ * Pseudo-random or satellite ID number for the satellite, a.k.a. Space Vehicle (SV), or OSN
+ * number for Glonass.
+ *
+ * <p>The distinction is made by looking at the constellation field. Values must be in the range
+ * of:
+ *
+ * <p>- GPS: 1-32
+ *
+ * <p>- GLONASS: 1-25
+ *
+ * <p>- QZSS: 183-206
+ *
+ * <p>- Galileo: 1-36
+ *
+ * <p>- Beidou: 1-63
+ */
+ private final int mSvid;
+
+ /** The list of available signal types for the satellite. */
+ @NonNull private final List<GnssSignalType> mAvailableSignalTypes;
+
+ /**
+ * Glonass carrier frequency number of the satellite. This is required for Glonass.
+ *
+ * <p>This is defined in Glonass ICD v5.1 section 3.3.1.1.
+ */
+ private final int mFrequencyChannelNumber;
+
+ /** BDS B1C satellite orbit type. This is required for Beidou. */
+ private final @BeidouB1CSatelliteOrbitType int mSatType;
+
+ private AuxiliaryInformation(Builder builder) {
+ // Allow Svid beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(builder.mSvid >= 1);
+ Preconditions.checkNotNull(
+ builder.mAvailableSignalTypes, "AvailableSignalTypes cannot be null");
+ Preconditions.checkArgument(builder.mAvailableSignalTypes.size() > 0);
+ Preconditions.checkArgumentInRange(
+ builder.mFrequencyChannelNumber, -7, 6, "FrequencyChannelNumber");
+ Preconditions.checkArgumentInRange(
+ builder.mSatType, BDS_B1C_ORBIT_TYPE_UNDEFINED, BDS_B1C_ORBIT_TYPE_MEO, "SatType");
+ mSvid = builder.mSvid;
+ mAvailableSignalTypes =
+ Collections.unmodifiableList(new ArrayList<>(builder.mAvailableSignalTypes));
+ mFrequencyChannelNumber = builder.mFrequencyChannelNumber;
+ mSatType = builder.mSatType;
+ }
+
+ /**
+ * Returns the Pseudo-random or satellite ID number for the satellite, a.k.a. Space Vehicle
+ * (SV), or OSN number for Glonass.
+ *
+ * <p>The distinction is made by looking at the constellation field. Values must be in the range
+ * of:
+ *
+ * <p>- GPS: 1-32
+ *
+ * <p>- GLONASS: 1-25
+ *
+ * <p>- QZSS: 183-206
+ *
+ * <p>- Galileo: 1-36
+ *
+ * <p>- Beidou: 1-63
+ */
+ @IntRange(from = 1)
+ public int getSvid() {
+ return mSvid;
+ }
+
+ /** Returns the list of available signal types for the satellite. */
+ @NonNull
+ public List<GnssSignalType> getAvailableSignalTypes() {
+ return mAvailableSignalTypes;
+ }
+
+ /** Returns the Glonass carrier frequency number of the satellite. */
+ @IntRange(from = -7, to = 6)
+ public int getFrequencyChannelNumber() {
+ return mFrequencyChannelNumber;
+ }
+
+ /** Returns the BDS B1C satellite orbit type. */
+ @BeidouB1CSatelliteOrbitType
+ public int getSatType() {
+ return mSatType;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mSvid);
+ dest.writeTypedList(mAvailableSignalTypes);
+ dest.writeInt(mFrequencyChannelNumber);
+ dest.writeInt(mSatType);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("AuxiliaryInformation[");
+ builder.append("svid = ").append(mSvid);
+ builder.append(", availableSignalTypes = ").append(mAvailableSignalTypes);
+ builder.append(", frequencyChannelNumber = ").append(mFrequencyChannelNumber);
+ builder.append(", satType = ").append(mSatType);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ public static final @NonNull Parcelable.Creator<AuxiliaryInformation> CREATOR =
+ new Parcelable.Creator<AuxiliaryInformation>() {
+ @Override
+ public AuxiliaryInformation createFromParcel(@NonNull Parcel in) {
+ return new AuxiliaryInformation.Builder()
+ .setSvid(in.readInt())
+ .setAvailableSignalTypes(
+ in.createTypedArrayList(GnssSignalType.CREATOR))
+ .setFrequencyChannelNumber(in.readInt())
+ .setSatType(in.readInt())
+ .build();
+ }
+
+ @Override
+ public AuxiliaryInformation[] newArray(int size) {
+ return new AuxiliaryInformation[size];
+ }
+ };
+
+ /** A builder class for {@link AuxiliaryInformation}. */
+ public static final class Builder {
+ private int mSvid;
+ private List<GnssSignalType> mAvailableSignalTypes;
+ private int mFrequencyChannelNumber;
+ private @BeidouB1CSatelliteOrbitType int mSatType;
+
+ /**
+ * Sets the Pseudo-random or satellite ID number for the satellite, a.k.a. Space Vehicle
+ * (SV), or OSN number for Glonass.
+ *
+ * <p>The distinction is made by looking at the constellation field. Values must be in the
+ * range of:
+ *
+ * <p>- GPS: 1-32
+ *
+ * <p>- GLONASS: 1-25
+ *
+ * <p>- QZSS: 183-206
+ *
+ * <p>- Galileo: 1-36
+ *
+ * <p>- Beidou: 1-63
+ */
+ @NonNull
+ public Builder setSvid(@IntRange(from = 1) int svid) {
+ mSvid = svid;
+ return this;
+ }
+
+ /**
+ * Sets the list of available signal types for the satellite.
+ *
+ * <p>The list must be set and cannot be an empty list.
+ */
+ @NonNull
+ public Builder setAvailableSignalTypes(@NonNull List<GnssSignalType> availableSignalTypes) {
+ mAvailableSignalTypes = availableSignalTypes;
+ return this;
+ }
+
+ /** Sets the Glonass carrier frequency number of the satellite. */
+ @NonNull
+ public Builder setFrequencyChannelNumber(
+ @IntRange(from = -7, to = 6) int frequencyChannelNumber) {
+ mFrequencyChannelNumber = frequencyChannelNumber;
+ return this;
+ }
+
+ /** Sets the BDS B1C satellite orbit type. */
+ @NonNull
+ public Builder setSatType(@BeidouB1CSatelliteOrbitType int satType) {
+ mSatType = satType;
+ return this;
+ }
+
+ /** Builds a {@link AuxiliaryInformation} instance as specified by this builder. */
+ @NonNull
+ public AuxiliaryInformation build() {
+ return new AuxiliaryInformation(this);
+ }
+ }
+}
diff --git a/location/java/android/location/BeidouAssistance.java b/location/java/android/location/BeidouAssistance.java
index f55249e6..e35493e 100644
--- a/location/java/android/location/BeidouAssistance.java
+++ b/location/java/android/location/BeidouAssistance.java
@@ -19,7 +19,6 @@
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.location.GnssAssistance.GnssSatelliteCorrections;
import android.location.flags.Flags;
@@ -51,6 +50,9 @@
/** The leap seconds model. */
@Nullable private final LeapSecondsModel mLeapSecondsModel;
+ /** The auxiliary information. */
+ @Nullable private final AuxiliaryInformation mAuxiliaryInformation;
+
/** The list of time models. */
@NonNull private final List<TimeModel> mTimeModels;
@@ -68,6 +70,7 @@
mIonosphericModel = builder.mIonosphericModel;
mUtcModel = builder.mUtcModel;
mLeapSecondsModel = builder.mLeapSecondsModel;
+ mAuxiliaryInformation = builder.mAuxiliaryInformation;
if (builder.mTimeModels != null) {
mTimeModels = Collections.unmodifiableList(new ArrayList<>(builder.mTimeModels));
} else {
@@ -117,6 +120,12 @@
return mLeapSecondsModel;
}
+ /** Returns the auxiliary information. */
+ @Nullable
+ public AuxiliaryInformation getAuxiliaryInformation() {
+ return mAuxiliaryInformation;
+ }
+
/** Returns the list of time models. */
@NonNull
public List<TimeModel> getTimeModels() {
@@ -154,6 +163,7 @@
builder.append(", ionosphericModel = ").append(mIonosphericModel);
builder.append(", utcModel = ").append(mUtcModel);
builder.append(", leapSecondsModel = ").append(mLeapSecondsModel);
+ builder.append(", auxiliaryInformation = ").append(mAuxiliaryInformation);
builder.append(", timeModels = ").append(mTimeModels);
builder.append(", satelliteEphemeris = ").append(mSatelliteEphemeris);
builder.append(", realTimeIntegrityModels = ").append(mRealTimeIntegrityModels);
@@ -168,6 +178,7 @@
dest.writeTypedObject(mIonosphericModel, flags);
dest.writeTypedObject(mUtcModel, flags);
dest.writeTypedObject(mLeapSecondsModel, flags);
+ dest.writeTypedObject(mAuxiliaryInformation, flags);
dest.writeTypedList(mTimeModels);
dest.writeTypedList(mSatelliteEphemeris);
dest.writeTypedList(mRealTimeIntegrityModels);
@@ -184,6 +195,8 @@
in.readTypedObject(KlobucharIonosphericModel.CREATOR))
.setUtcModel(in.readTypedObject(UtcModel.CREATOR))
.setLeapSecondsModel(in.readTypedObject(LeapSecondsModel.CREATOR))
+ .setAuxiliaryInformation(
+ in.readTypedObject(AuxiliaryInformation.CREATOR))
.setTimeModels(in.createTypedArrayList(TimeModel.CREATOR))
.setSatelliteEphemeris(
in.createTypedArrayList(BeidouSatelliteEphemeris.CREATOR))
@@ -206,6 +219,7 @@
private KlobucharIonosphericModel mIonosphericModel;
private UtcModel mUtcModel;
private LeapSecondsModel mLeapSecondsModel;
+ private AuxiliaryInformation mAuxiliaryInformation;
private List<TimeModel> mTimeModels;
private List<BeidouSatelliteEphemeris> mSatelliteEphemeris;
private List<RealTimeIntegrityModel> mRealTimeIntegrityModels;
@@ -239,10 +253,17 @@
return this;
}
+ /** Sets the auxiliary information. */
+ @NonNull
+ public Builder setAuxiliaryInformation(
+ @Nullable AuxiliaryInformation auxiliaryInformation) {
+ mAuxiliaryInformation = auxiliaryInformation;
+ return this;
+ }
+
/** Sets the list of time models. */
@NonNull
- public Builder setTimeModels(
- @Nullable @SuppressLint("NullableCollection") List<TimeModel> timeModels) {
+ public Builder setTimeModels(@NonNull List<TimeModel> timeModels) {
mTimeModels = timeModels;
return this;
}
@@ -250,8 +271,7 @@
/** Sets the list of Beidou ephemeris. */
@NonNull
public Builder setSatelliteEphemeris(
- @Nullable @SuppressLint("NullableCollection")
- List<BeidouSatelliteEphemeris> satelliteEphemeris) {
+ @NonNull List<BeidouSatelliteEphemeris> satelliteEphemeris) {
mSatelliteEphemeris = satelliteEphemeris;
return this;
}
@@ -259,8 +279,7 @@
/** Sets the list of real time integrity models. */
@NonNull
public Builder setRealTimeIntegrityModels(
- @Nullable @SuppressLint("NullableCollection")
- List<RealTimeIntegrityModel> realTimeIntegrityModels) {
+ @NonNull List<RealTimeIntegrityModel> realTimeIntegrityModels) {
mRealTimeIntegrityModels = realTimeIntegrityModels;
return this;
}
@@ -268,8 +287,7 @@
/** Sets the list of Beidou satellite corrections. */
@NonNull
public Builder setSatelliteCorrections(
- @Nullable @SuppressLint("NullableCollection")
- List<GnssSatelliteCorrections> satelliteCorrections) {
+ @NonNull List<GnssSatelliteCorrections> satelliteCorrections) {
mSatelliteCorrections = satelliteCorrections;
return this;
}
diff --git a/location/java/android/location/GalileoAssistance.java b/location/java/android/location/GalileoAssistance.java
index 07c5bab..8a09e66 100644
--- a/location/java/android/location/GalileoAssistance.java
+++ b/location/java/android/location/GalileoAssistance.java
@@ -19,7 +19,6 @@
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.location.GnssAssistance.GnssSatelliteCorrections;
import android.location.flags.Flags;
@@ -51,6 +50,9 @@
/** The leap seconds model. */
@Nullable private final LeapSecondsModel mLeapSecondsModel;
+ /** The auxiliary information. */
+ @Nullable private final AuxiliaryInformation mAuxiliaryInformation;
+
/** The list of time models. */
@NonNull private final List<TimeModel> mTimeModels;
@@ -68,6 +70,7 @@
mIonosphericModel = builder.mIonosphericModel;
mUtcModel = builder.mUtcModel;
mLeapSecondsModel = builder.mLeapSecondsModel;
+ mAuxiliaryInformation = builder.mAuxiliaryInformation;
if (builder.mTimeModels != null) {
mTimeModels = Collections.unmodifiableList(new ArrayList<>(builder.mTimeModels));
} else {
@@ -117,6 +120,12 @@
return mLeapSecondsModel;
}
+ /** Returns the auxiliary information. */
+ @Nullable
+ public AuxiliaryInformation getAuxiliaryInformation() {
+ return mAuxiliaryInformation;
+ }
+
/** Returns the list of time models. */
@NonNull
public List<TimeModel> getTimeModels() {
@@ -152,6 +161,7 @@
dest.writeTypedObject(mIonosphericModel, flags);
dest.writeTypedObject(mUtcModel, flags);
dest.writeTypedObject(mLeapSecondsModel, flags);
+ dest.writeTypedObject(mAuxiliaryInformation, flags);
dest.writeTypedList(mTimeModels);
dest.writeTypedList(mSatelliteEphemeris);
dest.writeTypedList(mRealTimeIntegrityModels);
@@ -166,6 +176,7 @@
builder.append(", ionosphericModel = ").append(mIonosphericModel);
builder.append(", utcModel = ").append(mUtcModel);
builder.append(", leapSecondsModel = ").append(mLeapSecondsModel);
+ builder.append(", auxiliaryInformation = ").append(mAuxiliaryInformation);
builder.append(", timeModels = ").append(mTimeModels);
builder.append(", satelliteEphemeris = ").append(mSatelliteEphemeris);
builder.append(", realTimeIntegrityModels = ").append(mRealTimeIntegrityModels);
@@ -184,6 +195,8 @@
in.readTypedObject(KlobucharIonosphericModel.CREATOR))
.setUtcModel(in.readTypedObject(UtcModel.CREATOR))
.setLeapSecondsModel(in.readTypedObject(LeapSecondsModel.CREATOR))
+ .setAuxiliaryInformation(
+ in.readTypedObject(AuxiliaryInformation.CREATOR))
.setTimeModels(in.createTypedArrayList(TimeModel.CREATOR))
.setSatelliteEphemeris(
in.createTypedArrayList(GalileoSatelliteEphemeris.CREATOR))
@@ -206,6 +219,7 @@
private KlobucharIonosphericModel mIonosphericModel;
private UtcModel mUtcModel;
private LeapSecondsModel mLeapSecondsModel;
+ private AuxiliaryInformation mAuxiliaryInformation;
private List<TimeModel> mTimeModels;
private List<GalileoSatelliteEphemeris> mSatelliteEphemeris;
private List<RealTimeIntegrityModel> mRealTimeIntegrityModels;
@@ -239,10 +253,17 @@
return this;
}
+ /** Sets the auxiliary information. */
+ @NonNull
+ public Builder setAuxiliaryInformation(
+ @Nullable AuxiliaryInformation auxiliaryInformation) {
+ mAuxiliaryInformation = auxiliaryInformation;
+ return this;
+ }
+
/** Sets the list of time models. */
@NonNull
- public Builder setTimeModels(
- @Nullable @SuppressLint("NullableCollection") List<TimeModel> timeModels) {
+ public Builder setTimeModels(@NonNull List<TimeModel> timeModels) {
mTimeModels = timeModels;
return this;
}
@@ -250,8 +271,7 @@
/** Sets the list of Galileo ephemeris. */
@NonNull
public Builder setSatelliteEphemeris(
- @Nullable @SuppressLint("NullableCollection")
- List<GalileoSatelliteEphemeris> satelliteEphemeris) {
+ @NonNull List<GalileoSatelliteEphemeris> satelliteEphemeris) {
mSatelliteEphemeris = satelliteEphemeris;
return this;
}
@@ -259,8 +279,7 @@
/** Sets the list of real time integrity models. */
@NonNull
public Builder setRealTimeIntegrityModels(
- @Nullable @SuppressLint("NullableCollection")
- List<RealTimeIntegrityModel> realTimeIntegrityModels) {
+ @NonNull List<RealTimeIntegrityModel> realTimeIntegrityModels) {
mRealTimeIntegrityModels = realTimeIntegrityModels;
return this;
}
@@ -268,8 +287,7 @@
/** Sets the list of Galileo satellite corrections. */
@NonNull
public Builder setSatelliteCorrections(
- @Nullable @SuppressLint("NullableCollection")
- List<GnssSatelliteCorrections> satelliteCorrections) {
+ @NonNull List<GnssSatelliteCorrections> satelliteCorrections) {
mSatelliteCorrections = satelliteCorrections;
return this;
}
diff --git a/location/java/android/location/GlonassAssistance.java b/location/java/android/location/GlonassAssistance.java
index cc08201..c7ed1c5 100644
--- a/location/java/android/location/GlonassAssistance.java
+++ b/location/java/android/location/GlonassAssistance.java
@@ -19,7 +19,6 @@
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.location.GnssAssistance.GnssSatelliteCorrections;
import android.location.flags.Flags;
@@ -45,6 +44,9 @@
/** The UTC model. */
@Nullable private final UtcModel mUtcModel;
+ /** The auxiliary information. */
+ @Nullable private final AuxiliaryInformation mAuxiliaryInformation;
+
/** The list of time models. */
@NonNull private final List<TimeModel> mTimeModels;
@@ -57,6 +59,7 @@
private GlonassAssistance(Builder builder) {
mAlmanac = builder.mAlmanac;
mUtcModel = builder.mUtcModel;
+ mAuxiliaryInformation = builder.mAuxiliaryInformation;
if (builder.mTimeModels != null) {
mTimeModels = Collections.unmodifiableList(new ArrayList<>(builder.mTimeModels));
} else {
@@ -106,6 +109,12 @@
return mSatelliteCorrections;
}
+ /** Returns the auxiliary information. */
+ @Nullable
+ public AuxiliaryInformation getAuxiliaryInformation() {
+ return mAuxiliaryInformation;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -115,6 +124,7 @@
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeTypedObject(mAlmanac, flags);
dest.writeTypedObject(mUtcModel, flags);
+ dest.writeTypedObject(mAuxiliaryInformation, flags);
dest.writeTypedList(mTimeModels);
dest.writeTypedList(mSatelliteEphemeris);
dest.writeTypedList(mSatelliteCorrections);
@@ -126,6 +136,7 @@
StringBuilder builder = new StringBuilder("GlonassAssistance[");
builder.append("almanac = ").append(mAlmanac);
builder.append(", utcModel = ").append(mUtcModel);
+ builder.append(", auxiliaryInformation = ").append(mAuxiliaryInformation);
builder.append(", timeModels = ").append(mTimeModels);
builder.append(", satelliteEphemeris = ").append(mSatelliteEphemeris);
builder.append(", satelliteCorrections = ").append(mSatelliteCorrections);
@@ -140,6 +151,8 @@
return new GlonassAssistance.Builder()
.setAlmanac(in.readTypedObject(GlonassAlmanac.CREATOR))
.setUtcModel(in.readTypedObject(UtcModel.CREATOR))
+ .setAuxiliaryInformation(
+ in.readTypedObject(AuxiliaryInformation.CREATOR))
.setTimeModels(in.createTypedArrayList(TimeModel.CREATOR))
.setSatelliteEphemeris(
in.createTypedArrayList(GlonassSatelliteEphemeris.CREATOR))
@@ -158,30 +171,36 @@
public static final class Builder {
private GlonassAlmanac mAlmanac;
private UtcModel mUtcModel;
+ private AuxiliaryInformation mAuxiliaryInformation;
private List<TimeModel> mTimeModels;
private List<GlonassSatelliteEphemeris> mSatelliteEphemeris;
private List<GnssSatelliteCorrections> mSatelliteCorrections;
/** Sets the Glonass almanac. */
@NonNull
- public Builder setAlmanac(
- @Nullable @SuppressLint("NullableCollection") GlonassAlmanac almanac) {
+ public Builder setAlmanac(@Nullable GlonassAlmanac almanac) {
mAlmanac = almanac;
return this;
}
/** Sets the UTC model. */
@NonNull
- public Builder setUtcModel(
- @Nullable @SuppressLint("NullableCollection") UtcModel utcModel) {
+ public Builder setUtcModel(@Nullable UtcModel utcModel) {
mUtcModel = utcModel;
return this;
}
+ /** Sets the auxiliary information. */
+ @NonNull
+ public Builder setAuxiliaryInformation(
+ @Nullable AuxiliaryInformation auxiliaryInformation) {
+ mAuxiliaryInformation = auxiliaryInformation;
+ return this;
+ }
+
/** Sets the list of time models. */
@NonNull
- public Builder setTimeModels(
- @Nullable @SuppressLint("NullableCollection") List<TimeModel> timeModels) {
+ public Builder setTimeModels(@NonNull List<TimeModel> timeModels) {
mTimeModels = timeModels;
return this;
}
@@ -189,8 +208,7 @@
/** Sets the list of Glonass satellite ephemeris. */
@NonNull
public Builder setSatelliteEphemeris(
- @Nullable @SuppressLint("NullableCollection")
- List<GlonassSatelliteEphemeris> satelliteEphemeris) {
+ @NonNull List<GlonassSatelliteEphemeris> satelliteEphemeris) {
mSatelliteEphemeris = satelliteEphemeris;
return this;
}
@@ -198,8 +216,7 @@
/** Sets the list of Glonass satellite corrections. */
@NonNull
public Builder setSatelliteCorrections(
- @Nullable @SuppressLint("NullableCollection")
- List<GnssSatelliteCorrections> satelliteCorrections) {
+ @NonNull List<GnssSatelliteCorrections> satelliteCorrections) {
mSatelliteCorrections = satelliteCorrections;
return this;
}
diff --git a/location/java/android/location/GpsAssistance.java b/location/java/android/location/GpsAssistance.java
index 5202fc4..5a8802f 100644
--- a/location/java/android/location/GpsAssistance.java
+++ b/location/java/android/location/GpsAssistance.java
@@ -51,6 +51,9 @@
/** The leap seconds model. */
@Nullable private final LeapSecondsModel mLeapSecondsModel;
+ /** The auxiliary information. */
+ @Nullable private final AuxiliaryInformation mAuxiliaryInformation;
+
/** The list of time models. */
@NonNull private final List<TimeModel> mTimeModels;
@@ -68,6 +71,7 @@
mIonosphericModel = builder.mIonosphericModel;
mUtcModel = builder.mUtcModel;
mLeapSecondsModel = builder.mLeapSecondsModel;
+ mAuxiliaryInformation = builder.mAuxiliaryInformation;
if (builder.mTimeModels != null) {
mTimeModels = Collections.unmodifiableList(new ArrayList<>(builder.mTimeModels));
} else {
@@ -117,6 +121,12 @@
return mLeapSecondsModel;
}
+ /** Returns the auxiliary information. */
+ @Nullable
+ public AuxiliaryInformation getAuxiliaryInformation() {
+ return mAuxiliaryInformation;
+ }
+
/** Returns the list of time models. */
@NonNull
public List<TimeModel> getTimeModels() {
@@ -152,6 +162,8 @@
in.readTypedObject(KlobucharIonosphericModel.CREATOR))
.setUtcModel(in.readTypedObject(UtcModel.CREATOR))
.setLeapSecondsModel(in.readTypedObject(LeapSecondsModel.CREATOR))
+ .setAuxiliaryInformation(
+ in.readTypedObject(AuxiliaryInformation.CREATOR))
.setTimeModels(in.createTypedArrayList(TimeModel.CREATOR))
.setSatelliteEphemeris(
in.createTypedArrayList(GpsSatelliteEphemeris.CREATOR))
@@ -179,6 +191,7 @@
dest.writeTypedObject(mIonosphericModel, flags);
dest.writeTypedObject(mUtcModel, flags);
dest.writeTypedObject(mLeapSecondsModel, flags);
+ dest.writeTypedObject(mAuxiliaryInformation, flags);
dest.writeTypedList(mTimeModels);
dest.writeTypedList(mSatelliteEphemeris);
dest.writeTypedList(mRealTimeIntegrityModels);
@@ -193,6 +206,7 @@
builder.append(", ionosphericModel = ").append(mIonosphericModel);
builder.append(", utcModel = ").append(mUtcModel);
builder.append(", leapSecondsModel = ").append(mLeapSecondsModel);
+ builder.append(", auxiliaryInformation = ").append(mAuxiliaryInformation);
builder.append(", timeModels = ").append(mTimeModels);
builder.append(", satelliteEphemeris = ").append(mSatelliteEphemeris);
builder.append(", realTimeIntegrityModels = ").append(mRealTimeIntegrityModels);
@@ -207,6 +221,7 @@
private KlobucharIonosphericModel mIonosphericModel;
private UtcModel mUtcModel;
private LeapSecondsModel mLeapSecondsModel;
+ private AuxiliaryInformation mAuxiliaryInformation;
private List<TimeModel> mTimeModels;
private List<GpsSatelliteEphemeris> mSatelliteEphemeris;
private List<RealTimeIntegrityModel> mRealTimeIntegrityModels;
@@ -222,33 +237,36 @@
/** Sets the Klobuchar ionospheric model. */
@NonNull
- public Builder setIonosphericModel(
- @Nullable @SuppressLint("NullableCollection")
- KlobucharIonosphericModel ionosphericModel) {
+ public Builder setIonosphericModel(@Nullable KlobucharIonosphericModel ionosphericModel) {
mIonosphericModel = ionosphericModel;
return this;
}
/** Sets the UTC model. */
@NonNull
- public Builder setUtcModel(
- @Nullable @SuppressLint("NullableCollection") UtcModel utcModel) {
+ public Builder setUtcModel(@Nullable UtcModel utcModel) {
mUtcModel = utcModel;
return this;
}
/** Sets the leap seconds model. */
@NonNull
- public Builder setLeapSecondsModel(
- @Nullable @SuppressLint("NullableCollection") LeapSecondsModel leapSecondsModel) {
+ public Builder setLeapSecondsModel(@Nullable LeapSecondsModel leapSecondsModel) {
mLeapSecondsModel = leapSecondsModel;
return this;
}
+ /** Sets the auxiliary information. */
+ @NonNull
+ public Builder setAuxiliaryInformation(
+ @Nullable AuxiliaryInformation auxiliaryInformation) {
+ mAuxiliaryInformation = auxiliaryInformation;
+ return this;
+ }
+
/** Sets the list of time models. */
@NonNull
- public Builder setTimeModels(
- @Nullable @SuppressLint("NullableCollection") List<TimeModel> timeModels) {
+ public Builder setTimeModels(@NonNull List<TimeModel> timeModels) {
mTimeModels = timeModels;
return this;
}
@@ -256,8 +274,7 @@
/** Sets the list of GPS ephemeris. */
@NonNull
public Builder setSatelliteEphemeris(
- @Nullable @SuppressLint("NullableCollection")
- List<GpsSatelliteEphemeris> satelliteEphemeris) {
+ @NonNull List<GpsSatelliteEphemeris> satelliteEphemeris) {
mSatelliteEphemeris = satelliteEphemeris;
return this;
}
@@ -265,8 +282,7 @@
/** Sets the list of real time integrity models. */
@NonNull
public Builder setRealTimeIntegrityModels(
- @Nullable @SuppressLint("NullableCollection")
- List<RealTimeIntegrityModel> realTimeIntegrityModels) {
+ @NonNull List<RealTimeIntegrityModel> realTimeIntegrityModels) {
mRealTimeIntegrityModels = realTimeIntegrityModels;
return this;
}
@@ -274,8 +290,7 @@
/** Sets the list of GPS satellite corrections. */
@NonNull
public Builder setSatelliteCorrections(
- @Nullable @SuppressLint("NullableCollection")
- List<GnssSatelliteCorrections> satelliteCorrections) {
+ @NonNull List<GnssSatelliteCorrections> satelliteCorrections) {
mSatelliteCorrections = satelliteCorrections;
return this;
}
diff --git a/location/java/android/location/QzssAssistance.java b/location/java/android/location/QzssAssistance.java
index 9383ce3..27c3437 100644
--- a/location/java/android/location/QzssAssistance.java
+++ b/location/java/android/location/QzssAssistance.java
@@ -19,7 +19,6 @@
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.location.GnssAssistance.GnssSatelliteCorrections;
import android.location.flags.Flags;
@@ -51,6 +50,9 @@
/** The leap seconds model. */
@Nullable private final LeapSecondsModel mLeapSecondsModel;
+ /** The auxiliary information. */
+ @Nullable private final AuxiliaryInformation mAuxiliaryInformation;
+
/** The list of time models. */
@NonNull private final List<TimeModel> mTimeModels;
@@ -68,6 +70,7 @@
mIonosphericModel = builder.mIonosphericModel;
mUtcModel = builder.mUtcModel;
mLeapSecondsModel = builder.mLeapSecondsModel;
+ mAuxiliaryInformation = builder.mAuxiliaryInformation;
if (builder.mTimeModels != null) {
mTimeModels = Collections.unmodifiableList(new ArrayList<>(builder.mTimeModels));
} else {
@@ -117,6 +120,12 @@
return mLeapSecondsModel;
}
+ /** Returns the auxiliary information. */
+ @Nullable
+ public AuxiliaryInformation getAuxiliaryInformation() {
+ return mAuxiliaryInformation;
+ }
+
/** Returns the list of time models. */
@NonNull
public List<TimeModel> getTimeModels() {
@@ -147,19 +156,23 @@
@NonNull
public QzssAssistance createFromParcel(Parcel in) {
return new QzssAssistance.Builder()
- .setAlmanac(in.readTypedObject(GnssAlmanac.CREATOR))
- .setIonosphericModel(in.readTypedObject(KlobucharIonosphericModel.CREATOR))
- .setUtcModel(in.readTypedObject(UtcModel.CREATOR))
- .setLeapSecondsModel(in.readTypedObject(LeapSecondsModel.CREATOR))
- .setTimeModels(in.createTypedArrayList(TimeModel.CREATOR))
- .setSatelliteEphemeris(
- in.createTypedArrayList(QzssSatelliteEphemeris.CREATOR))
- .setRealTimeIntegrityModels(
- in.createTypedArrayList(RealTimeIntegrityModel.CREATOR))
- .setSatelliteCorrections(
- in.createTypedArrayList(GnssSatelliteCorrections.CREATOR))
- .build();
+ .setAlmanac(in.readTypedObject(GnssAlmanac.CREATOR))
+ .setIonosphericModel(
+ in.readTypedObject(KlobucharIonosphericModel.CREATOR))
+ .setUtcModel(in.readTypedObject(UtcModel.CREATOR))
+ .setLeapSecondsModel(in.readTypedObject(LeapSecondsModel.CREATOR))
+ .setAuxiliaryInformation(
+ in.readTypedObject(AuxiliaryInformation.CREATOR))
+ .setTimeModels(in.createTypedArrayList(TimeModel.CREATOR))
+ .setSatelliteEphemeris(
+ in.createTypedArrayList(QzssSatelliteEphemeris.CREATOR))
+ .setRealTimeIntegrityModels(
+ in.createTypedArrayList(RealTimeIntegrityModel.CREATOR))
+ .setSatelliteCorrections(
+ in.createTypedArrayList(GnssSatelliteCorrections.CREATOR))
+ .build();
}
+
@Override
public QzssAssistance[] newArray(int size) {
return new QzssAssistance[size];
@@ -177,6 +190,7 @@
dest.writeTypedObject(mIonosphericModel, flags);
dest.writeTypedObject(mUtcModel, flags);
dest.writeTypedObject(mLeapSecondsModel, flags);
+ dest.writeTypedObject(mAuxiliaryInformation, flags);
dest.writeTypedList(mTimeModels);
dest.writeTypedList(mSatelliteEphemeris);
dest.writeTypedList(mRealTimeIntegrityModels);
@@ -191,6 +205,7 @@
builder.append(", ionosphericModel = ").append(mIonosphericModel);
builder.append(", utcModel = ").append(mUtcModel);
builder.append(", leapSecondsModel = ").append(mLeapSecondsModel);
+ builder.append(", auxiliaryInformation = ").append(mAuxiliaryInformation);
builder.append(", timeModels = ").append(mTimeModels);
builder.append(", satelliteEphemeris = ").append(mSatelliteEphemeris);
builder.append(", realTimeIntegrityModels = ").append(mRealTimeIntegrityModels);
@@ -205,6 +220,7 @@
private KlobucharIonosphericModel mIonosphericModel;
private UtcModel mUtcModel;
private LeapSecondsModel mLeapSecondsModel;
+ private AuxiliaryInformation mAuxiliaryInformation;
private List<TimeModel> mTimeModels;
private List<QzssSatelliteEphemeris> mSatelliteEphemeris;
private List<RealTimeIntegrityModel> mRealTimeIntegrityModels;
@@ -238,10 +254,17 @@
return this;
}
+ /** Sets the auxiliary information. */
+ @NonNull
+ public Builder setAuxiliaryInformation(
+ @Nullable AuxiliaryInformation auxiliaryInformation) {
+ mAuxiliaryInformation = auxiliaryInformation;
+ return this;
+ }
+
/** Sets the list of time models. */
@NonNull
- public Builder setTimeModels(
- @Nullable @SuppressLint("NullableCollection") List<TimeModel> timeModels) {
+ public Builder setTimeModels(@NonNull List<TimeModel> timeModels) {
mTimeModels = timeModels;
return this;
}
@@ -249,8 +272,7 @@
/** Sets the list of QZSS ephemeris. */
@NonNull
public Builder setSatelliteEphemeris(
- @Nullable @SuppressLint("NullableCollection")
- List<QzssSatelliteEphemeris> satelliteEphemeris) {
+ @NonNull List<QzssSatelliteEphemeris> satelliteEphemeris) {
mSatelliteEphemeris = satelliteEphemeris;
return this;
}
@@ -258,8 +280,7 @@
/** Sets the list of real time integrity model. */
@NonNull
public Builder setRealTimeIntegrityModels(
- @Nullable @SuppressLint("NullableCollection")
- List<RealTimeIntegrityModel> realTimeIntegrityModels) {
+ @NonNull List<RealTimeIntegrityModel> realTimeIntegrityModels) {
mRealTimeIntegrityModels = realTimeIntegrityModels;
return this;
}
@@ -267,8 +288,7 @@
/** Sets the list of QZSS satellite correction. */
@NonNull
public Builder setSatelliteCorrections(
- @Nullable @SuppressLint("NullableCollection")
- List<GnssSatelliteCorrections> satelliteCorrections) {
+ @NonNull List<GnssSatelliteCorrections> satelliteCorrections) {
mSatelliteCorrections = satelliteCorrections;
return this;
}
diff --git a/location/java/android/location/RealTimeIntegrityModel.java b/location/java/android/location/RealTimeIntegrityModel.java
index d268926..f065def 100644
--- a/location/java/android/location/RealTimeIntegrityModel.java
+++ b/location/java/android/location/RealTimeIntegrityModel.java
@@ -26,6 +26,10 @@
import com.android.internal.util.Preconditions;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
/**
* A class contains the real time integrity status of a GNSS satellite based on notice advisory.
*
@@ -35,8 +39,7 @@
@SystemApi
public final class RealTimeIntegrityModel implements Parcelable {
/**
- * Pseudo-random or satellite ID number for the satellite,
- * a.k.a. Space Vehicle (SV), or OSN number for Glonass.
+ * Bad satellite ID number or OSN number for Glonass.
*
* <p>The distinction is made by looking at the constellation field. Values
* must be in the range of:
@@ -47,10 +50,14 @@
* <p> - Galileo: 1-36
* <p> - Beidou: 1-63
*/
- private final int mSvid;
+ private final int mBadSvid;
- /** Indicates whether the satellite is currently usable for navigation. */
- private final boolean mUsable;
+ /**
+ * The type of the bad signal or signals.
+ *
+ * <p>An empty list means that all signals on the specific SV are not healthy.
+ */
+ @NonNull private final List<GnssSignalType> mBadSignalTypes;
/** UTC timestamp (in seconds) when the advisory was published. */
private final long mPublishDateSeconds;
@@ -81,14 +88,19 @@
private RealTimeIntegrityModel(Builder builder) {
// Allow SV ID beyond the range to support potential future extensibility.
- Preconditions.checkArgument(builder.mSvid >= 1);
+ Preconditions.checkArgument(builder.mBadSvid >= 1);
Preconditions.checkArgument(builder.mPublishDateSeconds > 0);
Preconditions.checkArgument(builder.mStartDateSeconds > 0);
Preconditions.checkArgument(builder.mEndDateSeconds > 0);
Preconditions.checkNotNull(builder.mAdvisoryType, "AdvisoryType cannot be null");
Preconditions.checkNotNull(builder.mAdvisoryNumber, "AdvisoryNumber cannot be null");
- mSvid = builder.mSvid;
- mUsable = builder.mUsable;
+ if (builder.mBadSignalTypes == null) {
+ mBadSignalTypes = new ArrayList<>();
+ } else {
+ mBadSignalTypes = Collections.unmodifiableList(
+ new ArrayList<>(builder.mBadSignalTypes));
+ }
+ mBadSvid = builder.mBadSvid;
mPublishDateSeconds = builder.mPublishDateSeconds;
mStartDateSeconds = builder.mStartDateSeconds;
mEndDateSeconds = builder.mEndDateSeconds;
@@ -110,13 +122,18 @@
* <p> - Beidou: 1-63
*/
@IntRange(from = 1, to = 206)
- public int getSvid() {
- return mSvid;
+ public int getBadSvid() {
+ return mBadSvid;
}
- /** Returns whether the satellite is usable or not. */
- public boolean isUsable() {
- return mUsable;
+ /**
+ * Returns the type of the bad signal or signals.
+ *
+ * <p>An empty list means that all signals on the specific SV are not healthy.
+ */
+ @NonNull
+ public List<GnssSignalType> getBadSignalTypes() {
+ return mBadSignalTypes;
}
/** Returns the UTC timestamp (in seconds) when the advisory was published */
@@ -156,8 +173,9 @@
public RealTimeIntegrityModel createFromParcel(Parcel in) {
RealTimeIntegrityModel realTimeIntegrityModel =
new RealTimeIntegrityModel.Builder()
- .setSvid(in.readInt())
- .setUsable(in.readBoolean())
+ .setBadSvid(in.readInt())
+ .setBadSignalTypes(
+ in.createTypedArrayList(GnssSignalType.CREATOR))
.setPublishDateSeconds(in.readLong())
.setStartDateSeconds(in.readLong())
.setEndDateSeconds(in.readLong())
@@ -180,8 +198,8 @@
@Override
public void writeToParcel(@NonNull Parcel parcel, int flags) {
- parcel.writeInt(mSvid);
- parcel.writeBoolean(mUsable);
+ parcel.writeInt(mBadSvid);
+ parcel.writeTypedList(mBadSignalTypes);
parcel.writeLong(mPublishDateSeconds);
parcel.writeLong(mStartDateSeconds);
parcel.writeLong(mEndDateSeconds);
@@ -193,8 +211,8 @@
@NonNull
public String toString() {
StringBuilder builder = new StringBuilder("RealTimeIntegrityModel[");
- builder.append("svid = ").append(mSvid);
- builder.append(", usable = ").append(mUsable);
+ builder.append("badSvid = ").append(mBadSvid);
+ builder.append(", badSignalTypes = ").append(mBadSignalTypes);
builder.append(", publishDateSeconds = ").append(mPublishDateSeconds);
builder.append(", startDateSeconds = ").append(mStartDateSeconds);
builder.append(", endDateSeconds = ").append(mEndDateSeconds);
@@ -206,8 +224,8 @@
/** Builder for {@link RealTimeIntegrityModel} */
public static final class Builder {
- private int mSvid;
- private boolean mUsable;
+ private int mBadSvid;
+ private List<GnssSignalType> mBadSignalTypes;
private long mPublishDateSeconds;
private long mStartDateSeconds;
private long mEndDateSeconds;
@@ -215,8 +233,7 @@
private String mAdvisoryNumber;
/**
- * Sets the Pseudo-random or satellite ID number for the satellite,
- * a.k.a. Space Vehicle (SV), or OSN number for Glonass.
+ * Sets the bad satellite ID number or OSN number for Glonass.
*
* <p>The distinction is made by looking at the constellation field. Values
* must be in the range of:
@@ -228,15 +245,19 @@
* <p> - Beidou: 1-63
*/
@NonNull
- public Builder setSvid(@IntRange(from = 1, to = 206) int svid) {
- mSvid = svid;
+ public Builder setBadSvid(@IntRange(from = 1, to = 206) int badSvid) {
+ mBadSvid = badSvid;
return this;
}
- /** Sets whether the satellite is usable or not. */
+ /**
+ * Sets the type of the bad signal or signals.
+ *
+ * <p>An empty list means that all signals on the specific SV are not healthy.
+ */
@NonNull
- public Builder setUsable(boolean usable) {
- mUsable = usable;
+ public Builder setBadSignalTypes(@NonNull List<GnssSignalType> badSignalTypes) {
+ mBadSignalTypes = badSignalTypes;
return this;
}
diff --git a/media/java/android/media/FadeManagerConfiguration.java b/media/java/android/media/FadeManagerConfiguration.java
index 6d84e70..b91a5b5 100644
--- a/media/java/android/media/FadeManagerConfiguration.java
+++ b/media/java/android/media/FadeManagerConfiguration.java
@@ -673,6 +673,7 @@
return config != null ? config.getDuration() : DURATION_NOT_SET;
}
+ @Nullable
private VolumeShaper.Configuration getVolumeShaperConfigFromWrapper(
FadeVolumeShaperConfigsWrapper wrapper, boolean isFadeIn) {
// if no volume shaper config is available, return null
diff --git a/nfc/tests/src/android/nfc/NfcManagerTest.java b/nfc/tests/src/android/nfc/NfcManagerTest.java
new file mode 100644
index 0000000..06314cc
--- /dev/null
+++ b/nfc/tests/src/android/nfc/NfcManagerTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.nfc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+@RunWith(AndroidJUnit4.class)
+public class NfcManagerTest {
+
+ private MockitoSession mMockitoSession;
+ private NfcManager mNfcManager;
+ @Mock
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mMockitoSession = ExtendedMockito.mockitoSession()
+ .mockStatic(NfcAdapter.class)
+ .strictness(Strictness.LENIENT)
+ .startMocking();
+ MockitoAnnotations.initMocks(this);
+
+ when(NfcAdapter.getNfcAdapter(any())).thenReturn(mock(NfcAdapter.class));
+ when(mContext.getApplicationContext()).thenReturn(mContext);
+ mNfcManager = new NfcManager(mContext);
+ }
+
+ @After
+ public void tearDown() {
+ mMockitoSession.finishMocking();
+ }
+
+ @Test
+ public void testGetDefaultAdapter() {
+ NfcAdapter nfcAdapter = mNfcManager.getDefaultAdapter();
+ assertThat(nfcAdapter).isNotNull();
+ }
+}
diff --git a/nfc/tests/src/android/nfc/cardemulation/CardemulationTest.java b/nfc/tests/src/android/nfc/cardemulation/CardemulationTest.java
new file mode 100644
index 0000000..6be95ad
--- /dev/null
+++ b/nfc/tests/src/android/nfc/cardemulation/CardemulationTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.nfc.cardemulation;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.nfc.INfcCardEmulation;
+import android.nfc.NfcAdapter;
+import android.os.RemoteException;
+import android.os.UserHandle;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+@RunWith(AndroidJUnit4.class)
+public class CardemulationTest {
+
+ private CardEmulation mCardEmulation;
+ @Mock
+ private Context mContext;
+ @Mock
+ private INfcCardEmulation mINfcCardEmulation;
+ @Mock
+ private NfcAdapter mNfcAdapter;
+ @Mock
+ private PackageManager mPackageManager;
+ private MockitoSession mMockitoSession;
+
+ @Before
+ public void setUp() {
+ mMockitoSession = ExtendedMockito.mockitoSession()
+ .mockStatic(NfcAdapter.class)
+ .strictness(Strictness.LENIENT)
+ .startMocking();
+ MockitoAnnotations.initMocks(this);
+
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION))
+ .thenReturn(true);
+ when(mContext.getApplicationContext()).thenReturn(mContext);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ assertThat(mNfcAdapter).isNotNull();
+ when(mNfcAdapter.getCardEmulationService()).thenReturn(mINfcCardEmulation);
+ when(mNfcAdapter.getContext()).thenReturn(mContext);
+ mCardEmulation = CardEmulation.getInstance(mNfcAdapter);
+ }
+
+ @After
+ public void tearDown() {
+ mMockitoSession.finishMocking();
+ }
+
+ @Test
+ public void testIsDefaultServiceForCategory() throws RemoteException {
+ ComponentName componentName = mock(ComponentName.class);
+ UserHandle userHandle = mock(UserHandle.class);
+ when(userHandle.getIdentifier()).thenReturn(1);
+ when(mContext.getUser()).thenReturn(userHandle);
+ when(mINfcCardEmulation.isDefaultServiceForCategory(1, componentName,
+ "payment")).thenReturn(true);
+ boolean result = mCardEmulation.isDefaultServiceForCategory(componentName,
+ "payment");
+ assertThat(result).isTrue();
+ verify(mINfcCardEmulation).isDefaultServiceForCategory(1, componentName,
+ "payment");
+
+ }
+
+ @Test
+ public void testIsDefaultServiceForAid() throws RemoteException {
+ ComponentName componentName = mock(ComponentName.class);
+ UserHandle userHandle = mock(UserHandle.class);
+ when(userHandle.getIdentifier()).thenReturn(1);
+ when(mContext.getUser()).thenReturn(userHandle);
+ when(mINfcCardEmulation.isDefaultServiceForAid(1, componentName,
+ "payment")).thenReturn(true);
+ boolean result = mCardEmulation.isDefaultServiceForAid(componentName,
+ "payment");
+ assertThat(result).isTrue();
+ verify(mINfcCardEmulation).isDefaultServiceForAid(1, componentName,
+ "payment");
+ }
+}
diff --git a/nfc/tests/src/android/nfc/tech/NfcBTest.java b/nfc/tests/src/android/nfc/tech/NfcBTest.java
new file mode 100644
index 0000000..98d6070
--- /dev/null
+++ b/nfc/tests/src/android/nfc/tech/NfcBTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.nfc.tech;
+
+import static android.nfc.tech.NfcB.EXTRA_APPDATA;
+import static android.nfc.tech.NfcB.EXTRA_PROTINFO;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.nfc.INfcTag;
+import android.nfc.Tag;
+import android.nfc.TransceiveResult;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.IOException;
+
+public class NfcBTest {
+ private final byte[] mSampleAppDate = new byte[] {1, 2, 3};
+ private final byte[] mSampleProtInfo = new byte[] {3, 2, 1};
+ @Mock
+ private Tag mMockTag;
+ @Mock
+ private Bundle mMockBundle;
+ @Mock
+ private INfcTag mMockTagService;
+ private NfcB mNfcB;
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+ when(mMockBundle.getByteArray(EXTRA_APPDATA)).thenReturn(mSampleAppDate);
+ when(mMockBundle.getByteArray(EXTRA_PROTINFO)).thenReturn(mSampleProtInfo);
+ when(mMockTag.getTechExtras(TagTechnology.NFC_B)).thenReturn(mMockBundle);
+
+ mNfcB = new NfcB(mMockTag);
+ }
+
+ @Test
+ public void testGetApplicationData() {
+ assertNotNull(mNfcB.getApplicationData());
+ }
+
+ @Test
+ public void testGetProtocolInfo() {
+ assertNotNull(mNfcB.getProtocolInfo());
+ }
+
+ @Test
+ public void testGetNfcBInstance() {
+ Tag tag = mock(Tag.class);
+ when(tag.hasTech(TagTechnology.NFC_B)).thenReturn(true);
+ when(tag.getTechExtras(TagTechnology.NFC_B)).thenReturn(mMockBundle);
+
+ assertNotNull(NfcB.get(tag));
+ verify(tag).hasTech(TagTechnology.NFC_B);
+ verify(tag).getTechExtras(TagTechnology.NFC_B);
+ }
+
+ @Test
+ public void testGetNfcBNullInstance() {
+ Tag tag = mock(Tag.class);
+ when(tag.hasTech(TagTechnology.NFC_B)).thenReturn(false);
+
+ assertNull(NfcB.get(tag));
+ verify(tag).hasTech(TagTechnology.NFC_B);
+ verify(tag, never()).getTechExtras(TagTechnology.NFC_B);
+ }
+
+
+ @Test
+ public void testTransceive() throws IOException, RemoteException {
+ byte[] sampleData = new byte[] {1, 2, 3, 4, 5};
+ TransceiveResult mockTransceiveResult = mock(TransceiveResult.class);
+ when(mMockTag.getConnectedTechnology()).thenReturn(TagTechnology.NFC_B);
+ when(mMockTag.getTagService()).thenReturn(mMockTagService);
+ when(mMockTag.getServiceHandle()).thenReturn(1);
+ when(mMockTagService.transceive(1, sampleData, true))
+ .thenReturn(mockTransceiveResult);
+ when(mockTransceiveResult.getResponseOrThrow()).thenReturn(sampleData);
+
+ mNfcB.transceive(sampleData);
+ verify(mMockTag).getTagService();
+ verify(mMockTag).getServiceHandle();
+ }
+
+ @Test
+ public void testGetMaxTransceiveLength() throws RemoteException {
+ when(mMockTag.getTagService()).thenReturn(mMockTagService);
+ when(mMockTagService.getMaxTransceiveLength(TagTechnology.NFC_B)).thenReturn(1);
+
+ mNfcB.getMaxTransceiveLength();
+ verify(mMockTag).getTagService();
+ }
+}
diff --git a/nfc/tests/src/android/nfc/tech/NfcFTest.java b/nfc/tests/src/android/nfc/tech/NfcFTest.java
new file mode 100644
index 0000000..31a6943
--- /dev/null
+++ b/nfc/tests/src/android/nfc/tech/NfcFTest.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.nfc.tech;
+
+import static android.nfc.tech.NfcF.EXTRA_PMM;
+import static android.nfc.tech.NfcF.EXTRA_SC;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.nfc.ErrorCodes;
+import android.nfc.INfcTag;
+import android.nfc.Tag;
+import android.nfc.TransceiveResult;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.IOException;
+
+public class NfcFTest {
+ private final byte[] mSampleSystemCode = new byte[] {1, 2, 3};
+ private final byte[] mSampleManufacturer = new byte[] {3, 2, 1};
+ @Mock
+ private Tag mMockTag;
+ @Mock
+ private INfcTag mMockTagService;
+ @Mock
+ private Bundle mMockBundle;
+ private NfcF mNfcF;
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+ when(mMockBundle.getByteArray(EXTRA_SC)).thenReturn(mSampleSystemCode);
+ when(mMockBundle.getByteArray(EXTRA_PMM)).thenReturn(mSampleManufacturer);
+ when(mMockTag.getTechExtras(TagTechnology.NFC_F)).thenReturn(mMockBundle);
+
+ mNfcF = new NfcF(mMockTag);
+ }
+
+ @Test
+ public void testGetSystemCode() {
+ assertNotNull(mNfcF.getSystemCode());
+ }
+
+ @Test
+ public void testGetManufacturer() {
+ assertNotNull(mNfcF.getManufacturer());
+ }
+
+ @Test
+ public void testGetNfcFInstanceWithTech() {
+ Tag tag = mock(Tag.class);
+ when(tag.getTechExtras(TagTechnology.NFC_F)).thenReturn(mMockBundle);
+ when(tag.hasTech(TagTechnology.NFC_F)).thenReturn(true);
+
+ assertNotNull(NfcF.get(tag));
+ verify(tag).getTechExtras(TagTechnology.NFC_F);
+ verify(tag).hasTech(TagTechnology.NFC_F);
+ }
+
+ @Test
+ public void testGetNfcFInstanceWithoutTech() {
+ Tag tag = mock(Tag.class);
+ when(tag.hasTech(TagTechnology.NFC_F)).thenReturn(false);
+
+ assertNull(NfcF.get(tag));
+ verify(tag).hasTech(TagTechnology.NFC_F);
+ verify(tag, never()).getTechExtras(TagTechnology.NFC_F);
+ }
+
+ @Test
+ public void testTransceive() throws IOException, RemoteException {
+ byte[] sampleData = new byte[]{1, 2, 3, 4, 5};
+ TransceiveResult mockTransceiveResult = mock(TransceiveResult.class);
+ when(mMockTag.getConnectedTechnology()).thenReturn(TagTechnology.NFC_F);
+ when(mMockTag.getTagService()).thenReturn(mMockTagService);
+ when(mMockTag.getServiceHandle()).thenReturn(1);
+ when(mMockTagService.transceive(1, sampleData, true))
+ .thenReturn(mockTransceiveResult);
+ when(mockTransceiveResult.getResponseOrThrow()).thenReturn(sampleData);
+
+ mNfcF.transceive(sampleData);
+ verify(mMockTag).getTagService();
+ verify(mMockTag).getServiceHandle();
+ }
+
+ @Test
+ public void testGetMaxTransceiveLength() throws RemoteException {
+ when(mMockTag.getTagService()).thenReturn(mMockTagService);
+ when(mMockTagService.getMaxTransceiveLength(TagTechnology.NFC_F)).thenReturn(1);
+
+ mNfcF.getMaxTransceiveLength();
+ verify(mMockTag).getTagService();
+ }
+
+ @Test
+ public void testGetTimeout() {
+ when(mMockTag.getTagService()).thenReturn(mMockTagService);
+ try {
+ when(mMockTagService.getTimeout(TagTechnology.NFC_F)).thenReturn(2000);
+
+ assertEquals(2000, mNfcF.getTimeout());
+ verify(mMockTag).getTagService();
+ verify(mMockTagService).getTimeout(TagTechnology.NFC_F);
+ } catch (Exception e) {
+ fail("Unexpected exception during valid getTimeout: " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void testGetTimeoutRemoteException() {
+ when(mMockTag.getTagService()).thenReturn(mMockTagService);
+ try {
+ when(mMockTagService.getTimeout(TagTechnology.NFC_F)).thenThrow(new RemoteException());
+
+ assertEquals(0, mNfcF.getTimeout());
+ } catch (Exception e) {
+ fail("Unexpected exception during RemoteException in getTimeout: " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void testSetTimeout() {
+ when(mMockTag.getTagService()).thenReturn(mMockTagService);
+ try {
+ when(mMockTagService.setTimeout(TagTechnology.NFC_F, 1000)).thenReturn(
+ ErrorCodes.SUCCESS);
+
+ mNfcF.setTimeout(1000);
+ verify(mMockTag).getTagService();
+ verify(mMockTagService).setTimeout(TagTechnology.NFC_F, 1000);
+ } catch (Exception e) {
+ fail("Unexpected exception during valid setTimeout: " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void testSetTimeoutInvalidTimeout() {
+ when(mMockTag.getTagService()).thenReturn(mMockTagService);
+ try {
+ when(mMockTagService.setTimeout(TagTechnology.NFC_F, -1)).thenReturn(
+ ErrorCodes.ERROR_TIMEOUT);
+
+ assertThrows(IllegalArgumentException.class, () -> mNfcF.setTimeout(-1));
+ } catch (Exception e) {
+ fail("Unexpected exception during invalid setTimeout: " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void testSetTimeoutRemoteException() {
+ when(mMockTag.getTagService()).thenReturn(mMockTagService);
+ try {
+ when(mMockTagService.setTimeout(TagTechnology.NFC_F, 1000)).thenThrow(
+ new RemoteException());
+
+ mNfcF.setTimeout(1000);
+ verify(mMockTag).getTagService();
+ verify(mMockTagService).setTimeout(TagTechnology.NFC_F, 1000);
+ } catch (Exception e) {
+ fail("Unexpected exception during RemoteException in setTimeout: " + e.getMessage());
+ }
+ }
+}
diff --git a/packages/CrashRecovery/framework/api/system-current.txt b/packages/CrashRecovery/framework/api/system-current.txt
index 68429ea..ad17ec69 100644
--- a/packages/CrashRecovery/framework/api/system-current.txt
+++ b/packages/CrashRecovery/framework/api/system-current.txt
@@ -9,7 +9,7 @@
method @NonNull public abstract java.util.List<java.lang.String> onGetRequestedPackages();
method @NonNull public abstract java.util.List<android.service.watchdog.ExplicitHealthCheckService.PackageConfig> onGetSupportedPackages();
method public abstract void onRequestHealthCheck(@NonNull String);
- method @FlaggedApi("android.crashrecovery.flags.enable_crashrecovery") public final void setHealthCheckResultCallback(@Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<android.os.Bundle>);
+ method @FlaggedApi("android.crashrecovery.flags.enable_crashrecovery") public final void setHealthCheckPassedCallback(@Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<android.os.Bundle>);
field public static final String BIND_PERMISSION = "android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE";
field @FlaggedApi("android.crashrecovery.flags.enable_crashrecovery") public static final String EXTRA_HEALTH_CHECK_PASSED_PACKAGE = "android.service.watchdog.extra.HEALTH_CHECK_PASSED_PACKAGE";
field public static final String SERVICE_INTERFACE = "android.service.watchdog.ExplicitHealthCheckService";
diff --git a/packages/CrashRecovery/framework/java/android/service/watchdog/ExplicitHealthCheckService.java b/packages/CrashRecovery/framework/java/android/service/watchdog/ExplicitHealthCheckService.java
index b03e376..fdb0fc5 100644
--- a/packages/CrashRecovery/framework/java/android/service/watchdog/ExplicitHealthCheckService.java
+++ b/packages/CrashRecovery/framework/java/android/service/watchdog/ExplicitHealthCheckService.java
@@ -180,7 +180,7 @@
* passed the health check.
*/
@FlaggedApi(Flags.FLAG_ENABLE_CRASHRECOVERY)
- public final void setHealthCheckResultCallback(@CallbackExecutor @Nullable Executor executor,
+ public final void setHealthCheckPassedCallback(@CallbackExecutor @Nullable Executor executor,
@Nullable Consumer<Bundle> callback) {
mCallbackExecutor = executor;
mHealthCheckResultCallback = callback;
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
index 70f5bb3..ef46906 100644
--- a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
@@ -347,8 +347,8 @@
* and boot loops.
* @param executor Executor for the thread on which observers would receive callbacks
*/
- public void registerHealthObserver(@NonNull PackageHealthObserver observer,
- @NonNull @CallbackExecutor Executor executor) {
+ public void registerHealthObserver(@NonNull @CallbackExecutor Executor executor,
+ @NonNull PackageHealthObserver observer) {
synchronized (sLock) {
ObserverInternal internalObserver = mAllObservers.get(observer.getUniqueIdentifier());
if (internalObserver != null) {
@@ -390,8 +390,8 @@
*
* @throws IllegalStateException if the observer was not previously registered
*/
- public void startExplicitHealthCheck(@NonNull PackageHealthObserver observer,
- @NonNull List<String> packageNames, long timeoutMs) {
+ public void startExplicitHealthCheck(@NonNull List<String> packageNames, long timeoutMs,
+ @NonNull PackageHealthObserver observer) {
synchronized (sLock) {
if (!mAllObservers.containsKey(observer.getUniqueIdentifier())) {
Slog.wtf(TAG, "No observer found, need to register the observer: "
@@ -767,14 +767,6 @@
}
/**
- * Indicates that the result of a mitigation executed during
- * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} or
- * {@link PackageHealthObserver#onExecuteBootLoopMitigation} is unknown.
- */
- public static final int MITIGATION_RESULT_UNKNOWN =
- ObserverMitigationResult.MITIGATION_RESULT_UNKNOWN;
-
- /**
* Indicates that a mitigation was successfully triggered or executed during
* {@link PackageHealthObserver#onExecuteHealthCheckMitigation} or
* {@link PackageHealthObserver#onExecuteBootLoopMitigation}.
@@ -790,23 +782,6 @@
public static final int MITIGATION_RESULT_SKIPPED =
ObserverMitigationResult.MITIGATION_RESULT_SKIPPED;
- /**
- * Indicates that a mitigation executed during
- * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} or
- * {@link PackageHealthObserver#onExecuteBootLoopMitigation} failed,
- * but the failure is potentially retryable.
- */
- public static final int MITIGATION_RESULT_FAILURE_RETRYABLE =
- ObserverMitigationResult.MITIGATION_RESULT_FAILURE_RETRYABLE;
-
- /**
- * Indicates that a mitigation executed during
- * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} or
- * {@link PackageHealthObserver#onExecuteBootLoopMitigation} failed,
- * and the failure is not retryable.
- */
- public static final int MITIGATION_RESULT_FAILURE_NON_RETRYABLE =
- ObserverMitigationResult.MITIGATION_RESULT_FAILURE_NON_RETRYABLE;
/**
* Possible return values of the for mitigations executed during
@@ -816,18 +791,12 @@
*/
@Retention(SOURCE)
@IntDef(prefix = "MITIGATION_RESULT_", value = {
- ObserverMitigationResult.MITIGATION_RESULT_UNKNOWN,
ObserverMitigationResult.MITIGATION_RESULT_SUCCESS,
ObserverMitigationResult.MITIGATION_RESULT_SKIPPED,
- ObserverMitigationResult.MITIGATION_RESULT_FAILURE_RETRYABLE,
- ObserverMitigationResult.MITIGATION_RESULT_FAILURE_NON_RETRYABLE,
})
public @interface ObserverMitigationResult {
- int MITIGATION_RESULT_UNKNOWN = 0;
int MITIGATION_RESULT_SUCCESS = 1;
int MITIGATION_RESULT_SKIPPED = 2;
- int MITIGATION_RESULT_FAILURE_RETRYABLE = 3;
- int MITIGATION_RESULT_FAILURE_NON_RETRYABLE = 4;
}
/**
@@ -921,11 +890,6 @@
* @param mitigationCount the number of times mitigation has been called for this package
* (including this time).
* @return {@link #MITIGATION_RESULT_SUCCESS} if the mitigation was successful,
- * {@link #MITIGATION_RESULT_FAILURE_RETRYABLE} if the mitigation failed but can be
- * retried,
- * {@link #MITIGATION_RESULT_FAILURE_NON_RETRYABLE} if the mitigation failed and
- * cannot be retried,
- * {@link #MITIGATION_RESULT_UNKNOWN} if the result of the mitigation is unknown,
* or {@link #MITIGATION_RESULT_SKIPPED} if the mitigation was skipped.
*/
@ObserverMitigationResult int onExecuteHealthCheckMitigation(
@@ -957,11 +921,6 @@
* boot loop (including this time).
*
* @return {@link #MITIGATION_RESULT_SUCCESS} if the mitigation was successful,
- * {@link #MITIGATION_RESULT_FAILURE_RETRYABLE} if the mitigation failed but can be
- * retried,
- * {@link #MITIGATION_RESULT_FAILURE_NON_RETRYABLE} if the mitigation failed and
- * cannot be retried,
- * {@link #MITIGATION_RESULT_UNKNOWN} if the result of the mitigation is unknown,
* or {@link #MITIGATION_RESULT_SKIPPED} if the mitigation was skipped.
*/
default @ObserverMitigationResult int onExecuteBootLoopMitigation(int mitigationCount) {
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
index bb9e962..40bc5f7 100644
--- a/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
@@ -162,7 +162,7 @@
/** Register the Rescue Party observer as a Package Watchdog health observer */
public static void registerHealthObserver(Context context) {
PackageWatchdog.getInstance(context).registerHealthObserver(
- RescuePartyObserver.getInstance(context), context.getMainExecutor());
+ context.getMainExecutor(), RescuePartyObserver.getInstance(context));
}
private static boolean isDisabled() {
@@ -316,9 +316,9 @@
Slog.i(TAG, "Starting to observe: " + callingPackageList + ", updated namespace: "
+ updatedNamespace);
PackageWatchdog.getInstance(context).startExplicitHealthCheck(
- rescuePartyObserver,
callingPackageList,
- DEFAULT_OBSERVING_DURATION_MS);
+ DEFAULT_OBSERVING_DURATION_MS,
+ rescuePartyObserver);
}
}
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index c75f3aa..4978df4 100644
--- a/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -113,8 +113,8 @@
dataDir.mkdirs();
mLastStagedRollbackIdsFile = new File(dataDir, "last-staged-rollback-ids");
mTwoPhaseRollbackEnabledFile = new File(dataDir, "two-phase-rollback-enabled");
- PackageWatchdog.getInstance(mContext).registerHealthObserver(this,
- context.getMainExecutor());
+ PackageWatchdog.getInstance(mContext).registerHealthObserver(context.getMainExecutor(),
+ this);
if (SystemProperties.getBoolean("sys.boot_completed", false)) {
// Load the value from the file if system server has crashed and restarted
diff --git a/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
index ac815f8..4a00ed3 100644
--- a/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
+++ b/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
@@ -363,7 +363,7 @@
* it will resume observing any packages requested from a previous boot.
* @hide
*/
- public void registerHealthObserver(PackageHealthObserver observer, Executor ignoredExecutor) {
+ public void registerHealthObserver(Executor ignoredExecutor, PackageHealthObserver observer) {
synchronized (mLock) {
ObserverInternal internalObserver = mAllObservers.get(observer.getUniqueIdentifier());
if (internalObserver != null) {
@@ -397,8 +397,8 @@
* {@link #DEFAULT_OBSERVING_DURATION_MS} will be used.
* @hide
*/
- public void startExplicitHealthCheck(PackageHealthObserver observer, List<String> packageNames,
- long durationMs) {
+ public void startExplicitHealthCheck(List<String> packageNames, long durationMs,
+ PackageHealthObserver observer) {
if (packageNames.isEmpty()) {
Slog.wtf(TAG, "No packages to observe, " + observer.getUniqueIdentifier());
return;
@@ -446,7 +446,7 @@
}
// Register observer in case not already registered
- registerHealthObserver(observer, null);
+ registerHealthObserver(null, observer);
// Sync after we add the new packages to the observers. We may have received packges
// requiring an earlier schedule than we are currently scheduled for.
diff --git a/packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java
index c6452d3..8251fb4 100644
--- a/packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java
+++ b/packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java
@@ -168,7 +168,7 @@
/** Register the Rescue Party observer as a Package Watchdog health observer */
public static void registerHealthObserver(Context context) {
PackageWatchdog.getInstance(context).registerHealthObserver(
- RescuePartyObserver.getInstance(context), null);
+ null, RescuePartyObserver.getInstance(context));
}
private static boolean isDisabled() {
@@ -390,9 +390,9 @@
Slog.i(TAG, "Starting to observe: " + callingPackageList + ", updated namespace: "
+ updatedNamespace);
PackageWatchdog.getInstance(context).startExplicitHealthCheck(
- rescuePartyObserver,
callingPackageList,
- DEFAULT_OBSERVING_DURATION_MS);
+ DEFAULT_OBSERVING_DURATION_MS,
+ rescuePartyObserver);
}
}
diff --git a/packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 0411537..5c4e57e 100644
--- a/packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -112,7 +112,7 @@
dataDir.mkdirs();
mLastStagedRollbackIdsFile = new File(dataDir, "last-staged-rollback-ids");
mTwoPhaseRollbackEnabledFile = new File(dataDir, "two-phase-rollback-enabled");
- PackageWatchdog.getInstance(mContext).registerHealthObserver(this, null);
+ PackageWatchdog.getInstance(mContext).registerHealthObserver(null, this);
mApexManager = apexManager;
if (SystemProperties.getBoolean("sys.boot_completed", false)) {
@@ -286,7 +286,7 @@
@AnyThread
@NonNull
public void startObservingHealth(@NonNull List<String> packages, @NonNull long durationMs) {
- PackageWatchdog.getInstance(mContext).startExplicitHealthCheck(this, packages, durationMs);
+ PackageWatchdog.getInstance(mContext).startExplicitHealthCheck(packages, durationMs, this);
}
@AnyThread
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
index e029f3a..4da7359 100644
--- a/packages/PackageInstaller/AndroidManifest.xml
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -24,6 +24,7 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SYSTEM_EXEMPTED" />
<uses-permission android:name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER" />
+ <uses-permission android:name="android.permission.RESOLVE_COMPONENT_FOR_UID" />
<uses-permission android:name="com.google.android.permission.INSTALL_WEARABLE_PACKAGES" />
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
index 635ae20..6c06fab 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@@ -26,6 +26,7 @@
import android.content.ContentResolver;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
+import android.content.pm.Flags;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
@@ -274,8 +275,20 @@
}
private boolean canPackageQuery(int callingUid, Uri packageUri) {
- ProviderInfo info = mPackageManager.resolveContentProvider(packageUri.getAuthority(),
- PackageManager.ComponentInfoFlags.of(0));
+ ProviderInfo info;
+ try {
+ if (Flags.uidBasedProviderLookup()) {
+ info = mPackageManager.resolveContentProviderForUid(packageUri.getAuthority(),
+ PackageManager.ComponentInfoFlags.of(0), callingUid);
+ } else {
+ info = mPackageManager.resolveContentProvider(packageUri.getAuthority(),
+ PackageManager.ComponentInfoFlags.of(0));
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Caller cannot access " + packageUri, e);
+ return false;
+ }
+
if (info == null) {
return false;
}
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsGlobalStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsGlobalStore.kt
index 53507fe..8335ee4 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsGlobalStore.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsGlobalStore.kt
@@ -16,6 +16,7 @@
package com.android.settingslib.datastore
+import android.Manifest
import android.content.ContentResolver
import android.content.Context
import android.net.Uri
@@ -82,5 +83,11 @@
instance = it
}
}
+
+ /** Returns the required permissions to read [Global] settings. */
+ fun getReadPermissions() = arrayOf<String>()
+
+ /** Returns the required permissions to write [Global] settings. */
+ fun getWritePermissions() = arrayOf(Manifest.permission.WRITE_SECURE_SETTINGS)
}
}
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSecureStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSecureStore.kt
index ca7fd7b..c117b92 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSecureStore.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSecureStore.kt
@@ -16,6 +16,7 @@
package com.android.settingslib.datastore
+import android.Manifest
import android.content.ContentResolver
import android.content.Context
import android.net.Uri
@@ -82,5 +83,11 @@
instance = it
}
}
+
+ /** Returns the required permissions to read [Secure] settings. */
+ fun getReadPermissions() = arrayOf<String>()
+
+ /** Returns the required permissions to write [Secure] settings. */
+ fun getWritePermissions() = arrayOf(Manifest.permission.WRITE_SECURE_SETTINGS)
}
}
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSystemStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSystemStore.kt
index 20a74d3..f5a2f94 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSystemStore.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSystemStore.kt
@@ -16,6 +16,7 @@
package com.android.settingslib.datastore
+import android.Manifest
import android.content.ContentResolver
import android.content.Context
import android.net.Uri
@@ -82,5 +83,11 @@
instance = it
}
}
+
+ /** Returns the required permissions to read [System] settings. */
+ fun getReadPermissions() = arrayOf<String>()
+
+ /** Returns the required permissions to write [System] settings. */
+ fun getWritePermissions() = arrayOf(Manifest.permission.WRITE_SETTINGS)
}
}
diff --git a/packages/SettingsLib/Graph/graph.proto b/packages/SettingsLib/Graph/graph.proto
index f611793..2aa619a 100644
--- a/packages/SettingsLib/Graph/graph.proto
+++ b/packages/SettingsLib/Graph/graph.proto
@@ -81,6 +81,10 @@
optional PreferenceValueDescriptorProto value_descriptor = 15;
// Indicate how sensitive of the preference.
optional int32 sensitivity_level = 16;
+ // The required permissions to read preference value.
+ repeated string read_permissions = 17;
+ // The required permissions to write preference value.
+ repeated string write_permissions = 18;
// Target of an Intent
message ActionTarget {
@@ -108,6 +112,7 @@
oneof value {
bool boolean_value = 1;
int32 int_value = 2;
+ float float_value = 3;
}
}
@@ -116,6 +121,7 @@
oneof type {
bool boolean_type = 1;
RangeValueProto range_value = 2;
+ bool float_type = 3;
}
}
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
index eaa7926..91dec03 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
@@ -42,6 +42,7 @@
import com.android.settingslib.graph.proto.PreferenceScreenProto
import com.android.settingslib.graph.proto.TextProto
import com.android.settingslib.metadata.BooleanValue
+import com.android.settingslib.metadata.FloatPersistentPreference
import com.android.settingslib.metadata.PersistentPreference
import com.android.settingslib.metadata.PreferenceAvailabilityProvider
import com.android.settingslib.metadata.PreferenceHierarchy
@@ -390,7 +391,13 @@
}
persistent = metadata.isPersistent(context)
if (persistent) {
- if (metadata is PersistentPreference<*>) sensitivityLevel = metadata.sensitivityLevel
+ if (metadata is PersistentPreference<*>) {
+ sensitivityLevel = metadata.sensitivityLevel
+ val readPermissions = metadata.getReadPermissions(context)
+ readPermissions.forEach { addReadPermissions(it) }
+ val writePermissions = metadata.getWritePermissions(context)
+ writePermissions.forEach { addWritePermissions(it) }
+ }
if (
flags.includeValue() &&
enabled &&
@@ -399,15 +406,13 @@
metadata is PersistentPreference<*> &&
metadata.evalReadPermit(context, callingPid, callingUid) == ReadWritePermit.ALLOW
) {
+ val storage = metadata.storage(context)
value = preferenceValueProto {
when (metadata) {
- is BooleanValue ->
- metadata.storage(context).getBoolean(metadata.key)?.let {
- booleanValue = it
- }
- is RangeValue -> {
- metadata.storage(context).getInt(metadata.key)?.let { intValue = it }
- }
+ is BooleanValue -> storage.getBoolean(metadata.key)?.let { booleanValue = it }
+ is RangeValue -> storage.getInt(metadata.key)?.let { intValue = it }
+ is FloatPersistentPreference ->
+ storage.getFloat(metadata.key)?.let { floatValue = it }
else -> {}
}
}
@@ -421,6 +426,7 @@
max = metadata.getMaxValue(context)
step = metadata.getIncrementStep(context)
}
+ is FloatPersistentPreference -> floatType = true
else -> {}
}
}
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
index d72ba08..83c4304 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
@@ -159,6 +159,12 @@
}
storage.setInt(key, intValue)
return PreferenceSetterResult.OK
+ } else if (value.hasFloatValue()) {
+ val floatValue = value.floatValue
+ val resultCode = metadata.checkWritePermit(floatValue)
+ if (resultCode != PreferenceSetterResult.OK) return resultCode
+ storage.setFloat(key, floatValue)
+ return PreferenceSetterResult.OK
}
} catch (e: Exception) {
return PreferenceSetterResult.INTERNAL_ERROR
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/ProtoDsl.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/ProtoDsl.kt
index dee32d9..adbe773 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/ProtoDsl.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/ProtoDsl.kt
@@ -35,8 +35,9 @@
/** Kotlin DSL-style builder for [PreferenceScreenProto]. */
@JvmSynthetic
-inline fun preferenceScreenProto(init: PreferenceScreenProto.Builder.() -> Unit) =
- PreferenceScreenProto.newBuilder().also(init).build()
+inline fun preferenceScreenProto(
+ init: PreferenceScreenProto.Builder.() -> Unit
+): PreferenceScreenProto = PreferenceScreenProto.newBuilder().also(init).build()
/** Returns preference or null. */
val PreferenceOrGroupProto.preferenceOrNull
@@ -48,8 +49,9 @@
/** Kotlin DSL-style builder for [PreferenceOrGroupProto]. */
@JvmSynthetic
-inline fun preferenceOrGroupProto(init: PreferenceOrGroupProto.Builder.() -> Unit) =
- PreferenceOrGroupProto.newBuilder().also(init).build()
+inline fun preferenceOrGroupProto(
+ init: PreferenceOrGroupProto.Builder.() -> Unit
+): PreferenceOrGroupProto = PreferenceOrGroupProto.newBuilder().also(init).build()
/** Returns preference or null. */
val PreferenceGroupProto.preferenceOrNull
@@ -57,8 +59,9 @@
/** Kotlin DSL-style builder for [PreferenceGroupProto]. */
@JvmSynthetic
-inline fun preferenceGroupProto(init: PreferenceGroupProto.Builder.() -> Unit) =
- PreferenceGroupProto.newBuilder().also(init).build()
+inline fun preferenceGroupProto(
+ init: PreferenceGroupProto.Builder.() -> Unit
+): PreferenceGroupProto = PreferenceGroupProto.newBuilder().also(init).build()
/** Returns title or null. */
val PreferenceProto.titleOrNull
@@ -74,7 +77,7 @@
/** Kotlin DSL-style builder for [PreferenceProto]. */
@JvmSynthetic
-inline fun preferenceProto(init: PreferenceProto.Builder.() -> Unit) =
+inline fun preferenceProto(init: PreferenceProto.Builder.() -> Unit): PreferenceProto =
PreferenceProto.newBuilder().also(init).build()
/** Returns intent or null. */
@@ -83,39 +86,42 @@
/** Kotlin DSL-style builder for [ActionTarget]. */
@JvmSynthetic
-inline fun actionTargetProto(init: ActionTarget.Builder.() -> Unit) =
+inline fun actionTargetProto(init: ActionTarget.Builder.() -> Unit): ActionTarget =
ActionTarget.newBuilder().also(init).build()
/** Kotlin DSL-style builder for [PreferenceValueProto]. */
@JvmSynthetic
-inline fun preferenceValueProto(init: PreferenceValueProto.Builder.() -> Unit) =
- PreferenceValueProto.newBuilder().also(init).build()
+inline fun preferenceValueProto(
+ init: PreferenceValueProto.Builder.() -> Unit
+): PreferenceValueProto = PreferenceValueProto.newBuilder().also(init).build()
/** Kotlin DSL-style builder for [PreferenceValueDescriptorProto]. */
@JvmSynthetic
-inline fun preferenceValueDescriptorProto(init: PreferenceValueDescriptorProto.Builder.() -> Unit) =
- PreferenceValueDescriptorProto.newBuilder().also(init).build()
+inline fun preferenceValueDescriptorProto(
+ init: PreferenceValueDescriptorProto.Builder.() -> Unit
+): PreferenceValueDescriptorProto = PreferenceValueDescriptorProto.newBuilder().also(init).build()
/** Kotlin DSL-style builder for [RangeValueProto]. */
@JvmSynthetic
-inline fun rangeValueProto(init: RangeValueProto.Builder.() -> Unit) =
+inline fun rangeValueProto(init: RangeValueProto.Builder.() -> Unit): RangeValueProto =
RangeValueProto.newBuilder().also(init).build()
/** Kotlin DSL-style builder for [TextProto]. */
@JvmSynthetic
-inline fun textProto(init: TextProto.Builder.() -> Unit) = TextProto.newBuilder().also(init).build()
+inline fun textProto(init: TextProto.Builder.() -> Unit): TextProto =
+ TextProto.newBuilder().also(init).build()
/** Kotlin DSL-style builder for [IntentProto]. */
@JvmSynthetic
-inline fun intentProto(init: IntentProto.Builder.() -> Unit) =
+inline fun intentProto(init: IntentProto.Builder.() -> Unit): IntentProto =
IntentProto.newBuilder().also(init).build()
/** Kotlin DSL-style builder for [BundleProto]. */
@JvmSynthetic
-inline fun bundleProto(init: BundleProto.Builder.() -> Unit) =
+inline fun bundleProto(init: BundleProto.Builder.() -> Unit): BundleProto =
BundleProto.newBuilder().also(init).build()
/** Kotlin DSL-style builder for [BundleValue]. */
@JvmSynthetic
-inline fun bundleValueProto(init: BundleValue.Builder.() -> Unit) =
+inline fun bundleValueProto(init: BundleValue.Builder.() -> Unit): BundleValue =
BundleValue.newBuilder().also(init).build()
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
index adc4f316..bc4f1f9 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
@@ -167,6 +167,7 @@
if (mLottieDynamicColor) {
LottieColorUtils.applyDynamicColors(getContext(), illustrationView);
}
+ LottieColorUtils.applyMaterialColor(getContext(), illustrationView);
if (mOnBindListener != null) {
mOnBindListener.onBind(illustrationView);
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
index 98a7290..4421424 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
@@ -21,14 +21,14 @@
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
-import com.android.settingslib.color.R;
+import androidx.annotation.NonNull;
+
+import com.android.settingslib.widget.theme.R;
import com.airbnb.lottie.LottieAnimationView;
import com.airbnb.lottie.LottieProperty;
import com.airbnb.lottie.model.KeyPath;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.Map;
/**
@@ -37,52 +37,97 @@
*/
public class LottieColorUtils {
private static final Map<String, Integer> DARK_TO_LIGHT_THEME_COLOR_MAP;
+ private static final Map<String, Integer> MATERIAL_COLOR_MAP;
static {
- HashMap<String, Integer> map = new HashMap<>();
- map.put(
- ".grey200",
- R.color.settingslib_color_grey800);
- map.put(
- ".grey600",
- R.color.settingslib_color_grey400);
- map.put(
- ".grey800",
- R.color.settingslib_color_grey300);
- map.put(
- ".grey900",
- R.color.settingslib_color_grey50);
- map.put(
- ".red100",
- R.color.settingslib_color_red500);
- map.put(
- ".red200",
- R.color.settingslib_color_red500);
- map.put(
- ".red400",
- R.color.settingslib_color_red600);
- map.put(
- ".black",
- android.R.color.white);
- map.put(
- ".blue200",
- R.color.settingslib_color_blue700);
- map.put(
- ".blue400",
- R.color.settingslib_color_blue600);
- map.put(
- ".green100",
- R.color.settingslib_color_green500);
- map.put(
- ".green200",
- R.color.settingslib_color_green500);
- map.put(
- ".green400",
- R.color.settingslib_color_green600);
- map.put(
- ".cream",
- R.color.settingslib_color_charcoal);
- DARK_TO_LIGHT_THEME_COLOR_MAP = Collections.unmodifiableMap(map);
+ DARK_TO_LIGHT_THEME_COLOR_MAP = Map.ofEntries(
+ Map.entry(".grey200",
+ com.android.settingslib.color.R.color.settingslib_color_grey800),
+ Map.entry(".grey600",
+ com.android.settingslib.color.R.color.settingslib_color_grey400),
+ Map.entry(".grey800",
+ com.android.settingslib.color.R.color.settingslib_color_grey300),
+ Map.entry(".grey900",
+ com.android.settingslib.color.R.color.settingslib_color_grey50),
+ Map.entry(".red100",
+ com.android.settingslib.color.R.color.settingslib_color_red500),
+ Map.entry(".red200",
+ com.android.settingslib.color.R.color.settingslib_color_red500),
+ Map.entry(".red400",
+ com.android.settingslib.color.R.color.settingslib_color_red600),
+ Map.entry(".black",
+ android.R.color.white),
+ Map.entry(".blue200",
+ com.android.settingslib.color.R.color.settingslib_color_blue700),
+ Map.entry(".blue400",
+ com.android.settingslib.color.R.color.settingslib_color_blue600),
+ Map.entry(".green100",
+ com.android.settingslib.color.R.color.settingslib_color_green500),
+ Map.entry(".green200",
+ com.android.settingslib.color.R.color.settingslib_color_green500),
+ Map.entry(".green400",
+ com.android.settingslib.color.R.color.settingslib_color_green600),
+ Map.entry(".cream",
+ com.android.settingslib.color.R.color.settingslib_color_charcoal));
+
+ MATERIAL_COLOR_MAP = Map.ofEntries(
+ Map.entry(".primary", R.color.settingslib_materialColorPrimary),
+ Map.entry(".onPrimary", R.color.settingslib_materialColorOnPrimary),
+ Map.entry(".primaryContainer", R.color.settingslib_materialColorPrimaryContainer),
+ Map.entry(".onPrimaryContainer",
+ R.color.settingslib_materialColorOnPrimaryContainer),
+ Map.entry(".primaryInverse", R.color.settingslib_materialColorPrimaryInverse),
+ Map.entry(".primaryFixed", R.color.settingslib_materialColorPrimaryFixed),
+ Map.entry(".primaryFixedDim", R.color.settingslib_materialColorPrimaryFixedDim),
+ Map.entry(".onPrimaryFixed", R.color.settingslib_materialColorOnPrimaryFixed),
+ Map.entry(".onPrimaryFixedVariant",
+ R.color.settingslib_materialColorOnPrimaryFixedVariant),
+ Map.entry(".secondary", R.color.settingslib_materialColorSecondary),
+ Map.entry(".onSecondary", R.color.settingslib_materialColorOnSecondary),
+ Map.entry(".secondaryContainer",
+ R.color.settingslib_materialColorSecondaryContainer),
+ Map.entry(".onSecondaryContainer",
+ R.color.settingslib_materialColorOnSecondaryContainer),
+ Map.entry(".secondaryFixed", R.color.settingslib_materialColorSecondaryFixed),
+ Map.entry(".secondaryFixedDim", R.color.settingslib_materialColorSecondaryFixedDim),
+ Map.entry(".onSecondaryFixed", R.color.settingslib_materialColorOnSecondaryFixed),
+ Map.entry(".onSecondaryFixedVariant",
+ R.color.settingslib_materialColorOnSecondaryFixedVariant),
+ Map.entry(".tertiary", R.color.settingslib_materialColorTertiary),
+ Map.entry(".onTertiary", R.color.settingslib_materialColorOnTertiary),
+ Map.entry(".tertiaryContainer", R.color.settingslib_materialColorTertiaryContainer),
+ Map.entry(".onTertiaryContainer",
+ R.color.settingslib_materialColorOnTertiaryContainer),
+ Map.entry(".tertiaryFixed", R.color.settingslib_materialColorTertiaryFixed),
+ Map.entry(".tertiaryFixedDim", R.color.settingslib_materialColorTertiaryFixedDim),
+ Map.entry(".onTertiaryFixed", R.color.settingslib_materialColorOnTertiaryFixed),
+ Map.entry(".onTertiaryFixedVariant",
+ R.color.settingslib_materialColorOnTertiaryFixedVariant),
+ Map.entry(".error", R.color.settingslib_materialColorError),
+ Map.entry(".onError", R.color.settingslib_materialColorOnError),
+ Map.entry(".errorContainer", R.color.settingslib_materialColorErrorContainer),
+ Map.entry(".onErrorContainer", R.color.settingslib_materialColorOnErrorContainer),
+ Map.entry(".outline", R.color.settingslib_materialColorOutline),
+ Map.entry(".outlineVariant", R.color.settingslib_materialColorOutlineVariant),
+ Map.entry(".background", R.color.settingslib_materialColorBackground),
+ Map.entry(".onBackground", R.color.settingslib_materialColorOnBackground),
+ Map.entry(".surface", R.color.settingslib_materialColorSurface),
+ Map.entry(".onSurface", R.color.settingslib_materialColorOnSurface),
+ Map.entry(".surfaceVariant", R.color.settingslib_materialColorSurfaceVariant),
+ Map.entry(".onSurfaceVariant", R.color.settingslib_materialColorOnSurfaceVariant),
+ Map.entry(".surfaceInverse", R.color.settingslib_materialColorSurfaceInverse),
+ Map.entry(".onSurfaceInverse", R.color.settingslib_materialColorOnSurfaceInverse),
+ Map.entry(".surfaceBright", R.color.settingslib_materialColorSurfaceBright),
+ Map.entry(".surfaceDim", R.color.settingslib_materialColorSurfaceDim),
+ Map.entry(".surfaceContainer", R.color.settingslib_materialColorSurfaceContainer),
+ Map.entry(".surfaceContainerLow",
+ R.color.settingslib_materialColorSurfaceContainerLow),
+ Map.entry(".surfaceContainerLowest",
+ R.color.settingslib_materialColorSurfaceContainerLowest),
+ Map.entry(".surfaceContainerHigh",
+ R.color.settingslib_materialColorSurfaceContainerHigh),
+ Map.entry(".surfaceContainerHighest",
+ R.color.settingslib_materialColorSurfaceContainerHighest));
}
private LottieColorUtils() {
@@ -108,4 +153,20 @@
frameInfo -> new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP));
}
}
+
+ /** Applies material colors. */
+ public static void applyMaterialColor(@NonNull Context context,
+ @NonNull LottieAnimationView lottieAnimationView) {
+ if (!SettingsThemeHelper.isExpressiveTheme(context)) {
+ return;
+ }
+
+ for (String key : MATERIAL_COLOR_MAP.keySet()) {
+ final int color = context.getColor(MATERIAL_COLOR_MAP.get(key));
+ lottieAnimationView.addValueCallback(
+ new KeyPath("**", key, "**"),
+ LottieProperty.COLOR_FILTER,
+ frameInfo -> new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP));
+ }
+ }
}
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PersistentPreference.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PersistentPreference.kt
index d3a7316..3dd6c47 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PersistentPreference.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PersistentPreference.kt
@@ -201,3 +201,6 @@
override fun isValidValue(context: Context, index: Int) =
index in getMinValue(context)..getMaxValue(context)
}
+
+/** A persistent preference that has a float value. */
+interface FloatPersistentPreference : PersistentPreference<Float>
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index d82b58e..cc996c5 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -199,3 +199,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "disable_audio_sharing_auto_pick_fallback_in_ui"
+ namespace: "cross_device_experiences"
+ description: "Do not auto pick audio sharing fallback device in UI"
+ bug: "383469911"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index e1929b7..6cf9e83 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -229,6 +229,8 @@
<string name="bluetooth_hearing_aid_right_active">Active (right only)</string>
<!-- Connected device settings. Message when the left-side and right-side hearing aids device are active. [CHAR LIMIT=NONE] -->
<string name="bluetooth_hearing_aid_left_and_right_active">Active (left and right)</string>
+ <!-- Connected device settings.: Message when changing remote ambient state failed. [CHAR LIMIT=NONE] -->
+ <string name="bluetooth_hearing_device_ambient_error">Couldn\u2019t update surroundings</string>
<!-- Connected devices settings. Message when Bluetooth is connected and active for media only, showing remote device status and battery level. [CHAR LIMIT=NONE] -->
<string name="bluetooth_active_media_only_battery_level">Active (media only). <xliff:g id="battery_level_as_percentage">%1$s</xliff:g> battery.</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeUi.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeUi.java
new file mode 100644
index 0000000..881a97b
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeUi.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.bluetooth;
+
+import static com.android.settingslib.bluetooth.HearingAidInfo.DeviceSide.SIDE_LEFT;
+import static com.android.settingslib.bluetooth.HearingAidInfo.DeviceSide.SIDE_RIGHT;
+
+import android.bluetooth.BluetoothDevice;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.List;
+import java.util.Map;
+
+/** Interface for the ambient volume UI. */
+public interface AmbientVolumeUi {
+
+ /** Interface definition for a callback to be invoked when event happens in AmbientVolumeUi. */
+ interface AmbientVolumeUiListener {
+ /** Called when the expand icon is clicked. */
+ void onExpandIconClick();
+
+ /** Called when the ambient volume icon is clicked. */
+ void onAmbientVolumeIconClick();
+
+ /** Called when the slider of the specified side is changed. */
+ void onSliderValueChange(int side, int value);
+ };
+
+ /** The rotation degree of the expand icon when the UI is in collapsed mode. */
+ float ROTATION_COLLAPSED = 0f;
+ /** The rotation degree of the expand icon when the UI is in expanded mode. */
+ float ROTATION_EXPANDED = 180f;
+
+ /**
+ * The default ambient volume level for hearing device ambient volume icon
+ *
+ * <p> This icon visually represents the current ambient volume. It displays separate
+ * levels for the left and right sides, each with 5 levels ranging from 0 to 4.
+ *
+ * <p> To represent the combined left/right levels with a single value, the following
+ * calculation is used:
+ * finalLevel = (leftLevel * 5) + rightLevel
+ * For example:
+ * <ul>
+ * <li>If left level is 2 and right level is 3, the final level will be 13 (2 * 5 + 3)</li>
+ * <li>If both left and right levels are 0, the final level will be 0</li>
+ * <li>If both left and right levels are 4, the final level will be 24</li>
+ * </ul>
+ */
+ int AMBIENT_VOLUME_LEVEL_DEFAULT = 24;
+ /**
+ * The minimum ambient volume level for hearing device ambient volume icon
+ *
+ * @see #AMBIENT_VOLUME_LEVEL_DEFAULT
+ */
+ int AMBIENT_VOLUME_LEVEL_MIN = 0;
+ /**
+ * The maximum ambient volume level for hearing device ambient volume icon
+ *
+ * @see #AMBIENT_VOLUME_LEVEL_DEFAULT
+ */
+ int AMBIENT_VOLUME_LEVEL_MAX = 24;
+
+ /**
+ * Ths side identifier for slider in collapsed mode which can unified control the ambient
+ * volume of all devices in the same set.
+ */
+ int SIDE_UNIFIED = 999;
+
+ /** All valid side of the sliders in the UI. */
+ List<Integer> VALID_SIDES = List.of(SIDE_UNIFIED, SIDE_LEFT, SIDE_RIGHT);
+
+ /** Sets if the UI is visible. */
+ void setVisible(boolean visible);
+
+ /**
+ * Sets if the UI is expandable between expanded and collapsed mode.
+ *
+ * <p> If the UI is not expandable, it implies the UI will always stay in collapsed mode
+ */
+ void setExpandable(boolean expandable);
+
+ /** @return if the UI is expandable. */
+ boolean isExpandable();
+
+ /** Sets if the UI is in expanded mode. */
+ void setExpanded(boolean expanded);
+
+ /** @return if the UI is in expanded mode. */
+ boolean isExpanded();
+
+ /**
+ * Sets if the UI is capable to mute the ambient of the remote device.
+ *
+ * <p> If the value is {@code false}, it implies the remote device ambient will always be
+ * unmute and can not be mute from the UI
+ */
+ void setMutable(boolean mutable);
+
+ /** @return if the UI is capable to mute the ambient of remote device. */
+ boolean isMutable();
+
+ /** Sets if the UI shows mute state. */
+ void setMuted(boolean muted);
+
+ /** @return if the UI shows mute state */
+ boolean isMuted();
+
+ /**
+ * Sets listener on the UI.
+ *
+ * @see AmbientVolumeUiListener
+ */
+ void setListener(@Nullable AmbientVolumeUiListener listener);
+
+ /**
+ * Sets up sliders in the UI.
+ *
+ * <p> For each side of device, the UI should hava a corresponding slider to control it's
+ * ambient volume.
+ * <p> For all devices in the same set, the UI should have a slider to control all devices'
+ * ambient volume at once.
+ * @param sideToDeviceMap the side and device mapping of all devices in the same set
+ */
+ void setupSliders(@NonNull Map<Integer, BluetoothDevice> sideToDeviceMap);
+
+ /**
+ * Sets if the slider is enabled.
+ *
+ * @param side the side of the slider
+ * @param enabled the enabled state
+ */
+ void setSliderEnabled(int side, boolean enabled);
+
+ /**
+ * Sets the slider value.
+ *
+ * @param side the side of the slider
+ * @param value the ambient value
+ */
+ void setSliderValue(int side, int value);
+
+ /**
+ * Sets the slider's minimum and maximum value.
+ *
+ * @param side the side of the slider
+ * @param min the minimum ambient value
+ * @param max the maximum ambient value
+ */
+ void setSliderRange(int side, int min, int max);
+
+ /** Updates the UI according to current state. */
+ void updateLayout();
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeUiController.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeUiController.java
new file mode 100644
index 0000000..ce392b12
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeUiController.java
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.bluetooth;
+
+import static android.bluetooth.AudioInputControl.MUTE_NOT_MUTED;
+import static android.bluetooth.AudioInputControl.MUTE_MUTED;
+import static android.bluetooth.BluetoothDevice.BOND_BONDED;
+
+import static com.android.settingslib.bluetooth.AmbientVolumeUi.SIDE_UNIFIED;
+import static com.android.settingslib.bluetooth.AmbientVolumeUi.VALID_SIDES;
+import static com.android.settingslib.bluetooth.HearingAidInfo.DeviceSide.SIDE_INVALID;
+import static com.android.settingslib.bluetooth.HearingAidInfo.DeviceSide.SIDE_LEFT;
+import static com.android.settingslib.bluetooth.HearingAidInfo.DeviceSide.SIDE_RIGHT;
+import static com.android.settingslib.bluetooth.HearingDeviceLocalDataManager.Data.INVALID_VOLUME;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.util.ArraySet;
+import android.util.Log;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settingslib.R;
+import com.android.settingslib.utils.ThreadUtils;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+
+import java.util.Map;
+import java.util.Set;
+
+/** This class controls ambient volume UI with local and remote ambient data. */
+public class AmbientVolumeUiController implements
+ HearingDeviceLocalDataManager.OnDeviceLocalDataChangeListener,
+ AmbientVolumeController.AmbientVolumeControlCallback,
+ AmbientVolumeUi.AmbientVolumeUiListener, BluetoothCallback, CachedBluetoothDevice.Callback {
+
+ private static final boolean DEBUG = true;
+ private static final String TAG = "AmbientVolumeUiController";
+
+ private final Context mContext;
+ private final LocalBluetoothProfileManager mProfileManager;
+ private final BluetoothEventManager mEventManager;
+ private final AmbientVolumeUi mAmbientLayout;
+ private final AmbientVolumeController mVolumeController;
+ private final HearingDeviceLocalDataManager mLocalDataManager;
+
+ private final Set<CachedBluetoothDevice> mCachedDevices = new ArraySet<>();
+ private final BiMap<Integer, BluetoothDevice> mSideToDeviceMap = HashBiMap.create();
+ private CachedBluetoothDevice mCachedDevice;
+ private boolean mShowUiWhenLocalDataExist = true;
+
+ public AmbientVolumeUiController(@NonNull Context context,
+ @NonNull LocalBluetoothManager bluetoothManager,
+ @NonNull AmbientVolumeUi ambientLayout) {
+ mContext = context;
+ mProfileManager = bluetoothManager.getProfileManager();
+ mEventManager = bluetoothManager.getEventManager();
+ mAmbientLayout = ambientLayout;
+ mAmbientLayout.setListener(this);
+ mVolumeController = new AmbientVolumeController(mProfileManager, this);
+ mLocalDataManager = new HearingDeviceLocalDataManager(context);
+ mLocalDataManager.setOnDeviceLocalDataChangeListener(this,
+ ThreadUtils.getBackgroundExecutor());
+ }
+
+ @VisibleForTesting
+ public AmbientVolumeUiController(@NonNull Context context,
+ @NonNull LocalBluetoothManager bluetoothManager,
+ @NonNull AmbientVolumeUi ambientLayout,
+ @NonNull AmbientVolumeController volumeController,
+ @NonNull HearingDeviceLocalDataManager localDataManager) {
+ mContext = context;
+ mProfileManager = bluetoothManager.getProfileManager();
+ mEventManager = bluetoothManager.getEventManager();
+ mAmbientLayout = ambientLayout;
+ mVolumeController = volumeController;
+ mLocalDataManager = localDataManager;
+ }
+
+
+ @Override
+ public void onDeviceLocalDataChange(@NonNull String address,
+ @Nullable HearingDeviceLocalDataManager.Data data) {
+ if (data == null) {
+ // The local data is removed because the device is unpaired, do nothing
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "onDeviceLocalDataChange, address:" + address + ", data:" + data);
+ }
+ for (BluetoothDevice device : mSideToDeviceMap.values()) {
+ if (device.getAnonymizedAddress().equals(address)) {
+ postOnMainThread(() -> loadLocalDataToUi(device));
+ return;
+ }
+ }
+ }
+
+ @Override
+ public void onVolumeControlServiceConnected() {
+ mCachedDevices.forEach(device -> mVolumeController.registerCallback(
+ ThreadUtils.getBackgroundExecutor(), device.getDevice()));
+ }
+
+ @Override
+ public void onAmbientChanged(@NonNull BluetoothDevice device, int gainSettings) {
+ if (DEBUG) {
+ Log.d(TAG, "onAmbientChanged, value:" + gainSettings + ", device:" + device);
+ }
+ HearingDeviceLocalDataManager.Data data = mLocalDataManager.get(device);
+ final boolean expanded = mAmbientLayout.isExpanded();
+ final boolean isInitiatedFromUi = (expanded && data.ambient() == gainSettings)
+ || (!expanded && data.groupAmbient() == gainSettings);
+ if (isInitiatedFromUi) {
+ // The change is initiated from UI, no need to update UI
+ return;
+ }
+
+ // We have to check if we need to expand the controls by getting all remote
+ // device's ambient value, delay for a while to wait all remote devices update
+ // to the latest value to avoid unnecessary expand action.
+ postDelayedOnMainThread(this::refresh, 1200L);
+ }
+
+ @Override
+ public void onMuteChanged(@NonNull BluetoothDevice device, int mute) {
+ if (DEBUG) {
+ Log.d(TAG, "onMuteChanged, mute:" + mute + ", device:" + device);
+ }
+ final boolean muted = mAmbientLayout.isMuted();
+ boolean isInitiatedFromUi = (muted && mute == MUTE_MUTED)
+ || (!muted && mute == MUTE_NOT_MUTED);
+ if (isInitiatedFromUi) {
+ // The change is initiated from UI, no need to update UI
+ return;
+ }
+
+ // We have to check if we need to mute the devices by getting all remote
+ // device's mute state, delay for a while to wait all remote devices update
+ // to the latest value.
+ postDelayedOnMainThread(this::refresh, 1200L);
+ }
+
+ @Override
+ public void onCommandFailed(@NonNull BluetoothDevice device) {
+ Log.w(TAG, "onCommandFailed, device:" + device);
+ postOnMainThread(() -> {
+ showErrorToast(R.string.bluetooth_hearing_device_ambient_error);
+ refresh();
+ });
+ }
+
+ @Override
+ public void onExpandIconClick() {
+ mSideToDeviceMap.forEach((s, d) -> {
+ if (!mAmbientLayout.isMuted()) {
+ // Apply previous collapsed/expanded volume to remote device
+ HearingDeviceLocalDataManager.Data data = mLocalDataManager.get(d);
+ int volume = mAmbientLayout.isExpanded()
+ ? data.ambient() : data.groupAmbient();
+ mVolumeController.setAmbient(d, volume);
+ }
+ // Update new value to local data
+ mLocalDataManager.updateAmbientControlExpanded(d,
+ mAmbientLayout.isExpanded());
+ });
+ mLocalDataManager.flush();
+ }
+
+ @Override
+ public void onAmbientVolumeIconClick() {
+ if (!mAmbientLayout.isMuted()) {
+ loadLocalDataToUi();
+ }
+ for (BluetoothDevice device : mSideToDeviceMap.values()) {
+ mVolumeController.setMuted(device, mAmbientLayout.isMuted());
+ }
+ }
+
+ @Override
+ public void onSliderValueChange(int side, int value) {
+ if (DEBUG) {
+ Log.d(TAG, "onSliderValueChange: side=" + side + ", value=" + value);
+ }
+ setVolumeIfValid(side, value);
+
+ Runnable setAmbientRunnable = () -> {
+ if (side == SIDE_UNIFIED) {
+ mSideToDeviceMap.forEach((s, d) -> mVolumeController.setAmbient(d, value));
+ } else {
+ final BluetoothDevice device = mSideToDeviceMap.get(side);
+ mVolumeController.setAmbient(device, value);
+ }
+ };
+
+ if (mAmbientLayout.isMuted()) {
+ // User drag on the volume slider when muted. Unmute the devices first.
+ mAmbientLayout.setMuted(false);
+
+ for (BluetoothDevice device : mSideToDeviceMap.values()) {
+ mVolumeController.setMuted(device, false);
+ }
+ // Restore the value before muted
+ loadLocalDataToUi();
+ // Delay set ambient on remote device since the immediately sequential command
+ // might get failed sometimes
+ postDelayedOnMainThread(setAmbientRunnable, 1000L);
+ } else {
+ setAmbientRunnable.run();
+ }
+ }
+
+ @Override
+ public void onProfileConnectionStateChanged(@NonNull CachedBluetoothDevice cachedDevice,
+ int state, int bluetoothProfile) {
+ if (bluetoothProfile == BluetoothProfile.VOLUME_CONTROL
+ && state == BluetoothProfile.STATE_CONNECTED
+ && mCachedDevices.contains(cachedDevice)) {
+ // After VCP connected, AICS may not ready yet and still return invalid value, delay
+ // a while to wait AICS ready as a workaround
+ postDelayedOnMainThread(this::refresh, 1000L);
+ }
+ }
+
+ @Override
+ public void onDeviceAttributesChanged() {
+ mCachedDevices.forEach(device -> {
+ device.unregisterCallback(this);
+ mVolumeController.unregisterCallback(device.getDevice());
+ });
+ postOnMainThread(()-> {
+ loadDevice(mCachedDevice);
+ ThreadUtils.postOnBackgroundThread(()-> {
+ mCachedDevices.forEach(device -> {
+ device.registerCallback(ThreadUtils.getBackgroundExecutor(), this);
+ mVolumeController.registerCallback(ThreadUtils.getBackgroundExecutor(),
+ device.getDevice());
+ });
+ });
+ });
+ }
+
+ /**
+ * Registers callbacks and listeners, this should be called when needs to start listening to
+ * events.
+ */
+ public void start() {
+ mEventManager.registerCallback(this);
+ mLocalDataManager.start();
+ mCachedDevices.forEach(device -> {
+ device.registerCallback(ThreadUtils.getBackgroundExecutor(), this);
+ mVolumeController.registerCallback(ThreadUtils.getBackgroundExecutor(),
+ device.getDevice());
+ });
+ }
+
+ /**
+ * Unregisters callbacks and listeners, this should be called when no longer needs to listen to
+ * events.
+ */
+ public void stop() {
+ mEventManager.unregisterCallback(this);
+ mLocalDataManager.stop();
+ mCachedDevices.forEach(device -> {
+ device.unregisterCallback(this);
+ mVolumeController.unregisterCallback(device.getDevice());
+ });
+ }
+
+ /**
+ * Loads all devices in the same set with {@code cachedDevice} and create corresponding sliders.
+ *
+ * <p>If the devices has valid ambient control points, the ambient volume UI will be visible.
+ * @param cachedDevice the remote device
+ */
+ public void loadDevice(CachedBluetoothDevice cachedDevice) {
+ if (DEBUG) {
+ Log.d(TAG, "loadDevice, device=" + cachedDevice);
+ }
+ mCachedDevice = cachedDevice;
+ mSideToDeviceMap.clear();
+ mCachedDevices.clear();
+ boolean deviceSupportVcp =
+ cachedDevice != null && cachedDevice.getProfiles().stream().anyMatch(
+ p -> p instanceof VolumeControlProfile);
+ if (!deviceSupportVcp) {
+ mAmbientLayout.setVisible(false);
+ return;
+ }
+
+ // load devices in the same set
+ if (VALID_SIDES.contains(cachedDevice.getDeviceSide())
+ && cachedDevice.getBondState() == BOND_BONDED) {
+ mSideToDeviceMap.put(cachedDevice.getDeviceSide(), cachedDevice.getDevice());
+ mCachedDevices.add(cachedDevice);
+ }
+ for (CachedBluetoothDevice memberDevice : cachedDevice.getMemberDevice()) {
+ if (VALID_SIDES.contains(memberDevice.getDeviceSide())
+ && memberDevice.getBondState() == BOND_BONDED) {
+ mSideToDeviceMap.put(memberDevice.getDeviceSide(), memberDevice.getDevice());
+ mCachedDevices.add(memberDevice);
+ }
+ }
+
+ mAmbientLayout.setExpandable(mSideToDeviceMap.size() > 1);
+ mAmbientLayout.setupSliders(mSideToDeviceMap);
+ refresh();
+ }
+
+ /** Refreshes the ambient volume UI. */
+ public void refresh() {
+ if (isAmbientControlAvailable()) {
+ mAmbientLayout.setVisible(true);
+ loadRemoteDataToUi();
+ } else {
+ mAmbientLayout.setVisible(false);
+ }
+ }
+
+ /** Sets if the ambient volume UI should be visible when local ambient data exist. */
+ public void setShowUiWhenLocalDataExist(boolean shouldShow) {
+ mShowUiWhenLocalDataExist = shouldShow;
+ }
+
+ /** Updates the ambient sliders according to current state. */
+ private void updateSliderUi() {
+ boolean isAnySliderEnabled = false;
+ for (Map.Entry<Integer, BluetoothDevice> entry : mSideToDeviceMap.entrySet()) {
+ final int side = entry.getKey();
+ final BluetoothDevice device = entry.getValue();
+ final boolean enabled = isDeviceConnectedToVcp(device)
+ && mVolumeController.isAmbientControlAvailable(device);
+ isAnySliderEnabled |= enabled;
+ mAmbientLayout.setSliderEnabled(side, enabled);
+ }
+ mAmbientLayout.setSliderEnabled(SIDE_UNIFIED, isAnySliderEnabled);
+ mAmbientLayout.updateLayout();
+ }
+
+ /** Sets the ambient to the corresponding control slider. */
+ private void setVolumeIfValid(int side, int volume) {
+ if (volume == INVALID_VOLUME) {
+ return;
+ }
+ mAmbientLayout.setSliderValue(side, volume);
+ // Update new value to local data
+ if (side == SIDE_UNIFIED) {
+ mSideToDeviceMap.forEach((s, d) -> mLocalDataManager.updateGroupAmbient(d, volume));
+ } else {
+ mLocalDataManager.updateAmbient(mSideToDeviceMap.get(side), volume);
+ }
+ mLocalDataManager.flush();
+ }
+
+ private void loadLocalDataToUi() {
+ mSideToDeviceMap.forEach((s, d) -> loadLocalDataToUi(d));
+ }
+
+ private void loadLocalDataToUi(BluetoothDevice device) {
+ final HearingDeviceLocalDataManager.Data data = mLocalDataManager.get(device);
+ if (DEBUG) {
+ Log.d(TAG, "loadLocalDataToUi, data=" + data + ", device=" + device);
+ }
+ if (isDeviceConnectedToVcp(device) && !mAmbientLayout.isMuted()) {
+ final int side = mSideToDeviceMap.inverse().getOrDefault(device, SIDE_INVALID);
+ setVolumeIfValid(side, data.ambient());
+ setVolumeIfValid(SIDE_UNIFIED, data.groupAmbient());
+ }
+ setAmbientControlExpanded(data.ambientControlExpanded());
+ updateSliderUi();
+ }
+
+ private void loadRemoteDataToUi() {
+ BluetoothDevice leftDevice = mSideToDeviceMap.get(SIDE_LEFT);
+ AmbientVolumeController.RemoteAmbientState leftState =
+ mVolumeController.refreshAmbientState(leftDevice);
+ BluetoothDevice rightDevice = mSideToDeviceMap.get(SIDE_RIGHT);
+ AmbientVolumeController.RemoteAmbientState rightState =
+ mVolumeController.refreshAmbientState(rightDevice);
+ if (DEBUG) {
+ Log.d(TAG, "loadRemoteDataToUi, left=" + leftState + ", right=" + rightState);
+ }
+ mSideToDeviceMap.forEach((side, device) -> {
+ int ambientMax = mVolumeController.getAmbientMax(device);
+ int ambientMin = mVolumeController.getAmbientMin(device);
+ if (ambientMin != ambientMax) {
+ mAmbientLayout.setSliderRange(side, ambientMin, ambientMax);
+ mAmbientLayout.setSliderRange(SIDE_UNIFIED, ambientMin, ambientMax);
+ }
+ });
+
+ // Update ambient volume
+ final int leftAmbient = leftState != null ? leftState.gainSetting() : INVALID_VOLUME;
+ final int rightAmbient = rightState != null ? rightState.gainSetting() : INVALID_VOLUME;
+ if (mAmbientLayout.isExpanded()) {
+ setVolumeIfValid(SIDE_LEFT, leftAmbient);
+ setVolumeIfValid(SIDE_RIGHT, rightAmbient);
+ } else {
+ if (leftAmbient != rightAmbient && leftAmbient != INVALID_VOLUME
+ && rightAmbient != INVALID_VOLUME) {
+ setVolumeIfValid(SIDE_LEFT, leftAmbient);
+ setVolumeIfValid(SIDE_RIGHT, rightAmbient);
+ setAmbientControlExpanded(true);
+ } else {
+ int unifiedAmbient = leftAmbient != INVALID_VOLUME ? leftAmbient : rightAmbient;
+ setVolumeIfValid(SIDE_UNIFIED, unifiedAmbient);
+ }
+ }
+ // Initialize local data between side and group value
+ initLocalAmbientDataIfNeeded();
+
+ // Update mute state
+ boolean mutable = true;
+ boolean muted = true;
+ if (isDeviceConnectedToVcp(leftDevice) && leftState != null) {
+ mutable &= leftState.isMutable();
+ muted &= leftState.isMuted();
+ }
+ if (isDeviceConnectedToVcp(rightDevice) && rightState != null) {
+ mutable &= rightState.isMutable();
+ muted &= rightState.isMuted();
+ }
+ mAmbientLayout.setMutable(mutable);
+ mAmbientLayout.setMuted(muted);
+
+ // Ensure remote device mute state is synced
+ syncMuteStateIfNeeded(leftDevice, leftState, muted);
+ syncMuteStateIfNeeded(rightDevice, rightState, muted);
+
+ updateSliderUi();
+ }
+
+ private void setAmbientControlExpanded(boolean expanded) {
+ mAmbientLayout.setExpanded(expanded);
+ mSideToDeviceMap.forEach((s, d) -> {
+ // Update new value to local data
+ mLocalDataManager.updateAmbientControlExpanded(d, expanded);
+ });
+ mLocalDataManager.flush();
+ }
+
+ /** Checks if any device in the same set has valid ambient control points */
+ private boolean isAmbientControlAvailable() {
+ for (BluetoothDevice device : mSideToDeviceMap.values()) {
+ if (mShowUiWhenLocalDataExist) {
+ // Found local ambient data
+ if (mLocalDataManager.get(device).hasAmbientData()) {
+ return true;
+ }
+ }
+ // Found remote ambient control points
+ if (mVolumeController.isAmbientControlAvailable(device)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void initLocalAmbientDataIfNeeded() {
+ int smallerVolumeAmongGroup = Integer.MAX_VALUE;
+ for (BluetoothDevice device : mSideToDeviceMap.values()) {
+ HearingDeviceLocalDataManager.Data data = mLocalDataManager.get(device);
+ if (data.ambient() != INVALID_VOLUME) {
+ smallerVolumeAmongGroup = Math.min(data.ambient(), smallerVolumeAmongGroup);
+ } else if (data.groupAmbient() != INVALID_VOLUME) {
+ // Initialize side ambient from group ambient value
+ mLocalDataManager.updateAmbient(device, data.groupAmbient());
+ }
+ }
+ if (smallerVolumeAmongGroup != Integer.MAX_VALUE) {
+ for (BluetoothDevice device : mSideToDeviceMap.values()) {
+ HearingDeviceLocalDataManager.Data data = mLocalDataManager.get(device);
+ if (data.groupAmbient() == INVALID_VOLUME) {
+ // Initialize group ambient from smaller side ambient value
+ mLocalDataManager.updateGroupAmbient(device, smallerVolumeAmongGroup);
+ }
+ }
+ }
+ mLocalDataManager.flush();
+ }
+
+ private void syncMuteStateIfNeeded(@Nullable BluetoothDevice device,
+ @Nullable AmbientVolumeController.RemoteAmbientState state, boolean muted) {
+ if (isDeviceConnectedToVcp(device) && state != null && state.isMutable()) {
+ if (state.isMuted() != muted) {
+ mVolumeController.setMuted(device, muted);
+ }
+ }
+ }
+
+ private boolean isDeviceConnectedToVcp(@Nullable BluetoothDevice device) {
+ return device != null && device.isConnected()
+ && mProfileManager.getVolumeControlProfile().getConnectionStatus(device)
+ == BluetoothProfile.STATE_CONNECTED;
+ }
+
+ private void postOnMainThread(Runnable runnable) {
+ mContext.getMainThreadHandler().post(runnable);
+ }
+
+ private void postDelayedOnMainThread(Runnable runnable, long delay) {
+ mContext.getMainThreadHandler().postDelayed(runnable, delay);
+ }
+
+ private void showErrorToast(int stringResId) {
+ Toast.makeText(mContext, stringResId, Toast.LENGTH_SHORT).show();
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 429e4c9..0c642d7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -656,7 +656,8 @@
@WorkerThread
public static boolean isAudioSharingHysteresisModeFixAvailable(@Nullable Context context) {
return (audioSharingHysteresisModeFix() && Flags.enableLeAudioSharing())
- || (context != null && isAudioSharingPreviewEnabled(context.getContentResolver()));
+ || (context != null && Flags.audioSharingDeveloperOption()
+ && getAudioSharingPreviewValue(context.getContentResolver()));
}
/** Returns if the le audio sharing is enabled. */
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingDeviceLocalDataManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingDeviceLocalDataManager.java
index 6725558..3cd3732 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingDeviceLocalDataManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingDeviceLocalDataManager.java
@@ -148,6 +148,14 @@
}
}
+ /** Flushes the data into Settings . */
+ public synchronized void flush() {
+ if (!mIsStarted) {
+ return;
+ }
+ putAmbientVolumeSettings();
+ }
+
/**
* Puts the local data of the corresponding hearing device.
*
@@ -274,9 +282,6 @@
notifyIfDataChanged(mAddrToDataMap, updatedAddrToDataMap);
mAddrToDataMap.clear();
mAddrToDataMap.putAll(updatedAddrToDataMap);
- if (DEBUG) {
- Log.v(TAG, "getLocalDataFromSettings, " + mAddrToDataMap + ", manager: " + this);
- }
}
}
@@ -287,12 +292,10 @@
builder.append(KEY_ADDR).append("=").append(entry.getKey());
builder.append(entry.getValue().toSettingsFormat()).append(";");
}
- if (DEBUG) {
- Log.v(TAG, "putAmbientVolumeSettings, " + builder + ", manager: " + this);
- }
- Settings.Global.putStringForUser(mContext.getContentResolver(),
- LOCAL_AMBIENT_VOLUME_SETTINGS, builder.toString(),
- UserHandle.USER_SYSTEM);
+ ThreadUtils.postOnBackgroundThread(() -> {
+ Settings.Global.putStringForUser(mContext.getContentResolver(),
+ LOCAL_AMBIENT_VOLUME_SETTINGS, builder.toString(), UserHandle.USER_SYSTEM);
+ });
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
index b52ed42..2c99a2d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
@@ -101,6 +101,7 @@
public @interface BroadcastState {}
private static final String SETTINGS_PKG = "com.android.settings";
+ private static final String SYSUI_PKG = "com.android.systemui";
private static final String TAG = "LocalBluetoothLeBroadcast";
private static final boolean DEBUG = BluetoothUtils.D;
@@ -216,6 +217,7 @@
}
setLatestBroadcastId(broadcastId);
setAppSourceName(mNewAppSourceName, /* updateContentResolver= */ true);
+ notifyBroadcastStateChange(BROADCAST_STATE_ON);
}
@Override
@@ -232,7 +234,6 @@
Log.d(TAG, "onBroadcastMetadataChanged(), broadcastId = " + broadcastId);
}
setLatestBluetoothLeBroadcastMetadata(metadata);
- notifyBroadcastStateChange(BROADCAST_STATE_ON);
}
@Override
@@ -1247,8 +1248,9 @@
}
private void notifyBroadcastStateChange(@BroadcastState int state) {
- if (!mContext.getPackageName().equals(SETTINGS_PKG)) {
- Log.d(TAG, "Skip notifyBroadcastStateChange, not triggered by Settings.");
+ String packageName = mContext.getPackageName();
+ if (!packageName.equals(SETTINGS_PKG) && !packageName.equals(SYSUI_PKG)) {
+ Log.d(TAG, "Skip notifyBroadcastStateChange, not triggered by Settings or SystemUI.");
return;
}
if (isWorkProfile(mContext)) {
@@ -1257,8 +1259,8 @@
}
Intent intent = new Intent(ACTION_LE_AUDIO_SHARING_STATE_CHANGE);
intent.putExtra(EXTRA_LE_AUDIO_SHARING_STATE, state);
- intent.setPackage(mContext.getPackageName());
- Log.d(TAG, "notifyBroadcastStateChange for state = " + state);
+ intent.setPackage(SETTINGS_PKG);
+ Log.d(TAG, "notifyBroadcastStateChange for state = " + state + " by pkg = " + packageName);
mContext.sendBroadcast(intent);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt
index e01f279..c71b19c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt
@@ -501,7 +501,7 @@
val wifiManager = context.getSystemService(WifiManager::class.java) ?: return@launch
val aapmManager = context.getSystemService(AdvancedProtectionManager::class.java)
if (isAdvancedProtectionEnabled(aapmManager)) {
- val intent = aapmManager.createSupportIntent(
+ val intent = AdvancedProtectionManager.createSupportIntent(
AdvancedProtectionManager.FEATURE_ID_DISALLOW_WEP,
AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION)
onStartActivity(intent)
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/AmbientVolumeUiControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/AmbientVolumeUiControllerTest.java
new file mode 100644
index 0000000..8b606e2
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/AmbientVolumeUiControllerTest.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.bluetooth;
+
+import static android.bluetooth.AudioInputControl.MUTE_DISABLED;
+import static android.bluetooth.AudioInputControl.MUTE_MUTED;
+import static android.bluetooth.AudioInputControl.MUTE_NOT_MUTED;
+import static android.bluetooth.BluetoothDevice.BOND_BONDED;
+
+import static com.android.settingslib.bluetooth.HearingAidInfo.DeviceSide.SIDE_LEFT;
+import static com.android.settingslib.bluetooth.HearingAidInfo.DeviceSide.SIDE_RIGHT;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.never;
+import static org.robolectric.Shadows.shadowOf;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.stubbing.Answer;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Executor;
+
+/** Tests for {@link AmbientVolumeUiController}. */
+@RunWith(RobolectricTestRunner.class)
+public class AmbientVolumeUiControllerTest {
+
+ @Rule
+ public MockitoRule mockito = MockitoJUnit.rule();
+
+ private static final String TEST_ADDRESS = "00:00:00:00:11";
+ private static final String TEST_MEMBER_ADDRESS = "00:00:00:00:22";
+
+ @Mock
+ LocalBluetoothManager mBluetoothManager;
+ @Mock
+ LocalBluetoothProfileManager mProfileManager;
+ @Mock
+ BluetoothEventManager mEventManager;
+ @Mock
+ VolumeControlProfile mVolumeControlProfile;
+ @Mock
+ AmbientVolumeUi mAmbientLayout;
+ @Mock
+ private AmbientVolumeController mVolumeController;
+ @Mock
+ private HearingDeviceLocalDataManager mLocalDataManager;
+ @Mock
+ private CachedBluetoothDevice mCachedDevice;
+ @Mock
+ private CachedBluetoothDevice mCachedMemberDevice;
+ @Mock
+ private BluetoothDevice mDevice;
+ @Mock
+ private BluetoothDevice mMemberDevice;
+ @Mock
+ private Handler mTestHandler;
+
+ @Spy
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ private AmbientVolumeUiController mController;
+
+ @Before
+ public void setUp() {
+ when(mBluetoothManager.getProfileManager()).thenReturn(mProfileManager);
+ when(mBluetoothManager.getEventManager()).thenReturn(mEventManager);
+
+ mController = spy(new AmbientVolumeUiController(mContext, mBluetoothManager,
+ mAmbientLayout, mVolumeController, mLocalDataManager));
+
+ when(mProfileManager.getVolumeControlProfile()).thenReturn(mVolumeControlProfile);
+ when(mVolumeControlProfile.getConnectionStatus(mDevice)).thenReturn(
+ BluetoothProfile.STATE_CONNECTED);
+ when(mVolumeControlProfile.getConnectionStatus(mMemberDevice)).thenReturn(
+ BluetoothProfile.STATE_CONNECTED);
+ when(mVolumeController.isAmbientControlAvailable(mDevice)).thenReturn(true);
+ when(mVolumeController.isAmbientControlAvailable(mMemberDevice)).thenReturn(true);
+ when(mLocalDataManager.get(any(BluetoothDevice.class))).thenReturn(
+ new HearingDeviceLocalDataManager.Data.Builder().build());
+
+ when(mContext.getMainThreadHandler()).thenReturn(mTestHandler);
+ Answer<Object> answer = invocationOnMock -> {
+ invocationOnMock.getArgument(0, Runnable.class).run();
+ return null;
+ };
+ when(mTestHandler.post(any(Runnable.class))).thenAnswer(answer);
+ when(mTestHandler.postDelayed(any(Runnable.class), anyLong())).thenAnswer(answer);
+
+ prepareDevice(/* hasMember= */ true);
+ mController.loadDevice(mCachedDevice);
+ Mockito.reset(mController);
+ Mockito.reset(mAmbientLayout);
+ }
+
+ @Test
+ public void loadDevice_deviceWithoutMember_controlNotExpandable() {
+ prepareDevice(/* hasMember= */ false);
+
+ mController.loadDevice(mCachedDevice);
+
+ verify(mAmbientLayout).setExpandable(false);
+ }
+
+ @Test
+ public void loadDevice_deviceWithMember_controlExpandable() {
+ prepareDevice(/* hasMember= */ true);
+
+ mController.loadDevice(mCachedDevice);
+
+ verify(mAmbientLayout).setExpandable(true);
+ }
+
+ @Test
+ public void loadDevice_deviceNotSupportVcp_ambientLayoutGone() {
+ when(mCachedDevice.getProfiles()).thenReturn(List.of());
+
+ mController.loadDevice(mCachedDevice);
+
+ verify(mAmbientLayout).setVisible(false);
+ }
+
+ @Test
+ public void loadDevice_ambientControlNotAvailable_ambientLayoutGone() {
+ when(mVolumeController.isAmbientControlAvailable(mDevice)).thenReturn(false);
+ when(mVolumeController.isAmbientControlAvailable(mMemberDevice)).thenReturn(false);
+
+ mController.loadDevice(mCachedDevice);
+
+ verify(mAmbientLayout).setVisible(false);
+ }
+
+ @Test
+ public void loadDevice_supportVcpAndAmbientControlAvailable_ambientLayoutVisible() {
+ when(mCachedDevice.getProfiles()).thenReturn(List.of(mVolumeControlProfile));
+ when(mVolumeController.isAmbientControlAvailable(mDevice)).thenReturn(true);
+
+ mController.loadDevice(mCachedDevice);
+
+ verify(mAmbientLayout).setVisible(true);
+ }
+
+ @Test
+ public void start_callbackRegistered() {
+ mController.start();
+
+ verify(mEventManager).registerCallback(mController);
+ verify(mLocalDataManager).start();
+ verify(mVolumeController).registerCallback(any(Executor.class), eq(mDevice));
+ verify(mVolumeController).registerCallback(any(Executor.class), eq(mMemberDevice));
+ verify(mCachedDevice).registerCallback(any(Executor.class),
+ any(CachedBluetoothDevice.Callback.class));
+ verify(mCachedMemberDevice).registerCallback(any(Executor.class),
+ any(CachedBluetoothDevice.Callback.class));
+ }
+
+ @Test
+ public void stop_callbackUnregistered() {
+ mController.stop();
+
+ verify(mEventManager).unregisterCallback(mController);
+ verify(mLocalDataManager).stop();
+ verify(mVolumeController).unregisterCallback(mDevice);
+ verify(mVolumeController).unregisterCallback(mMemberDevice);
+ verify(mCachedDevice).unregisterCallback(any(CachedBluetoothDevice.Callback.class));
+ verify(mCachedMemberDevice).unregisterCallback(any(CachedBluetoothDevice.Callback.class));
+ }
+
+ @Test
+ public void onDeviceLocalDataChange_verifySetExpandedAndDataUpdated() {
+ final boolean testExpanded = true;
+ HearingDeviceLocalDataManager.Data data = new HearingDeviceLocalDataManager.Data.Builder()
+ .ambient(0).groupAmbient(0).ambientControlExpanded(testExpanded).build();
+ when(mLocalDataManager.get(mDevice)).thenReturn(data);
+
+ mController.onDeviceLocalDataChange(TEST_ADDRESS, data);
+ shadowOf(Looper.getMainLooper()).idle();
+
+ verify(mAmbientLayout).setExpanded(testExpanded);
+ verifyDeviceDataUpdated(mDevice);
+ }
+
+ @Test
+ public void onAmbientChanged_refreshWhenNotInitiateFromUi() {
+ HearingDeviceLocalDataManager.Data data = new HearingDeviceLocalDataManager.Data.Builder()
+ .ambient(10).groupAmbient(10).ambientControlExpanded(true).build();
+ when(mLocalDataManager.get(mDevice)).thenReturn(data);
+ when(mAmbientLayout.isExpanded()).thenReturn(true);
+
+ mController.onAmbientChanged(mDevice, 10);
+ verify(mController, never()).refresh();
+
+ mController.onAmbientChanged(mDevice, 20);
+ verify(mController).refresh();
+ }
+
+ @Test
+ public void onMuteChanged_refreshWhenNotInitiateFromUi() {
+ AmbientVolumeController.RemoteAmbientState state =
+ new AmbientVolumeController.RemoteAmbientState(MUTE_NOT_MUTED, 0);
+ when(mVolumeController.refreshAmbientState(mDevice)).thenReturn(state);
+ when(mAmbientLayout.isExpanded()).thenReturn(false);
+
+ mController.onMuteChanged(mDevice, MUTE_NOT_MUTED);
+ verify(mController, never()).refresh();
+
+ mController.onMuteChanged(mDevice, MUTE_MUTED);
+ verify(mController).refresh();
+ }
+
+ @Test
+ public void refresh_leftAndRightDifferentGainSetting_expandControl() {
+ prepareRemoteData(mDevice, 10, MUTE_NOT_MUTED);
+ prepareRemoteData(mMemberDevice, 20, MUTE_NOT_MUTED);
+ when(mAmbientLayout.isExpanded()).thenReturn(false);
+
+ mController.refresh();
+
+ verify(mAmbientLayout).setExpanded(true);
+ }
+
+ @Test
+ public void refresh_oneSideNotMutable_controlNotMutableAndNotMuted() {
+ prepareRemoteData(mDevice, 10, MUTE_DISABLED);
+ prepareRemoteData(mMemberDevice, 20, MUTE_NOT_MUTED);
+
+ mController.refresh();
+
+ verify(mAmbientLayout).setMutable(false);
+ verify(mAmbientLayout).setMuted(false);
+ }
+
+ @Test
+ public void refresh_oneSideNotMuted_controlNotMutedAndSyncToRemote() {
+ prepareRemoteData(mDevice, 10, MUTE_MUTED);
+ prepareRemoteData(mMemberDevice, 20, MUTE_NOT_MUTED);
+
+ mController.refresh();
+
+ verify(mAmbientLayout).setMutable(true);
+ verify(mAmbientLayout).setMuted(false);
+ verify(mVolumeController).setMuted(mDevice, false);
+ }
+
+ private void prepareDevice(boolean hasMember) {
+ when(mCachedDevice.getDeviceSide()).thenReturn(SIDE_LEFT);
+ when(mCachedDevice.getDevice()).thenReturn(mDevice);
+ when(mCachedDevice.getBondState()).thenReturn(BOND_BONDED);
+ when(mCachedDevice.getProfiles()).thenReturn(List.of(mVolumeControlProfile));
+ when(mDevice.getAddress()).thenReturn(TEST_ADDRESS);
+ when(mDevice.getAnonymizedAddress()).thenReturn(TEST_ADDRESS);
+ when(mDevice.isConnected()).thenReturn(true);
+ if (hasMember) {
+ when(mCachedDevice.getMemberDevice()).thenReturn(Set.of(mCachedMemberDevice));
+ when(mCachedMemberDevice.getDeviceSide()).thenReturn(SIDE_RIGHT);
+ when(mCachedMemberDevice.getDevice()).thenReturn(mMemberDevice);
+ when(mCachedMemberDevice.getBondState()).thenReturn(BOND_BONDED);
+ when(mCachedMemberDevice.getProfiles()).thenReturn(List.of(mVolumeControlProfile));
+ when(mMemberDevice.getAddress()).thenReturn(TEST_MEMBER_ADDRESS);
+ when(mMemberDevice.getAnonymizedAddress()).thenReturn(TEST_MEMBER_ADDRESS);
+ when(mMemberDevice.isConnected()).thenReturn(true);
+ } else {
+ when(mCachedDevice.getMemberDevice()).thenReturn(Set.of());
+ }
+ }
+
+ private void prepareRemoteData(BluetoothDevice device, int gainSetting, int mute) {
+ when(mVolumeController.refreshAmbientState(device)).thenReturn(
+ new AmbientVolumeController.RemoteAmbientState(gainSetting, mute));
+ }
+
+ private void verifyDeviceDataUpdated(BluetoothDevice device) {
+ verify(mLocalDataManager).updateAmbient(eq(device), anyInt());
+ verify(mLocalDataManager).updateGroupAmbient(eq(device), anyInt());
+ verify(mLocalDataManager).updateAmbientControlExpanded(eq(device),
+ anyBoolean());
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index fa5d542..ab9f871 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -970,8 +970,10 @@
when(cachedBluetoothDevice2.getGroupId()).thenReturn(2);
BluetoothDevice device1 = mock(BluetoothDevice.class);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(device1);
when(mDeviceManager.findDevice(device1)).thenReturn(mCachedBluetoothDevice);
BluetoothDevice device2 = mock(BluetoothDevice.class);
+ when(cachedBluetoothDevice2.getDevice()).thenReturn(device2);
when(mDeviceManager.findDevice(device2)).thenReturn(cachedBluetoothDevice2);
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(device1, device2));
@@ -991,8 +993,10 @@
when(cachedBluetoothDevice2.getGroupId()).thenReturn(2);
BluetoothDevice device1 = mock(BluetoothDevice.class);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(device1);
when(mDeviceManager.findDevice(device1)).thenReturn(mCachedBluetoothDevice);
BluetoothDevice device2 = mock(BluetoothDevice.class);
+ when(cachedBluetoothDevice2.getDevice()).thenReturn(device2);
when(mDeviceManager.findDevice(device2)).thenReturn(cachedBluetoothDevice2);
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(device1, device2));
@@ -1012,8 +1016,10 @@
when(cachedBluetoothDevice2.getGroupId()).thenReturn(2);
BluetoothDevice device1 = mock(BluetoothDevice.class);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(device1);
when(mDeviceManager.findDevice(device1)).thenReturn(mCachedBluetoothDevice);
BluetoothDevice device2 = mock(BluetoothDevice.class);
+ when(cachedBluetoothDevice2.getDevice()).thenReturn(device2);
when(mDeviceManager.findDevice(device2)).thenReturn(cachedBluetoothDevice2);
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(device2));
@@ -1035,10 +1041,13 @@
when(cachedBluetoothDevice3.getGroupId()).thenReturn(3);
BluetoothDevice device1 = mock(BluetoothDevice.class);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(device1);
when(mDeviceManager.findDevice(device1)).thenReturn(mCachedBluetoothDevice);
BluetoothDevice device2 = mock(BluetoothDevice.class);
+ when(cachedBluetoothDevice2.getDevice()).thenReturn(device2);
when(mDeviceManager.findDevice(device2)).thenReturn(cachedBluetoothDevice2);
BluetoothDevice device3 = mock(BluetoothDevice.class);
+ when(cachedBluetoothDevice3.getDevice()).thenReturn(device3);
when(mDeviceManager.findDevice(device3)).thenReturn(cachedBluetoothDevice3);
when(mAssistant.getAllConnectedDevices())
@@ -1280,6 +1289,8 @@
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mSetFlagsRule.disableFlags(Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION);
+ Settings.Global.putInt(mContext.getContentResolver(),
+ BluetoothUtils.DEVELOPER_OPTION_PREVIEW_KEY, 1);
assertThat(BluetoothUtils.isAudioSharingHysteresisModeFixAvailable(mContext)).isTrue();
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingDeviceLocalDataManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingDeviceLocalDataManagerTest.java
index 6d83588..6485636 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingDeviceLocalDataManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingDeviceLocalDataManagerTest.java
@@ -31,6 +31,8 @@
import androidx.test.core.app.ApplicationProvider;
+import com.android.settingslib.utils.ThreadUtils;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -49,7 +51,10 @@
/** Tests for {@link HearingDeviceLocalDataManager}. */
@RunWith(RobolectricTestRunner.class)
-@Config(shadows = {HearingDeviceLocalDataManagerTest.ShadowGlobal.class})
+@Config(shadows = {
+ HearingDeviceLocalDataManagerTest.ShadowGlobal.class,
+ HearingDeviceLocalDataManagerTest.ShadowThreadUtils.class,
+})
public class HearingDeviceLocalDataManagerTest {
private static final String TEST_ADDRESS = "XX:XX:XX:XX:11:22";
@@ -249,4 +254,12 @@
return sDataMap.computeIfAbsent(cr, k -> new HashMap<>());
}
}
+
+ @Implements(value = ThreadUtils.class)
+ public static class ShadowThreadUtils {
+ @Implementation
+ protected static void postOnBackgroundThread(Runnable runnable) {
+ runnable.run();
+ }
+ }
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index fb4293a..46bd88f 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -994,6 +994,9 @@
<uses-permission android:name="android.permission.ACCESS_TEXT_CLASSIFIER_BY_TYPE"
android:featureFlag="android.permission.flags.text_classifier_choice_api_enabled"/>
+ <!-- Permission required for CTS test - CtsContentProviderMultiUserTest -->
+ <uses-permission android:name="android.permission.RESOLVE_COMPONENT_FOR_UID" />
+
<application
android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 5ff2d1b..e02bd9e 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1904,3 +1904,10 @@
description: "Invokes edit mode directly from long press in glanceable hub"
bug: "382531177"
}
+
+flag {
+ name: "notification_magic_actions_treatment"
+ namespace: "systemui"
+ description: "Special UI treatment for magic actions"
+ bug: "383567383"
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index 9744424..141736f 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -29,6 +29,7 @@
import com.android.compose.nestedscroll.ScrollController
import com.android.compose.ui.util.SpaceVectorConverter
import kotlin.math.absoluteValue
+import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -359,13 +360,24 @@
return swipeAnimation.animateOffset(velocity, targetContent)
}
- overscrollEffect.applyToFling(
- velocity = velocity.toVelocity(),
- performFling = {
- val velocityLeft = it.toFloat()
- swipeAnimation.animateOffset(velocityLeft, targetContent).toVelocity()
- },
- )
+ val overscrollCompletable = CompletableDeferred<Unit>()
+ try {
+ overscrollEffect.applyToFling(
+ velocity = velocity.toVelocity(),
+ performFling = {
+ val velocityLeft = it.toFloat()
+ swipeAnimation
+ .animateOffset(
+ velocityLeft,
+ targetContent,
+ overscrollCompletable = overscrollCompletable,
+ )
+ .toVelocity()
+ },
+ )
+ } finally {
+ overscrollCompletable.complete(Unit)
+ }
return velocity
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SharedElement.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SharedElement.kt
index 599a152a..167928b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SharedElement.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SharedElement.kt
@@ -30,7 +30,8 @@
// the transition is running. If the [renderAuthority.size] is 1 it means that that this element
// is currently composed only in one nesting level, which means that the render authority
// is determined by "classic" shared element code.
- return renderAuthority.size == 1 || renderAuthority.first() == content
+ return renderAuthority.size > 0 &&
+ (renderAuthority.size == 1 || renderAuthority.first() == content)
}
/**
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
index 5aaeda8..47daa76 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
@@ -271,7 +271,7 @@
/** The offset animation that animates the offset once the user lifts their finger. */
private var offsetAnimation: Animatable<Float, AnimationVector1D>? by mutableStateOf(null)
- private val offsetAnimationRunnable = CompletableDeferred<(suspend () -> Unit)?>()
+ private val offsetAnimationRunnable = CompletableDeferred<suspend () -> Unit>()
val isUserInputOngoing: Boolean
get() = offsetAnimation == null
@@ -333,6 +333,7 @@
initialVelocity: Float,
targetContent: T,
spec: AnimationSpec<Float>? = null,
+ overscrollCompletable: CompletableDeferred<Unit>? = null,
): Float {
check(!isAnimatingOffset()) { "SwipeAnimation.animateOffset() can only be called once" }
@@ -391,7 +392,12 @@
// detail).
if (skipAnimation) {
// Unblock the job.
- offsetAnimationRunnable.complete(null)
+ offsetAnimationRunnable.complete {
+ // Wait for overscroll to finish so that the transition is removed from the STLState
+ // only after the overscroll is done, to avoid dropping frame right when the user
+ // lifts their finger and overscroll is animated to 0.
+ overscrollCompletable?.await()
+ }
return 0f
}
@@ -450,6 +456,11 @@
// The animation consumed the whole available velocity
velocityConsumed.complete(initialVelocity)
}
+
+ // Wait for overscroll to finish so that the transition is removed from the STLState
+ // only after the overscroll is done, to avoid dropping frame right when the user
+ // lifts their finger and overscroll is animated to 0.
+ overscrollCompletable?.await()
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayoutTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayoutTest.java
new file mode 100644
index 0000000..455329f
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayoutTest.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.accessibility.hearingaid;
+
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
+
+import static com.android.settingslib.bluetooth.HearingAidInfo.DeviceSide.SIDE_LEFT;
+import static com.android.settingslib.bluetooth.HearingAidInfo.DeviceSide.SIDE_RIGHT;
+import static com.android.systemui.accessibility.hearingaid.AmbientVolumeLayout.ROTATION_COLLAPSED;
+import static com.android.systemui.accessibility.hearingaid.AmbientVolumeLayout.ROTATION_EXPANDED;
+import static com.android.systemui.accessibility.hearingaid.AmbientVolumeLayout.SIDE_UNIFIED;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.util.ArrayMap;
+import android.view.View;
+import android.widget.ImageView;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.settingslib.bluetooth.AmbientVolumeUi;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.Map;
+
+/** Tests for {@link AmbientVolumeLayout}. */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class AmbientVolumeLayoutTest extends SysuiTestCase {
+
+ private static final int TEST_LEFT_VOLUME_LEVEL = 1;
+ private static final int TEST_RIGHT_VOLUME_LEVEL = 2;
+ private static final int TEST_UNIFIED_VOLUME_LEVEL = 3;
+
+ @Rule
+ public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+ @Spy
+ private Context mContext = ApplicationProvider.getApplicationContext();
+ @Mock
+ private AmbientVolumeUi.AmbientVolumeUiListener mListener;
+
+ private AmbientVolumeLayout mLayout;
+ private ImageView mExpandIcon;
+ private ImageView mVolumeIcon;
+ private final Map<Integer, BluetoothDevice> mSideToDeviceMap = new ArrayMap<>();
+
+ @Before
+ public void setUp() {
+ mLayout = new AmbientVolumeLayout(mContext);
+ mLayout.setListener(mListener);
+ mLayout.setExpandable(true);
+ mLayout.setMutable(true);
+
+ prepareDevices();
+ mLayout.setupSliders(mSideToDeviceMap);
+ mLayout.getSliders().forEach((side, slider) -> {
+ slider.setMin(0);
+ slider.setMax(4);
+ if (side == SIDE_LEFT) {
+ slider.setValue(TEST_LEFT_VOLUME_LEVEL);
+ } else if (side == SIDE_RIGHT) {
+ slider.setValue(TEST_RIGHT_VOLUME_LEVEL);
+ } else if (side == SIDE_UNIFIED) {
+ slider.setValue(TEST_UNIFIED_VOLUME_LEVEL);
+ }
+ });
+
+ mExpandIcon = mLayout.getExpandIcon();
+ mVolumeIcon = mLayout.getVolumeIcon();
+ }
+
+ @Test
+ public void setExpandable_expandable_expandIconVisible() {
+ mLayout.setExpandable(true);
+
+ assertThat(mExpandIcon.getVisibility()).isEqualTo(VISIBLE);
+ }
+
+ @Test
+ public void setExpandable_notExpandable_expandIconGone() {
+ mLayout.setExpandable(false);
+
+ assertThat(mExpandIcon.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void setExpanded_expanded_assertControlUiCorrect() {
+ mLayout.setExpanded(true);
+
+ assertControlUiCorrect();
+ }
+
+ @Test
+ public void setExpanded_notExpanded_assertControlUiCorrect() {
+ mLayout.setExpanded(false);
+
+ assertControlUiCorrect();
+ }
+
+ @Test
+ public void setMutable_mutable_clickOnMuteIconChangeMuteState() {
+ mLayout.setMutable(true);
+ mLayout.setMuted(false);
+
+ mVolumeIcon.callOnClick();
+
+ assertThat(mLayout.isMuted()).isTrue();
+ }
+
+ @Test
+ public void setMutable_notMutable_clickOnMuteIconWontChangeMuteState() {
+ mLayout.setMutable(false);
+ mLayout.setMuted(false);
+
+ mVolumeIcon.callOnClick();
+
+ assertThat(mLayout.isMuted()).isFalse();
+ }
+
+ @Test
+ public void updateLayout_mute_volumeIconIsCorrect() {
+ mLayout.setMuted(true);
+ mLayout.updateLayout();
+
+ assertThat(mVolumeIcon.getDrawable().getLevel()).isEqualTo(0);
+ }
+
+ @Test
+ public void updateLayout_unmuteAndExpanded_volumeIconIsCorrect() {
+ mLayout.setMuted(false);
+ mLayout.setExpanded(true);
+ mLayout.updateLayout();
+
+ int expectedLevel = calculateVolumeLevel(TEST_LEFT_VOLUME_LEVEL, TEST_RIGHT_VOLUME_LEVEL);
+ assertThat(mVolumeIcon.getDrawable().getLevel()).isEqualTo(expectedLevel);
+ }
+
+ @Test
+ public void updateLayout_unmuteAndNotExpanded_volumeIconIsCorrect() {
+ mLayout.setMuted(false);
+ mLayout.setExpanded(false);
+ mLayout.updateLayout();
+
+ int expectedLevel = calculateVolumeLevel(TEST_UNIFIED_VOLUME_LEVEL,
+ TEST_UNIFIED_VOLUME_LEVEL);
+ assertThat(mVolumeIcon.getDrawable().getLevel()).isEqualTo(expectedLevel);
+ }
+
+ @Test
+ public void setSliderEnabled_expandedAndLeftIsDisabled_volumeIconIsCorrect() {
+ mLayout.setExpanded(true);
+ mLayout.setSliderEnabled(SIDE_LEFT, false);
+
+ int expectedLevel = calculateVolumeLevel(0, TEST_RIGHT_VOLUME_LEVEL);
+ assertThat(mVolumeIcon.getDrawable().getLevel()).isEqualTo(expectedLevel);
+ }
+
+ @Test
+ public void setSliderValue_expandedAndLeftValueChanged_volumeIconIsCorrect() {
+ mLayout.setExpanded(true);
+ mLayout.setSliderValue(SIDE_LEFT, 4);
+
+ int expectedLevel = calculateVolumeLevel(4, TEST_RIGHT_VOLUME_LEVEL);
+ assertThat(mVolumeIcon.getDrawable().getLevel()).isEqualTo(expectedLevel);
+ }
+
+ private int calculateVolumeLevel(int left, int right) {
+ return left * 5 + right;
+ }
+
+ private void assertControlUiCorrect() {
+ final boolean expanded = mLayout.isExpanded();
+ final Map<Integer, AmbientVolumeSlider> sliders = mLayout.getSliders();
+ if (expanded) {
+ assertThat(sliders.get(SIDE_UNIFIED).getVisibility()).isEqualTo(GONE);
+ assertThat(sliders.get(SIDE_LEFT).getVisibility()).isEqualTo(VISIBLE);
+ assertThat(sliders.get(SIDE_RIGHT).getVisibility()).isEqualTo(VISIBLE);
+ assertThat(mExpandIcon.getRotation()).isEqualTo(ROTATION_EXPANDED);
+ } else {
+ assertThat(sliders.get(SIDE_UNIFIED).getVisibility()).isEqualTo(VISIBLE);
+ assertThat(sliders.get(SIDE_LEFT).getVisibility()).isEqualTo(GONE);
+ assertThat(sliders.get(SIDE_RIGHT).getVisibility()).isEqualTo(GONE);
+ assertThat(mExpandIcon.getRotation()).isEqualTo(ROTATION_COLLAPSED);
+ }
+ }
+
+ private void prepareDevices() {
+ mSideToDeviceMap.put(SIDE_LEFT, mock(BluetoothDevice.class));
+ mSideToDeviceMap.put(SIDE_RIGHT, mock(BluetoothDevice.class));
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeSliderTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeSliderTest.java
new file mode 100644
index 0000000..78dfda8
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeSliderTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.accessibility.hearingaid;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+/** Tests for {@link AmbientVolumeLayout}. */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class AmbientVolumeSliderTest extends SysuiTestCase {
+
+ @Rule
+ public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+ @Spy
+ private Context mContext = ApplicationProvider.getApplicationContext();
+
+ private AmbientVolumeSlider mSlider;
+
+ @Before
+ public void setUp() {
+ mSlider = new AmbientVolumeSlider(mContext);
+ }
+
+ @Test
+ public void setTitle_titleCorrect() {
+ final String testTitle = "test";
+ mSlider.setTitle(testTitle);
+
+ assertThat(mSlider.getTitle()).isEqualTo(testTitle);
+ }
+
+ @Test
+ public void getVolumeLevel_valueMin_volumeLevelIsZero() {
+ prepareSlider(/* min= */ 0, /* max= */ 100, /* value= */ 0);
+
+ // The volume level is divided into 5 levels:
+ // Level 0 corresponds to the minimum volume value. The range between the minimum and
+ // maximum volume is divided into 4 equal intervals, represented by levels 1 to 4.
+ assertThat(mSlider.getVolumeLevel()).isEqualTo(0);
+ }
+
+ @Test
+ public void getVolumeLevel_valueMax_volumeLevelIsFour() {
+ prepareSlider(/* min= */ 0, /* max= */ 100, /* value= */ 100);
+
+ assertThat(mSlider.getVolumeLevel()).isEqualTo(4);
+ }
+
+ @Test
+ public void getVolumeLevel_volumeLevelIsCorrect() {
+ prepareSlider(/* min= */ 0, /* max= */ 100, /* value= */ 73);
+
+ assertThat(mSlider.getVolumeLevel()).isEqualTo(3);
+ }
+
+ private void prepareSlider(float min, float max, float value) {
+ mSlider.setMin(min);
+ mSlider.setMax(max);
+ mSlider.setValue(value);
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
index ad12c61..43d0d69c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
@@ -16,8 +16,11 @@
package com.android.systemui.accessibility.hearingaid;
+import static android.bluetooth.BluetoothDevice.BOND_BONDED;
import static android.bluetooth.BluetoothHapClient.PRESET_INDEX_UNAVAILABLE;
+import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
+import static com.android.settingslib.bluetooth.HearingAidInfo.DeviceSide.SIDE_LEFT;
import static com.android.systemui.accessibility.hearingaid.HearingDevicesDialogDelegate.LIVE_CAPTION_INTENT;
import static com.google.common.truth.Truth.assertThat;
@@ -31,6 +34,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.bluetooth.AudioInputControl;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHapPresetInfo;
import android.bluetooth.BluetoothProfile;
@@ -61,6 +65,7 @@
import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.settingslib.bluetooth.VolumeControlProfile;
import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogTransitionAnimator;
@@ -90,6 +95,7 @@
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
+
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
@@ -120,6 +126,8 @@
@Mock
private HapClientProfile mHapClientProfile;
@Mock
+ private VolumeControlProfile mVolumeControlProfile;
+ @Mock
private CachedBluetoothDeviceManager mCachedDeviceManager;
@Mock
private BluetoothEventManager mBluetoothEventManager;
@@ -151,21 +159,25 @@
when(mLocalBluetoothManager.getBluetoothAdapter()).thenReturn(mLocalBluetoothAdapter);
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mProfileManager);
when(mProfileManager.getHapClientProfile()).thenReturn(mHapClientProfile);
+ when(mProfileManager.getVolumeControlProfile()).thenReturn(mVolumeControlProfile);
when(mLocalBluetoothAdapter.isEnabled()).thenReturn(true);
when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(List.of(mCachedDevice));
when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager);
when(mSysUiState.setFlag(anyLong(), anyBoolean())).thenReturn(mSysUiState);
- when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mDevice.getBondState()).thenReturn(BOND_BONDED);
when(mDevice.isConnected()).thenReturn(true);
when(mCachedDevice.getDevice()).thenReturn(mDevice);
when(mCachedDevice.getAddress()).thenReturn(DEVICE_ADDRESS);
when(mCachedDevice.getName()).thenReturn(DEVICE_NAME);
- when(mCachedDevice.getProfiles()).thenReturn(List.of(mHapClientProfile));
+ when(mCachedDevice.getProfiles()).thenReturn(
+ List.of(mHapClientProfile, mVolumeControlProfile));
when(mCachedDevice.isActiveDevice(BluetoothProfile.HEARING_AID)).thenReturn(true);
when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
when(mCachedDevice.isConnectedHapClientDevice()).thenReturn(true);
when(mCachedDevice.getDrawableWithDescription()).thenReturn(new Pair<>(mDrawable, ""));
+ when(mCachedDevice.getBondState()).thenReturn(BOND_BONDED);
+ when(mCachedDevice.getDeviceSide()).thenReturn(SIDE_LEFT);
when(mHearingDeviceItem.getCachedBluetoothDevice()).thenReturn(mCachedDevice);
mContext.setMockPackageManager(mPackageManager);
@@ -292,6 +304,46 @@
}
@Test
+ @EnableFlags(com.android.settingslib.flags.Flags.FLAG_HEARING_DEVICES_AMBIENT_VOLUME_CONTROL)
+ public void showDialog_deviceNotSupportVcp_ambientLayoutGone() {
+ when(mCachedDevice.getProfiles()).thenReturn(List.of());
+
+ setUpDeviceDialogWithoutPairNewDeviceButton();
+ mDialog.show();
+
+ ViewGroup ambientLayout = getAmbientLayout(mDialog);
+ assertThat(ambientLayout.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ @EnableFlags(com.android.settingslib.flags.Flags.FLAG_HEARING_DEVICES_AMBIENT_VOLUME_CONTROL)
+ public void showDialog_ambientControlNotAvailable_ambientLayoutGone() {
+ when(mVolumeControlProfile.getAudioInputControlServices(mDevice)).thenReturn(List.of());
+
+ setUpDeviceDialogWithoutPairNewDeviceButton();
+ mDialog.show();
+
+ ViewGroup ambientLayout = getAmbientLayout(mDialog);
+ assertThat(ambientLayout.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ @EnableFlags(com.android.settingslib.flags.Flags.FLAG_HEARING_DEVICES_AMBIENT_VOLUME_CONTROL)
+ public void showDialog_supportVcpAndAmbientControlAvailable_ambientLayoutVisible() {
+ when(mCachedDevice.getProfiles()).thenReturn(List.of(mVolumeControlProfile));
+ AudioInputControl audioInputControl = prepareAudioInputControl();
+ when(mVolumeControlProfile.getAudioInputControlServices(mDevice)).thenReturn(
+ List.of(audioInputControl));
+ when(mVolumeControlProfile.getConnectionStatus(mDevice)).thenReturn(STATE_CONNECTED);
+
+ setUpDeviceDialogWithoutPairNewDeviceButton();
+ mDialog.show();
+
+ ViewGroup ambientLayout = getAmbientLayout(mDialog);
+ assertThat(ambientLayout.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
public void onActiveDeviceChanged_presetExist_presetSelected() {
setUpDeviceDialogWithoutPairNewDeviceButton();
mDialog.show();
@@ -368,6 +420,10 @@
return dialog.requireViewById(R.id.preset_layout);
}
+ private ViewGroup getAmbientLayout(SystemUIDialog dialog) {
+ return dialog.requireViewById(R.id.ambient_layout);
+ }
+
private int countChildWithoutSpace(ViewGroup viewGroup) {
int spaceCount = 0;
@@ -388,6 +444,16 @@
assertThat(toolsLayout.getVisibility()).isEqualTo(targetVisibility);
}
+ private AudioInputControl prepareAudioInputControl() {
+ AudioInputControl audioInputControl = mock(AudioInputControl.class);
+ when(audioInputControl.getAudioInputType()).thenReturn(
+ AudioInputControl.AUDIO_INPUT_TYPE_AMBIENT);
+ when(audioInputControl.getGainMode()).thenReturn(AudioInputControl.GAIN_MODE_MANUAL);
+ when(audioInputControl.getAudioInputStatus()).thenReturn(
+ AudioInputControl.AUDIO_INPUT_STATUS_ACTIVE);
+ return audioInputControl;
+ }
+
@After
public void reset() {
if (mDialogDelegate != null) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
index 271cd3a..cbb6f81 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
@@ -168,17 +168,6 @@
}
@Test
- fun testOnBackRequested_closeUserSwitcherIfOpen() {
- whenever(shadeBackActionInteractor.closeUserSwitcherIfOpen()).thenReturn(true)
-
- val result = backActionInteractor.onBackRequested()
-
- assertTrue(result)
- verify(statusBarKeyguardViewManager, never()).onBackPressed()
- verify(shadeBackActionInteractor, never()).animateCollapseQs(anyBoolean())
- }
-
- @Test
fun testOnBackRequested_returnsFalse() {
// make shouldBackBeHandled return false
whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalAppWidgetViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalAppWidgetViewModelTest.kt
index a8a3873..9271980 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalAppWidgetViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalAppWidgetViewModelTest.kt
@@ -30,6 +30,7 @@
import com.android.systemui.communal.widgets.GlanceableHubWidgetManager
import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.backgroundCoroutineContext
import com.android.systemui.kosmos.runCurrent
import com.android.systemui.kosmos.runTest
@@ -57,8 +58,8 @@
private val Kosmos.listenerDelegateFactory by
Kosmos.Fixture {
- AppWidgetHostListenerDelegate.Factory { listener ->
- AppWidgetHostListenerDelegate(fakeExecutor, listener)
+ AppWidgetHostListenerDelegate.Factory { tag, listener ->
+ AppWidgetHostListenerDelegate(applicationCoroutineScope, tag, listener)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalWidgetHostTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalWidgetHostTest.kt
index 017c778..214cd1a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalWidgetHostTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalWidgetHostTest.kt
@@ -27,10 +27,11 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.shared.model.fakeGlanceableHubMultiUserHelper
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.coroutines.collectValues
import com.android.systemui.kosmos.applicationCoroutineScope
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.collectValues
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.testKosmos
import com.android.systemui.user.data.model.SelectedUserModel
@@ -43,10 +44,6 @@
import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
import java.util.Optional
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -58,11 +55,9 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
class CommunalWidgetHostTest : SysuiTestCase() {
- private val kosmos = testKosmos()
- private val testScope = kosmos.testScope
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
@Mock private lateinit var appWidgetManager: AppWidgetManager
@Mock private lateinit var appWidgetHost: CommunalAppWidgetHost
@@ -103,12 +98,11 @@
@Test
fun allocateIdAndBindWidget_withCurrentUser() =
- testScope.runTest {
+ kosmos.runTest {
val provider = ComponentName("pkg_name", "cls_name")
val widgetId = 1
val userId by collectLastValue(selectedUserInteractor.selectedUser)
selectUser()
- runCurrent()
val user = UserHandle(checkNotNull(userId))
whenever(appWidgetHost.allocateAppWidgetId()).thenReturn(widgetId)
@@ -129,7 +123,7 @@
@Test
fun allocateIdAndBindWidget_onSuccess() =
- testScope.runTest {
+ kosmos.runTest {
val provider = ComponentName("pkg_name", "cls_name")
val widgetId = 1
val user = UserHandle(0)
@@ -152,7 +146,7 @@
@Test
fun allocateIdAndBindWidget_onFailure() =
- testScope.runTest {
+ kosmos.runTest {
val provider = ComponentName("pkg_name", "cls_name")
val widgetId = 1
val user = UserHandle(0)
@@ -179,12 +173,11 @@
@Test
fun listener_exactlyOneListenerRegisteredForEachWidgetWhenHostStartListening() =
- testScope.runTest {
+ kosmos.runTest {
// 3 widgets registered with the host
whenever(appWidgetHost.appWidgetIds).thenReturn(intArrayOf(1, 2, 3))
underTest.startObservingHost()
- runCurrent()
// Make sure no listener is set before host starts listening
verify(appWidgetHost, never()).setListener(any(), any())
@@ -195,7 +188,6 @@
verify(appWidgetHost).addObserver(capture())
}
observer.onHostStartListening()
- runCurrent()
// Verify a listener is set for each widget
verify(appWidgetHost, times(3)).setListener(any(), any())
@@ -206,12 +198,11 @@
@Test
fun listener_listenersRemovedWhenHostStopListening() =
- testScope.runTest {
+ kosmos.runTest {
// 3 widgets registered with the host
whenever(appWidgetHost.appWidgetIds).thenReturn(intArrayOf(1, 2, 3))
underTest.startObservingHost()
- runCurrent()
// Host starts listening
val observer =
@@ -219,7 +210,6 @@
verify(appWidgetHost).addObserver(capture())
}
observer.onHostStartListening()
- runCurrent()
// Verify none of the listener is removed before host stop listening
verify(appWidgetHost, never()).removeListener(any())
@@ -235,7 +225,7 @@
@Test
fun listener_addNewListenerWhenNewIdAllocated() =
- testScope.runTest {
+ kosmos.runTest {
whenever(appWidgetHost.appWidgetIds).thenReturn(intArrayOf())
val observer = start()
@@ -251,7 +241,7 @@
@Test
fun listener_removeListenerWhenWidgetDeleted() =
- testScope.runTest {
+ kosmos.runTest {
whenever(appWidgetHost.appWidgetIds).thenReturn(intArrayOf(1))
val observer = start()
@@ -267,7 +257,7 @@
@Test
fun providerInfo_populatesWhenStartListening() =
- testScope.runTest {
+ kosmos.runTest {
whenever(appWidgetHost.appWidgetIds).thenReturn(intArrayOf(1, 2))
whenever(appWidgetManager.getAppWidgetInfo(1)).thenReturn(providerInfo1)
whenever(appWidgetManager.getAppWidgetInfo(2)).thenReturn(providerInfo2)
@@ -279,7 +269,6 @@
assertThat(providerInfoValues[0]).isEmpty()
start()
- runCurrent()
// Assert that the provider info map is populated after host started listening, and that
// all providers are emitted at once
@@ -290,13 +279,12 @@
@Test
fun providerInfo_clearsWhenStopListening() =
- testScope.runTest {
+ kosmos.runTest {
whenever(appWidgetHost.appWidgetIds).thenReturn(intArrayOf(1, 2))
whenever(appWidgetManager.getAppWidgetInfo(1)).thenReturn(providerInfo1)
whenever(appWidgetManager.getAppWidgetInfo(2)).thenReturn(providerInfo2)
val observer = start()
- runCurrent()
// Assert that the provider info map is populated
val providerInfo by collectLastValue(underTest.appWidgetProviders)
@@ -312,7 +300,7 @@
@Test
fun providerInfo_onUpdate() =
- testScope.runTest {
+ kosmos.runTest {
whenever(appWidgetHost.appWidgetIds).thenReturn(intArrayOf(1, 2))
whenever(appWidgetManager.getAppWidgetInfo(1)).thenReturn(providerInfo1)
whenever(appWidgetManager.getAppWidgetInfo(2)).thenReturn(providerInfo2)
@@ -320,7 +308,6 @@
val providerInfo by collectLastValue(underTest.appWidgetProviders)
start()
- runCurrent()
// Assert that the provider info map is populated
assertThat(providerInfo)
@@ -332,7 +319,6 @@
verify(appWidgetHost).setListener(eq(1), capture())
}
listener.onUpdateProviderInfo(providerInfo3)
- runCurrent()
// Assert that the update is reflected in the flow
assertThat(providerInfo)
@@ -341,7 +327,7 @@
@Test
fun providerInfo_updateWhenANewWidgetIsBound() =
- testScope.runTest {
+ kosmos.runTest {
whenever(appWidgetHost.appWidgetIds).thenReturn(intArrayOf(1, 2))
whenever(appWidgetManager.getAppWidgetInfo(1)).thenReturn(providerInfo1)
whenever(appWidgetManager.getAppWidgetInfo(2)).thenReturn(providerInfo2)
@@ -349,7 +335,6 @@
val providerInfo by collectLastValue(underTest.appWidgetProviders)
start()
- runCurrent()
// Assert that the provider info map is populated
assertThat(providerInfo)
@@ -360,7 +345,6 @@
whenever(appWidgetManager.getAppWidgetInfo(3)).thenReturn(providerInfo3)
val newWidgetComponentName = ComponentName.unflattenFromString("pkg_new/cls_new")!!
underTest.allocateIdAndBindWidget(newWidgetComponentName)
- runCurrent()
// Assert that the new provider is reflected in the flow
assertThat(providerInfo)
@@ -371,7 +355,7 @@
@Test
fun providerInfo_updateWhenWidgetRemoved() =
- testScope.runTest {
+ kosmos.runTest {
whenever(appWidgetHost.appWidgetIds).thenReturn(intArrayOf(1, 2))
whenever(appWidgetManager.getAppWidgetInfo(1)).thenReturn(providerInfo1)
whenever(appWidgetManager.getAppWidgetInfo(2)).thenReturn(providerInfo2)
@@ -379,7 +363,6 @@
val providerInfo by collectLastValue(underTest.appWidgetProviders)
val observer = start()
- runCurrent()
// Assert that the provider info map is populated
assertThat(providerInfo)
@@ -387,7 +370,6 @@
// Remove widget 1
observer.onDeleteAppWidgetId(1)
- runCurrent()
// Assert that provider info for widget 1 is removed
assertThat(providerInfo).containsExactlyEntriesIn(mapOf(Pair(2, providerInfo2)))
@@ -401,9 +383,8 @@
)
}
- private fun TestScope.start(): CommunalAppWidgetHost.Observer {
+ private fun start(): CommunalAppWidgetHost.Observer {
underTest.startObservingHost()
- runCurrent()
val observer =
withArgCaptor<CommunalAppWidgetHost.Observer> {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfigTest.kt
index 789b10b..ac06a3b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfigTest.kt
@@ -24,21 +24,19 @@
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.communalSceneRepository
-import com.android.systemui.communal.domain.interactor.communalInteractor
-import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
+import com.android.systemui.communal.domain.interactor.setCommunalV2Available
import com.android.systemui.communal.domain.interactor.setCommunalV2Enabled
import com.android.systemui.communal.shared.model.CommunalScenes
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.andSceneContainer
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.flags.parameterizeSceneContainerFlag
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
import com.android.systemui.scene.data.repository.sceneContainerRepository
-import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -47,14 +45,11 @@
import platform.test.runner.parameterized.Parameters
@SmallTest
-@OptIn(ExperimentalCoroutinesApi::class)
-@EnableFlags(Flags.FLAG_GLANCEABLE_HUB_SHORTCUT_BUTTON, Flags.FLAG_GLANCEABLE_HUB_V2)
+@EnableFlags(Flags.FLAG_GLANCEABLE_HUB_V2)
@RunWith(ParameterizedAndroidJunit4::class)
class GlanceableHubQuickAffordanceConfigTest(flags: FlagsParameterization?) : SysuiTestCase() {
private val kosmos = testKosmos()
- private val testScope = kosmos.testScope
-
- private lateinit var underTest: GlanceableHubQuickAffordanceConfig
+ private val Kosmos.underTest by Kosmos.Fixture { glanceableHubQuickAffordanceConfig }
init {
mSetFlagsRule.setFlagsParameterization(flags!!)
@@ -64,20 +59,16 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
- underTest =
- GlanceableHubQuickAffordanceConfig(
- context = context,
- communalInteractor = kosmos.communalInteractor,
- communalSceneRepository = kosmos.communalSceneRepository,
- communalSettingsInteractor = kosmos.communalSettingsInteractor,
- sceneInteractor = kosmos.sceneInteractor,
- )
+ // Access the class immediately so that flows are instantiated.
+ // GlanceableHubQuickAffordanceConfig accesses StateFlow.value directly so we need the flows
+ // to start flowing before runCurrent is called in the tests.
+ kosmos.underTest
}
@Test
fun lockscreenState_whenGlanceableHubEnabled_returnsVisible() =
- testScope.runTest {
- kosmos.setCommunalV2Enabled(true)
+ kosmos.runTest {
+ kosmos.setCommunalV2Available(true)
runCurrent()
val lockScreenState by collectLastValue(underTest.lockScreenState)
@@ -88,8 +79,21 @@
@Test
fun lockscreenState_whenGlanceableHubDisabled_returnsHidden() =
- testScope.runTest {
- kosmos.setCommunalV2Enabled(false)
+ kosmos.runTest {
+ setCommunalV2Enabled(false)
+ val lockScreenState by collectLastValue(underTest.lockScreenState)
+ runCurrent()
+
+ assertThat(lockScreenState)
+ .isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+ }
+
+ @Test
+ fun lockscreenState_whenGlanceableHubNotAvailable_returnsHidden() =
+ kosmos.runTest {
+ // Hub is enabled, but not available.
+ setCommunalV2Enabled(true)
+ fakeKeyguardRepository.setKeyguardShowing(false)
val lockScreenState by collectLastValue(underTest.lockScreenState)
runCurrent()
@@ -99,8 +103,8 @@
@Test
fun pickerScreenState_whenGlanceableHubEnabled_returnsDefault() =
- testScope.runTest {
- kosmos.setCommunalV2Enabled(true)
+ kosmos.runTest {
+ setCommunalV2Enabled(true)
runCurrent()
assertThat(underTest.getPickerScreenState())
@@ -109,8 +113,8 @@
@Test
fun pickerScreenState_whenGlanceableHubDisabled_returnsDisabled() =
- testScope.runTest {
- kosmos.setCommunalV2Enabled(false)
+ kosmos.runTest {
+ setCommunalV2Enabled(false)
runCurrent()
assertThat(
@@ -122,7 +126,7 @@
@Test
@DisableFlags(Flags.FLAG_SCENE_CONTAINER)
fun onTriggered_changesSceneToCommunal() =
- testScope.runTest {
+ kosmos.runTest {
underTest.onTriggered(expandable = null)
runCurrent()
@@ -133,7 +137,7 @@
@Test
@EnableFlags(Flags.FLAG_SCENE_CONTAINER)
fun testTransitionToGlanceableHub_sceneContainer() =
- testScope.runTest {
+ kosmos.runTest {
underTest.onTriggered(expandable = null)
runCurrent()
@@ -145,11 +149,7 @@
@JvmStatic
@Parameters(name = "{0}")
fun getParams(): List<FlagsParameterization> {
- return FlagsParameterization.allCombinationsOf(
- Flags.FLAG_GLANCEABLE_HUB_SHORTCUT_BUTTON,
- Flags.FLAG_GLANCEABLE_HUB_V2,
- )
- .andSceneContainer()
+ return parameterizeSceneContainerFlag()
}
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
index 4a422f0..8c54ca1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
@@ -23,6 +23,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
@@ -84,6 +85,7 @@
.thenReturn(FakeSharedPreferences())
},
userTracker = FakeUserTracker(),
+ communalSettingsInteractor = kosmos.communalSettingsInteractor,
broadcastDispatcher = fakeBroadcastDispatcher,
)
settings.putInt(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS, 0)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
index 0f3e78b..bc2c2d2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
@@ -20,13 +20,17 @@
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.UserInfo
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.backup.BackupHelper
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.res.R
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.UserFileManager
+import com.android.systemui.testKosmos
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -54,6 +58,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class KeyguardQuickAffordanceLocalUserSelectionManagerTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
@Mock private lateinit var userFileManager: UserFileManager
@@ -80,6 +85,7 @@
context = context,
userFileManager = userFileManager,
userTracker = userTracker,
+ communalSettingsInteractor = kosmos.communalSettingsInteractor,
broadcastDispatcher = fakeBroadcastDispatcher,
)
}
@@ -110,27 +116,13 @@
val affordanceId2 = "affordance2"
val affordanceId3 = "affordance3"
- underTest.setSelections(
- slotId = slotId1,
- affordanceIds = listOf(affordanceId1),
- )
- assertSelections(
- affordanceIdsBySlotId.last(),
- mapOf(
- slotId1 to listOf(affordanceId1),
- ),
- )
+ underTest.setSelections(slotId = slotId1, affordanceIds = listOf(affordanceId1))
+ assertSelections(affordanceIdsBySlotId.last(), mapOf(slotId1 to listOf(affordanceId1)))
- underTest.setSelections(
- slotId = slotId2,
- affordanceIds = listOf(affordanceId2),
- )
+ underTest.setSelections(slotId = slotId2, affordanceIds = listOf(affordanceId2))
assertSelections(
affordanceIdsBySlotId.last(),
- mapOf(
- slotId1 to listOf(affordanceId1),
- slotId2 to listOf(affordanceId2),
- )
+ mapOf(slotId1 to listOf(affordanceId1), slotId2 to listOf(affordanceId2)),
)
underTest.setSelections(
@@ -139,34 +131,19 @@
)
assertSelections(
affordanceIdsBySlotId.last(),
- mapOf(
- slotId1 to listOf(affordanceId1, affordanceId3),
- slotId2 to listOf(affordanceId2),
- )
+ mapOf(slotId1 to listOf(affordanceId1, affordanceId3), slotId2 to listOf(affordanceId2)),
)
- underTest.setSelections(
- slotId = slotId1,
- affordanceIds = listOf(affordanceId3),
- )
+ underTest.setSelections(slotId = slotId1, affordanceIds = listOf(affordanceId3))
assertSelections(
affordanceIdsBySlotId.last(),
- mapOf(
- slotId1 to listOf(affordanceId3),
- slotId2 to listOf(affordanceId2),
- )
+ mapOf(slotId1 to listOf(affordanceId3), slotId2 to listOf(affordanceId2)),
)
- underTest.setSelections(
- slotId = slotId2,
- affordanceIds = listOf(),
- )
+ underTest.setSelections(slotId = slotId2, affordanceIds = listOf())
assertSelections(
affordanceIdsBySlotId.last(),
- mapOf(
- slotId1 to listOf(affordanceId3),
- slotId2 to listOf(),
- )
+ mapOf(slotId1 to listOf(affordanceId3), slotId2 to listOf()),
)
job.cancel()
@@ -174,10 +151,7 @@
@Test
fun remembersSelectionsByUser() = runTest {
- overrideResource(
- R.array.config_keyguardQuickAffordanceDefaults,
- arrayOf<String>(),
- )
+ overrideResource(R.array.config_keyguardQuickAffordanceDefaults, arrayOf<String>())
val slot1 = "slot_1"
val slot2 = "slot_2"
val affordance1 = "affordance_1"
@@ -195,60 +169,28 @@
UserInfo(/* id= */ 0, "zero", /* flags= */ 0),
UserInfo(/* id= */ 1, "one", /* flags= */ 0),
)
- userTracker.set(
- userInfos = userInfos,
- selectedUserIndex = 0,
- )
- underTest.setSelections(
- slotId = slot1,
- affordanceIds = listOf(affordance1),
- )
- underTest.setSelections(
- slotId = slot2,
- affordanceIds = listOf(affordance2),
- )
+ userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
+ underTest.setSelections(slotId = slot1, affordanceIds = listOf(affordance1))
+ underTest.setSelections(slotId = slot2, affordanceIds = listOf(affordance2))
// Switch to user 1
- userTracker.set(
- userInfos = userInfos,
- selectedUserIndex = 1,
- )
+ userTracker.set(userInfos = userInfos, selectedUserIndex = 1)
// We never set selections on user 1, so it should be empty.
- assertSelections(
- observed = affordanceIdsBySlotId.last(),
- expected = emptyMap(),
- )
+ assertSelections(observed = affordanceIdsBySlotId.last(), expected = emptyMap())
// Now, let's set selections on user 1.
- underTest.setSelections(
- slotId = slot1,
- affordanceIds = listOf(affordance2),
- )
- underTest.setSelections(
- slotId = slot2,
- affordanceIds = listOf(affordance3),
- )
+ underTest.setSelections(slotId = slot1, affordanceIds = listOf(affordance2))
+ underTest.setSelections(slotId = slot2, affordanceIds = listOf(affordance3))
assertSelections(
observed = affordanceIdsBySlotId.last(),
- expected =
- mapOf(
- slot1 to listOf(affordance2),
- slot2 to listOf(affordance3),
- ),
+ expected = mapOf(slot1 to listOf(affordance2), slot2 to listOf(affordance3)),
)
// Switch back to user 0.
- userTracker.set(
- userInfos = userInfos,
- selectedUserIndex = 0,
- )
+ userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
// Assert that we still remember the old selections for user 0.
assertSelections(
observed = affordanceIdsBySlotId.last(),
- expected =
- mapOf(
- slot1 to listOf(affordance1),
- slot2 to listOf(affordance2),
- ),
+ expected = mapOf(slot1 to listOf(affordance1), slot2 to listOf(affordance2)),
)
job.cancel()
@@ -276,10 +218,7 @@
assertSelections(
affordanceIdsBySlotId.last(),
- mapOf(
- slotId1 to listOf(affordanceId1, affordanceId3),
- slotId2 to listOf(affordanceId2),
- ),
+ mapOf(slotId1 to listOf(affordanceId1, affordanceId3), slotId2 to listOf(affordanceId2)),
)
job.cancel()
@@ -308,10 +247,7 @@
underTest.setSelections(slotId1, listOf(affordanceId2))
assertSelections(
affordanceIdsBySlotId.last(),
- mapOf(
- slotId1 to listOf(affordanceId2),
- slotId2 to listOf(affordanceId2),
- ),
+ mapOf(slotId1 to listOf(affordanceId2), slotId2 to listOf(affordanceId2)),
)
job.cancel()
@@ -340,10 +276,7 @@
underTest.setSelections(slotId1, listOf())
assertSelections(
affordanceIdsBySlotId.last(),
- mapOf(
- slotId1 to listOf(),
- slotId2 to listOf(affordanceId2),
- ),
+ mapOf(slotId1 to listOf(), slotId2 to listOf(affordanceId2)),
)
job.cancel()
@@ -373,18 +306,36 @@
overrideResource(R.bool.custom_lockscreen_shortcuts_enabled, false)
overrideResource(
R.array.config_keyguardQuickAffordanceDefaults,
- arrayOf("leftTest:testShortcut1", "rightTest:testShortcut2")
+ arrayOf("leftTest:testShortcut1", "rightTest:testShortcut2"),
)
assertThat(underTest.getSelections())
.isEqualTo(
- mapOf(
- "leftTest" to listOf("testShortcut1"),
- "rightTest" to listOf("testShortcut2"),
- )
+ mapOf("leftTest" to listOf("testShortcut1"), "rightTest" to listOf("testShortcut2"))
)
}
+ @EnableFlags(Flags.FLAG_GLANCEABLE_HUB_V2)
+ @Test
+ fun getSelections_returnsSelectionsIfHubV2Enabled() = runTest {
+ overrideResource(R.bool.custom_lockscreen_shortcuts_enabled, false)
+ overrideResource(com.android.internal.R.bool.config_glanceableHubEnabled, true)
+
+ overrideResource(R.array.config_keyguardQuickAffordanceDefaults, arrayOf<String>())
+ val affordanceIdsBySlotId = mutableListOf<Map<String, List<String>>>()
+ val job =
+ launch(UnconfinedTestDispatcher()) {
+ underTest.selections.toList(affordanceIdsBySlotId)
+ }
+ val slotId1 = "slot1"
+ val affordanceId1 = "affordance1"
+
+ underTest.setSelections(slotId = slotId1, affordanceIds = listOf(affordanceId1))
+ assertSelections(affordanceIdsBySlotId.last(), mapOf(slotId1 to listOf(affordanceId1)))
+
+ job.cancel()
+ }
+
private fun assertSelections(
observed: Map<String, List<String>>?,
expected: Map<String, List<String>>,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
index 1582e47..a101818 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
@@ -22,6 +22,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory
@@ -84,16 +85,11 @@
context = context,
userFileManager =
mock<UserFileManager>().apply {
- whenever(
- getSharedPreferences(
- anyString(),
- anyInt(),
- anyInt(),
- )
- )
+ whenever(getSharedPreferences(anyString(), anyInt(), anyInt()))
.thenReturn(FakeSharedPreferences())
},
userTracker = userTracker,
+ communalSettingsInteractor = kosmos.communalSettingsInteractor,
broadcastDispatcher = fakeBroadcastDispatcher,
)
client1 = FakeCustomizationProviderClient()
@@ -103,9 +99,8 @@
scope = testScope.backgroundScope,
userTracker = userTracker,
clientFactory =
- FakeKeyguardQuickAffordanceProviderClientFactory(
- userTracker,
- ) { selectedUserId ->
+ FakeKeyguardQuickAffordanceProviderClientFactory(userTracker) { selectedUserId
+ ->
when (selectedUserId) {
SECONDARY_USER_1 -> client1
SECONDARY_USER_2 -> client2
@@ -115,10 +110,7 @@
userHandle = UserHandle.SYSTEM,
)
- overrideResource(
- R.array.config_keyguardQuickAffordanceDefaults,
- arrayOf<String>(),
- )
+ overrideResource(R.array.config_keyguardQuickAffordanceDefaults, arrayOf<String>())
underTest =
KeyguardQuickAffordanceRepository(
@@ -155,30 +147,19 @@
val slotId2 = "slot2"
underTest.setSelections(slotId1, listOf(config1.key))
- assertSelections(
- configsBySlotId(),
- mapOf(
- slotId1 to listOf(config1),
- ),
- )
+ assertSelections(configsBySlotId(), mapOf(slotId1 to listOf(config1)))
underTest.setSelections(slotId2, listOf(config2.key))
assertSelections(
configsBySlotId(),
- mapOf(
- slotId1 to listOf(config1),
- slotId2 to listOf(config2),
- ),
+ mapOf(slotId1 to listOf(config1), slotId2 to listOf(config2)),
)
underTest.setSelections(slotId1, emptyList())
underTest.setSelections(slotId2, listOf(config1.key))
assertSelections(
configsBySlotId(),
- mapOf(
- slotId1 to emptyList(),
- slotId2 to listOf(config1),
- ),
+ mapOf(slotId1 to emptyList(), slotId2 to listOf(config1)),
)
}
@@ -209,28 +190,15 @@
val slot3 = "slot3"
context.orCreateTestableResources.addOverride(
R.array.config_keyguardQuickAffordanceSlots,
- arrayOf(
- "$slot1:2",
- "$slot2:4",
- "$slot3:5",
- ),
+ arrayOf("$slot1:2", "$slot2:4", "$slot3:5"),
)
assertThat(underTest.getSlotPickerRepresentations())
.isEqualTo(
listOf(
- KeyguardSlotPickerRepresentation(
- id = slot1,
- maxSelectedAffordances = 2,
- ),
- KeyguardSlotPickerRepresentation(
- id = slot2,
- maxSelectedAffordances = 4,
- ),
- KeyguardSlotPickerRepresentation(
- id = slot3,
- maxSelectedAffordances = 5,
- ),
+ KeyguardSlotPickerRepresentation(id = slot1, maxSelectedAffordances = 2),
+ KeyguardSlotPickerRepresentation(id = slot2, maxSelectedAffordances = 4),
+ KeyguardSlotPickerRepresentation(id = slot3, maxSelectedAffordances = 5),
)
)
}
@@ -243,28 +211,15 @@
val slot3 = "slot3"
context.orCreateTestableResources.addOverride(
R.array.config_keyguardQuickAffordanceSlots,
- arrayOf(
- "$slot1:2",
- "$slot2:4",
- "$slot3:5",
- ),
+ arrayOf("$slot1:2", "$slot2:4", "$slot3:5"),
)
assertThat(underTest.getSlotPickerRepresentations())
.isEqualTo(
listOf(
- KeyguardSlotPickerRepresentation(
- id = slot3,
- maxSelectedAffordances = 5,
- ),
- KeyguardSlotPickerRepresentation(
- id = slot2,
- maxSelectedAffordances = 4,
- ),
- KeyguardSlotPickerRepresentation(
- id = slot1,
- maxSelectedAffordances = 2,
- ),
+ KeyguardSlotPickerRepresentation(id = slot3, maxSelectedAffordances = 5),
+ KeyguardSlotPickerRepresentation(id = slot2, maxSelectedAffordances = 4),
+ KeyguardSlotPickerRepresentation(id = slot1, maxSelectedAffordances = 2),
)
)
}
@@ -275,21 +230,9 @@
userTracker.set(
userInfos =
listOf(
- UserInfo(
- UserHandle.USER_SYSTEM,
- "Primary",
- /* flags= */ 0,
- ),
- UserInfo(
- SECONDARY_USER_1,
- "Secondary 1",
- /* flags= */ 0,
- ),
- UserInfo(
- SECONDARY_USER_2,
- "Secondary 2",
- /* flags= */ 0,
- ),
+ UserInfo(UserHandle.USER_SYSTEM, "Primary", /* flags= */ 0),
+ UserInfo(SECONDARY_USER_1, "Secondary 1", /* flags= */ 0),
+ UserInfo(SECONDARY_USER_2, "Secondary 2", /* flags= */ 0),
),
selectedUserIndex = 2,
)
@@ -302,12 +245,7 @@
assertSelections(
observed = observed(),
expected =
- mapOf(
- KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to
- listOf(
- config2,
- ),
- )
+ mapOf(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to listOf(config2)),
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 9de0215..fe9da0d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -27,6 +27,7 @@
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.dock.DockManager
@@ -147,6 +148,7 @@
.thenReturn(FakeSharedPreferences())
},
userTracker = userTracker,
+ communalSettingsInteractor = kosmos.communalSettingsInteractor,
broadcastDispatcher = fakeBroadcastDispatcher,
)
val remoteUserSelectionManager =
@@ -196,6 +198,7 @@
biometricSettingsRepository = biometricSettingsRepository,
backgroundDispatcher = kosmos.testDispatcher,
appContext = context,
+ communalSettingsInteractor = kosmos.communalSettingsInteractor,
sceneInteractor = { kosmos.sceneInteractor },
)
kosmos.keyguardQuickAffordanceInteractor = underTest
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt
index ad5eeab..26fe379 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt
@@ -31,6 +31,7 @@
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.data.repository.sceneContainerRepository
+import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -118,6 +119,50 @@
)
}
+ /** STL: Ls -> overlay, then settle with Idle(overlay). */
+ @Test
+ fun transition_overlay_from_ls_scene_end_in_gone() =
+ testScope.runTest {
+ sceneTransitions.value =
+ ObservableTransitionState.Transition.ShowOrHideOverlay(
+ overlay = Overlays.NotificationsShade,
+ fromContent = Scenes.Lockscreen,
+ toContent = Overlays.NotificationsShade,
+ currentScene = Scenes.Lockscreen,
+ currentOverlays = flowOf(emptySet()),
+ progress,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ previewProgress = flowOf(0f),
+ isInPreviewStage = flowOf(false),
+ )
+
+ val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.RUNNING,
+ progress = 0f,
+ )
+
+ progress.value = 0.4f
+ assertTransition(step = currentStep!!, state = TransitionState.RUNNING, progress = 0.4f)
+
+ sceneTransitions.value =
+ ObservableTransitionState.Idle(
+ Scenes.Lockscreen,
+ setOf(Overlays.NotificationsShade),
+ )
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.FINISHED,
+ progress = 1f,
+ )
+ }
+
/**
* STL: Ls -> Gone, then settle with Idle(Ls). KTF in this scenario needs to invert the
* transition LS -> UNDEFINED to UNDEFINED -> LS as there is no mechanism in KTF to
@@ -259,6 +304,47 @@
)
}
+ /** STL: Ls with overlay, then settle with Idle(Ls). */
+ @Test
+ fun transition_overlay_to_ls_scene_end_in_ls() =
+ testScope.runTest {
+ val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+ sceneTransitions.value =
+ ObservableTransitionState.Transition.ShowOrHideOverlay(
+ overlay = Overlays.NotificationsShade,
+ fromContent = Overlays.NotificationsShade,
+ toContent = Scenes.Lockscreen,
+ currentScene = Scenes.Lockscreen,
+ currentOverlays = flowOf(setOf(Overlays.NotificationsShade)),
+ progress,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ previewProgress = flowOf(0f),
+ isInPreviewStage = flowOf(false),
+ )
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.RUNNING,
+ progress = 0f,
+ )
+
+ progress.value = 0.4f
+ assertTransition(step = currentStep!!, state = TransitionState.RUNNING, progress = 0.4f)
+
+ sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Lockscreen)
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.FINISHED,
+ progress = 1f,
+ )
+ }
+
/** STL: Gone -> Ls (AOD), will transition to AOD once */
@Test
fun transition_to_ls_scene_with_changed_next_scene_is_respected_just_once() =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt
index 056efb3..c47a412 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt
@@ -117,7 +117,6 @@
"test_spec:\n" +
" QSTileState(" +
"icon=Resource(res=0, contentDescription=Resource(res=0)), " +
- "iconRes=null, " +
"label=test_data, " +
"activationState=INACTIVE, " +
"secondaryLabel=null, " +
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt
index 00460bf..557f4ea 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt
@@ -93,8 +93,7 @@
): QSTileState {
val label = context.getString(R.string.airplane_mode)
return QSTileState(
- Icon.Loaded(context.getDrawable(iconRes)!!, null),
- iconRes,
+ Icon.Loaded(context.getDrawable(iconRes)!!, null, iconRes),
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
index 632aae0..24e4668 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
@@ -178,8 +178,7 @@
): QSTileState {
val label = context.getString(R.string.status_bar_alarm)
return QSTileState(
- Icon.Loaded(context.getDrawable(R.drawable.ic_alarm)!!, null),
- R.drawable.ic_alarm,
+ Icon.Loaded(context.getDrawable(R.drawable.ic_alarm)!!, null, R.drawable.ic_alarm),
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt
index 5385f94..2ddaddd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt
@@ -253,8 +253,7 @@
): QSTileState {
val label = context.getString(R.string.battery_detail_switch_title)
return QSTileState(
- Icon.Loaded(context.getDrawable(iconRes)!!, null),
- iconRes,
+ Icon.Loaded(context.getDrawable(iconRes)!!, null, iconRes),
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt
index 356b98e..a3c159820 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt
@@ -77,8 +77,11 @@
): QSTileState {
val label = context.getString(R.string.quick_settings_color_correction_label)
return QSTileState(
- Icon.Loaded(context.getDrawable(R.drawable.ic_qs_color_correction)!!, null),
- R.drawable.ic_qs_color_correction,
+ Icon.Loaded(
+ context.getDrawable(R.drawable.ic_qs_color_correction)!!,
+ null,
+ R.drawable.ic_qs_color_correction,
+ ),
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt
index 8236c4c..608adf1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt
@@ -253,7 +253,6 @@
): QSTileState {
return QSTileState(
icon?.let { com.android.systemui.common.shared.model.Icon.Loaded(icon, null) },
- null,
"test label",
activationState,
"test subtitle",
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
index 587585c..a115c12 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
@@ -73,7 +73,11 @@
mapper.map(qsTileConfig, FlashlightTileModel.FlashlightAvailable(true))
val expectedIcon =
- Icon.Loaded(context.getDrawable(R.drawable.qs_flashlight_icon_on)!!, null)
+ Icon.Loaded(
+ context.getDrawable(R.drawable.qs_flashlight_icon_on)!!,
+ null,
+ R.drawable.qs_flashlight_icon_on,
+ )
val actualIcon = tileState.icon
assertThat(actualIcon).isEqualTo(expectedIcon)
}
@@ -84,7 +88,11 @@
mapper.map(qsTileConfig, FlashlightTileModel.FlashlightAvailable(false))
val expectedIcon =
- Icon.Loaded(context.getDrawable(R.drawable.qs_flashlight_icon_off)!!, null)
+ Icon.Loaded(
+ context.getDrawable(R.drawable.qs_flashlight_icon_off)!!,
+ null,
+ R.drawable.qs_flashlight_icon_off,
+ )
val actualIcon = tileState.icon
assertThat(actualIcon).isEqualTo(expectedIcon)
}
@@ -95,7 +103,11 @@
mapper.map(qsTileConfig, FlashlightTileModel.FlashlightTemporarilyUnavailable)
val expectedIcon =
- Icon.Loaded(context.getDrawable(R.drawable.qs_flashlight_icon_off)!!, null)
+ Icon.Loaded(
+ context.getDrawable(R.drawable.qs_flashlight_icon_off)!!,
+ null,
+ R.drawable.qs_flashlight_icon_off,
+ )
val actualIcon = tileState.icon
assertThat(actualIcon).isEqualTo(expectedIcon)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt
index e81771e..8f8f710 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt
@@ -58,8 +58,11 @@
private fun createFontScalingTileState(): QSTileState =
QSTileState(
- Icon.Loaded(context.getDrawable(R.drawable.ic_qs_font_scaling)!!, null),
- R.drawable.ic_qs_font_scaling,
+ Icon.Loaded(
+ context.getDrawable(R.drawable.ic_qs_font_scaling)!!,
+ null,
+ R.drawable.ic_qs_font_scaling,
+ ),
context.getString(R.string.quick_settings_font_scaling_label),
QSTileState.ActivationState.ACTIVE,
null,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt
index 12d604f..3d3447d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt
@@ -102,8 +102,7 @@
val label = context.getString(R.string.quick_settings_hearing_devices_label)
val iconRes = R.drawable.qs_hearing_devices_icon
return QSTileState(
- Icon.Loaded(context.getDrawable(iconRes)!!, null),
- iconRes,
+ Icon.Loaded(context.getDrawable(iconRes)!!, null, iconRes),
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
index 9dcf49e..b087bbc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
@@ -82,7 +82,6 @@
QSTileState.ActivationState.ACTIVE,
context.getString(R.string.quick_settings_networks_available),
Icon.Loaded(signalDrawable, null),
- null,
context.getString(R.string.quick_settings_internet_label),
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
@@ -120,8 +119,11 @@
createInternetTileState(
QSTileState.ActivationState.ACTIVE,
inputModel.secondaryLabel.loadText(context).toString(),
- Icon.Loaded(context.getDrawable(expectedSatIcon!!.res)!!, null),
- expectedSatIcon.res,
+ Icon.Loaded(
+ context.getDrawable(expectedSatIcon!!.res)!!,
+ null,
+ expectedSatIcon.res,
+ ),
expectedSatIcon.contentDescription.loadContentDescription(context).toString(),
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
@@ -144,8 +146,7 @@
createInternetTileState(
QSTileState.ActivationState.ACTIVE,
context.getString(R.string.quick_settings_networks_available),
- Icon.Loaded(context.getDrawable(wifiRes)!!, contentDescription = null),
- wifiRes,
+ Icon.Loaded(context.getDrawable(wifiRes)!!, null, wifiRes),
context.getString(R.string.quick_settings_internet_label),
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
@@ -171,8 +172,8 @@
Icon.Loaded(
context.getDrawable(R.drawable.ic_qs_no_internet_unavailable)!!,
contentDescription = null,
+ R.drawable.ic_qs_no_internet_unavailable,
),
- R.drawable.ic_qs_no_internet_unavailable,
context.getString(R.string.quick_settings_networks_unavailable),
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
@@ -182,13 +183,11 @@
activationState: QSTileState.ActivationState,
secondaryLabel: String,
icon: Icon,
- iconRes: Int? = null,
contentDescription: String,
): QSTileState {
val label = context.getString(R.string.quick_settings_internet_label)
return QSTileState(
icon,
- iconRes,
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt
index 30fce73..780d58c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt
@@ -90,8 +90,7 @@
): QSTileState {
val label = context.getString(R.string.quick_settings_inversion_label)
return QSTileState(
- Icon.Loaded(context.getDrawable(iconRes)!!, null),
- iconRes,
+ Icon.Loaded(context.getDrawable(iconRes)!!, null, iconRes),
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
index 37e8a60..4ebe23dc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
@@ -69,7 +69,12 @@
fun mapsEnabledDataToOnIconState() {
val tileState: QSTileState = mapper.map(qsTileConfig, LocationTileModel(true))
- val expectedIcon = Icon.Loaded(context.getDrawable(R.drawable.qs_location_icon_on)!!, null)
+ val expectedIcon =
+ Icon.Loaded(
+ context.getDrawable(R.drawable.qs_location_icon_on)!!,
+ null,
+ R.drawable.qs_location_icon_on,
+ )
val actualIcon = tileState.icon
Truth.assertThat(actualIcon).isEqualTo(expectedIcon)
}
@@ -78,7 +83,12 @@
fun mapsDisabledDataToOffIconState() {
val tileState: QSTileState = mapper.map(qsTileConfig, LocationTileModel(false))
- val expectedIcon = Icon.Loaded(context.getDrawable(R.drawable.qs_location_icon_off)!!, null)
+ val expectedIcon =
+ Icon.Loaded(
+ context.getDrawable(R.drawable.qs_location_icon_off)!!,
+ null,
+ R.drawable.qs_location_icon_off,
+ )
val actualIcon = tileState.icon
Truth.assertThat(actualIcon).isEqualTo(expectedIcon)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
index d16342b..44e6b4d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
@@ -146,13 +146,13 @@
// Tile starts with the generic Modes icon.
runCurrent()
assertThat(tileData?.icon).isEqualTo(MODES_ICON)
- assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID)
+ assertThat(tileData?.icon!!.res).isEqualTo(MODES_DRAWABLE_ID)
// Add an inactive mode -> Still modes icon
zenModeRepository.addMode(id = "Mode", active = false)
runCurrent()
assertThat(tileData?.icon).isEqualTo(MODES_ICON)
- assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID)
+ assertThat(tileData?.icon!!.res).isEqualTo(MODES_DRAWABLE_ID)
// Add an active mode with a default icon: icon should be the mode icon, and the
// iconResId is also populated, because we know it's a system icon.
@@ -163,7 +163,7 @@
)
runCurrent()
assertThat(tileData?.icon).isEqualTo(BEDTIME_ICON)
- assertThat(tileData?.iconResId).isEqualTo(R.drawable.ic_zen_mode_type_bedtime)
+ assertThat(tileData?.icon!!.res).isEqualTo(BEDTIME_DRAWABLE_ID)
// Add another, less-prioritized mode that has a *custom* icon: for now, icon should
// remain the first mode icon
@@ -178,20 +178,20 @@
)
runCurrent()
assertThat(tileData?.icon).isEqualTo(BEDTIME_ICON)
- assertThat(tileData?.iconResId).isEqualTo(R.drawable.ic_zen_mode_type_bedtime)
+ assertThat(tileData?.icon!!.res).isEqualTo(BEDTIME_DRAWABLE_ID)
// Deactivate more important mode: icon should be the less important, still active mode
// And because it's a package-provided icon, iconResId is not populated.
zenModeRepository.deactivateMode("Bedtime with default icon")
runCurrent()
assertThat(tileData?.icon).isEqualTo(CUSTOM_ICON)
- assertThat(tileData?.iconResId).isNull()
+ assertThat(tileData?.icon!!.res).isNull()
// Deactivate remaining mode: back to the default modes icon
zenModeRepository.deactivateMode("Driving with custom icon")
runCurrent()
assertThat(tileData?.icon).isEqualTo(MODES_ICON)
- assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID)
+ assertThat(tileData?.icon!!.res).isEqualTo(MODES_DRAWABLE_ID)
}
@Test
@@ -206,18 +206,18 @@
runCurrent()
assertThat(tileData?.icon).isEqualTo(MODES_ICON)
- assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID)
+ assertThat(tileData?.icon!!.res).isEqualTo(MODES_DRAWABLE_ID)
// Activate a Mode -> Icon doesn't change.
zenModeRepository.addMode(id = "Mode", active = true)
runCurrent()
assertThat(tileData?.icon).isEqualTo(MODES_ICON)
- assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID)
+ assertThat(tileData?.icon!!.res).isEqualTo(MODES_DRAWABLE_ID)
zenModeRepository.deactivateMode(id = "Mode")
runCurrent()
assertThat(tileData?.icon).isEqualTo(MODES_ICON)
- assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID)
+ assertThat(tileData?.icon!!.res).isEqualTo(MODES_DRAWABLE_ID)
}
@EnableFlags(Flags.FLAG_MODES_UI)
@@ -257,10 +257,10 @@
val TEST_USER = UserHandle.of(1)!!
const val CUSTOM_PACKAGE = "com.some.mode.owner.package"
- val MODES_DRAWABLE_ID = R.drawable.ic_zen_priority_modes
+ const val MODES_DRAWABLE_ID = R.drawable.ic_zen_priority_modes
const val CUSTOM_DRAWABLE_ID = 12345
- val BEDTIME_DRAWABLE_ID = R.drawable.ic_zen_mode_type_bedtime
+ const val BEDTIME_DRAWABLE_ID = R.drawable.ic_zen_mode_type_bedtime
val MODES_DRAWABLE = TestStubDrawable("modes_icon")
val BEDTIME_DRAWABLE = TestStubDrawable("bedtime")
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt
index 88b0046..04e094f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt
@@ -156,6 +156,10 @@
}
private fun modelOf(isActivated: Boolean, activeModeNames: List<String>): ModesTileModel {
- return ModesTileModel(isActivated, activeModeNames, TestStubDrawable("icon").asIcon(), 123)
+ return ModesTileModel(
+ isActivated,
+ activeModeNames,
+ TestStubDrawable("icon").asIcon(res = 123),
+ )
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt
index 4e91d16..d73044f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt
@@ -99,18 +99,11 @@
@Test
fun state_modelHasIconResId_includesIconResId() {
- val icon = TestStubDrawable("res123").asIcon()
- val model =
- ModesTileModel(
- isActivated = false,
- activeModes = emptyList(),
- icon = icon,
- iconResId = 123,
- )
+ val icon = TestStubDrawable("res123").asIcon(res = 123)
+ val model = ModesTileModel(isActivated = false, activeModes = emptyList(), icon = icon)
val state = underTest.map(config, model)
assertThat(state.icon).isEqualTo(icon)
- assertThat(state.iconRes).isEqualTo(123)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt
index 1457f53..7c85326 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt
@@ -289,8 +289,7 @@
if (TextUtils.isEmpty(secondaryLabel)) label
else TextUtils.concat(label, ", ", secondaryLabel)
return QSTileState(
- Icon.Loaded(context.getDrawable(iconRes)!!, null),
- iconRes,
+ Icon.Loaded(context.getDrawable(iconRes)!!, null, iconRes),
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt
index 2ac3e08..b6caa22 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt
@@ -58,8 +58,11 @@
private fun createNotesTileState(): QSTileState =
QSTileState(
- Icon.Loaded(context.getDrawable(R.drawable.ic_qs_notes)!!, null),
- R.drawable.ic_qs_notes,
+ Icon.Loaded(
+ context.getDrawable(R.drawable.ic_qs_notes)!!,
+ null,
+ R.drawable.ic_qs_notes,
+ ),
context.getString(R.string.quick_settings_notes_label),
QSTileState.ActivationState.INACTIVE,
/* secondaryLabel= */ null,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt
index 7782d2b..5b39810 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt
@@ -66,11 +66,7 @@
val outputState = mapper.map(config, inputModel)
val expectedState =
- createOneHandedModeTileState(
- QSTileState.ActivationState.INACTIVE,
- subtitleArray[1],
- com.android.internal.R.drawable.ic_qs_one_handed_mode,
- )
+ createOneHandedModeTileState(QSTileState.ActivationState.INACTIVE, subtitleArray[1])
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -81,23 +77,21 @@
val outputState = mapper.map(config, inputModel)
val expectedState =
- createOneHandedModeTileState(
- QSTileState.ActivationState.ACTIVE,
- subtitleArray[2],
- com.android.internal.R.drawable.ic_qs_one_handed_mode,
- )
+ createOneHandedModeTileState(QSTileState.ActivationState.ACTIVE, subtitleArray[2])
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
private fun createOneHandedModeTileState(
activationState: QSTileState.ActivationState,
secondaryLabel: String,
- iconRes: Int,
): QSTileState {
val label = context.getString(R.string.quick_settings_onehanded_label)
return QSTileState(
- Icon.Loaded(context.getDrawable(iconRes)!!, null),
- iconRes,
+ Icon.Loaded(
+ context.getDrawable(com.android.internal.R.drawable.ic_qs_one_handed_mode)!!,
+ null,
+ com.android.internal.R.drawable.ic_qs_one_handed_mode,
+ ),
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt
index ed33250..c572ff6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt
@@ -93,8 +93,8 @@
Icon.Loaded(
context.getDrawable(com.android.systemui.res.R.drawable.ic_qr_code_scanner)!!,
null,
+ com.android.systemui.res.R.drawable.ic_qr_code_scanner,
),
- com.android.systemui.res.R.drawable.ic_qr_code_scanner,
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt
index 85111fd..00017f9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt
@@ -85,8 +85,7 @@
R.drawable.qs_extra_dim_icon_on
else R.drawable.qs_extra_dim_icon_off
return QSTileState(
- Icon.Loaded(context.getDrawable(iconRes)!!, null),
- iconRes,
+ Icon.Loaded(context.getDrawable(iconRes)!!, null, iconRes),
label,
activationState,
context.resources
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
index 53671ba..7401014 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
@@ -180,8 +180,7 @@
): QSTileState {
val label = context.getString(R.string.quick_settings_rotation_unlocked_label)
return QSTileState(
- Icon.Loaded(context.getDrawable(iconRes)!!, null),
- iconRes,
+ Icon.Loaded(context.getDrawable(iconRes)!!, null, iconRes),
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
index 9a45065..1fb5048 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
@@ -91,8 +91,7 @@
else context.resources.getStringArray(R.array.tile_states_saver)[0]
return QSTileState(
- Icon.Loaded(context.getDrawable(iconRes)!!, null),
- iconRes,
+ Icon.Loaded(context.getDrawable(iconRes)!!, null, iconRes),
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt
index cd683c4..3632556 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt
@@ -110,8 +110,7 @@
val label = context.getString(R.string.quick_settings_screen_record_label)
return QSTileState(
- Icon.Loaded(context.getDrawable(iconRes)!!, null),
- iconRes,
+ Icon.Loaded(context.getDrawable(iconRes)!!, null, iconRes),
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt
index c569403..e4cd0e0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt
@@ -146,8 +146,7 @@
else context.getString(R.string.quick_settings_mic_label)
return QSTileState(
- Icon.Loaded(context.getDrawable(iconRes)!!, null),
- iconRes,
+ Icon.Loaded(context.getDrawable(iconRes)!!, null, iconRes),
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
index 0d2ebe4..8f5f2d3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
@@ -69,8 +69,7 @@
expandedAccessibilityClass: KClass<out View>? = Switch::class,
): QSTileState {
return QSTileState(
- Icon.Loaded(context.getDrawable(iconRes)!!, null),
- iconRes,
+ Icon.Loaded(context.getDrawable(iconRes)!!, null, iconRes),
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt
index 86321ea..2c81f39 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt
@@ -109,8 +109,7 @@
val label = testLabel
val iconRes = com.android.internal.R.drawable.stat_sys_managed_profile_status
return QSTileState(
- icon = Icon.Loaded(context.getDrawable(iconRes)!!, null),
- iconRes = iconRes,
+ icon = Icon.Loaded(context.getDrawable(iconRes)!!, null, iconRes),
label = label,
activationState = activationState,
secondaryLabel =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index d3b5828..07a408b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -45,7 +45,6 @@
import android.content.ContentResolver;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.database.ContentObserver;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
@@ -57,7 +56,6 @@
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.ViewPropertyAnimator;
-import android.view.ViewStub;
import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityManager;
@@ -74,10 +72,8 @@
import com.android.keyguard.KeyguardStatusView;
import com.android.keyguard.KeyguardStatusViewController;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent;
import com.android.keyguard.dagger.KeyguardStatusBarViewComponent;
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
-import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
import com.android.keyguard.logging.KeyguardLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
@@ -147,12 +143,13 @@
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.ConversationNotificationManager;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinatorLogger;
import com.android.systemui.statusbar.notification.data.repository.NotificationsKeyguardViewStateRepository;
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor;
import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -172,17 +169,13 @@
import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.ShadeTouchableRegionManager;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.TapAgainViewController;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
-import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController;
-import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
import com.android.systemui.statusbar.policy.SplitShadeStateController;
import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository;
@@ -227,8 +220,6 @@
@Mock protected HeadsUpManager mHeadsUpManager;
@Mock protected NotificationGutsManager mGutsManager;
@Mock protected KeyguardStatusBarView mKeyguardStatusBar;
- @Mock protected KeyguardUserSwitcherView mUserSwitcherView;
- @Mock protected ViewStub mUserSwitcherStubView;
@Mock protected HeadsUpTouchHelper.Callback mHeadsUpCallback;
@Mock protected KeyguardUpdateMonitor mUpdateMonitor;
@Mock protected KeyguardBypassController mKeyguardBypassController;
@@ -254,12 +245,6 @@
@Mock protected ConversationNotificationManager mConversationNotificationManager;
@Mock protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Mock protected KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
- @Mock protected KeyguardQsUserSwitchComponent.Factory mKeyguardQsUserSwitchComponentFactory;
- @Mock protected KeyguardQsUserSwitchComponent mKeyguardQsUserSwitchComponent;
- @Mock protected KeyguardQsUserSwitchController mKeyguardQsUserSwitchController;
- @Mock protected KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponentFactory;
- @Mock protected KeyguardUserSwitcherComponent mKeyguardUserSwitcherComponent;
- @Mock protected KeyguardUserSwitcherController mKeyguardUserSwitcherController;
@Mock protected KeyguardStatusViewComponent mKeyguardStatusViewComponent;
@Mock protected KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory;
@Mock protected KeyguardStatusBarViewComponent mKeyguardStatusBarViewComponent;
@@ -477,9 +462,6 @@
.thenReturn(SPLIT_SHADE_FULL_TRANSITION_DISTANCE);
when(mView.getContext()).thenReturn(getContext());
when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar);
- when(mView.findViewById(R.id.keyguard_user_switcher_view)).thenReturn(mUserSwitcherView);
- when(mView.findViewById(R.id.keyguard_user_switcher_stub)).thenReturn(
- mUserSwitcherStubView);
when(mView.findViewById(R.id.keyguard_clock_container)).thenReturn(mKeyguardClockSwitch);
when(mView.findViewById(R.id.notification_stack_scroller))
.thenReturn(mNotificationStackScrollLayout);
@@ -511,14 +493,6 @@
when(mFragmentService.getFragmentHostManager(mView)).thenReturn(mFragmentHostManager);
FlingAnimationUtils.Builder flingAnimationUtilsBuilder = new FlingAnimationUtils.Builder(
mDisplayMetrics);
- when(mKeyguardQsUserSwitchComponentFactory.build(any()))
- .thenReturn(mKeyguardQsUserSwitchComponent);
- when(mKeyguardQsUserSwitchComponent.getKeyguardQsUserSwitchController())
- .thenReturn(mKeyguardQsUserSwitchController);
- when(mKeyguardUserSwitcherComponentFactory.build(any()))
- .thenReturn(mKeyguardUserSwitcherComponent);
- when(mKeyguardUserSwitcherComponent.getKeyguardUserSwitcherController())
- .thenReturn(mKeyguardUserSwitcherController);
when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(true);
when(mQs.getView()).thenReturn(mView);
when(mQSFragment.getView()).thenReturn(mView);
@@ -627,8 +601,6 @@
.thenReturn(mKeyguardStatusBarViewController);
when(mLayoutInflater.inflate(eq(R.layout.keyguard_status_view), any(), anyBoolean()))
.thenReturn(keyguardStatusView);
- when(mLayoutInflater.inflate(eq(R.layout.keyguard_user_switcher), any(), anyBoolean()))
- .thenReturn(mUserSwitcherView);
when(mNotificationRemoteInputManager.isRemoteInputActive())
.thenReturn(false);
doAnswer(invocation -> {
@@ -690,8 +662,6 @@
mNotificationsQSContainerController,
mNotificationStackScrollLayoutController,
mKeyguardStatusViewComponentFactory,
- mKeyguardQsUserSwitchComponentFactory,
- mKeyguardUserSwitcherComponentFactory,
mKeyguardStatusBarViewComponentFactory,
mLockscreenShadeTransitionController,
mAuthController,
@@ -883,16 +853,6 @@
mNotificationPanelViewController.updateResources();
}
- protected void updateMultiUserSetting(boolean enabled) {
- when(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)).thenReturn(false);
- when(mUserManager.isUserSwitcherEnabled(false)).thenReturn(enabled);
- final ArgumentCaptor<ContentObserver> observerCaptor =
- ArgumentCaptor.forClass(ContentObserver.class);
- verify(mContentResolver)
- .registerContentObserver(any(), anyBoolean(), observerCaptor.capture());
- observerCaptor.getValue().onChange(/* selfChange */ false);
- }
-
protected void updateSmallestScreenWidth(int smallestScreenWidthDp) {
Configuration configuration = new Configuration();
configuration.smallestScreenWidthDp = smallestScreenWidthDp;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 550fcf7..51f00a0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -16,59 +16,28 @@
package com.android.systemui.shade;
-import static com.android.keyguard.KeyguardClockSwitch.LARGE;
-import static com.android.keyguard.KeyguardClockSwitch.SMALL;
-import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED;
-import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_OPEN;
-import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_OPENING;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
-import static com.android.systemui.statusbar.StatusBarState.SHADE;
-import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyFloat;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.animation.Animator;
-import android.animation.ValueAnimator;
-import android.graphics.Point;
-import android.os.PowerManager;
-import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
-import android.view.accessibility.AccessibilityNodeInfo;
-import androidx.constraintlayout.widget.ConstraintSet;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.DejankUtils;
import com.android.systemui.flags.DisableSceneContainer;
-import com.android.systemui.flags.Flags;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.power.domain.interactor.PowerInteractor;
-import com.android.systemui.res.R;
-import com.android.systemui.statusbar.notification.row.ExpandableView;
-import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener;
-import com.android.systemui.statusbar.notification.stack.AmbientState;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
-import com.android.systemui.statusbar.phone.KeyguardClockPositionAlgorithm;
import com.google.android.msdl.data.model.MSDLToken;
@@ -76,10 +45,6 @@
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.InOrder;
-
-import java.util.List;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -91,83 +56,6 @@
DejankUtils.setImmediate(true);
}
- /**
- * When the Back gesture starts (progress 0%), the scrim will stay at 100% scale (1.0f).
- */
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testBackGesture_min_scrimAtMaxScale() {
- mNotificationPanelViewController.onBackProgressed(0.0f);
- verify(mScrimController).applyBackScaling(1.0f);
- }
-
- /**
- * When the Back gesture is at max (progress 100%), the scrim will be scaled to its minimum.
- */
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testBackGesture_max_scrimAtMinScale() {
- mNotificationPanelViewController.onBackProgressed(1.0f);
- verify(mScrimController).applyBackScaling(
- NotificationPanelViewController.SHADE_BACK_ANIM_MIN_SCALE);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void onNotificationHeightChangeWhileOnKeyguardWillComputeMaxKeyguardNotifications() {
- mStatusBarStateController.setState(KEYGUARD);
- ArgumentCaptor<OnHeightChangedListener> captor =
- ArgumentCaptor.forClass(OnHeightChangedListener.class);
- verify(mNotificationStackScrollLayoutController)
- .setOnHeightChangedListener(captor.capture());
- OnHeightChangedListener listener = captor.getValue();
-
- clearInvocations(mNotificationStackSizeCalculator);
- listener.onHeightChanged(mock(ExpandableView.class), false);
-
- verify(mNotificationStackSizeCalculator)
- .computeMaxKeyguardNotifications(any(), anyFloat(), anyFloat(), anyFloat());
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void onNotificationHeightChangeWhileInShadeWillNotComputeMaxKeyguardNotifications() {
- mStatusBarStateController.setState(SHADE);
- ArgumentCaptor<OnHeightChangedListener> captor =
- ArgumentCaptor.forClass(OnHeightChangedListener.class);
- verify(mNotificationStackScrollLayoutController)
- .setOnHeightChangedListener(captor.capture());
- OnHeightChangedListener listener = captor.getValue();
-
- clearInvocations(mNotificationStackSizeCalculator);
- listener.onHeightChanged(mock(ExpandableView.class), false);
-
- verify(mNotificationStackSizeCalculator, never())
- .computeMaxKeyguardNotifications(any(), anyFloat(), anyFloat(), anyFloat());
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void computeMaxKeyguardNotifications_lockscreenToShade_returnsExistingMax() {
- when(mAmbientState.getFractionToShade()).thenReturn(0.5f);
- mNotificationPanelViewController.setMaxDisplayedNotifications(-1);
-
- // computeMaxKeyguardNotifications sets maxAllowed to 0 at minimum if it updates the value
- assertThat(mNotificationPanelViewController.computeMaxKeyguardNotifications())
- .isEqualTo(-1);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void computeMaxKeyguardNotifications_noTransition_updatesMax() {
- when(mAmbientState.getFractionToShade()).thenReturn(0f);
- mNotificationPanelViewController.setMaxDisplayedNotifications(-1);
-
- // computeMaxKeyguardNotifications sets maxAllowed to 0 at minimum if it updates the value
- assertThat(mNotificationPanelViewController.computeMaxKeyguardNotifications())
- .isNotEqualTo(-1);
- }
-
@Test
@Ignore("b/261472011 - Test appears inconsistent across environments")
public void getVerticalSpaceForLockscreenNotifications_useLockIconBottomPadding_returnsSpaceAvailable() {
@@ -205,165 +93,6 @@
}
@Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void getVerticalSpaceForLockscreenShelf_useLockIconBottomPadding_returnsShelfHeight() {
- enableSplitShade(/* enabled= */ false);
- setBottomPadding(/* stackScrollLayoutBottom= */ 100,
- /* lockIconPadding= */ 20,
- /* indicationPadding= */ 0,
- /* ambientPadding= */ 0);
-
- when(mNotificationStackScrollLayoutController.getShelfHeight()).thenReturn(5);
- assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
- .isEqualTo(5);
-
- assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
- .isEqualTo(5);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void getVerticalSpaceForLockscreenShelf_useIndicationBottomPadding_returnsZero() {
- enableSplitShade(/* enabled= */ false);
- setBottomPadding(/* stackScrollLayoutBottom= */ 100,
- /* lockIconPadding= */ 0,
- /* indicationPadding= */ 30,
- /* ambientPadding= */ 0);
-
- when(mNotificationStackScrollLayoutController.getShelfHeight()).thenReturn(5);
- assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
- .isEqualTo(0);
-
- assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
- .isEqualTo(0);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void getVerticalSpaceForLockscreenShelf_useAmbientBottomPadding_returnsZero() {
- enableSplitShade(/* enabled= */ false);
- setBottomPadding(/* stackScrollLayoutBottom= */ 100,
- /* lockIconPadding= */ 0,
- /* indicationPadding= */ 0,
- /* ambientPadding= */ 40);
-
- when(mNotificationStackScrollLayoutController.getShelfHeight()).thenReturn(5);
- assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
- .isEqualTo(0);
-
- assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
- .isEqualTo(0);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void getVerticalSpaceForLockscreenShelf_useLockIconPadding_returnsLessThanShelfHeight() {
- enableSplitShade(/* enabled= */ false);
- setBottomPadding(/* stackScrollLayoutBottom= */ 100,
- /* lockIconPadding= */ 10,
- /* indicationPadding= */ 8,
- /* ambientPadding= */ 0);
-
- when(mNotificationStackScrollLayoutController.getShelfHeight()).thenReturn(5);
- assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
- .isEqualTo(2);
-
- assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
- .isEqualTo(2);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void getVerticalSpaceForLockscreenShelf_splitShade() {
- enableSplitShade(/* enabled= */ true);
- setBottomPadding(/* stackScrollLayoutBottom= */ 100,
- /* lockIconPadding= */ 10,
- /* indicationPadding= */ 8,
- /* ambientPadding= */ 0);
-
- when(mNotificationStackScrollLayoutController.getShelfHeight()).thenReturn(5);
- assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
- .isEqualTo(0);
-
- assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
- .isEqualTo(0);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testSetPanelScrimMinFractionWhenHeadsUpIsDragged() {
- mNotificationPanelViewController.setHeadsUpDraggingStartingHeight(
- mNotificationPanelViewController.getMaxPanelHeight() / 2);
- verify(mNotificationShadeDepthController).setPanelPullDownMinFraction(eq(0.5f));
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testSetDozing_notifiesNsslAndStateController() {
- mNotificationPanelViewController.setDozing(true /* dozing */, false /* animate */);
- verify(mNotificationStackScrollLayoutController).setDozing(eq(true), eq(false));
- assertThat(mStatusBarStateController.getDozeAmount()).isEqualTo(1f);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testOnDozeAmountChanged_positionClockAndNotificationsUsesUdfpsLocation() {
- // GIVEN UDFPS is enrolled and we're on the keyguard
- final Point udfpsLocationCenter = new Point(0, 100);
- final float udfpsRadius = 10f;
- when(mUpdateMonitor.isUdfpsEnrolled()).thenReturn(true);
- when(mAuthController.getUdfpsLocation()).thenReturn(udfpsLocationCenter);
- when(mAuthController.getUdfpsRadius()).thenReturn(udfpsRadius);
- mNotificationPanelViewController.getStatusBarStateListener().onStateChanged(KEYGUARD);
-
- // WHEN the doze amount changes
- mNotificationPanelViewController.mClockPositionAlgorithm = mock(
- KeyguardClockPositionAlgorithm.class);
- mNotificationPanelViewController.getStatusBarStateListener().onDozeAmountChanged(1f, 1f);
-
- // THEN the clock positions accounts for the UDFPS location & its worst case burn in
- final float udfpsTop = udfpsLocationCenter.y - udfpsRadius - mMaxUdfpsBurnInOffsetY;
- verify(mNotificationPanelViewController.mClockPositionAlgorithm).setup(
- anyInt(),
- anyFloat(),
- anyInt(),
- anyInt(),
- anyInt(),
- /* darkAmount */ eq(1f),
- anyFloat(),
- anyBoolean(),
- anyInt(),
- anyFloat(),
- anyInt(),
- anyBoolean(),
- /* udfpsTop */ eq(udfpsTop),
- anyFloat(),
- anyBoolean()
- );
- }
-
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testSetExpandedHeight() {
- mNotificationPanelViewController.setExpandedHeight(200);
- assertThat((int) mNotificationPanelViewController.getExpandedHeight()).isEqualTo(200);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testOnTouchEvent_expansionCanBeBlocked() {
- onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0));
- onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 200f, 0));
- assertThat((int) mNotificationPanelViewController.getExpandedHeight()).isEqualTo(200);
-
- mNotificationPanelViewController.blockExpansionForCurrentTouch();
- onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 300f, 0));
- // Expansion should not have changed because it was blocked
- assertThat((int) mNotificationPanelViewController.getExpandedHeight()).isEqualTo(200);
- }
-
- @Test
@EnableFlags(com.android.systemui.Flags.FLAG_SHADE_EXPANDS_ON_STATUS_BAR_LONG_PRESS)
public void onStatusBarLongPress_shadeExpands() {
long downTime = 42L;
@@ -422,71 +151,6 @@
}
@Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void test_pulsing_onTouchEvent_noTracking() {
- // GIVEN device is pulsing
- mNotificationPanelViewController.setPulsing(true);
-
- // WHEN touch DOWN & MOVE events received
- onTouchEvent(MotionEvent.obtain(0L /* downTime */,
- 0L /* eventTime */, MotionEvent.ACTION_DOWN, 0f /* x */, 0f /* y */,
- 0 /* metaState */));
- onTouchEvent(MotionEvent.obtain(0L /* downTime */,
- 0L /* eventTime */, MotionEvent.ACTION_MOVE, 0f /* x */, 200f /* y */,
- 0 /* metaState */));
-
- // THEN touch is NOT tracked (since the device is pulsing)
- assertThat(mNotificationPanelViewController.isTracking()).isFalse();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void alternateBouncerVisible_onTouchEvent_notHandled() {
- // GIVEN alternate bouncer is visible
- when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-
- // WHEN touch DOWN event received; THEN touch is NOT handled
- assertThat(onTouchEvent(MotionEvent.obtain(0L /* downTime */,
- 0L /* eventTime */, MotionEvent.ACTION_DOWN, 0f /* x */, 0f /* y */,
- 0 /* metaState */))).isFalse();
-
- // WHEN touch MOVE event received; THEN touch is NOT handled
- assertThat(onTouchEvent(MotionEvent.obtain(0L /* downTime */,
- 0L /* eventTime */, MotionEvent.ACTION_MOVE, 0f /* x */, 200f /* y */,
- 0 /* metaState */))).isFalse();
-
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void test_onTouchEvent_startTracking() {
- // GIVEN device is NOT pulsing
- mNotificationPanelViewController.setPulsing(false);
-
- // WHEN touch DOWN & MOVE events received
- onTouchEvent(MotionEvent.obtain(0L /* downTime */,
- 0L /* eventTime */, MotionEvent.ACTION_DOWN, 0f /* x */, 0f /* y */,
- 0 /* metaState */));
- onTouchEvent(MotionEvent.obtain(0L /* downTime */,
- 0L /* eventTime */, MotionEvent.ACTION_MOVE, 0f /* x */, 200f /* y */,
- 0 /* metaState */));
-
- // THEN touch is tracked
- assertThat(mNotificationPanelViewController.isTracking()).isTrue();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void onInterceptTouchEvent_nsslMigrationOff_userActivity() {
- mTouchHandler.onInterceptTouchEvent(MotionEvent.obtain(0L /* downTime */,
- 0L /* eventTime */, MotionEvent.ACTION_DOWN, 0f /* x */, 0f /* y */,
- 0 /* metaState */));
-
- verify(mCentralSurfaces).userActivity();
- }
-
- @Test
- @EnableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
public void onInterceptTouchEvent_nsslMigrationOn_userActivity_not_called() {
mTouchHandler.onInterceptTouchEvent(MotionEvent.obtain(0L /* downTime */,
0L /* eventTime */, MotionEvent.ACTION_DOWN, 0f /* x */, 0f /* y */,
@@ -496,279 +160,6 @@
}
@Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testOnTouchEvent_expansionResumesAfterBriefTouch() {
- mFalsingManager.setIsClassifierEnabled(true);
- mFalsingManager.setIsFalseTouch(false);
- mNotificationPanelViewController.setForceFlingAnimationForTest(true);
- // Start shade collapse with swipe up
- onTouchEvent(MotionEvent.obtain(0L /* downTime */,
- 0L /* eventTime */, MotionEvent.ACTION_DOWN, 0f /* x */, 0f /* y */,
- 0 /* metaState */));
- onTouchEvent(MotionEvent.obtain(0L /* downTime */,
- 0L /* eventTime */, MotionEvent.ACTION_MOVE, 0f /* x */, 300f /* y */,
- 0 /* metaState */));
- onTouchEvent(MotionEvent.obtain(0L /* downTime */,
- 0L /* eventTime */, MotionEvent.ACTION_UP, 0f /* x */, 300f /* y */,
- 0 /* metaState */));
-
- assertThat(mNotificationPanelViewController.isClosing()).isTrue();
- assertThat(mNotificationPanelViewController.isFlinging()).isTrue();
-
- // simulate touch that does not exceed touch slop
- onTouchEvent(MotionEvent.obtain(2L /* downTime */,
- 2L /* eventTime */, MotionEvent.ACTION_DOWN, 0f /* x */, 300f /* y */,
- 0 /* metaState */));
-
- mNotificationPanelViewController.setTouchSlopExceeded(false);
-
- onTouchEvent(MotionEvent.obtain(2L /* downTime */,
- 2L /* eventTime */, MotionEvent.ACTION_UP, 0f /* x */, 300f /* y */,
- 0 /* metaState */));
-
- // fling should still be called after a touch that does not exceed touch slop
- assertThat(mNotificationPanelViewController.isClosing()).isTrue();
- assertThat(mNotificationPanelViewController.isFlinging()).isTrue();
- mNotificationPanelViewController.setForceFlingAnimationForTest(false);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testA11y_initializeNode() {
- AccessibilityNodeInfo nodeInfo = new AccessibilityNodeInfo();
- mAccessibilityDelegate.onInitializeAccessibilityNodeInfo(mView, nodeInfo);
-
- List<AccessibilityNodeInfo.AccessibilityAction> actionList = nodeInfo.getActionList();
- assertThat(actionList).containsAtLeastElementsIn(
- new AccessibilityNodeInfo.AccessibilityAction[] {
- AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD,
- AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP}
- );
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testA11y_scrollForward() {
- mAccessibilityDelegate.performAccessibilityAction(
- mView,
- AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD.getId(),
- null);
-
- verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(true);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testA11y_scrollUp() {
- mAccessibilityDelegate.performAccessibilityAction(
- mView,
- AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP.getId(),
- null);
-
- verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(true);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testKeyguardStatusViewInSplitShade_changesConstraintsDependingOnNotifications() {
- mStatusBarStateController.setState(KEYGUARD);
- enableSplitShade(/* enabled= */ true);
-
- when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
- when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
- mNotificationPanelViewController.updateResources();
- assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd)
- .isEqualTo(R.id.qs_edge_guideline);
-
- when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
- when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(false);
- mNotificationPanelViewController.updateResources();
- assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd)
- .isEqualTo(ConstraintSet.PARENT_ID);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void keyguardStatusView_splitShade_dozing_alwaysDozingOn_isCentered() {
- when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
- when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
- mStatusBarStateController.setState(KEYGUARD);
- enableSplitShade(/* enabled= */ true);
-
- setDozing(/* dozing= */ true, /* dozingAlwaysOn= */ true);
-
- assertKeyguardStatusViewCentered();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void keyguardStatusView_splitShade_dozing_alwaysDozingOff_isNotCentered() {
- when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
- when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
- mStatusBarStateController.setState(KEYGUARD);
- enableSplitShade(/* enabled= */ true);
-
- setDozing(/* dozing= */ true, /* dozingAlwaysOn= */ false);
-
- assertKeyguardStatusViewNotCentered();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void keyguardStatusView_splitShade_notDozing_alwaysDozingOn_isNotCentered() {
- when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
- when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
- mStatusBarStateController.setState(KEYGUARD);
- enableSplitShade(/* enabled= */ true);
-
- setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ true);
-
- assertKeyguardStatusViewNotCentered();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void keyguardStatusView_splitShade_pulsing_isNotCentered() {
- when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
- when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
- when(mNotificationListContainer.hasPulsingNotifications()).thenReturn(true);
- mStatusBarStateController.setState(KEYGUARD);
- enableSplitShade(/* enabled= */ true);
-
- setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ false);
-
- assertKeyguardStatusViewNotCentered();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void keyguardStatusView_splitShade_notPulsing_isNotCentered() {
- when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
- when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
- when(mNotificationListContainer.hasPulsingNotifications()).thenReturn(false);
- mStatusBarStateController.setState(KEYGUARD);
- enableSplitShade(/* enabled= */ true);
-
- setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ false);
-
- assertKeyguardStatusViewNotCentered();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void keyguardStatusView_singleShade_isCentered() {
- enableSplitShade(/* enabled= */ false);
- // The conditions below would make the clock NOT be centered on split shade.
- // On single shade it should always be centered though.
- when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
- when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
- when(mNotificationListContainer.hasPulsingNotifications()).thenReturn(false);
- mStatusBarStateController.setState(KEYGUARD);
- setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ false);
-
- assertKeyguardStatusViewCentered();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void keyguardStatusView_willPlayDelayedDoze_isCentered_thenNot() {
- when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
- when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
- mStatusBarStateController.setState(KEYGUARD);
- enableSplitShade(/* enabled= */ true);
-
- mNotificationPanelViewController.setWillPlayDelayedDozeAmountAnimation(true);
- setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ false);
- assertKeyguardStatusViewCentered();
-
- mNotificationPanelViewController.setWillPlayDelayedDozeAmountAnimation(false);
- assertKeyguardStatusViewNotCentered();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void keyguardStatusView_willPlayDelayedDoze_notifiesKeyguardMediaController() {
- when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
- when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
- mStatusBarStateController.setState(KEYGUARD);
- enableSplitShade(/* enabled= */ true);
-
- mNotificationPanelViewController.setWillPlayDelayedDozeAmountAnimation(true);
-
- verify(mKeyguardMediaController).setDozeWakeUpAnimationWaiting(true);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void keyguardStatusView_willPlayDelayedDoze_isCentered_thenStillCenteredIfNoNotifs() {
- when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
- when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(false);
- mStatusBarStateController.setState(KEYGUARD);
- enableSplitShade(/* enabled= */ true);
-
- mNotificationPanelViewController.setWillPlayDelayedDozeAmountAnimation(true);
- setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ false);
- assertKeyguardStatusViewCentered();
-
- mNotificationPanelViewController.setWillPlayDelayedDozeAmountAnimation(false);
- assertKeyguardStatusViewCentered();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void onKeyguardStatusViewHeightChange_animatesNextTopPaddingChangeForNSSL() {
- ArgumentCaptor<View.OnLayoutChangeListener> captor =
- ArgumentCaptor.forClass(View.OnLayoutChangeListener.class);
- verify(mKeyguardStatusView).addOnLayoutChangeListener(captor.capture());
- View.OnLayoutChangeListener listener = captor.getValue();
-
- clearInvocations(mNotificationStackScrollLayoutController);
-
- when(mKeyguardStatusView.getHeight()).thenReturn(0);
- listener.onLayoutChange(mKeyguardStatusView, /* left= */ 0, /* top= */ 0, /* right= */
- 0, /* bottom= */ 0, /* oldLeft= */ 0, /* oldTop= */ 0, /* oldRight= */
- 0, /* oldBottom = */ 200);
-
- verify(mNotificationStackScrollLayoutController).animateNextTopPaddingChange();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testCanCollapsePanelOnTouch_trueForKeyGuard() {
- mStatusBarStateController.setState(KEYGUARD);
-
- assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isTrue();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testCanCollapsePanelOnTouch_trueWhenScrolledToBottom() {
- mStatusBarStateController.setState(SHADE);
- when(mNotificationStackScrollLayoutController.isScrolledToBottom()).thenReturn(true);
-
- assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isTrue();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testCanCollapsePanelOnTouch_trueWhenInSettings() {
- mStatusBarStateController.setState(SHADE);
- when(mQsController.getExpanded()).thenReturn(true);
-
- assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isTrue();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testCanCollapsePanelOnTouch_falseInDualPaneShade() {
- mStatusBarStateController.setState(SHADE);
- enableSplitShade(/* enabled= */ true);
- when(mQsController.getExpanded()).thenReturn(true);
-
- assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isFalse();
- }
-
- @Test
@Ignore("b/341163515 - fails to clean up animators correctly")
public void testSwipeWhileLocked_notifiesKeyguardState() {
mStatusBarStateController.setState(KEYGUARD);
@@ -786,515 +177,6 @@
}
@Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testCancelSwipeWhileLocked_notifiesKeyguardState() {
- mStatusBarStateController.setState(KEYGUARD);
-
- // Fling expanded (cancelling the keyguard exit swipe). We should notify keyguard state that
- // the fling occurred and did not dismiss the keyguard.
- mNotificationPanelViewController.flingToHeight(
- 0f, true /* expand */, 1000f, 1f, false);
- mNotificationPanelViewController.cancelHeightAnimator();
- verify(mKeyguardStateController).notifyPanelFlingEnd();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testSwipe_exactlyToTarget_notifiesNssl() {
- // No over-expansion
- mNotificationPanelViewController.setOverExpansion(0f);
- // Fling to a target that is equal to the current position (i.e. a no-op fling).
- mNotificationPanelViewController.flingToHeight(
- 0f,
- true,
- mNotificationPanelViewController.getExpandedHeight(),
- 1f,
- false);
- // Verify that the NSSL is notified that the panel is *not* flinging.
- verify(mNotificationStackScrollLayoutController).setPanelFlinging(false);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testRotatingToSplitShadeWithQsExpanded_transitionsToShadeLocked() {
- mStatusBarStateController.setState(KEYGUARD);
- when(mQsController.getExpanded()).thenReturn(true);
-
- enableSplitShade(true);
-
- assertThat(mStatusBarStateController.getState()).isEqualTo(SHADE_LOCKED);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testUnlockedSplitShadeTransitioningToKeyguard_closesQS() {
- enableSplitShade(true);
- mStatusBarStateController.setState(SHADE);
- mStatusBarStateController.setState(KEYGUARD);
-
- verify(mQsController).closeQs();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testLockedSplitShadeTransitioningToKeyguard_closesQS() {
- enableSplitShade(true);
- mStatusBarStateController.setState(SHADE_LOCKED);
- mStatusBarStateController.setState(KEYGUARD);
-
- verify(mQsController).closeQs();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testSwitchesToCorrectClockInSinglePaneShade() {
- mStatusBarStateController.setState(KEYGUARD);
-
- when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
- when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(false);
- triggerPositionClockAndNotifications();
- verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
-
- when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
- when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
- triggerPositionClockAndNotifications();
- verify(mKeyguardStatusViewController).displayClock(SMALL, /* animate */ true);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testSwitchesToCorrectClockInSplitShade() {
- mStatusBarStateController.setState(KEYGUARD);
- enableSplitShade(/* enabled= */ true);
- clearInvocations(mKeyguardStatusViewController);
-
- when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
- when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(false);
- triggerPositionClockAndNotifications();
- verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
-
- when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
- when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
- triggerPositionClockAndNotifications();
- verify(mKeyguardStatusViewController, times(2))
- .displayClock(LARGE, /* animate */ true);
- verify(mKeyguardStatusViewController, never())
- .displayClock(SMALL, /* animate */ true);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testHasNotifications_switchesToLargeClockWhenEnteringSplitShade() {
- mStatusBarStateController.setState(KEYGUARD);
- when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
- when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
-
- enableSplitShade(/* enabled= */ true);
-
- verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testNoNotifications_switchesToLargeClockWhenEnteringSplitShade() {
- mStatusBarStateController.setState(KEYGUARD);
- when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
- when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(false);
-
- enableSplitShade(/* enabled= */ true);
-
- verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testHasNotifications_switchesToSmallClockWhenExitingSplitShade() {
- mStatusBarStateController.setState(KEYGUARD);
- enableSplitShade(/* enabled= */ true);
- clearInvocations(mKeyguardStatusViewController);
- when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
- when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
-
- enableSplitShade(/* enabled= */ false);
-
- verify(mKeyguardStatusViewController).displayClock(SMALL, /* animate */ true);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testNoNotifications_switchesToLargeClockWhenExitingSplitShade() {
- mStatusBarStateController.setState(KEYGUARD);
- enableSplitShade(/* enabled= */ true);
- clearInvocations(mKeyguardStatusViewController);
- when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
- when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(false);
-
- enableSplitShade(/* enabled= */ false);
-
- verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void clockSize_mediaShowing_inSplitShade_onAod_isLarge() {
- when(mDozeParameters.getAlwaysOn()).thenReturn(true);
- mStatusBarStateController.setState(KEYGUARD);
- enableSplitShade(/* enabled= */ true);
- when(mMediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(true);
- when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
- when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
- clearInvocations(mKeyguardStatusViewController);
-
- mNotificationPanelViewController.setDozing(/* dozing= */ true, /* animate= */ false);
-
- verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate= */ true);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void clockSize_mediaShowing_inSplitShade_screenOff_notAod_isSmall() {
- when(mDozeParameters.getAlwaysOn()).thenReturn(false);
- mStatusBarStateController.setState(KEYGUARD);
- enableSplitShade(/* enabled= */ true);
- when(mMediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(true);
- when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
- when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
- clearInvocations(mKeyguardStatusViewController);
-
- mNotificationPanelViewController.setDozing(/* dozing= */ true, /* animate= */ false);
-
- verify(mKeyguardStatusViewController).displayClock(SMALL, /* animate= */ true);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void onQsSetExpansionHeightCalled_qsFullyExpandedOnKeyguard_showNSSL() {
- // GIVEN
- mStatusBarStateController.setState(KEYGUARD);
- when(mKeyguardBypassController.getBypassEnabled()).thenReturn(false);
- when(mQsController.getFullyExpanded()).thenReturn(true);
- when(mQsController.getExpanded()).thenReturn(true);
-
- // WHEN
- int transitionDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance();
- mNotificationPanelViewController.setExpandedHeight(transitionDistance);
-
- // THEN
- // We are interested in the last value of the stack alpha.
- ArgumentCaptor<Float> alphaCaptor = ArgumentCaptor.forClass(Float.class);
- verify(mNotificationStackScrollLayoutController, atLeastOnce())
- .setMaxAlphaForKeyguard(alphaCaptor.capture(), any());
- assertThat(alphaCaptor.getValue()).isEqualTo(1.0f);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void onQsSetExpansionHeightCalled_qsFullyExpandedOnKeyguard_hideNSSL() {
- // GIVEN
- mStatusBarStateController.setState(KEYGUARD);
- when(mKeyguardBypassController.getBypassEnabled()).thenReturn(false);
- when(mQsController.getFullyExpanded()).thenReturn(false);
- when(mQsController.getExpanded()).thenReturn(true);
-
- // WHEN
- int transitionDistance = mNotificationPanelViewController
- .getMaxPanelTransitionDistance() / 2;
- mNotificationPanelViewController.setExpandedHeight(transitionDistance);
-
- // THEN
- // We are interested in the last value of the stack alpha.
- ArgumentCaptor<Float> alphaCaptor = ArgumentCaptor.forClass(Float.class);
- verify(mNotificationStackScrollLayoutController, atLeastOnce())
- .setMaxAlphaForKeyguard(alphaCaptor.capture(), any());
- assertThat(alphaCaptor.getValue()).isEqualTo(0.0f);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testSwitchesToBigClockInSplitShadeOnAodAnimateDisabled() {
- when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(false);
- mStatusBarStateController.setState(KEYGUARD);
- enableSplitShade(/* enabled= */ true);
- clearInvocations(mKeyguardStatusViewController);
- when(mMediaDataManager.hasActiveMedia()).thenReturn(true);
- when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
- when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
-
- mNotificationPanelViewController.setDozing(true, false);
-
- verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ false);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void switchesToBigClockInSplitShadeOn_landFlagOn_ForceSmallClock() {
- when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(false);
- mStatusBarStateController.setState(KEYGUARD);
- enableSplitShade(/* enabled= */ false);
- mNotificationPanelViewController.setDozing(false, false);
- mFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, true);
- when(mResources.getBoolean(R.bool.force_small_clock_on_lockscreen)).thenReturn(true);
- when(mMediaDataManager.hasActiveMedia()).thenReturn(false);
- when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
- when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(false);
- clearInvocations(mKeyguardStatusViewController);
-
- enableSplitShade(/* enabled= */ true);
- mNotificationPanelViewController.updateResources();
-
- verify(mKeyguardStatusViewController).displayClock(SMALL, /* animate */ false);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void switchesToBigClockInSplitShadeOn_landFlagOff_DontForceSmallClock() {
- when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(false);
- mStatusBarStateController.setState(KEYGUARD);
- enableSplitShade(/* enabled= */ false);
- mNotificationPanelViewController.setDozing(false, false);
- mFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false);
- when(mResources.getBoolean(R.bool.force_small_clock_on_lockscreen)).thenReturn(true);
- when(mMediaDataManager.hasActiveMedia()).thenReturn(false);
- when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
- when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(false);
- clearInvocations(mKeyguardStatusViewController);
-
- enableSplitShade(/* enabled= */ true);
- mNotificationPanelViewController.updateResources();
-
- verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ false);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testDisplaysSmallClockOnLockscreenInSplitShadeWhenMediaIsPlaying() {
- mStatusBarStateController.setState(KEYGUARD);
- enableSplitShade(/* enabled= */ true);
- clearInvocations(mKeyguardStatusViewController);
- when(mMediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(true);
-
- // one notification + media player visible
- when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
- when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
- triggerPositionClockAndNotifications();
- verify(mKeyguardStatusViewController).displayClock(SMALL, /* animate */ true);
-
- // only media player visible
- when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
- when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(false);
- triggerPositionClockAndNotifications();
- verify(mKeyguardStatusViewController, times(2)).displayClock(SMALL, true);
- verify(mKeyguardStatusViewController, never()).displayClock(LARGE, /* animate */ true);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testFoldToAodAnimationCleansupInAnimationEnd() {
- ArgumentCaptor<Animator.AnimatorListener> animCaptor =
- ArgumentCaptor.forClass(Animator.AnimatorListener.class);
- ArgumentCaptor<ValueAnimator.AnimatorUpdateListener> updateCaptor =
- ArgumentCaptor.forClass(ValueAnimator.AnimatorUpdateListener.class);
-
- // Start fold animation & Capture Listeners
- mNotificationPanelViewController.getShadeFoldAnimator()
- .startFoldToAodAnimation(() -> {}, () -> {}, () -> {});
- verify(mViewPropertyAnimator).setListener(animCaptor.capture());
- verify(mViewPropertyAnimator).setUpdateListener(updateCaptor.capture());
-
- // End animation and validate listeners were unset
- animCaptor.getValue().onAnimationEnd(null);
- verify(mViewPropertyAnimator).setListener(null);
- verify(mViewPropertyAnimator).setUpdateListener(null);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testExpandWithQsMethodIsUsingLockscreenTransitionController() {
- enableSplitShade(/* enabled= */ true);
- mStatusBarStateController.setState(KEYGUARD);
-
- mNotificationPanelViewController.expandToQs();
-
- verify(mLockscreenShadeTransitionController).goToLockedShade(
- /* expandedView= */null, /* needsQSAnimation= */true);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void setKeyguardStatusBarAlpha_setsAlphaOnKeyguardStatusBarController() {
- float statusBarAlpha = 0.5f;
-
- mNotificationPanelViewController.setKeyguardStatusBarAlpha(statusBarAlpha);
-
- verify(mKeyguardStatusBarViewController).setAlpha(statusBarAlpha);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testQsToBeImmediatelyExpandedWhenOpeningPanelInSplitShade() {
- enableSplitShade(/* enabled= */ true);
- mShadeExpansionStateManager.updateState(STATE_OPEN);
- verify(mQsController).setExpandImmediate(false);
-
- mShadeExpansionStateManager.updateState(STATE_CLOSED);
- verify(mQsController, times(2)).setExpandImmediate(false);
-
- mShadeExpansionStateManager.updateState(STATE_OPENING);
- verify(mQsController).setExpandImmediate(true);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testQsNotToBeImmediatelyExpandedWhenGoingFromUnlockedToLocked() {
- enableSplitShade(/* enabled= */ true);
- mShadeExpansionStateManager.updateState(STATE_CLOSED);
-
- mStatusBarStateController.setState(KEYGUARD);
- // going to lockscreen would trigger STATE_OPENING
- mShadeExpansionStateManager.updateState(STATE_OPENING);
-
- verify(mQsController, never()).setExpandImmediate(true);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testQsImmediateResetsWhenPanelOpensOrCloses() {
- mShadeExpansionStateManager.updateState(STATE_OPEN);
- mShadeExpansionStateManager.updateState(STATE_CLOSED);
- verify(mQsController, times(2)).setExpandImmediate(false);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testQsExpansionChangedToDefaultWhenRotatingFromOrToSplitShade() {
- when(mCommandQueue.panelsEnabled()).thenReturn(true);
-
- // to make sure shade is in expanded state
- mNotificationPanelViewController.startInputFocusTransfer();
-
- // switch to split shade from portrait (default state)
- enableSplitShade(/* enabled= */ true);
- verify(mQsController).setExpanded(true);
-
- // switch to portrait from split shade
- enableSplitShade(/* enabled= */ false);
- verify(mQsController).setExpanded(false);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void testPanelClosedWhenClosingQsInSplitShade() {
- mShadeExpansionStateManager.onPanelExpansionChanged(/* fraction= */ 1,
- /* expanded= */ true, /* tracking= */ false);
- enableSplitShade(/* enabled= */ true);
- mNotificationPanelViewController.setExpandedFraction(1f);
-
- assertThat(mNotificationPanelViewController.isClosing()).isFalse();
- mNotificationPanelViewController.animateCollapseQs(false);
-
- assertThat(mNotificationPanelViewController.isClosing()).isTrue();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void getMaxPanelTransitionDistance_expanding_inSplitShade_returnsSplitShadeFullTransitionDistance() {
- enableSplitShade(true);
- mNotificationPanelViewController.expandToQs();
-
- int maxDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance();
-
- assertThat(maxDistance).isEqualTo(SPLIT_SHADE_FULL_TRANSITION_DISTANCE);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void isExpandingOrCollapsing_returnsTrue_whenQsLockscreenDragInProgress() {
- when(mQsController.getLockscreenShadeDragProgress()).thenReturn(0.5f);
- assertThat(mNotificationPanelViewController.isExpandingOrCollapsing()).isTrue();
- }
-
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void getMaxPanelTransitionDistance_inSplitShade_withHeadsUp_returnsBiggerValue() {
- enableSplitShade(true);
- mNotificationPanelViewController.expandToQs();
- when(mHeadsUpManager.isTrackingHeadsUp()).thenReturn(true);
- when(mQsController.calculatePanelHeightExpanded(anyInt())).thenReturn(10000);
- mNotificationPanelViewController.setHeadsUpDraggingStartingHeight(
- SPLIT_SHADE_FULL_TRANSITION_DISTANCE);
-
- int maxDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance();
-
- // make sure we're ignoring the placeholder value for Qs max height
- assertThat(maxDistance).isLessThan(10000);
- assertThat(maxDistance).isGreaterThan(SPLIT_SHADE_FULL_TRANSITION_DISTANCE);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void getMaxPanelTransitionDistance_expandingSplitShade_keyguard_returnsNonSplitShadeValue() {
- mStatusBarStateController.setState(KEYGUARD);
- enableSplitShade(true);
- mNotificationPanelViewController.expandToQs();
-
- int maxDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance();
-
- assertThat(maxDistance).isNotEqualTo(SPLIT_SHADE_FULL_TRANSITION_DISTANCE);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void getMaxPanelTransitionDistance_expanding_notSplitShade_returnsNonSplitShadeValue() {
- enableSplitShade(false);
- mNotificationPanelViewController.expandToQs();
-
- int maxDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance();
-
- assertThat(maxDistance).isNotEqualTo(SPLIT_SHADE_FULL_TRANSITION_DISTANCE);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void onLayoutChange_fullWidth_updatesQSWithFullWithTrue() {
- setIsFullWidth(true);
-
- verify(mQsController).setNotificationPanelFullWidth(true);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void onLayoutChange_notFullWidth_updatesQSWithFullWithFalse() {
- setIsFullWidth(false);
-
- verify(mQsController).setNotificationPanelFullWidth(false);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void onLayoutChange_qsNotSet_doesNotCrash() {
- mQuickSettingsController.setQs(null);
-
- triggerLayoutChange();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void onEmptySpaceClicked_notDozingAndOnKeyguard_requestsFaceAuth() {
- StatusBarStateController.StateListener statusBarStateListener =
- mNotificationPanelViewController.getStatusBarStateListener();
- statusBarStateListener.onStateChanged(KEYGUARD);
- mNotificationPanelViewController.setDozing(false, false);
-
- // This sets the dozing state that is read when onMiddleClicked is eventually invoked.
- mTouchHandler.onTouch(mock(View.class), mDownMotionEvent);
- mEmptySpaceClickListenerCaptor.getValue().onEmptySpaceClicked(0, 0);
-
- verify(mDeviceEntryFaceAuthInteractor).onNotificationPanelClicked();
- }
-
- @Test
- @EnableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
public void nsslFlagEnabled_allowOnlyExternalTouches() {
// This sets the dozing state that is read when onMiddleClicked is eventually invoked.
@@ -1306,130 +188,6 @@
}
@Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void onSplitShadeChanged_duringShadeExpansion_resetsOverScrollState() {
- // There was a bug where there was left-over overscroll state after going from split shade
- // to single shade.
- // Since on single shade we don't set overscroll values on QS nor Scrim, those values that
- // were there from split shade were never reset.
- // To prevent this, we will reset all overscroll state.
- enableSplitShade(true);
- reset(mQsController, mScrimController, mNotificationStackScrollLayoutController);
-
- mNotificationPanelViewController.setOverExpansion(123);
- verify(mQsController).setOverScrollAmount(123);
- verify(mScrimController).setNotificationsOverScrollAmount(123);
- verify(mNotificationStackScrollLayoutController).setOverExpansion(123);
-
- enableSplitShade(false);
- verify(mQsController).setOverScrollAmount(0);
- verify(mScrimController).setNotificationsOverScrollAmount(0);
- verify(mNotificationStackScrollLayoutController).setOverExpansion(0);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void onSplitShadeChanged_alwaysResetsOverScrollState() {
- enableSplitShade(true);
- enableSplitShade(false);
-
- verify(mQsController, times(2)).setOverScrollAmount(0);
- verify(mScrimController, times(2)).setNotificationsOverScrollAmount(0);
- verify(mNotificationStackScrollLayoutController, times(2)).setOverExpansion(0);
- verify(mNotificationStackScrollLayoutController, times(2)).setOverScrollAmount(0);
- }
-
- /**
- * When shade is flinging to close and this fling is not intercepted,
- * {@link AmbientState#setIsClosing(boolean)} should be called before
- * {@link NotificationStackScrollLayoutController#onExpansionStopped()}
- * to ensure scrollY can be correctly set to be 0
- */
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void onShadeFlingClosingEnd_mAmbientStateSetClose_thenOnExpansionStopped() {
- // Given: Shade is expanded
- mNotificationPanelViewController.notifyExpandingFinished();
- mNotificationPanelViewController.setClosing(false);
-
- // When: Shade flings to close not canceled
- mNotificationPanelViewController.notifyExpandingStarted();
- mNotificationPanelViewController.setClosing(true);
- mNotificationPanelViewController.onFlingEnd(false);
-
- // Then: AmbientState's mIsClosing should be set to false
- // before mNotificationStackScrollLayoutController.onExpansionStopped() is called
- // to ensure NotificationStackScrollLayout.resetScrollPosition() -> resetScrollPosition
- // -> setOwnScrollY(0) can set scrollY to 0 when shade is closed
- InOrder inOrder = inOrder(mAmbientState, mNotificationStackScrollLayoutController);
- inOrder.verify(mAmbientState).setIsClosing(false);
- inOrder.verify(mNotificationStackScrollLayoutController).onExpansionStopped();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void onShadeFlingEnd_mExpandImmediateShouldBeReset() {
- mNotificationPanelViewController.onFlingEnd(false);
-
- verify(mQsController).setExpandImmediate(false);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void inUnlockedSplitShade_transitioningMaxTransitionDistance_makesShadeFullyExpanded() {
- mStatusBarStateController.setState(SHADE);
- enableSplitShade(true);
- int transitionDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance();
- mNotificationPanelViewController.setExpandedHeight(transitionDistance);
- assertThat(mNotificationPanelViewController.isFullyExpanded()).isTrue();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void shadeFullyExpanded_inShadeState() {
- mStatusBarStateController.setState(SHADE);
-
- mNotificationPanelViewController.setExpandedHeight(0);
- assertThat(mNotificationPanelViewController.isShadeFullyExpanded()).isFalse();
-
- int transitionDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance();
- mNotificationPanelViewController.setExpandedHeight(transitionDistance);
- assertThat(mNotificationPanelViewController.isShadeFullyExpanded()).isTrue();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void shadeFullyExpanded_onKeyguard() {
- mStatusBarStateController.setState(KEYGUARD);
-
- int transitionDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance();
- mNotificationPanelViewController.setExpandedHeight(transitionDistance);
- assertThat(mNotificationPanelViewController.isShadeFullyExpanded()).isFalse();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void shadeFullyExpanded_onShadeLocked() {
- mStatusBarStateController.setState(SHADE_LOCKED);
- assertThat(mNotificationPanelViewController.isShadeFullyExpanded()).isTrue();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void shadeExpanded_whenHasHeight() {
- int transitionDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance();
- mNotificationPanelViewController.setExpandedHeight(transitionDistance);
- assertThat(mNotificationPanelViewController.isExpanded()).isTrue();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void shadeExpanded_whenInstantExpanding() {
- mNotificationPanelViewController.expand(true);
- assertThat(mNotificationPanelViewController.isExpanded()).isTrue();
- }
-
- @Test
@DisableSceneContainer
public void shadeExpanded_whenHunIsPresent() {
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
@@ -1437,84 +195,6 @@
}
@Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void shadeExpanded_whenUnlockedOffscreenAnimationRunning() {
- when(mUnlockedScreenOffAnimationController.isAnimationPlaying()).thenReturn(true);
- assertThat(mNotificationPanelViewController.isExpanded()).isTrue();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void shadeExpanded_whenInputFocusTransferStarted() {
- when(mCommandQueue.panelsEnabled()).thenReturn(true);
-
- mNotificationPanelViewController.startInputFocusTransfer();
-
- assertThat(mNotificationPanelViewController.isExpanded()).isTrue();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void shadeNotExpanded_whenInputFocusTransferStartedButPanelsDisabled() {
- when(mCommandQueue.panelsEnabled()).thenReturn(false);
-
- mNotificationPanelViewController.startInputFocusTransfer();
-
- assertThat(mNotificationPanelViewController.isExpanded()).isFalse();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void cancelInputFocusTransfer_shadeCollapsed() {
- when(mCommandQueue.panelsEnabled()).thenReturn(true);
- mNotificationPanelViewController.startInputFocusTransfer();
-
- mNotificationPanelViewController.cancelInputFocusTransfer();
-
- assertThat(mNotificationPanelViewController.isExpanded()).isFalse();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void finishInputFocusTransfer_shadeFlingingOpen() {
- when(mCommandQueue.panelsEnabled()).thenReturn(true);
- mNotificationPanelViewController.startInputFocusTransfer();
-
- mNotificationPanelViewController.finishInputFocusTransfer(/* velocity= */ 0f);
-
- assertThat(mNotificationPanelViewController.isFlinging()).isTrue();
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void getFalsingThreshold_deviceNotInteractive_isQsThreshold() {
- PowerInteractor.Companion.setAsleepForTest(
- mPowerInteractor, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON);
- when(mQsController.getFalsingThreshold()).thenReturn(14);
-
- assertThat(mNotificationPanelViewController.getFalsingThreshold()).isEqualTo(14);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void getFalsingThreshold_lastWakeNotDueToTouch_isQsThreshold() {
- PowerInteractor.Companion.setAwakeForTest(
- mPowerInteractor, PowerManager.WAKE_REASON_POWER_BUTTON);
- when(mQsController.getFalsingThreshold()).thenReturn(14);
-
- assertThat(mNotificationPanelViewController.getFalsingThreshold()).isEqualTo(14);
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- public void getFalsingThreshold_lastWakeDueToTouch_greaterThanQsThreshold() {
- PowerInteractor.Companion.setAwakeForTest(mPowerInteractor, PowerManager.WAKE_REASON_TAP);
- when(mQsController.getFalsingThreshold()).thenReturn(14);
-
- assertThat(mNotificationPanelViewController.getFalsingThreshold()).isGreaterThan(14);
- }
-
- @Test
@EnableFlags(com.android.systemui.Flags.FLAG_MSDL_FEEDBACK)
public void performHapticFeedback_withMSDL_forGestureStart_deliversDragThresholdToken() {
mNotificationPanelViewController
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
deleted file mode 100644
index 5289554..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
-package com.android.systemui.shade
-
-import android.platform.test.annotations.DisableFlags
-import android.testing.TestableLooper
-import android.view.HapticFeedbackConstants
-import android.view.View
-import android.view.ViewStub
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.internal.util.CollectionUtils
-import com.android.keyguard.KeyguardClockSwitch.LARGE
-import com.android.systemui.Flags
-import com.android.systemui.res.R
-import com.android.systemui.statusbar.StatusBarState.KEYGUARD
-import com.android.systemui.statusbar.StatusBarState.SHADE
-import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.whenever
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.cancelChildren
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.advanceUntilIdle
-import kotlinx.coroutines.test.runTest
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.Captor
-import org.mockito.Mockito.atLeastOnce
-import org.mockito.Mockito.clearInvocations
-import org.mockito.Mockito.never
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
-
-@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-@SmallTest
-@DisableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
-class NotificationPanelViewControllerWithCoroutinesTest :
- NotificationPanelViewControllerBaseTest() {
-
- @Captor private lateinit var viewCaptor: ArgumentCaptor<View>
-
- override fun getMainDispatcher() = Dispatchers.Main.immediate
-
- @Test
- fun testDisableUserSwitcherAfterEnabling_returnsViewStubToTheViewHierarchy() = runTest {
- launch(Dispatchers.Main.immediate) { givenViewAttached() }
- advanceUntilIdle()
-
- whenever(mResources.getBoolean(com.android.internal.R.bool.config_keyguardUserSwitcher))
- .thenReturn(true)
- updateMultiUserSetting(true)
- clearInvocations(mView)
-
- updateMultiUserSetting(false)
-
- verify(mView, atLeastOnce()).addView(viewCaptor.capture(), anyInt())
- val userSwitcherStub =
- CollectionUtils.find(viewCaptor.allValues) { view ->
- view.id == R.id.keyguard_user_switcher_stub
- }
- assertThat(userSwitcherStub).isNotNull()
- assertThat(userSwitcherStub).isInstanceOf(ViewStub::class.java)
- }
-
- @Test
- fun testChangeSmallestScreenWidthAndUserSwitchEnabled_inflatesUserSwitchView() = runTest {
- launch(Dispatchers.Main.immediate) { givenViewAttached() }
- advanceUntilIdle()
-
- whenever(mView.findViewById<View>(R.id.keyguard_user_switcher_view)).thenReturn(null)
- updateSmallestScreenWidth(300)
- whenever(mResources.getBoolean(com.android.internal.R.bool.config_keyguardUserSwitcher))
- .thenReturn(true)
- whenever(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user))
- .thenReturn(false)
- whenever(mUserManager.isUserSwitcherEnabled(false)).thenReturn(true)
-
- updateSmallestScreenWidth(800)
-
- verify(mUserSwitcherStubView).inflate()
- }
-
- @Test
- fun testFinishInflate_userSwitcherDisabled_doNotInflateUserSwitchView_initClock() = runTest {
- launch(Dispatchers.Main.immediate) { givenViewAttached() }
- advanceUntilIdle()
-
- whenever(mResources.getBoolean(com.android.internal.R.bool.config_keyguardUserSwitcher))
- .thenReturn(true)
- whenever(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user))
- .thenReturn(false)
- whenever(mUserManager.isUserSwitcherEnabled(false /* showEvenIfNotActionable */))
- .thenReturn(false)
-
- mNotificationPanelViewController.onFinishInflate()
-
- verify(mUserSwitcherStubView, never()).inflate()
- verify(mKeyguardStatusViewController, times(3)).displayClock(LARGE, /* animate */ true)
-
- coroutineContext.cancelChildren()
- }
-
- @Test
- fun testReInflateViews_userSwitcherDisabled_doNotInflateUserSwitchView() = runTest {
- launch(Dispatchers.Main.immediate) { givenViewAttached() }
- advanceUntilIdle()
-
- whenever(mResources.getBoolean(com.android.internal.R.bool.config_keyguardUserSwitcher))
- .thenReturn(true)
- whenever(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user))
- .thenReturn(false)
- whenever(mUserManager.isUserSwitcherEnabled(false /* showEvenIfNotActionable */))
- .thenReturn(false)
-
- mNotificationPanelViewController.reInflateViews()
-
- verify(mUserSwitcherStubView, never()).inflate()
-
- coroutineContext.cancelChildren()
- }
-
- @Test
- fun testDoubleTapRequired_Keyguard() = runTest {
- launch(Dispatchers.Main.immediate) {
- val listener = getFalsingTapListener()
- mStatusBarStateController.setState(KEYGUARD)
-
- listener.onAdditionalTapRequired()
-
- verify(mKeyguardIndicationController).showTransientIndication(anyInt())
- }
- advanceUntilIdle()
- }
-
- @Test
- @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
- fun doubleTapRequired_onKeyguard_usesPerformHapticFeedback() = runTest {
- launch(Dispatchers.Main.immediate) {
- val listener = getFalsingTapListener()
- mStatusBarStateController.setState(KEYGUARD)
-
- listener.onAdditionalTapRequired()
- verify(mKeyguardIndicationController).showTransientIndication(anyInt())
- verify(mVibratorHelper)
- .performHapticFeedback(eq(mView), eq(HapticFeedbackConstants.REJECT))
- }
- advanceUntilIdle()
- }
-
- @Test
- fun testDoubleTapRequired_ShadeLocked() = runTest {
- launch(Dispatchers.Main.immediate) {
- val listener = getFalsingTapListener()
- mStatusBarStateController.setState(SHADE_LOCKED)
-
- listener.onAdditionalTapRequired()
-
- verify(mTapAgainViewController).show()
- }
- advanceUntilIdle()
- }
-
- @Test
- @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
- fun doubleTapRequired_shadeLocked_usesPerformHapticFeedback() = runTest {
- launch(Dispatchers.Main.immediate) {
- val listener = getFalsingTapListener()
- mStatusBarStateController.setState(SHADE_LOCKED)
-
- listener.onAdditionalTapRequired()
- verify(mVibratorHelper)
- .performHapticFeedback(eq(mView), eq(HapticFeedbackConstants.REJECT))
-
- verify(mTapAgainViewController).show()
- }
- advanceUntilIdle()
- }
-
- @Test
- fun testOnAttachRefreshStatusBarState() = runTest {
- launch(Dispatchers.Main.immediate) {
- mStatusBarStateController.setState(KEYGUARD)
- whenever(mKeyguardStateController.isKeyguardFadingAway()).thenReturn(false)
- mOnAttachStateChangeListeners.forEach { it.onViewAttachedToWindow(mView) }
- verify(mKeyguardStatusViewController)
- .setKeyguardStatusViewVisibility(
- KEYGUARD /*statusBarState*/,
- false /*keyguardFadingAway*/,
- false /*goingToFullShade*/,
- SHADE, /*oldStatusBarState*/
- )
- }
- advanceUntilIdle()
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt
index ad2b23e..a47db2e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt
@@ -118,12 +118,12 @@
}
@Test
- fun getTopEdgeSplitFraction_wideScreen_leftSideLarger() =
+ fun getTopEdgeSplitFraction_wideScreen_splitInHalf() =
testScope.runTest {
// Ensure isShadeLayoutWide is collected.
val isShadeLayoutWide by collectLastValue(underTest.isShadeLayoutWide)
kosmos.shadeRepository.setShadeLayoutWide(true)
- assertThat(underTest.getTopEdgeSplitFraction()).isGreaterThan(0.5f)
+ assertThat(underTest.getTopEdgeSplitFraction()).isEqualTo(0.5f)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/view/ChipTextTruncationHelperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/view/ChipTextTruncationHelperTest.kt
new file mode 100644
index 0000000..d727089
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/view/ChipTextTruncationHelperTest.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.chips.ui.view
+
+import android.view.View
+import android.widget.TextView
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import org.junit.Before
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ChipTextTruncationHelperTest : SysuiTestCase() {
+
+ val underTest by lazy { ChipTextTruncationHelper(TextView(context)) }
+
+ @Before
+ fun setUp() {
+ mContext.getOrCreateTestableResources().apply {
+ this.addOverride(R.dimen.ongoing_activity_chip_max_text_width, MAX_WIDTH)
+ }
+ }
+
+ @Test
+ fun shouldShowText_desiredLessThanMax_true() {
+ val result =
+ underTest.shouldShowText(
+ desiredTextWidthPx = MAX_WIDTH / 2,
+ widthMeasureSpec = UNLIMITED_WIDTH_SPEC,
+ )
+
+ assertThat(result).isTrue()
+ }
+
+ @Test
+ fun shouldShowText_desiredSlightlyLargerThanMax_true() {
+ val result =
+ underTest.shouldShowText(
+ desiredTextWidthPx = (MAX_WIDTH * 1.1).toInt(),
+ widthMeasureSpec = UNLIMITED_WIDTH_SPEC,
+ )
+
+ assertThat(result).isTrue()
+ }
+
+ @Test
+ fun shouldShowText_desiredMoreThanTwiceMax_false() {
+ val result =
+ underTest.shouldShowText(
+ desiredTextWidthPx = (MAX_WIDTH * 2.2).toInt(),
+ widthMeasureSpec = UNLIMITED_WIDTH_SPEC,
+ )
+
+ assertThat(result).isFalse()
+ }
+
+ @Test
+ fun shouldShowText_widthSpecLessThanMax_usesWidthSpec() {
+ val smallerWidthSpec =
+ SysuiMeasureSpec(
+ View.MeasureSpec.makeMeasureSpec(MAX_WIDTH / 2, View.MeasureSpec.AT_MOST)
+ )
+
+ // WHEN desired is more than twice the smallerWidthSpec
+ val desiredWidth = (MAX_WIDTH * 1.1).toInt()
+
+ val result =
+ underTest.shouldShowText(
+ desiredTextWidthPx = desiredWidth,
+ widthMeasureSpec = smallerWidthSpec,
+ )
+
+ // THEN returns false because smallerWidthSpec is used as the requirement
+ assertThat(result).isFalse()
+ }
+
+ @Test
+ fun shouldShowText_maxLessThanWidthSpec_usesMax() {
+ val largerWidthSpec =
+ SysuiMeasureSpec(
+ View.MeasureSpec.makeMeasureSpec(MAX_WIDTH * 3, View.MeasureSpec.AT_MOST)
+ )
+
+ // WHEN desired is more than twice the max
+ val desiredWidth = (MAX_WIDTH * 2.2).toInt()
+
+ val result =
+ underTest.shouldShowText(
+ desiredTextWidthPx = desiredWidth,
+ widthMeasureSpec = largerWidthSpec,
+ )
+
+ // THEN returns false because the max is used as the requirement
+ assertThat(result).isFalse()
+ }
+
+ companion object {
+ private const val MAX_WIDTH = 200
+ private val UNLIMITED_WIDTH_SPEC =
+ SysuiMeasureSpec(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED))
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/commandline/ParseableCommandTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/commandline/ParseableCommandTest.kt
index 1a7c8a3..43bd7cf0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/commandline/ParseableCommandTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/commandline/ParseableCommandTest.kt
@@ -48,12 +48,14 @@
val mySubCommand =
object : ParseableCommand("subCommand") {
val flag by flag("flag")
+
override fun execute(pw: PrintWriter) {}
}
val mySubCommand2 =
object : ParseableCommand("subCommand2") {
val flag by flag("flag")
+
override fun execute(pw: PrintWriter) {}
}
@@ -141,6 +143,7 @@
val cmd =
object : ParseableCommand("test-command") {
val flag by flag("flag")
+
override fun execute(pw: PrintWriter) {}
}
@@ -162,6 +165,7 @@
var onParseFailedCalled = false
override fun execute(pw: PrintWriter) {}
+
override fun onParseFailed(error: ArgParseError) {
onParseFailedCalled = true
}
@@ -204,11 +208,7 @@
val cmd =
object : ParseableCommand(name) {
val singleRequiredParam: String by
- param(
- longName = "param1",
- shortName = "p",
- valueParser = Type.String,
- )
+ param(longName = "param1", shortName = "p", valueParser = Type.String)
.required()
override fun execute(pw: PrintWriter) {}
@@ -253,6 +253,7 @@
val cmd =
object : ParseableCommand(name) {
val subCmd by subCommand(subCmd)
+
override fun execute(pw: PrintWriter) {}
}
@@ -293,18 +294,72 @@
assertThat(myCommand.subCommand?.param1).isEqualTo("arg2")
}
- class MyCommand(
- private val onExecute: ((MyCommand) -> Unit)? = null,
- ) : ParseableCommand(name) {
+ @Test
+ fun commandWithSubCommand_allOptional_nothingPassed_execCalled() {
+ // GIVEN single sub command
+ val subName = "sub-command"
+ val subCmd =
+ object : ParseableCommand(subName) {
+ var execd = false
+
+ override fun execute(pw: PrintWriter) {
+ execd = true
+ }
+ }
+
+ // GIVEN command wrapping the optional subcommand
+ val cmd =
+ object : ParseableCommand(name) {
+ val sub: ParseableCommand? by subCommand(subCmd)
+ var execCalled = false
+
+ override fun execute(pw: PrintWriter) {
+ execCalled = true
+ }
+ }
+
+ // WHEN the base command is sent (i.e., sub-command is missing
+ cmd.execute(pw, listOf())
+ // THEN exec is still called, since this is a valid command
+ assertThat(cmd.execCalled).isTrue()
+ }
+
+ @Test
+ fun commandWithSubCommand_required_nothingPassed_execNotCalled() {
+ // GIVEN single sub command
+ val subName = "sub-command"
+ val subCmd =
+ object : ParseableCommand(subName) {
+ var execd = false
+
+ override fun execute(pw: PrintWriter) {
+ execd = true
+ }
+ }
+
+ // GIVEN command wrapping the required subcommand
+ val cmd =
+ object : ParseableCommand(name) {
+ val sub: ParseableCommand? by subCommand(subCmd).required()
+ var execCalled = false
+
+ override fun execute(pw: PrintWriter) {
+ execCalled = true
+ }
+ }
+
+ // WHEN the base command is sent (i.e., sub-command is missing
+ cmd.execute(pw, listOf())
+ // THEN exec is not called, since the subcommand is required
+ assertThat(cmd.execCalled).isFalse()
+ }
+
+ class MyCommand(private val onExecute: ((MyCommand) -> Unit)? = null) : ParseableCommand(name) {
val flag1 by flag(shortName = "f", longName = "flag1", description = "flag 1 for test")
val flag2 by flag(shortName = "g", longName = "flag2", description = "flag 2 for test")
val singleParam: String? by
- param(
- shortName = "a",
- longName = "arg1",
- valueParser = Type.String,
- )
+ param(shortName = "a", longName = "arg1", valueParser = Type.String)
override fun execute(pw: PrintWriter) {
onExecute?.invoke(this)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
index 2aeebe3..f25ba2c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
@@ -116,6 +116,7 @@
}
}
private val promotedNotificationContentExtractor = FakePromotedNotificationContentExtractor()
+ private val conversationNotificationProcessor: ConversationNotificationProcessor = mock()
@Before
fun setUp() {
@@ -132,7 +133,7 @@
NotificationRowContentBinderImpl(
cache,
mock(),
- mock<ConversationNotificationProcessor>(),
+ conversationNotificationProcessor,
mock(),
smartReplyStateInflater,
layoutInflaterFactoryProvider,
@@ -462,12 +463,15 @@
@Test
fun testInflatePublicSingleLineConversationView() {
val testPerson = Person.Builder().setName("Person").build()
+ val style = Notification.MessagingStyle(testPerson)
val messagingBuilder =
Notification.Builder(mContext, "no-id")
.setSmallIcon(R.drawable.ic_person)
.setContentTitle("Title")
.setContentText("Text")
- .setStyle(Notification.MessagingStyle(testPerson))
+ .setStyle(style)
+ whenever(conversationNotificationProcessor.processNotification(any(), any(), any()))
+ .thenReturn(style)
val messagingRow = spy(testHelper.createRow(messagingBuilder.build()))
messagingRow.publicLayout.removeAllViews()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt
index c3c5a48..b0b80a9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt
@@ -18,9 +18,14 @@
import android.app.PendingIntent
import android.content.Intent
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.shared.Flags as SharedFlags
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
@@ -32,6 +37,9 @@
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.never
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -54,6 +62,62 @@
)
}
+ @EnableFlags(
+ SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
+ SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
+ )
+ @EnableSceneContainer
+ @Test
+ fun registerTransition_forwardsTheRequest() {
+ val cookie = mock(ActivityTransitionAnimator.TransitionCookie::class.java)
+ val controllerFactory = mock(ActivityTransitionAnimator.ControllerFactory::class.java)
+
+ underTest.registerTransition(cookie, controllerFactory)
+
+ verify(activityStarterInternal).registerTransition(eq(cookie), eq(controllerFactory))
+ }
+
+ @DisableFlags(
+ SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
+ SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
+ )
+ @Test
+ fun registerTransition_doesNotForwardTheRequest_whenFlaggedOff() {
+ val cookie = mock(ActivityTransitionAnimator.TransitionCookie::class.java)
+ val controllerFactory = mock(ActivityTransitionAnimator.ControllerFactory::class.java)
+
+ underTest.registerTransition(cookie, controllerFactory)
+
+ verify(activityStarterInternal, never()).registerTransition(any(), any())
+ }
+
+ @EnableFlags(
+ SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
+ SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
+ )
+ @EnableSceneContainer
+ @Test
+ fun unregisterTransition_forwardsTheRequest() {
+ val cookie = mock(ActivityTransitionAnimator.TransitionCookie::class.java)
+
+ underTest.unregisterTransition(cookie)
+
+ verify(activityStarterInternal).unregisterTransition(eq(cookie))
+ }
+
+ @DisableFlags(
+ SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
+ SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
+ )
+ @Test
+ fun unregisterTransition_doesNotForwardTheRequest_whenFlaggedOff() {
+ val cookie = mock(ActivityTransitionAnimator.TransitionCookie::class.java)
+
+ underTest.unregisterTransition(cookie)
+
+ verify(activityStarterInternal, never()).unregisterTransition(any())
+ }
+
@Test
fun postStartActivityDismissingKeyguard_pendingIntent_postsOnMain() {
val intent = mock(PendingIntent::class.java)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt
index bac79a9..5406acf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt
@@ -48,6 +48,7 @@
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.shade.data.repository.ShadeAnimationRepository
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractorLegacyImpl
+import com.android.systemui.shared.Flags as SharedFlags
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationShadeWindowController
@@ -63,6 +64,7 @@
import java.util.Optional
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
+import org.junit.Assert.assertThrows
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -149,6 +151,63 @@
`when`(communalSceneInteractor.isLaunchingWidget).thenReturn(MutableStateFlow(false))
}
+ @EnableFlags(
+ SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
+ SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
+ )
+ @Test
+ fun registerTransition_registers() {
+ val cookie = mock(ActivityTransitionAnimator.TransitionCookie::class.java)
+ val controllerFactory = mock(ActivityTransitionAnimator.ControllerFactory::class.java)
+ `when`(controllerFactory.cookie).thenReturn(cookie)
+
+ underTest.registerTransition(cookie, controllerFactory)
+
+ verify(activityTransitionAnimator).register(eq(cookie), any())
+ }
+
+ @DisableFlags(
+ SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
+ SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
+ )
+ @Test
+ fun registerTransition_throws_whenFlagsAreDisabled() {
+ val cookie = mock(ActivityTransitionAnimator.TransitionCookie::class.java)
+ val controllerFactory = mock(ActivityTransitionAnimator.ControllerFactory::class.java)
+
+ assertThrows(IllegalStateException::class.java) {
+ underTest.registerTransition(cookie, controllerFactory)
+ }
+
+ verify(activityTransitionAnimator, never()).register(any(), any())
+ }
+
+ @EnableFlags(
+ SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
+ SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
+ )
+ @Test
+ fun unregisterTransition_unregisters() {
+ val cookie = mock(ActivityTransitionAnimator.TransitionCookie::class.java)
+
+ underTest.unregisterTransition(cookie)
+
+ verify(activityTransitionAnimator).unregister(eq(cookie))
+ }
+
+ @DisableFlags(
+ SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
+ SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
+ )
+ @Test
+ fun unregisterTransition_throws_whenFlagsAreDisabled() {
+ val cookie = mock(ActivityTransitionAnimator.TransitionCookie::class.java)
+
+ assertThrows(IllegalStateException::class.java) { underTest.unregisterTransition(cookie) }
+
+ verify(activityTransitionAnimator, never()).unregister(eq(cookie))
+ }
+
@Test
fun startActivityDismissingKeyguard_dismissShadeWhenOccluded_runAfterKeyguardGone() {
val intent = mock(Intent::class.java)
@@ -216,7 +275,7 @@
underTest.startPendingIntentDismissingKeyguard(
intent = pendingIntent,
dismissShade = true,
- customMessage = customMessage
+ customMessage = customMessage,
)
mainExecutor.runAllReady()
@@ -296,7 +355,7 @@
nullable(PendingIntent.OnFinished::class.java),
nullable(Handler::class.java),
nullable(String::class.java),
- bundleCaptor.capture()
+ bundleCaptor.capture(),
)
val options = ActivityOptions.fromBundle(bundleCaptor.firstValue)
assertThat(options.getPendingIntentBackgroundActivityStartMode())
@@ -339,7 +398,7 @@
dismissShade = true,
animationController = controller,
showOverLockscreen = true,
- skipLockscreenChecks = true
+ skipLockscreenChecks = true,
)
mainExecutor.runAllReady()
@@ -373,7 +432,7 @@
dismissShade = true,
animationController = controller,
showOverLockscreen = true,
- skipLockscreenChecks = true
+ skipLockscreenChecks = true,
)
mainExecutor.runAllReady()
@@ -413,7 +472,7 @@
dismissShade = false,
animationController = controller,
showOverLockscreen = true,
- skipLockscreenChecks = false
+ skipLockscreenChecks = false,
)
mainExecutor.runAllReady()
@@ -458,7 +517,7 @@
dismissShade = false,
animationController = controller,
showOverLockscreen = true,
- skipLockscreenChecks = false
+ skipLockscreenChecks = false,
)
mainExecutor.runAllReady()
@@ -583,7 +642,7 @@
},
{},
false,
- customMessage
+ customMessage,
)
verify(centralSurfaces).awakenDreams()
@@ -602,7 +661,7 @@
cancelAction = null,
dismissShade = false,
afterKeyguardGone = false,
- deferred = false
+ deferred = false,
)
verify(centralSurfaces, times(1)).awakenDreams()
@@ -620,7 +679,7 @@
cancelAction = null,
dismissShade = false,
afterKeyguardGone = false,
- deferred = false
+ deferred = false,
)
verify(centralSurfaces, never()).awakenDreams()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
deleted file mode 100644
index 41782a1..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT 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.phone;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE;
-import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK;
-import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-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.app.Notification;
-import android.app.PendingIntent;
-import android.app.StatusBarManager;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.InitController;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.power.domain.interactor.PowerInteractor;
-import com.android.systemui.settings.FakeDisplayTracker;
-import com.android.systemui.shade.NotificationShadeWindowView;
-import com.android.systemui.shade.QuickSettingsController;
-import com.android.systemui.shade.ShadeController;
-import com.android.systemui.shade.ShadeViewController;
-import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.LockscreenShadeTransitionController;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
-import com.android.systemui.statusbar.notification.domain.interactor.NotificationAlertsInteractor;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
-import com.android.systemui.statusbar.notification.interruption.VisualInterruptionCondition;
-import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider;
-import com.android.systemui.statusbar.notification.interruption.VisualInterruptionFilter;
-import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor;
-import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-
-import java.util.List;
-import java.util.Set;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-@RunWithLooper()
-public class StatusBarNotificationPresenterTest extends SysuiTestCase {
- private StatusBarNotificationPresenter mStatusBarNotificationPresenter;
- private final VisualInterruptionDecisionProvider mVisualInterruptionDecisionProvider =
- mock(VisualInterruptionDecisionProvider.class);
- private NotificationInterruptSuppressor mInterruptSuppressor;
- private VisualInterruptionCondition mAlertsDisabledCondition;
- private VisualInterruptionCondition mVrModeCondition;
- private VisualInterruptionFilter mNeedsRedactionFilter;
- private VisualInterruptionCondition mPanelsDisabledCondition;
- private CommandQueue mCommandQueue;
- private final ShadeController mShadeController = mock(ShadeController.class);
- private final NotificationAlertsInteractor mNotificationAlertsInteractor =
- mock(NotificationAlertsInteractor.class);
- private final KeyguardStateController mKeyguardStateController =
- mock(KeyguardStateController.class);
-
- @Before
- public void setup() {
- mCommandQueue = new CommandQueue(mContext, new FakeDisplayTracker(mContext));
- mDependency.injectTestDependency(StatusBarStateController.class,
- mock(SysuiStatusBarStateController.class));
- mDependency.injectTestDependency(ShadeController.class, mShadeController);
- mDependency.injectMockDependency(NotificationRemoteInputManager.Callback.class);
- mDependency.injectMockDependency(NotificationShadeWindowController.class);
-
- when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(true);
-
- createPresenter();
- if (VisualInterruptionRefactor.isEnabled()) {
- verifyAndCaptureSuppressors();
- } else {
- verifyAndCaptureLegacySuppressor();
- }
- }
-
- @Test
- @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testInit_refactorDisabled() {
- assertFalse(VisualInterruptionRefactor.isEnabled());
- assertNull(mAlertsDisabledCondition);
- assertNull(mVrModeCondition);
- assertNull(mNeedsRedactionFilter);
- assertNull(mPanelsDisabledCondition);
- assertNotNull(mInterruptSuppressor);
- }
-
- @Test
- @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testInit_refactorEnabled() {
- assertTrue(VisualInterruptionRefactor.isEnabled());
- assertNotNull(mAlertsDisabledCondition);
- assertNotNull(mVrModeCondition);
- assertNotNull(mNeedsRedactionFilter);
- assertNotNull(mPanelsDisabledCondition);
- assertNull(mInterruptSuppressor);
- }
-
- @Test
- @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testNoSuppressHeadsUp_default_refactorDisabled() {
- assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(createNotificationEntry()));
- }
-
- @Test
- @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testNoSuppressHeadsUp_default_refactorEnabled() {
- assertFalse(mAlertsDisabledCondition.shouldSuppress());
- assertFalse(mVrModeCondition.shouldSuppress());
- assertFalse(mNeedsRedactionFilter.shouldSuppress(createNotificationEntry()));
- assertFalse(mAlertsDisabledCondition.shouldSuppress());
- }
-
- @Test
- @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testSuppressHeadsUp_disabledStatusBar_refactorDisabled() {
- mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0,
- false /* animate */);
- TestableLooper.get(this).processAllMessages();
-
- assertTrue("The panel should suppress heads up while disabled",
- mInterruptSuppressor.suppressAwakeHeadsUp(createNotificationEntry()));
- }
-
- @Test
- @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testSuppressHeadsUp_disabledStatusBar_refactorEnabled() {
- mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0,
- false /* animate */);
- TestableLooper.get(this).processAllMessages();
-
- assertTrue("The panel should suppress heads up while disabled",
- mPanelsDisabledCondition.shouldSuppress());
- }
-
- @Test
- @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testSuppressHeadsUp_disabledNotificationShade_refactorDisabled() {
- mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
- false /* animate */);
- TestableLooper.get(this).processAllMessages();
-
- assertTrue("The panel should suppress interruptions while notification shade disabled",
- mInterruptSuppressor.suppressAwakeHeadsUp(createNotificationEntry()));
- }
-
- @Test
- @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testSuppressHeadsUp_disabledNotificationShade_refactorEnabled() {
- mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
- false /* animate */);
- TestableLooper.get(this).processAllMessages();
-
- assertTrue("The panel should suppress interruptions while notification shade disabled",
- mPanelsDisabledCondition.shouldSuppress());
- }
-
- @Test
- @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testPanelsDisabledConditionSuppressesPeek() {
- final Set<VisualInterruptionType> types = mPanelsDisabledCondition.getTypes();
- assertTrue(types.contains(PEEK));
- assertFalse(types.contains(PULSE));
- assertFalse(types.contains(BUBBLE));
- }
-
- @Test
- @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testNoSuppressHeadsUp_FSI_nonOccludedKeyguard_refactorDisabled() {
- when(mKeyguardStateController.isShowing()).thenReturn(true);
- when(mKeyguardStateController.isOccluded()).thenReturn(false);
-
- assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(createFsiNotificationEntry()));
- }
-
- @Test
- @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testNoSuppressHeadsUp_FSI_nonOccludedKeyguard_refactorEnabled() {
- when(mKeyguardStateController.isShowing()).thenReturn(true);
- when(mKeyguardStateController.isOccluded()).thenReturn(false);
-
- assertFalse(mNeedsRedactionFilter.shouldSuppress(createFsiNotificationEntry()));
-
- final Set<VisualInterruptionType> types = mNeedsRedactionFilter.getTypes();
- assertTrue(types.contains(PEEK));
- assertFalse(types.contains(PULSE));
- assertFalse(types.contains(BUBBLE));
- }
-
- @Test
- @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testSuppressInterruptions_vrMode_refactorDisabled() {
- mStatusBarNotificationPresenter.mVrMode = true;
-
- assertTrue("Vr mode should suppress interruptions",
- mInterruptSuppressor.suppressAwakeInterruptions(createNotificationEntry()));
- }
-
- @Test
- @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testSuppressInterruptions_vrMode_refactorEnabled() {
- mStatusBarNotificationPresenter.mVrMode = true;
-
- assertTrue("Vr mode should suppress interruptions", mVrModeCondition.shouldSuppress());
-
- final Set<VisualInterruptionType> types = mVrModeCondition.getTypes();
- assertTrue(types.contains(PEEK));
- assertFalse(types.contains(PULSE));
- assertTrue(types.contains(BUBBLE));
- }
-
- @Test
- @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testSuppressInterruptions_statusBarAlertsDisabled_refactorDisabled() {
- when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false);
-
- assertTrue("When alerts aren't enabled, interruptions are suppressed",
- mInterruptSuppressor.suppressInterruptions(createNotificationEntry()));
- }
-
- @Test
- @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
- public void testSuppressInterruptions_statusBarAlertsDisabled_refactorEnabled() {
- when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false);
-
- assertTrue("When alerts aren't enabled, interruptions are suppressed",
- mAlertsDisabledCondition.shouldSuppress());
-
- final Set<VisualInterruptionType> types = mAlertsDisabledCondition.getTypes();
- assertTrue(types.contains(PEEK));
- assertTrue(types.contains(PULSE));
- assertTrue(types.contains(BUBBLE));
- }
-
- private void createPresenter() {
- final ShadeViewController shadeViewController = mock(ShadeViewController.class);
-
- final NotificationShadeWindowView notificationShadeWindowView =
- mock(NotificationShadeWindowView.class);
- when(notificationShadeWindowView.getResources()).thenReturn(mContext.getResources());
-
- NotificationStackScrollLayoutController stackScrollLayoutController =
- mock(NotificationStackScrollLayoutController.class);
- when(stackScrollLayoutController.getView()).thenReturn(
- mock(NotificationStackScrollLayout.class));
-
- final InitController initController = new InitController();
-
- mStatusBarNotificationPresenter = new StatusBarNotificationPresenter(
- mContext,
- shadeViewController,
- mock(PanelExpansionInteractor.class),
- mock(QuickSettingsController.class),
- mock(HeadsUpManager.class),
- notificationShadeWindowView,
- mock(ActivityStarter.class),
- stackScrollLayoutController,
- mock(DozeScrimController.class),
- mock(NotificationShadeWindowController.class),
- mock(DynamicPrivacyController.class),
- mKeyguardStateController,
- mNotificationAlertsInteractor,
- mock(LockscreenShadeTransitionController.class),
- mock(PowerInteractor.class),
- mCommandQueue,
- mock(NotificationLockscreenUserManager.class),
- mock(SysuiStatusBarStateController.class),
- mock(NotifShadeEventSource.class),
- mock(NotificationMediaManager.class),
- mock(NotificationGutsManager.class),
- initController,
- mVisualInterruptionDecisionProvider,
- mock(NotificationRemoteInputManager.class),
- mock(NotificationRemoteInputManager.Callback.class),
- mock(NotificationListContainer.class));
-
- initController.executePostInitTasks();
- }
-
- private void verifyAndCaptureSuppressors() {
- mInterruptSuppressor = null;
-
- final ArgumentCaptor<VisualInterruptionCondition> conditionCaptor =
- ArgumentCaptor.forClass(VisualInterruptionCondition.class);
- verify(mVisualInterruptionDecisionProvider, times(3)).addCondition(
- conditionCaptor.capture());
- final List<VisualInterruptionCondition> conditions = conditionCaptor.getAllValues();
- mAlertsDisabledCondition = conditions.get(0);
- mVrModeCondition = conditions.get(1);
- mPanelsDisabledCondition = conditions.get(2);
-
- final ArgumentCaptor<VisualInterruptionFilter> needsRedactionFilterCaptor =
- ArgumentCaptor.forClass(VisualInterruptionFilter.class);
- verify(mVisualInterruptionDecisionProvider).addFilter(needsRedactionFilterCaptor.capture());
- mNeedsRedactionFilter = needsRedactionFilterCaptor.getValue();
- }
-
- private void verifyAndCaptureLegacySuppressor() {
- mAlertsDisabledCondition = null;
- mVrModeCondition = null;
- mNeedsRedactionFilter = null;
- mPanelsDisabledCondition = null;
-
- final ArgumentCaptor<NotificationInterruptSuppressor> suppressorCaptor =
- ArgumentCaptor.forClass(NotificationInterruptSuppressor.class);
- verify(mVisualInterruptionDecisionProvider).addLegacySuppressor(suppressorCaptor.capture());
- mInterruptSuppressor = suppressorCaptor.getValue();
- }
-
- private NotificationEntry createNotificationEntry() {
- return new NotificationEntryBuilder()
- .setPkg("a")
- .setOpPkg("a")
- .setTag("a")
- .setNotification(new Notification.Builder(getContext(), "a").build())
- .build();
- }
-
- private NotificationEntry createFsiNotificationEntry() {
- final Notification notification = new Notification.Builder(getContext(), "a")
- .setFullScreenIntent(mock(PendingIntent.class), true)
- .build();
-
- return new NotificationEntryBuilder()
- .setPkg("a")
- .setOpPkg("a")
- .setTag("a")
- .setNotification(notification)
- .build();
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.kt
new file mode 100644
index 0000000..baea1a1
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.kt
@@ -0,0 +1,429 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.phone
+
+import android.app.Notification
+import android.app.Notification.Builder
+import android.app.PendingIntent
+import android.app.StatusBarManager
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.testing.TestableLooper
+import android.testing.TestableLooper.RunWithLooper
+import android.view.Display.DEFAULT_DISPLAY
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.InitController
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.settings.FakeDisplayTracker
+import com.android.systemui.shade.domain.interactor.panelExpansionInteractor
+import com.android.systemui.shade.notificationShadeWindowView
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.commandQueue
+import com.android.systemui.statusbar.lockscreenShadeTransitionController
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.domain.interactor.notificationAlertsInteractor
+import com.android.systemui.statusbar.notification.dynamicPrivacyController
+import com.android.systemui.statusbar.notification.headsup.headsUpManager
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionCondition
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionFilter
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.stack.notificationStackScrollLayoutController
+import com.android.systemui.statusbar.notification.visualInterruptionDecisionProvider
+import com.android.systemui.statusbar.notificationLockscreenUserManager
+import com.android.systemui.statusbar.notificationRemoteInputManager
+import com.android.systemui.statusbar.notificationShadeWindowController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.statusbar.policy.keyguardStateController
+import com.android.systemui.statusbar.sysuiStatusBarStateController
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@RunWithLooper
+class StatusBarNotificationPresenterTest : SysuiTestCase() {
+ private val kosmos: Kosmos =
+ testKosmos().apply {
+ whenever(notificationShadeWindowView.resources).thenReturn(mContext.resources)
+ whenever(notificationStackScrollLayoutController.view).thenReturn(mock())
+ whenever(notificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(true)
+ commandQueue = CommandQueue(mContext, FakeDisplayTracker(mContext))
+
+ // override this controller with a mock, otherwise it would start some animators which
+ // are not cleaned up after these tests
+ lockscreenShadeTransitionController = mock()
+ }
+
+ // initiated by argumentCaptors later in the setup step, based on the flag states
+ private var interruptSuppressor: NotificationInterruptSuppressor? = null
+ private var alertsDisabledCondition: VisualInterruptionCondition? = null
+ private var vrModeCondition: VisualInterruptionCondition? = null
+ private var needsRedactionFilter: VisualInterruptionFilter? = null
+ private var panelsDisabledCondition: VisualInterruptionCondition? = null
+
+ private val commandQueue: CommandQueue = kosmos.commandQueue
+ private val keyguardStateController: KeyguardStateController = kosmos.keyguardStateController
+ private val notificationAlertsInteractor = kosmos.notificationAlertsInteractor
+ private val visualInterruptionDecisionProvider = kosmos.visualInterruptionDecisionProvider
+
+ private lateinit var underTest: StatusBarNotificationPresenter
+
+ @Before
+ fun setup() {
+ underTest = createPresenter()
+ if (VisualInterruptionRefactor.isEnabled) {
+ verifyAndCaptureSuppressors()
+ } else {
+ verifyAndCaptureLegacySuppressor()
+ }
+ }
+
+ @Test
+ @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testInit_refactorDisabled() {
+ assertThat(VisualInterruptionRefactor.isEnabled).isFalse()
+ assertThat(alertsDisabledCondition).isNull()
+ assertThat(vrModeCondition).isNull()
+ assertThat(needsRedactionFilter).isNull()
+ assertThat(panelsDisabledCondition).isNull()
+ assertThat(interruptSuppressor).isNotNull()
+ }
+
+ @Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testInit_refactorEnabled() {
+ assertThat(VisualInterruptionRefactor.isEnabled).isTrue()
+ assertThat(alertsDisabledCondition).isNotNull()
+ assertThat(vrModeCondition).isNotNull()
+ assertThat(needsRedactionFilter).isNotNull()
+ assertThat(panelsDisabledCondition).isNotNull()
+ assertThat(interruptSuppressor).isNull()
+ }
+
+ @Test
+ @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testNoSuppressHeadsUp_default_refactorDisabled() {
+ assertThat(interruptSuppressor!!.suppressAwakeHeadsUp(createNotificationEntry())).isFalse()
+ }
+
+ @Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testNoSuppressHeadsUp_default_refactorEnabled() {
+ assertThat(alertsDisabledCondition!!.shouldSuppress()).isFalse()
+ assertThat(vrModeCondition!!.shouldSuppress()).isFalse()
+ assertThat(needsRedactionFilter!!.shouldSuppress(createNotificationEntry())).isFalse()
+ assertThat(alertsDisabledCondition!!.shouldSuppress()).isFalse()
+ }
+
+ @Test
+ @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testSuppressHeadsUp_disabledStatusBar_refactorDisabled() {
+ commandQueue.disable(
+ /* displayId = */ DEFAULT_DISPLAY,
+ /* flags = */ StatusBarManager.DISABLE_EXPAND,
+ /* reason = */ 0,
+ /* animate = */ false,
+ )
+ TestableLooper.get(this).processAllMessages()
+ assertWithMessage("The panel should suppress heads up while disabled")
+ .that(interruptSuppressor!!.suppressAwakeHeadsUp(createNotificationEntry()))
+ .isTrue()
+ }
+
+ @Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testSuppressHeadsUp_disabledStatusBar_refactorEnabled() {
+ commandQueue.disable(
+ /* displayId = */ DEFAULT_DISPLAY,
+ /* flags = */ StatusBarManager.DISABLE_EXPAND,
+ /* reason = */ 0,
+ /* animate = */ false,
+ )
+ TestableLooper.get(this).processAllMessages()
+ assertWithMessage("The panel should suppress heads up while disabled")
+ .that(panelsDisabledCondition!!.shouldSuppress())
+ .isTrue()
+ }
+
+ @Test
+ @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testSuppressHeadsUp_disabledNotificationShade_refactorDisabled() {
+ commandQueue.disable(
+ /* displayId = */ DEFAULT_DISPLAY,
+ /* flags = */ 0,
+ /* reason = */ StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
+ /* animate = */ false,
+ )
+ TestableLooper.get(this).processAllMessages()
+ assertWithMessage(
+ "The panel should suppress interruptions while notification shade disabled"
+ )
+ .that(interruptSuppressor!!.suppressAwakeHeadsUp(createNotificationEntry()))
+ .isTrue()
+ }
+
+ @Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testSuppressHeadsUp_disabledNotificationShade_refactorEnabled() {
+ commandQueue.disable(
+ /* displayId = */ DEFAULT_DISPLAY,
+ /* flags = */ 0,
+ /* reason = */ StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
+ /* animate = */ false,
+ )
+ TestableLooper.get(this).processAllMessages()
+ assertWithMessage(
+ "The panel should suppress interruptions while notification shade disabled"
+ )
+ .that(panelsDisabledCondition!!.shouldSuppress())
+ .isTrue()
+ }
+
+ @Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testPanelsDisabledConditionSuppressesPeek() {
+ val types: Set<VisualInterruptionType> = panelsDisabledCondition!!.types
+ assertThat(types).contains(VisualInterruptionType.PEEK)
+ assertThat(types)
+ .containsNoneOf(VisualInterruptionType.BUBBLE, VisualInterruptionType.PULSE)
+ }
+
+ @Test
+ @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testNoSuppressHeadsUp_FSI_nonOccludedKeyguard_refactorDisabled() {
+ whenever(keyguardStateController.isShowing()).thenReturn(true)
+ whenever(keyguardStateController.isOccluded()).thenReturn(false)
+ assertThat(interruptSuppressor!!.suppressAwakeHeadsUp(createFsiNotificationEntry()))
+ .isFalse()
+ }
+
+ @Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testNoSuppressHeadsUp_FSI_nonOccludedKeyguard_refactorEnabled() {
+ whenever(keyguardStateController.isShowing()).thenReturn(true)
+ whenever(keyguardStateController.isOccluded()).thenReturn(false)
+ assertThat(needsRedactionFilter!!.shouldSuppress(createFsiNotificationEntry())).isFalse()
+ val types: Set<VisualInterruptionType> = needsRedactionFilter!!.types
+ assertThat(types).contains(VisualInterruptionType.PEEK)
+ assertThat(types)
+ .containsNoneOf(VisualInterruptionType.BUBBLE, VisualInterruptionType.PULSE)
+ }
+
+ @Test
+ @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testSuppressInterruptions_vrMode_refactorDisabled() {
+ underTest.mVrMode = true
+ assertWithMessage("Vr mode should suppress interruptions")
+ .that(interruptSuppressor!!.suppressAwakeInterruptions(createNotificationEntry()))
+ .isTrue()
+ }
+
+ @Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testSuppressInterruptions_vrMode_refactorEnabled() {
+ underTest.mVrMode = true
+ assertWithMessage("Vr mode should suppress interruptions")
+ .that(vrModeCondition!!.shouldSuppress())
+ .isTrue()
+ val types: Set<VisualInterruptionType> = vrModeCondition!!.types
+ assertThat(types).contains(VisualInterruptionType.PEEK)
+ assertThat(types).doesNotContain(VisualInterruptionType.PULSE)
+ assertThat(types).contains(VisualInterruptionType.BUBBLE)
+ }
+
+ @Test
+ @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testSuppressInterruptions_statusBarAlertsDisabled_refactorDisabled() {
+ whenever(notificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false)
+ assertWithMessage("When alerts aren't enabled, interruptions are suppressed")
+ .that(interruptSuppressor!!.suppressInterruptions(createNotificationEntry()))
+ .isTrue()
+ }
+
+ @Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
+ fun testSuppressInterruptions_statusBarAlertsDisabled_refactorEnabled() {
+ whenever(notificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false)
+ assertWithMessage("When alerts aren't enabled, interruptions are suppressed")
+ .that(alertsDisabledCondition!!.shouldSuppress())
+ .isTrue()
+ val types: Set<VisualInterruptionType> = alertsDisabledCondition!!.types
+ assertThat(types).contains(VisualInterruptionType.PEEK)
+ assertThat(types).contains(VisualInterruptionType.PULSE)
+ assertThat(types).contains(VisualInterruptionType.BUBBLE)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun testExpandSensitiveNotification_onLockScreen_opensShade() =
+ kosmos.runTest {
+ // Given we are on the keyguard
+ kosmos.sysuiStatusBarStateController.state = StatusBarState.KEYGUARD
+ // And the device is locked
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.Pin
+ )
+
+ // When the user expands a sensitive Notification
+ val row = createRow()
+ val entry =
+ row.entry.apply { setSensitive(/* sensitive= */ true, /* deviceSensitive= */ true) }
+
+ underTest.onExpandClicked(entry, mock(), /* nowExpanded= */ true)
+
+ // Then we open the locked shade
+ verify(kosmos.lockscreenShadeTransitionController)
+ // Explicit parameters to avoid issues with Kotlin default arguments in Mockito
+ .goToLockedShade(row, true)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun testExpandSensitiveNotification_onLockedShade_showsBouncer() =
+ kosmos.runTest {
+ // Given we are on the locked shade
+ kosmos.sysuiStatusBarStateController.state = StatusBarState.SHADE_LOCKED
+ // And the device is locked
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.Pin
+ )
+
+ // When the user expands a sensitive Notification
+ val entry =
+ createRow().entry.apply {
+ setSensitive(/* sensitive= */ true, /* deviceSensitive= */ true)
+ }
+ underTest.onExpandClicked(entry, mock(), /* nowExpanded= */ true)
+
+ // Then we show the bouncer
+ verify(kosmos.activityStarter).dismissKeyguardThenExecute(any(), eq(null), eq(false))
+ }
+
+ private fun createPresenter(): StatusBarNotificationPresenter {
+ val initController: InitController = InitController()
+ return StatusBarNotificationPresenter(
+ /* context = */ mContext,
+ /* panel = */ mock(),
+ kosmos.panelExpansionInteractor,
+ /* quickSettingsController = */ mock(),
+ kosmos.headsUpManager,
+ kosmos.notificationShadeWindowView,
+ kosmos.activityStarter,
+ kosmos.notificationStackScrollLayoutController,
+ kosmos.dozeScrimController,
+ kosmos.notificationShadeWindowController,
+ kosmos.dynamicPrivacyController,
+ kosmos.keyguardStateController,
+ kosmos.notificationAlertsInteractor,
+ kosmos.lockscreenShadeTransitionController,
+ kosmos.powerInteractor,
+ kosmos.commandQueue,
+ kosmos.notificationLockscreenUserManager,
+ kosmos.sysuiStatusBarStateController,
+ /* notifShadeEventSource = */ mock(),
+ /* notificationMediaManager = */ mock(),
+ /* notificationGutsManager = */ mock(),
+ /* initController = */ initController,
+ kosmos.visualInterruptionDecisionProvider,
+ kosmos.notificationRemoteInputManager,
+ /* remoteInputManagerCallback = */ mock(),
+ /* notificationListContainer = */ mock(),
+ kosmos.deviceUnlockedInteractor,
+ )
+ .also { initController.executePostInitTasks() }
+ }
+
+ private fun verifyAndCaptureSuppressors() {
+ interruptSuppressor = null
+
+ val conditionCaptor = argumentCaptor<VisualInterruptionCondition>()
+ verify(visualInterruptionDecisionProvider, times(3)).addCondition(conditionCaptor.capture())
+
+ val conditions: List<VisualInterruptionCondition> = conditionCaptor.allValues
+ alertsDisabledCondition = conditions[0]
+ vrModeCondition = conditions[1]
+ panelsDisabledCondition = conditions[2]
+
+ val needsRedactionFilterCaptor = argumentCaptor<VisualInterruptionFilter>()
+ verify(visualInterruptionDecisionProvider).addFilter(needsRedactionFilterCaptor.capture())
+ needsRedactionFilter = needsRedactionFilterCaptor.lastValue
+ }
+
+ private fun verifyAndCaptureLegacySuppressor() {
+ alertsDisabledCondition = null
+ vrModeCondition = null
+ needsRedactionFilter = null
+ panelsDisabledCondition = null
+
+ val suppressorCaptor = argumentCaptor<NotificationInterruptSuppressor>()
+ verify(visualInterruptionDecisionProvider).addLegacySuppressor(suppressorCaptor.capture())
+ interruptSuppressor = suppressorCaptor.lastValue
+ }
+
+ private fun createRow(): ExpandableNotificationRow {
+ val row: ExpandableNotificationRow = mock()
+ val entry: NotificationEntry = createNotificationEntry()
+ whenever(row.entry).thenReturn(entry)
+ entry.row = row
+ return row
+ }
+
+ private fun createNotificationEntry(): NotificationEntry =
+ NotificationEntryBuilder()
+ .setPkg("a")
+ .setOpPkg("a")
+ .setTag("a")
+ .setNotification(Builder(mContext, "a").build())
+ .build()
+
+ private fun createFsiNotificationEntry(): NotificationEntry {
+ val notification: Notification =
+ Builder(mContext, "a")
+ .setFullScreenIntent(mock<PendingIntent>(), /* highPriority= */ true)
+ .build()
+ return NotificationEntryBuilder()
+ .setPkg("a")
+ .setOpPkg("a")
+ .setTag("a")
+ .setNotification(notification)
+ .build()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt
deleted file mode 100644
index 9f74915..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt
+++ /dev/null
@@ -1,201 +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.policy
-
-import android.content.Context
-import android.content.pm.UserInfo
-import android.graphics.Bitmap
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.internal.util.UserIcons
-import com.android.systemui.res.R
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.qs.tiles.UserDetailItemView
-import com.android.systemui.user.data.source.UserRecord
-import com.android.systemui.util.mockito.whenever
-import org.junit.Assert.assertFalse
-import org.junit.Assert.assertNotNull
-import org.junit.Assert.assertTrue
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.anyBoolean
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
-import org.mockito.MockitoAnnotations
-
-@RunWith(AndroidJUnit4::class)
-@SmallTest
-class KeyguardUserSwitcherAdapterTest : SysuiTestCase() {
- @Mock
- private lateinit var userSwitcherController: UserSwitcherController
- @Mock
- private lateinit var parent: ViewGroup
- @Mock
- private lateinit var keyguardUserDetailItemView: KeyguardUserDetailItemView
- @Mock
- private lateinit var otherView: View
- @Mock
- private lateinit var inflatedUserDetailItemView: KeyguardUserDetailItemView
- @Mock
- private lateinit var layoutInflater: LayoutInflater
- @Mock
- private lateinit var keyguardUserSwitcherController: KeyguardUserSwitcherController
-
- private lateinit var adapter: KeyguardUserSwitcherController.KeyguardUserAdapter
- private lateinit var picture: Bitmap
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- whenever(userSwitcherController.isUserSwitcherEnabled).thenReturn(true)
-
- mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE, layoutInflater)
- `when`(layoutInflater.inflate(anyInt(), any(ViewGroup::class.java), anyBoolean()))
- .thenReturn(inflatedUserDetailItemView)
- adapter = KeyguardUserSwitcherController.KeyguardUserAdapter(
- mContext,
- mContext.resources,
- LayoutInflater.from(mContext),
- userSwitcherController, keyguardUserSwitcherController)
- picture = UserIcons.convertToBitmap(mContext.getDrawable(R.drawable.ic_avatar_user))
- }
-
- /**
- * Uses the KeyguardUserAdapter to create a UserDetailItemView where the convertView has an
- * incompatible type
- */
- private fun createViewFromDifferentType(
- isCurrentUser: Boolean,
- isGuestUser: Boolean
- ): UserDetailItemView? {
- val user = createUserRecord(isCurrentUser, isGuestUser)
- return adapter.createUserDetailItemView(otherView, parent, user)
- }
-
- /**
- * Uses the KeyguardUserAdapter to create a UserDetailItemView where the convertView is an
- * instance of KeyguardUserDetailItemView
- */
- private fun createViewFromSameType(
- isCurrentUser: Boolean,
- isGuestUser: Boolean
- ): UserDetailItemView? {
- val user = createUserRecord(isCurrentUser, isGuestUser)
- return adapter.createUserDetailItemView(keyguardUserDetailItemView, parent, user)
- }
-
- @Test
- fun shouldSetOnClickListener_notCurrentUser_notGuestUser_oldViewIsSameType() {
- val v: UserDetailItemView? = createViewFromSameType(
- isCurrentUser = false, isGuestUser = false)
- assertNotNull(v)
- verify(v)!!.setOnClickListener(adapter)
- }
-
- @Test
- fun shouldSetOnClickListener_notCurrentUser_guestUser_oldViewIsSameType() {
- val v: UserDetailItemView? = createViewFromSameType(
- isCurrentUser = false, isGuestUser = true)
- assertNotNull(v)
- verify(v)!!.setOnClickListener(adapter)
- }
-
- @Test
- fun shouldSetOnOnClickListener_currentUser_notGuestUser_oldViewIsSameType() {
- val v: UserDetailItemView? = createViewFromSameType(
- isCurrentUser = true, isGuestUser = false)
- assertNotNull(v)
- verify(v)!!.setOnClickListener(adapter)
- }
-
- @Test
- fun shouldSetOnClickListener_currentUser_guestUser_oldViewIsSameType() {
- val v: UserDetailItemView? = createViewFromSameType(
- isCurrentUser = true, isGuestUser = true)
- assertNotNull(v)
- verify(v)!!.setOnClickListener(adapter)
- }
-
- @Test
- fun shouldSetOnClickListener_notCurrentUser_notGuestUser_oldViewIsDifferentType() {
- val v: UserDetailItemView? = createViewFromDifferentType(
- isCurrentUser = false, isGuestUser = false)
- assertNotNull(v)
- verify(v)!!.setOnClickListener(adapter)
- }
-
- @Test
- fun shouldSetOnClickListener_notCurrentUser_guestUser_oldViewIsDifferentType() {
- val v: UserDetailItemView? = createViewFromDifferentType(
- isCurrentUser = false, isGuestUser = true)
- assertNotNull(v)
- verify(v)!!.setOnClickListener(adapter)
- }
-
- @Test
- fun shouldSetOnOnClickListener_currentUser_notGuestUser_oldViewIsDifferentType() {
- val v: UserDetailItemView? = createViewFromDifferentType(
- isCurrentUser = true, isGuestUser = false)
- assertNotNull(v)
- verify(v)!!.setOnClickListener(adapter)
- }
-
- @Test
- fun shouldSetOnClickListener_currentUser_guestUser_oldViewIsDifferentType() {
- val v: UserDetailItemView? = createViewFromDifferentType(
- isCurrentUser = true, isGuestUser = true)
- assertNotNull(v)
- verify(v)!!.setOnClickListener(adapter)
- }
-
- @Test
- fun testCurrentUserIsAlwaysFirst() {
- `when`(userSwitcherController.users).thenReturn(arrayListOf(
- createUserRecord(isCurrentUser = false, isGuestUser = false),
- createUserRecord(isCurrentUser = true, isGuestUser = false),
- createUserRecord(isCurrentUser = false, isGuestUser = true),
- createUserRecord(isCurrentUser = false, isGuestUser = false)
- ))
-
- adapter.notifyDataSetChanged()
- assertTrue("Expected current user to be first in list", adapter.getItem(0).isCurrent)
- assertFalse("Did not expect current user in position 1", adapter.getItem(1).isCurrent)
- assertFalse("Did not expect current user in position 2", adapter.getItem(2).isCurrent)
- assertTrue("Expected guest user to remain in position 2", adapter.getItem(2).isGuest)
- assertFalse("Did not expect current user in position 3", adapter.getItem(3).isCurrent)
- }
-
- private fun createUserRecord(isCurrentUser: Boolean, isGuestUser: Boolean) =
- UserRecord(
- UserInfo(0 /* id */, "name", 0 /* flags */),
- picture,
- isGuestUser,
- isCurrentUser,
- false /* isAddUser */,
- false /* isRestricted */,
- true /* isSwitchToEnabled */,
- false /* isAddSupervisedUser */
- )
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
index d5651ec..e2f363b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
@@ -52,7 +52,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
class GuestUserInteractorTest : SysuiTestCase() {
@Mock private lateinit var manager: UserManager
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractorTest.kt
index 3f995c6..87d782e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractorTest.kt
@@ -172,9 +172,9 @@
runCurrent()
assertThat(slidersModel!!.slider)
- .isEqualTo(VolumeDialogSliderType.Stream(AudioManager.STREAM_MUSIC))
+ .isEqualTo(VolumeDialogSliderType.Stream(AudioManager.STREAM_SYSTEM))
assertThat(slidersModel!!.floatingSliders)
- .containsExactly(VolumeDialogSliderType.Stream(AudioManager.STREAM_SYSTEM))
+ .containsExactly(VolumeDialogSliderType.Stream(AudioManager.STREAM_MUSIC))
}
}
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
index abb721a..55be9f7 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -16,6 +16,7 @@
import android.annotation.Nullable;
import android.app.PendingIntent;
+import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
import android.os.UserHandle;
@@ -33,6 +34,22 @@
public interface ActivityStarter {
int VERSION = 2;
+ /**
+ * Registers the given {@link ActivityTransitionAnimator.ControllerFactory} for launching and
+ * closing transitions matching the {@link ActivityTransitionAnimator.TransitionCookie} and the
+ * {@link ComponentName} that it contains.
+ */
+ void registerTransition(
+ ActivityTransitionAnimator.TransitionCookie cookie,
+ ActivityTransitionAnimator.ControllerFactory controllerFactory);
+
+ /**
+ * Unregisters the {@link ActivityTransitionAnimator.ControllerFactory} previously registered
+ * containing the given {@link ActivityTransitionAnimator.TransitionCookie}. If no such
+ * registration exists, this is a no-op.
+ */
+ void unregisterTransition(ActivityTransitionAnimator.TransitionCookie cookie);
+
void startPendingIntentDismissingKeyguard(PendingIntent intent);
/**
diff --git a/packages/SystemUI/plugin_core/processor/src/com/android/systemui/plugins/processor/ProtectedPluginProcessor.kt b/packages/SystemUI/plugin_core/processor/src/com/android/systemui/plugins/processor/ProtectedPluginProcessor.kt
index 6b54d89..d93f7d3 100644
--- a/packages/SystemUI/plugin_core/processor/src/com/android/systemui/plugins/processor/ProtectedPluginProcessor.kt
+++ b/packages/SystemUI/plugin_core/processor/src/com/android/systemui/plugins/processor/ProtectedPluginProcessor.kt
@@ -33,9 +33,10 @@
/**
* [ProtectedPluginProcessor] generates a proxy implementation for interfaces annotated with
- * [ProtectedInterface] which catches [LinkageError]s generated by the proxied target. This protects
- * the plugin host from crashing due to out-of-date plugin code, where some call has changed so that
- * the [ClassLoader] can no longer resolve it correctly.
+ * [ProtectedInterface] which catches [Exception]s generated by the proxied target. Production
+ * plugin interfaces should use this to catch [LinkagError]s as that protects the plugin host from
+ * crashing due to out-of-date plugin code, where some call has changed so that the [ClassLoader] is
+ * no longer able to resolve it correctly.
*
* [PluginInstance] observes these failures via [ProtectedMethodListener] and unloads the plugin in
* question to prevent further issues. This persists through further load/unload requests.
@@ -61,6 +62,7 @@
val sourcePkg: String,
val sourceName: String,
val outputName: String,
+ val exTypeAttr: ProtectedInterface,
)
override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
@@ -68,10 +70,19 @@
val additionalImports = mutableSetOf<String>()
for (attr in annotations) {
for (target in roundEnv.getElementsAnnotatedWith(attr)) {
+ // Find the target exception types to be used
+ var exTypeAttr = target.getAnnotation(ProtectedInterface::class.java)
+ if (exTypeAttr == null || exTypeAttr.exTypes.size == 0) {
+ exTypeAttr = ProtectedInterface.Default
+ }
+
val sourceName = "${target.simpleName}"
val outputName = "${sourceName}Protector"
val pkg = (target.getEnclosingElement() as PackageElement).qualifiedName.toString()
- targets.put("$target", TargetData(attr, target, pkg, sourceName, outputName))
+ targets.put(
+ "$target",
+ TargetData(attr, target, pkg, sourceName, outputName, exTypeAttr),
+ )
// This creates excessive imports, but it should be fine
additionalImports.add("$pkg.$sourceName")
@@ -80,7 +91,7 @@
}
if (targets.size <= 0) return false
- for ((_, sourceType, sourcePkg, sourceName, outputName) in targets.values) {
+ for ((_, sourceType, sourcePkg, sourceName, outputName, exTypeAttr) in targets.values) {
// Find all methods in this type and all super types to that need to be implemented
val types = ArrayDeque<TypeMirror>().apply { addLast(sourceType.asType()) }
val impAttrs = mutableListOf<GeneratedImport>()
@@ -105,7 +116,6 @@
// Imports used by the proxy implementation
line("import android.util.Log;")
- line("import java.lang.LinkageError;")
line("import com.android.systemui.plugins.PluginWrapper;")
line("import com.android.systemui.plugins.ProtectedPluginListener;")
line()
@@ -118,6 +128,14 @@
line()
}
+ // Imports of caught exceptions
+ if (exTypeAttr.exTypes.size > 0) {
+ for (exType in exTypeAttr.exTypes) {
+ line("import $exType;")
+ }
+ line()
+ }
+
// Imports declared via @GeneratedImport
if (impAttrs.size > 0) {
for (impAttr in impAttrs) {
@@ -232,11 +250,14 @@
// Protect callsite in try/catch block
braceBlock("try") { line(callStatement) }
- // Notify listener when a LinkageError is caught
- braceBlock("catch (LinkageError ex)") {
- line("Log.wtf(CLASS, \"Failed to execute: \" + METHOD, ex);")
- line("mHasError = mListener.onFail(CLASS, METHOD, ex);")
- line(errorStatement)
+ // Notify listener when a target exception is caught
+ for (exType in exTypeAttr.exTypes) {
+ val simpleName = exType.substringAfterLast(".")
+ braceBlock("catch ($simpleName ex)") {
+ line("Log.wtf(CLASS, \"Failed to execute: \" + METHOD, ex);")
+ line("mHasError = mListener.onFail(CLASS, METHOD, ex);")
+ line(errorStatement)
+ }
}
}
line()
diff --git a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/ProtectedPluginListener.kt b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/ProtectedPluginListener.kt
index 3a1f251..8e2528f 100644
--- a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/ProtectedPluginListener.kt
+++ b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/ProtectedPluginListener.kt
@@ -16,12 +16,11 @@
/** Listener for events from proxy types generated by [ProtectedPluginProcessor]. */
interface ProtectedPluginListener {
/**
- * Called when a method call produces a [LinkageError] before returning. This callback is
- * provided so that the host application can terminate the plugin or log the error as
- * appropriate.
+ * Called when a method call produces a [Exception] before returning. This callback is provided
+ * so that the host application can terminate the plugin or log the error as appropriate.
*
* @return true to terminate all methods within this object; false if the error is recoverable
* and the proxied plugin should continue to operate as normal.
*/
- fun onFail(className: String, methodName: String, failure: LinkageError): Boolean
+ fun onFail(className: String, methodName: String, failure: Throwable): Boolean
}
diff --git a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/annotations/ProtectedInterface.kt b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/annotations/ProtectedInterface.kt
index 12a977d..dc2ea8c 100644
--- a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/annotations/ProtectedInterface.kt
+++ b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/annotations/ProtectedInterface.kt
@@ -15,11 +15,15 @@
/**
* This annotation marks denotes that an interface should use a proxy layer to protect the plugin
- * host from crashing due to [LinkageError]s originating within the plugin's implementation.
+ * host from crashing due to the [Exception] types originating within the plugin's implementation.
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
-annotation class ProtectedInterface
+annotation class ProtectedInterface(vararg val exTypes: String) {
+ companion object {
+ val Default = ProtectedInterface("java.lang.Exception", "java.lang.LinkageError")
+ }
+}
/**
* This annotation specifies any additional imports that the processor will require when generating
@@ -32,9 +36,9 @@
annotation class GeneratedImport(val extraImport: String)
/**
- * This annotation provides default values to return when the proxy implementation catches a
- * [LinkageError]. The string specified should be a simple but valid java statement. In most cases
- * it should be a return statement of the appropriate type, but in some cases throwing a known
+ * This annotation provides default values to return when the proxy implementation catches a target
+ * [Exception]. The string specified should be a simple but valid java statement. In most cases it
+ * should be a return statement of the appropriate type, but in some cases throwing a known
* exception type may be preferred.
*
* This annotation is not required for methods that return void, but will behave the same way.
diff --git a/packages/SystemUI/res/drawable/hearing_devices_spinner_background.xml b/packages/SystemUI/res/drawable/hearing_devices_spinner_background.xml
index dfefb9d..4b7be35 100644
--- a/packages/SystemUI/res/drawable/hearing_devices_spinner_background.xml
+++ b/packages/SystemUI/res/drawable/hearing_devices_spinner_background.xml
@@ -27,18 +27,7 @@
<solid android:color="@android:color/transparent"/>
</shape>
</item>
- <item
- android:end="20dp"
- android:gravity="end|center_vertical">
- <vector
- android:width="@dimen/hearing_devices_preset_spinner_icon_size"
- android:height="@dimen/hearing_devices_preset_spinner_icon_size"
- android:viewportWidth="24"
- android:viewportHeight="24"
- android:tint="?androidprv:attr/colorControlNormal">
- <path
- android:fillColor="#FF000000"
- android:pathData="M7.41 7.84L12 12.42l4.59-4.58L18 9.25l-6 6-6-6z" />
- </vector>
- </item>
+ <item android:end="20dp"
+ android:gravity="end|center_vertical"
+ android:drawable="@drawable/ic_hearing_device_expand" />
</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_hearing_device_expand.xml b/packages/SystemUI/res/drawable/ic_hearing_device_expand.xml
new file mode 100644
index 0000000..fdfe713
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_hearing_device_expand.xml
@@ -0,0 +1,27 @@
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="@androidprv:color/materialColorOnSurface">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M7.41 7.84L12 12.42l4.59-4.58L18 9.25l-6 6-6-6z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-sw600dp/keyguard_user_switcher_item.xml b/packages/SystemUI/res/layout-sw600dp/keyguard_user_switcher_item.xml
deleted file mode 100644
index 9ce030e..0000000
--- a/packages/SystemUI/res/layout-sw600dp/keyguard_user_switcher_item.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ Copyright (C) 2020 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<!-- LinearLayout -->
-<com.android.systemui.statusbar.policy.KeyguardUserDetailItemView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:sysui="http://schemas.android.com/apk/res-auto"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="8dp"
- android:layout_marginEnd="32dp"
- android:gravity="end|center_vertical"
- android:clickable="true"
- android:background="@drawable/kg_user_switcher_rounded_bg"
- sysui:activatedTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher"
- sysui:regularTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher">
- <TextView android:id="@+id/user_name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="25dp"
- android:layout_marginEnd="12dp"
- />
- <com.android.systemui.statusbar.phone.UserAvatarView android:id="@+id/user_picture"
- android:layout_width="@dimen/kg_framed_avatar_size"
- android:layout_height="@dimen/kg_framed_avatar_size"
- android:contentDescription="@null"
- sysui:badgeDiameter="18dp"
- sysui:badgeMargin="1dp" />
-</com.android.systemui.statusbar.policy.KeyguardUserDetailItemView>
diff --git a/packages/SystemUI/res/layout/hearing_device_ambient_volume_layout.xml b/packages/SystemUI/res/layout/hearing_device_ambient_volume_layout.xml
new file mode 100644
index 0000000..fd409a5
--- /dev/null
+++ b/packages/SystemUI/res/layout/hearing_device_ambient_volume_layout.xml
@@ -0,0 +1,67 @@
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/ambient_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/bluetooth_dialog_layout_margin"
+ android:layout_marginEnd="@dimen/bluetooth_dialog_layout_margin"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+ <ImageView
+ android:id="@+id/ambient_volume_icon"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:padding="12dp"
+ android:contentDescription="@string/hearing_devices_ambient_unmute"
+ android:src="@drawable/ic_ambient_volume"
+ android:tint="@androidprv:color/materialColorOnSurface" />
+ <TextView
+ android:id="@+id/ambient_title"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingStart="10dp"
+ android:text="@string/hearing_devices_ambient_label"
+ android:textAppearance="@style/TextAppearance.Dialog.Title"
+ android:textDirection="locale"
+ android:textSize="16sp"
+ android:gravity="center_vertical"
+ android:fontFamily="@*android:string/config_headlineFontFamilyMedium" />
+ <ImageView
+ android:id="@+id/ambient_expand_icon"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:padding="10dp"
+ android:contentDescription="@string/hearing_devices_ambient_expand_controls"
+ android:src="@drawable/ic_hearing_device_expand"
+ android:tint="@androidprv:color/materialColorOnSurface" />
+ </LinearLayout>
+ <LinearLayout
+ android:id="@+id/ambient_control_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/hearing_device_ambient_volume_slider.xml b/packages/SystemUI/res/layout/hearing_device_ambient_volume_slider.xml
new file mode 100644
index 0000000..44ada89
--- /dev/null
+++ b/packages/SystemUI/res/layout/hearing_device_ambient_volume_slider.xml
@@ -0,0 +1,46 @@
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/bluetooth_dialog_layout_margin"
+ android:layout_marginEnd="@dimen/bluetooth_dialog_layout_margin"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/ambient_volume_slider_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingStart="@dimen/hearing_devices_small_title_padding_horizontal"
+ android:textAppearance="@style/TextAppearance.Dialog.Title"
+ android:textDirection="locale"
+ android:textSize="14sp"
+ android:labelFor="@+id/ambient_volume_slider"
+ android:gravity="center_vertical" />
+ <com.google.android.material.slider.Slider
+ style="@style/SystemUI.Material3.Slider"
+ android:id="@+id/ambient_volume_slider"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/bluetooth_dialog_device_height"
+ android:layout_gravity="center_vertical"
+ android:theme="@style/Theme.Material3.DayNight"
+ app:labelBehavior="gone" />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml b/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
index bf04a6f..949a6ab 100644
--- a/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
+++ b/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
@@ -85,13 +85,22 @@
android:longClickable="false"/>
</LinearLayout>
+ <com.android.systemui.accessibility.hearingaid.AmbientVolumeLayout
+ android:id="@+id/ambient_layout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/preset_layout"
+ android:layout_marginTop="@dimen/hearing_devices_layout_margin" />
+
<LinearLayout
android:id="@+id/tools_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toBottomOf="@id/preset_layout"
+ app:layout_constraintTop_toBottomOf="@id/ambient_layout"
android:layout_marginTop="@dimen/hearing_devices_layout_margin"
android:orientation="vertical">
<TextView
diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher.xml b/packages/SystemUI/res/layout/keyguard_user_switcher.xml
deleted file mode 100644
index 7aafd89..0000000
--- a/packages/SystemUI/res/layout/keyguard_user_switcher.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<!-- This is a view that shows a user switcher in Keyguard. -->
-<com.android.systemui.statusbar.policy.KeyguardUserSwitcherView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/keyguard_user_switcher_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="end">
-
- <com.android.systemui.statusbar.policy.KeyguardUserSwitcherListView
- android:id="@+id/keyguard_user_switcher_list"
- android:orientation="vertical"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_gravity="top|end"
- android:gravity="end" />
-
-</com.android.systemui.statusbar.policy.KeyguardUserSwitcherView>
diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
deleted file mode 100644
index e39f1a9..0000000
--- a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ Copyright (C) 2014 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<!-- LinearLayout -->
-<com.android.systemui.statusbar.policy.KeyguardUserDetailItemView
- android:id="@+id/user_item"
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res-auto"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="8dp"
- android:layout_marginEnd="8dp"
- android:gravity="end|center_vertical"
- android:clickable="true"
- android:background="@drawable/kg_user_switcher_rounded_bg"
- systemui:activatedTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher"
- systemui:regularTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher">
- <TextView
- android:id="@+id/user_name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="20dp"
- android:layout_marginEnd="16dp" />
- <com.android.systemui.statusbar.phone.UserAvatarView
- android:id="@+id/user_picture"
- android:layout_width="@dimen/kg_framed_avatar_size"
- android:layout_height="@dimen/kg_framed_avatar_size"
- systemui:avatarPadding="0dp"
- systemui:badgeDiameter="18dp"
- systemui:badgeMargin="1dp"
- systemui:frameWidth="0dp"
- systemui:framePadding="0dp"
- systemui:frameColor="@color/kg_user_avatar_frame" />
-</com.android.systemui.statusbar.policy.KeyguardUserDetailItemView>
diff --git a/packages/SystemUI/res/layout/ongoing_activity_chip.xml b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
index 7745af9..51217d4 100644
--- a/packages/SystemUI/res/layout/ongoing_activity_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
@@ -58,14 +58,14 @@
/>
<!-- Shows generic text. -->
- <TextView
+ <com.android.systemui.statusbar.chips.ui.view.ChipTextView
android:id="@+id/ongoing_activity_chip_text"
style="@style/StatusBar.Chip.Text.LimitedWidth"
android:visibility="gone"
/>
<!-- Shows a time delta in short form, like "15min" or "1hr". -->
- <android.widget.DateTimeView
+ <com.android.systemui.statusbar.chips.ui.view.ChipDateTimeView
android:id="@+id/ongoing_activity_chip_short_time_delta"
style="@style/StatusBar.Chip.Text.LimitedWidth"
android:visibility="gone"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 77fbb64..46a9d47 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -31,12 +31,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
- <ViewStub
- android:id="@+id/keyguard_qs_user_switch_stub"
- android:layout="@layout/keyguard_qs_user_switch"
- android:layout_height="match_parent"
- android:layout_width="match_parent" />
-
<include layout="@layout/status_bar_expanded_plugin_frame"/>
<com.android.systemui.shade.NotificationsQuickSettingsContainer
@@ -120,12 +114,6 @@
/>
</com.android.systemui.shade.NotificationsQuickSettingsContainer>
- <ViewStub
- android:id="@+id/keyguard_user_switcher_stub"
- android:layout="@layout/keyguard_user_switcher"
- android:layout_height="match_parent"
- android:layout_width="match_parent" />
-
<include layout="@layout/dock_info_bottom_area_overlay" />
<include
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 5ccedea..bad5711 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -71,6 +71,9 @@
android:layout_height="0dp"
android:layout_marginTop="@dimen/volume_dialog_floating_sliders_vertical_padding_negative"
android:layout_marginBottom="@dimen/volume_dialog_floating_sliders_vertical_padding_negative"
+ android:clipChildren="false"
+ android:clipToOutline="false"
+ android:clipToPadding="false"
android:divider="@drawable/volume_dialog_floating_sliders_spacer"
android:gravity="bottom"
android:orientation="horizontal"
diff --git a/packages/SystemUI/res/layout/volume_ringer_drawer.xml b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
index d850bbe..cd8f18f 100644
--- a/packages/SystemUI/res/layout/volume_ringer_drawer.xml
+++ b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
@@ -23,7 +23,6 @@
android:clipToPadding="false"
android:gravity="center"
android:layoutDirection="ltr"
- android:orientation="vertical"
app:layoutDescription="@xml/volume_dialog_ringer_drawer_motion_scene">
<!-- add ringer buttons here -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 35cfd08..05c4d1b 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -2106,6 +2106,8 @@
<fraction name="volume_dialog_half_opened_bias">0.2</fraction>
+ <dimen name="volume_dialog_slider_max_deviation">56dp</dimen>
+
<dimen name="volume_dialog_background_square_corner_radius">12dp</dimen>
<dimen name="volume_dialog_ringer_drawer_button_size">@dimen/volume_dialog_button_size</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d3ee63ba..c3d84ff 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1004,6 +1004,20 @@
<string name="hearing_devices_preset_label">Preset</string>
<!-- QuickSettings: Content description for the icon that indicates the item is selected [CHAR LIMIT=NONE]-->
<string name="hearing_devices_spinner_item_selected">Selected</string>
+ <!-- QuickSettings: Title for ambient controls. [CHAR LIMIT=40]-->
+ <string name="hearing_devices_ambient_label">Surroundings</string>
+ <!-- QuickSettings: The text to show the control is for left side device. [CHAR LIMIT=30] -->
+ <string name="hearing_devices_ambient_control_left">Left</string>
+ <!-- QuickSettings: The text to show the control is for right side device. [CHAR LIMIT=30] -->
+ <string name="hearing_devices_ambient_control_right">Right</string>
+ <!-- QuickSettings: Content description for a button, that expands ambient volume sliders [CHAR_LIMIT=NONE] -->
+ <string name="hearing_devices_ambient_expand_controls">Expand to left and right separated controls</string>
+ <!-- QuickSettings: Content description for a button, that collapses ambient volume sliders [CHAR LIMIT=NONE] -->
+ <string name="hearing_devices_ambient_collapse_controls">Collapse to unified control</string>
+ <!-- QuickSettings: Content description for a button, that mute ambient volume [CHAR_LIMIT=NONE] -->
+ <string name="hearing_devices_ambient_mute">Mute surroundings</string>
+ <!-- QuickSettings: Content description for a button, that unmute ambient volume [CHAR LIMIT=NONE] -->
+ <string name="hearing_devices_ambient_unmute">Unmute surroundings</string>
<!-- QuickSettings: Title for related tools of hearing. [CHAR LIMIT=40]-->
<string name="hearing_devices_tools_label">Tools</string>
<!-- QuickSettings: Tool name for hearing devices dialog related tools [CHAR LIMIT=40] [BACKUP_MESSAGE_ID=8916875614623730005]-->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index f6c1ecea..7180678 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -91,7 +91,6 @@
<item name="android:ellipsize">none</item>
<item name="android:requiresFadingEdge">horizontal</item>
<item name="android:fadingEdgeLength">@dimen/ongoing_activity_chip_text_fading_edge_length</item>
- <item name="android:maxWidth">@dimen/ongoing_activity_chip_max_text_width</item>
</style>
<style name="Chipbar" />
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
index 8298397..69f5a79 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
@@ -108,7 +108,8 @@
}
@Override
- public synchronized boolean onFail(String className, String methodName, LinkageError failure) {
+ public synchronized boolean onFail(String className, String methodName, Throwable failure) {
+ Log.e(TAG, "Failure from " + mPlugin + ". Disabling Plugin.");
mHasError = true;
unloadPlugin();
mListener.onPluginDetached(this);
@@ -118,7 +119,7 @@
/** Alerts listener and plugin that the plugin has been created. */
public synchronized void onCreate() {
if (mHasError) {
- log("Previous LinkageError detected for plugin class");
+ log("Previous Fatal Exception detected for plugin class");
return;
}
@@ -175,7 +176,7 @@
*/
public synchronized void loadPlugin() {
if (mHasError) {
- log("Previous LinkageError detected for plugin class");
+ log("Previous Fatal Exception detected for plugin class");
return;
}
diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt
index f9fe67a..c13bb3e 100644
--- a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt
+++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt
@@ -67,7 +67,7 @@
namespace: String = "systemui",
default: Boolean = false
): SysPropBooleanFlag {
- val flag = SysPropBooleanFlag(name = name, namespace = "systemui", default = default)
+ val flag = SysPropBooleanFlag(name = name, namespace, default = default)
checkForDupesAndAdd(flag)
return flag
}
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardQsUserSwitchComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardQsUserSwitchComponent.java
index 4331f52..a887011 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardQsUserSwitchComponent.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardQsUserSwitchComponent.java
@@ -29,7 +29,7 @@
@Subcomponent(modules = {KeyguardUserSwitcherModule.class})
@KeyguardUserSwitcherScope
public interface KeyguardQsUserSwitchComponent {
- /** Simple factory for {@link KeyguardUserSwitcherComponent}. */
+ /** Simple factory for {@link KeyguardQsUserSwitchComponent}. */
@Subcomponent.Factory
interface Factory {
KeyguardQsUserSwitchComponent build(@BindsInstance FrameLayout userAvatarContainer);
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherComponent.java
deleted file mode 100644
index 730c14d..0000000
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherComponent.java
+++ /dev/null
@@ -1,40 +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.keyguard.dagger;
-
-import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController;
-import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
-
-import dagger.BindsInstance;
-import dagger.Subcomponent;
-
-/**
- * Subcomponent for helping work with KeyguardUserSwitcher and its children.
- */
-@Subcomponent(modules = {KeyguardUserSwitcherModule.class})
-@KeyguardUserSwitcherScope
-public interface KeyguardUserSwitcherComponent {
- /** Simple factory for {@link KeyguardUserSwitcherComponent}. */
- @Subcomponent.Factory
- interface Factory {
- KeyguardUserSwitcherComponent build(
- @BindsInstance KeyguardUserSwitcherView keyguardUserSwitcherView);
- }
-
- /** Builds a {@link com.android.systemui.statusbar.policy.KeyguardUserSwitcherController}. */
- KeyguardUserSwitcherController getKeyguardUserSwitcherController();
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherModule.java
index b9184f4..1ad2d4c 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherModule.java
@@ -18,7 +18,7 @@
import dagger.Module;
-/** Dagger module for {@link KeyguardUserSwitcherComponent}. */
+/** Dagger module for {@link KeyguardQsUserSwitchComponent}. */
@Module
public abstract class KeyguardUserSwitcherModule {
}
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherScope.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherScope.java
index 864472e..12d1949 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherScope.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherScope.java
@@ -24,7 +24,7 @@
import javax.inject.Scope;
/**
- * Scope annotation for singleton items within the KeyguardUserSwitcherComponent.
+ * Scope annotation for singleton items within the KeyguardQsUserSwitchComponent.
*/
@Documented
@Retention(RUNTIME)
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayout.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayout.java
new file mode 100644
index 0000000..7c141c1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayout.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.accessibility.hearingaid;
+
+import static com.android.settingslib.bluetooth.HearingAidInfo.DeviceSide.SIDE_LEFT;
+import static com.android.settingslib.bluetooth.HearingAidInfo.DeviceSide.SIDE_RIGHT;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settingslib.bluetooth.AmbientVolumeUi;
+import com.android.systemui.res.R;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.primitives.Ints;
+
+import java.util.Map;
+
+/**
+ * A view of ambient volume controls.
+ *
+ * <p> It consists of a header with an expand icon and volume sliders for unified control and
+ * separated control for devices in the same set. Toggle the expand icon will make the UI switch
+ * between unified and separated control.
+ */
+public class AmbientVolumeLayout extends LinearLayout implements AmbientVolumeUi {
+
+ @Nullable
+ private AmbientVolumeUiListener mListener;
+ private ImageView mExpandIcon;
+ private ImageView mVolumeIcon;
+ private boolean mExpandable = true;
+ private boolean mExpanded = false;
+ private boolean mMutable = false;
+ private boolean mMuted = false;
+ private final BiMap<Integer, AmbientVolumeSlider> mSideToSliderMap = HashBiMap.create();
+ private int mVolumeLevel = AMBIENT_VOLUME_LEVEL_DEFAULT;
+
+ private final AmbientVolumeSlider.OnChangeListener mSliderOnChangeListener =
+ (slider, value) -> {
+ if (mListener != null) {
+ final int side = mSideToSliderMap.inverse().get(slider);
+ mListener.onSliderValueChange(side, value);
+ }
+ };
+
+ public AmbientVolumeLayout(@Nullable Context context) {
+ this(context, /* attrs= */ null);
+ }
+
+ public AmbientVolumeLayout(@Nullable Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, /* defStyleAttr= */ 0);
+ }
+
+ public AmbientVolumeLayout(@Nullable Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr) {
+ this(context, attrs, defStyleAttr, /* defStyleRes= */ 0);
+ }
+
+ public AmbientVolumeLayout(@Nullable Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ inflate(context, R.layout.hearing_device_ambient_volume_layout, /* root= */ this);
+ init();
+ }
+
+ private void init() {
+ mVolumeIcon = requireViewById(R.id.ambient_volume_icon);
+ mVolumeIcon.setImageResource(com.android.settingslib.R.drawable.ic_ambient_volume);
+ mVolumeIcon.setOnClickListener(v -> {
+ if (!mMutable) {
+ return;
+ }
+ setMuted(!mMuted);
+ if (mListener != null) {
+ mListener.onAmbientVolumeIconClick();
+ }
+ });
+ updateVolumeIcon();
+
+ mExpandIcon = requireViewById(R.id.ambient_expand_icon);
+ mExpandIcon.setOnClickListener(v -> {
+ setExpanded(!mExpanded);
+ if (mListener != null) {
+ mListener.onExpandIconClick();
+ }
+ });
+ updateExpandIcon();
+ }
+
+ @Override
+ public void setVisible(boolean visible) {
+ setVisibility(visible ? VISIBLE : GONE);
+ }
+
+ @Override
+ public void setExpandable(boolean expandable) {
+ mExpandable = expandable;
+ if (!mExpandable) {
+ setExpanded(false);
+ }
+ updateExpandIcon();
+ }
+
+ @Override
+ public boolean isExpandable() {
+ return mExpandable;
+ }
+
+ @Override
+ public void setExpanded(boolean expanded) {
+ if (!mExpandable && expanded) {
+ return;
+ }
+ mExpanded = expanded;
+ updateExpandIcon();
+ updateLayout();
+ }
+
+ @Override
+ public boolean isExpanded() {
+ return mExpanded;
+ }
+
+ @Override
+ public void setMutable(boolean mutable) {
+ mMutable = mutable;
+ if (!mMutable) {
+ mVolumeLevel = AMBIENT_VOLUME_LEVEL_DEFAULT;
+ setMuted(false);
+ }
+ updateVolumeIcon();
+ }
+
+ @Override
+ public boolean isMutable() {
+ return mMutable;
+ }
+
+ @Override
+ public void setMuted(boolean muted) {
+ if (!mMutable && muted) {
+ return;
+ }
+ mMuted = muted;
+ if (mMutable && mMuted) {
+ for (AmbientVolumeSlider slider : mSideToSliderMap.values()) {
+ slider.setValue(slider.getMin());
+ }
+ }
+ updateVolumeIcon();
+ }
+
+ @Override
+ public boolean isMuted() {
+ return mMuted;
+ }
+
+ @Override
+ public void setListener(@Nullable AmbientVolumeUiListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void setupSliders(@NonNull Map<Integer, BluetoothDevice> sideToDeviceMap) {
+ sideToDeviceMap.forEach((side, device) -> createSlider(side));
+ createSlider(SIDE_UNIFIED);
+
+ LinearLayout controlContainer = requireViewById(R.id.ambient_control_container);
+ controlContainer.removeAllViews();
+ if (!mSideToSliderMap.isEmpty()) {
+ for (int side : VALID_SIDES) {
+ final AmbientVolumeSlider slider = mSideToSliderMap.get(side);
+ if (slider != null) {
+ controlContainer.addView(slider);
+ }
+ }
+ }
+ updateLayout();
+ }
+
+ @Override
+ public void setSliderEnabled(int side, boolean enabled) {
+ AmbientVolumeSlider slider = mSideToSliderMap.get(side);
+ if (slider != null && slider.isEnabled() != enabled) {
+ slider.setEnabled(enabled);
+ updateLayout();
+ }
+ }
+
+ @Override
+ public void setSliderValue(int side, int value) {
+ AmbientVolumeSlider slider = mSideToSliderMap.get(side);
+ if (slider != null && slider.getValue() != value) {
+ slider.setValue(value);
+ updateVolumeLevel();
+ }
+ }
+
+ @Override
+ public void setSliderRange(int side, int min, int max) {
+ AmbientVolumeSlider slider = mSideToSliderMap.get(side);
+ if (slider != null) {
+ slider.setMin(min);
+ slider.setMax(max);
+ }
+ }
+
+ @Override
+ public void updateLayout() {
+ mSideToSliderMap.forEach((side, slider) -> {
+ if (side == SIDE_UNIFIED) {
+ slider.setVisibility(mExpanded ? GONE : VISIBLE);
+ } else {
+ slider.setVisibility(mExpanded ? VISIBLE : GONE);
+ }
+ if (!slider.isEnabled()) {
+ slider.setValue(slider.getMin());
+ }
+ });
+ updateVolumeLevel();
+ }
+
+ private void updateVolumeLevel() {
+ int leftLevel, rightLevel;
+ if (mExpanded) {
+ leftLevel = getVolumeLevel(SIDE_LEFT);
+ rightLevel = getVolumeLevel(SIDE_RIGHT);
+ } else {
+ final int unifiedLevel = getVolumeLevel(SIDE_UNIFIED);
+ leftLevel = unifiedLevel;
+ rightLevel = unifiedLevel;
+ }
+ mVolumeLevel = Ints.constrainToRange(leftLevel * 5 + rightLevel,
+ AMBIENT_VOLUME_LEVEL_MIN, AMBIENT_VOLUME_LEVEL_MAX);
+ updateVolumeIcon();
+ }
+
+ private int getVolumeLevel(int side) {
+ AmbientVolumeSlider slider = mSideToSliderMap.get(side);
+ if (slider == null || !slider.isEnabled()) {
+ return 0;
+ }
+ return slider.getVolumeLevel();
+ }
+
+ private void updateExpandIcon() {
+ mExpandIcon.setVisibility(mExpandable ? VISIBLE : GONE);
+ mExpandIcon.setRotation(mExpanded ? ROTATION_EXPANDED : ROTATION_COLLAPSED);
+ if (mExpandable) {
+ final int stringRes = mExpanded ? R.string.hearing_devices_ambient_collapse_controls
+ : R.string.hearing_devices_ambient_expand_controls;
+ mExpandIcon.setContentDescription(mContext.getString(stringRes));
+ } else {
+ mExpandIcon.setContentDescription(null);
+ }
+ }
+
+ private void updateVolumeIcon() {
+ mVolumeIcon.setImageLevel(mMuted ? 0 : mVolumeLevel);
+ if (mMutable) {
+ final int stringRes = mMuted ? R.string.hearing_devices_ambient_unmute
+ : R.string.hearing_devices_ambient_mute;
+ mVolumeIcon.setContentDescription(mContext.getString(stringRes));
+ mVolumeIcon.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+ } else {
+ mVolumeIcon.setContentDescription(null);
+ mVolumeIcon.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
+ }
+ }
+
+ private void createSlider(int side) {
+ if (mSideToSliderMap.containsKey(side)) {
+ return;
+ }
+ AmbientVolumeSlider slider = new AmbientVolumeSlider(mContext);
+ slider.addOnChangeListener(mSliderOnChangeListener);
+ if (side == SIDE_LEFT) {
+ slider.setTitle(mContext.getString(R.string.hearing_devices_ambient_control_left));
+ } else if (side == SIDE_RIGHT) {
+ slider.setTitle(mContext.getString(R.string.hearing_devices_ambient_control_right));
+ }
+ mSideToSliderMap.put(side, slider);
+ }
+
+ @VisibleForTesting
+ ImageView getVolumeIcon() {
+ return mVolumeIcon;
+ }
+
+ @VisibleForTesting
+ ImageView getExpandIcon() {
+ return mExpandIcon;
+ }
+
+ @VisibleForTesting
+ Map<Integer, AmbientVolumeSlider> getSliders() {
+ return mSideToSliderMap;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeSlider.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeSlider.java
new file mode 100644
index 0000000..92338ef
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeSlider.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.accessibility.hearingaid;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.systemui.res.R;
+
+import com.google.android.material.slider.Slider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A view of ambient volume slider.
+ * <p> It consists by a title {@link TextView} with a volume control {@link Slider}.
+ */
+public class AmbientVolumeSlider extends LinearLayout {
+
+ private final TextView mTitle;
+ private final Slider mSlider;
+ private final List<OnChangeListener> mChangeListeners = new ArrayList<>();
+ private final Slider.OnSliderTouchListener mSliderTouchListener =
+ new Slider.OnSliderTouchListener() {
+ @Override
+ public void onStartTrackingTouch(@NonNull Slider slider) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(@NonNull Slider slider) {
+ final int value = Math.round(slider.getValue());
+ for (OnChangeListener listener : mChangeListeners) {
+ listener.onValueChange(AmbientVolumeSlider.this, value);
+ }
+ }
+ };
+ public AmbientVolumeSlider(@Nullable Context context) {
+ this(context, /* attrs= */ null);
+ }
+
+ public AmbientVolumeSlider(@Nullable Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, /* defStyleAttr= */ 0);
+ }
+
+ public AmbientVolumeSlider(@Nullable Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr) {
+ this(context, attrs, defStyleAttr, /* defStyleRes= */ 0);
+ }
+
+ public AmbientVolumeSlider(@Nullable Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
+ inflate(context, R.layout.hearing_device_ambient_volume_slider, /* root= */ this);
+ mTitle = requireViewById(R.id.ambient_volume_slider_title);
+ mSlider = requireViewById(R.id.ambient_volume_slider);
+ mSlider.addOnSliderTouchListener(mSliderTouchListener);
+ }
+
+ /**
+ * Sets title for the ambient volume slider.
+ * <p> If text is null or empty, then {@link TextView} is hidden.
+ */
+ public void setTitle(@Nullable String text) {
+ mTitle.setText(text);
+ mTitle.setVisibility(TextUtils.isEmpty(text) ? GONE : VISIBLE);
+ }
+
+ /** Gets title for the ambient volume slider. */
+ public CharSequence getTitle() {
+ return mTitle.getText();
+ }
+
+ /**
+ * Adds the callback to the ambient volume slider to get notified when the value is changed by
+ * user.
+ * <p> Note: The {@link OnChangeListener#onValueChange(AmbientVolumeSlider, int)} will be
+ * called when user's finger take off from the slider.
+ */
+ public void addOnChangeListener(@Nullable OnChangeListener listener) {
+ if (listener == null) {
+ return;
+ }
+ mChangeListeners.add(listener);
+ }
+
+ /** Sets max value to the ambient volume slider. */
+ public void setMax(float max) {
+ mSlider.setValueTo(max);
+ }
+
+ /** Gets max value from the ambient volume slider. */
+ public float getMax() {
+ return mSlider.getValueTo();
+ }
+
+ /** Sets min value to the ambient volume slider. */
+ public void setMin(float min) {
+ mSlider.setValueFrom(min);
+ }
+
+ /** Gets min value from the ambient volume slider. */
+ public float getMin() {
+ return mSlider.getValueFrom();
+ }
+
+ /** Sets value to the ambient volume slider. */
+ public void setValue(float value) {
+ mSlider.setValue(value);
+ }
+
+ /** Gets value from the ambient volume slider. */
+ public float getValue() {
+ return mSlider.getValue();
+ }
+
+ /** Sets the enable state to the ambient volume slider. */
+ public void setEnabled(boolean enabled) {
+ mSlider.setEnabled(enabled);
+ }
+
+ /** Gets the enable state of the ambient volume slider. */
+ public boolean isEnabled() {
+ return mSlider.isEnabled();
+ }
+
+ /**
+ * Gets the volume value of the ambient volume slider.
+ * <p> The volume level is divided into 5 levels:
+ * Level 0 corresponds to the minimum volume value. The range between the minimum and maximum
+ * volume is divided into 4 equal intervals, represented by levels 1 to 4.
+ */
+ public int getVolumeLevel() {
+ if (!mSlider.isEnabled()) {
+ return 0;
+ }
+ final double min = mSlider.getValueFrom();
+ final double max = mSlider.getValueTo();
+ final double levelGap = (max - min) / 4.0;
+ final double value = mSlider.getValue();
+ return (int) Math.ceil((value - min) / levelGap);
+ }
+
+ /** Interface definition for a callback invoked when a slider's value is changed. */
+ public interface OnChangeListener {
+ /** Called when the finger is take off from the slider. */
+ void onValueChange(@NonNull AmbientVolumeSlider slider, int value);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
index 56435df..73aabc3 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
@@ -52,10 +52,12 @@
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+import com.android.settingslib.bluetooth.AmbientVolumeUiController;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.accessibility.hearingaid.HearingDevicesListAdapter.HearingDeviceItemCallback;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.bluetooth.qsdialog.ActiveHearingDeviceItemFactory;
@@ -108,7 +110,6 @@
private SystemUIDialog mDialog;
- private RecyclerView mDeviceList;
private List<DeviceItem> mHearingDeviceItemList;
private HearingDevicesListAdapter mDeviceListAdapter;
@@ -134,6 +135,8 @@
}
};
+ private AmbientVolumeUiController mAmbientController;
+
private final List<DeviceItemFactory> mHearingDeviceItemFactoryList = List.of(
new ActiveHearingDeviceItemFactory(),
new AvailableHearingDeviceItemFactory(),
@@ -225,13 +228,17 @@
public void onActiveDeviceChanged(@Nullable CachedBluetoothDevice activeDevice,
int bluetoothProfile) {
refreshDeviceUi();
- if (mPresetController != null) {
- mPresetController.setDevice(getActiveHearingDevice());
- mMainHandler.post(() -> {
+ mMainHandler.post(() -> {
+ CachedBluetoothDevice device = getActiveHearingDevice();
+ if (mPresetController != null) {
+ mPresetController.setDevice(device);
mPresetLayout.setVisibility(
mPresetController.isPresetControlAvailable() ? VISIBLE : GONE);
- });
- }
+ }
+ if (mAmbientController != null) {
+ mAmbientController.loadDevice(device);
+ }
+ });
}
@Override
@@ -272,13 +279,13 @@
}
mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_DIALOG_SHOW, mLaunchSourceId);
- mDeviceList = dialog.requireViewById(R.id.device_list);
- mPresetLayout = dialog.requireViewById(R.id.preset_layout);
- mPresetSpinner = dialog.requireViewById(R.id.preset_spinner);
setupDeviceListView(dialog);
- setupPresetSpinner(dialog);
setupPairNewDeviceButton(dialog);
+ setupPresetSpinner(dialog);
+ if (com.android.settingslib.flags.Flags.hearingDevicesAmbientVolumeControl()) {
+ setupAmbientControls();
+ }
if (com.android.systemui.Flags.hearingDevicesDialogRelatedTools()) {
setupRelatedToolsView(dialog);
}
@@ -286,41 +293,50 @@
@Override
public void onStart(@NonNull SystemUIDialog dialog) {
- if (mLocalBluetoothManager == null) {
- return;
- }
- mLocalBluetoothManager.getEventManager().registerCallback(this);
- if (mPresetController != null) {
- mPresetController.registerHapCallback();
- }
+ ThreadUtils.postOnBackgroundThread(() -> {
+ if (mLocalBluetoothManager != null) {
+ mLocalBluetoothManager.getEventManager().registerCallback(this);
+ }
+ if (mPresetController != null) {
+ mPresetController.registerHapCallback();
+ }
+ if (mAmbientController != null) {
+ mAmbientController.start();
+ }
+ });
}
@Override
public void onStop(@NonNull SystemUIDialog dialog) {
- if (mLocalBluetoothManager == null) {
- return;
- }
-
- if (mPresetController != null) {
- mPresetController.unregisterHapCallback();
- }
- mLocalBluetoothManager.getEventManager().unregisterCallback(this);
+ ThreadUtils.postOnBackgroundThread(() -> {
+ if (mLocalBluetoothManager != null) {
+ mLocalBluetoothManager.getEventManager().unregisterCallback(this);
+ }
+ if (mPresetController != null) {
+ mPresetController.unregisterHapCallback();
+ }
+ if (mAmbientController != null) {
+ mAmbientController.stop();
+ }
+ });
}
private void setupDeviceListView(SystemUIDialog dialog) {
- mDeviceList.setLayoutManager(new LinearLayoutManager(dialog.getContext()));
+ final RecyclerView deviceList = dialog.requireViewById(R.id.device_list);
+ deviceList.setLayoutManager(new LinearLayoutManager(dialog.getContext()));
mHearingDeviceItemList = getHearingDeviceItemList();
mDeviceListAdapter = new HearingDevicesListAdapter(mHearingDeviceItemList, this);
- mDeviceList.setAdapter(mDeviceListAdapter);
+ deviceList.setAdapter(mDeviceListAdapter);
}
private void setupPresetSpinner(SystemUIDialog dialog) {
mPresetController = new HearingDevicesPresetsController(mProfileManager, mPresetCallback);
mPresetController.setDevice(getActiveHearingDevice());
+ mPresetSpinner = dialog.requireViewById(R.id.preset_spinner);
mPresetInfoAdapter = new HearingDevicesSpinnerAdapter(dialog.getContext());
mPresetSpinner.setAdapter(mPresetInfoAdapter);
- // disable redundant Touch & Hold accessibility action for Switch Access
+ // Disable redundant Touch & Hold accessibility action for Switch Access
mPresetSpinner.setAccessibilityDelegate(new View.AccessibilityDelegate() {
@Override
public void onInitializeAccessibilityNodeInfo(@NonNull View host,
@@ -349,12 +365,20 @@
}
});
+ mPresetLayout = dialog.requireViewById(R.id.preset_layout);
mPresetLayout.setVisibility(mPresetController.isPresetControlAvailable() ? VISIBLE : GONE);
}
+ private void setupAmbientControls() {
+ final AmbientVolumeLayout ambientLayout = mDialog.requireViewById(R.id.ambient_layout);
+ mAmbientController = new AmbientVolumeUiController(
+ mDialog.getContext(), mLocalBluetoothManager, ambientLayout);
+ mAmbientController.setShowUiWhenLocalDataExist(false);
+ mAmbientController.loadDevice(getActiveHearingDevice());
+ }
+
private void setupPairNewDeviceButton(SystemUIDialog dialog) {
final Button pairButton = dialog.requireViewById(R.id.pair_new_device_button);
-
pairButton.setVisibility(mShowPairNewDevice ? VISIBLE : GONE);
if (mShowPairNewDevice) {
pairButton.setOnClickListener(v -> {
diff --git a/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
index 47910f3..11a6cb9 100644
--- a/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
@@ -114,9 +114,6 @@
shadeBackActionInteractor.animateCollapseQs(false)
return true
}
- if (shadeBackActionInteractor.closeUserSwitcherIfOpen()) {
- return true
- }
if (glanceableHubBackAction()) {
if (communalBackActionInteractor.canBeDismissed()) {
communalBackActionInteractor.onBackPressed()
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalAppWidgetViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalAppWidgetViewModel.kt
index 6bafd14f..051cb41 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalAppWidgetViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalAppWidgetViewModel.kt
@@ -83,7 +83,7 @@
}
private suspend fun handleSetListener(appWidgetId: Int, listener: AppWidgetHostListener) =
- withContextTraced("$TAG#setListenerInner", backgroundContext) {
+ withContextTraced("${TAG}_$appWidgetId#setListenerInner", backgroundContext) {
if (
multiUserHelper.glanceableHubHsumFlagEnabled &&
multiUserHelper.isInHeadlessSystemUser()
@@ -92,13 +92,19 @@
// remotely in the foreground user, and therefore the host listener needs to be
// registered through the widget manager.
with(glanceableHubWidgetManagerLazy.get()) {
- setAppWidgetHostListener(appWidgetId, listenerDelegateFactory.create(listener))
+ setAppWidgetHostListener(
+ appWidgetId,
+ listenerDelegateFactory.create("${TAG}_$appWidgetId", listener),
+ )
}
} else {
// Instead of setting the view as the listener directly, we wrap the view in a
// delegate which ensures the callbacks always get called on the main thread.
with(appWidgetHostLazy.get()) {
- setListener(appWidgetId, listenerDelegateFactory.create(listener))
+ setListener(
+ appWidgetId,
+ listenerDelegateFactory.create("${TAG}_$appWidgetId", listener),
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/AppWidgetHostListenerDelegate.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/AppWidgetHostListenerDelegate.kt
index 7d80acd..c0f7caa 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/AppWidgetHostListenerDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/AppWidgetHostListenerDelegate.kt
@@ -19,11 +19,12 @@
import android.appwidget.AppWidgetHost.AppWidgetHostListener
import android.appwidget.AppWidgetProviderInfo
import android.widget.RemoteViews
-import com.android.systemui.dagger.qualifiers.Main
+import com.android.app.tracing.coroutines.launchTraced
+import com.android.systemui.dagger.qualifiers.Application
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
-import java.util.concurrent.Executor
+import kotlinx.coroutines.CoroutineScope
/**
* Wrapper for an [AppWidgetHostListener] to ensure the callbacks are executed on the main thread.
@@ -31,24 +32,27 @@
class AppWidgetHostListenerDelegate
@AssistedInject
constructor(
- @Main private val mainExecutor: Executor,
+ @Application private val mainScope: CoroutineScope,
+ @Assisted private val tag: String,
@Assisted private val listener: AppWidgetHostListener,
) : AppWidgetHostListener {
@AssistedFactory
fun interface Factory {
- fun create(listener: AppWidgetHostListener): AppWidgetHostListenerDelegate
+ fun create(tag: String, listener: AppWidgetHostListener): AppWidgetHostListenerDelegate
}
override fun onUpdateProviderInfo(appWidget: AppWidgetProviderInfo?) {
- mainExecutor.execute { listener.onUpdateProviderInfo(appWidget) }
+ mainScope.launchTraced("$tag#onUpdateProviderInfo") {
+ listener.onUpdateProviderInfo(appWidget)
+ }
}
override fun updateAppWidget(views: RemoteViews?) {
- mainExecutor.execute { listener.updateAppWidget(views) }
+ mainScope.launchTraced("$tag#updateAppWidget") { listener.updateAppWidget(views) }
}
override fun onViewDataChanged(viewId: Int) {
- mainExecutor.execute { listener.onViewDataChanged(viewId) }
+ mainScope.launchTraced("$tag#onViewDataChanged") { listener.onViewDataChanged(viewId) }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/compose/ComposeModule.kt b/packages/SystemUI/src/com/android/systemui/compose/ComposeModule.kt
new file mode 100644
index 0000000..31b6f0f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/compose/ComposeModule.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.compose
+
+import com.android.systemui.CoreStartable
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+
+@Module
+interface ComposeModule {
+ @Binds
+ @IntoMap
+ @ClassKey(ComposeTracingStartable::class)
+ fun composeTracing(impl: ComposeTracingStartable): CoreStartable
+}
diff --git a/packages/SystemUI/src/com/android/systemui/compose/ComposeTracingStartable.kt b/packages/SystemUI/src/com/android/systemui/compose/ComposeTracingStartable.kt
new file mode 100644
index 0000000..a015900
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/compose/ComposeTracingStartable.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(InternalComposeTracingApi::class)
+
+package com.android.systemui.compose
+
+import android.os.Trace
+import android.util.Log
+import androidx.compose.runtime.Composer
+import androidx.compose.runtime.CompositionTracer
+import androidx.compose.runtime.InternalComposeTracingApi
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.commandline.Command
+import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.statusbar.commandline.ParseableCommand
+import java.io.PrintWriter
+import javax.inject.Inject
+
+private const val TAG = "ComposeTracingStartable"
+private const val COMMAND_NAME = "composition-tracing"
+private const val SUBCOMMAND_ENABLE = "enable"
+private const val SUBCOMMAND_DISABLE = "disable"
+
+/**
+ * Sets up a [Command] to enable or disable Composition tracing.
+ *
+ * Usage:
+ * ```
+ * adb shell cmd statusbar composition-tracing [enable|disable]
+ * ${ANDROID_BUILD_TOP}/external/perfetto/tools/record_android_trace -c ${ANDROID_BUILD_TOP}/prebuilts/tools/linux-x86_64/perfetto/configs/trace_config_detailed.textproto
+ * ```
+ */
+@SysUISingleton
+class ComposeTracingStartable @Inject constructor(private val commandRegistry: CommandRegistry) :
+ CoreStartable {
+ @OptIn(InternalComposeTracingApi::class)
+ override fun start() {
+ Log.i(TAG, "Set up Compose tracing command")
+ commandRegistry.registerCommand(COMMAND_NAME) { CompositionTracingCommand() }
+ }
+}
+
+private class CompositionTracingCommand : ParseableCommand(COMMAND_NAME) {
+ val enable by subCommand(EnableCommand())
+ val disable by subCommand(DisableCommand())
+
+ override fun execute(pw: PrintWriter) {
+ if ((enable != null) xor (disable != null)) {
+ enable?.execute(pw)
+ disable?.execute(pw)
+ } else {
+ help(pw)
+ }
+ }
+}
+
+private class EnableCommand : ParseableCommand(SUBCOMMAND_ENABLE) {
+ override fun execute(pw: PrintWriter) {
+ val msg = "Enabled Composition tracing"
+ Log.i(TAG, msg)
+ pw.println(msg)
+ enableCompositionTracing()
+ }
+
+ private fun enableCompositionTracing() {
+ Composer.setTracer(
+ object : CompositionTracer {
+ override fun traceEventStart(key: Int, dirty1: Int, dirty2: Int, info: String) {
+ Trace.traceBegin(Trace.TRACE_TAG_APP, info)
+ }
+
+ override fun traceEventEnd() = Trace.traceEnd(Trace.TRACE_TAG_APP)
+
+ override fun isTraceInProgress(): Boolean = Trace.isEnabled()
+ }
+ )
+ }
+}
+
+private class DisableCommand : ParseableCommand(SUBCOMMAND_DISABLE) {
+ override fun execute(pw: PrintWriter) {
+ val msg = "Disabled Composition tracing"
+ Log.i(TAG, msg)
+ pw.println(msg)
+ disableCompositionTracing()
+ }
+
+ private fun disableCompositionTracing() {
+ Composer.setTracer(null)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index d6f8957..7ebe52f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -54,6 +54,7 @@
import com.android.systemui.common.usagestats.data.CommonUsageStatsDataLayerModule;
import com.android.systemui.communal.dagger.CommunalModule;
import com.android.systemui.complication.dagger.ComplicationComponent;
+import com.android.systemui.compose.ComposeModule;
import com.android.systemui.controls.dagger.ControlsModule;
import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.dagger.qualifiers.Main;
@@ -133,6 +134,7 @@
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider;
import com.android.systemui.statusbar.notification.people.PeopleHubModule;
import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent;
@@ -141,7 +143,6 @@
import com.android.systemui.statusbar.phone.ConfigurationControllerModule;
import com.android.systemui.statusbar.phone.LetterboxModule;
import com.android.systemui.statusbar.pipeline.dagger.StatusBarPipelineModule;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.PolicyModule;
import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController;
@@ -214,6 +215,7 @@
ClockRegistryModule.class,
CommunalModule.class,
CommonDataLayerModule.class,
+ ComposeModule.class,
ConfigurationModule.class,
ConfigurationRepositoryModule.class,
CommonUsageStatsDataLayerModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt b/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt
index eaf5eac..73968da 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt
@@ -126,24 +126,24 @@
}
class ServerFlagReaderFake : ServerFlagReader {
- private val flagMap: MutableMap<String, Boolean> = mutableMapOf()
+ private val flagMap: MutableMap<Pair<String, String>, Boolean> = mutableMapOf()
private val listeners =
mutableListOf<Pair<ServerFlagReader.ChangeListener, Collection<Flag<*>>>>()
override fun hasOverride(namespace: String, name: String): Boolean {
- return flagMap.containsKey(name)
+ return flagMap.containsKey(namespace to name)
}
override fun readServerOverride(namespace: String, name: String, default: Boolean): Boolean {
- return flagMap.getOrDefault(name, default)
+ return flagMap.getOrDefault(namespace to name, default)
}
fun setFlagValue(namespace: String, name: String, value: Boolean) {
- flagMap.put(name, value)
+ flagMap.put(namespace to name, value)
for ((listener, flags) in listeners) {
flagLoop@ for (flag in flags) {
- if (name == flag.name) {
+ if (namespace == flag.namespace && name == flag.name) {
listener.onChange(flag, if (value) "true" else "false")
break@flagLoop
}
@@ -152,13 +152,13 @@
}
fun eraseFlag(namespace: String, name: String) {
- flagMap.remove(name)
+ flagMap.remove(namespace to name)
}
override fun listenForChanges(
flags: Collection<Flag<*>>,
listener: ServerFlagReader.ChangeListener
) {
- listeners.add(Pair(listener, flags))
+ listeners.add(listener to flags)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/grid/ui/compose/SpannedGrids.kt b/packages/SystemUI/src/com/android/systemui/grid/ui/compose/SpannedGrids.kt
index 62ab18b..9e89ed9 100644
--- a/packages/SystemUI/src/com/android/systemui/grid/ui/compose/SpannedGrids.kt
+++ b/packages/SystemUI/src/com/android/systemui/grid/ui/compose/SpannedGrids.kt
@@ -17,26 +17,40 @@
package com.android.systemui.grid.ui.compose
import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.BoxScope
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.SideEffect
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.IntrinsicMeasurable
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.Placeable
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.ParentDataModifierNode
import androidx.compose.ui.semantics.CollectionInfo
-import androidx.compose.ui.semantics.CollectionItemInfo
import androidx.compose.ui.semantics.collectionInfo
-import androidx.compose.ui.semantics.collectionItemInfo
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.fastMap
+import androidx.compose.ui.util.fastMapIndexed
import kotlin.math.max
+/** Creates a [SpannedGridState] that is remembered across recompositions. */
+@Composable
+fun rememberSpannedGridState(): SpannedGridState {
+ return remember { SpannedGridStateImpl() }
+}
+
/**
- * Horizontal (non lazy) grid that supports [spans] for its elements.
+ * Horizontal (non lazy) grid that supports spans for its elements.
*
* The elements will be laid down vertically first, and then by columns. So assuming LTR layout, it
* will be (for a span list `[2, 1, 2, 1, 1, 1, 1, 1]` and 4 rows):
@@ -50,8 +64,11 @@
* where repeated numbers show larger span. If an element doesn't fit in a column due to its span,
* it will start a new column.
*
- * Elements in [spans] must be in the interval `[1, rows]` ([rows] > 0), and the composables are
- * associated with the corresponding span based on their index.
+ * Elements in [composables] can provide their span using [SpannedGridScope.span] and have a default
+ * span of 1. Spans must be in the interval `[1, columns]` ([columns] > 0).
+ *
+ * Passing a [SpannedGridState] can be useful to get access to the [SpannedGridState.positions],
+ * representing the row and column of each item.
*
* Due to the fact that elements are seen as a linear list that's laid out in a grid, the semantics
* represent the collection as a list of elements.
@@ -61,23 +78,23 @@
rows: Int,
columnSpacing: Dp,
rowSpacing: Dp,
- spans: List<Int>,
modifier: Modifier = Modifier,
- composables: @Composable BoxScope.(spanIndex: Int) -> Unit,
+ state: SpannedGridState = rememberSpannedGridState(),
+ composables: @Composable SpannedGridScope.() -> Unit,
) {
SpannedGrid(
primarySpaces = rows,
crossAxisSpacing = rowSpacing,
mainAxisSpacing = columnSpacing,
- spans = spans,
isVertical = false,
+ state = state,
modifier = modifier,
composables = composables,
)
}
/**
- * Horizontal (non lazy) grid that supports [spans] for its elements.
+ * Horizontal (non lazy) grid that supports spans for its elements.
*
* The elements will be laid down horizontally first, and then by rows. So assuming LTR layout, it
* will be (for a span list `[2, 1, 2, 1, 1, 1, 1, 1]` and 4 columns):
@@ -90,8 +107,11 @@
* where repeated numbers show larger span. If an element doesn't fit in a row due to its span, it
* will start a new row.
*
- * Elements in [spans] must be in the interval `[1, columns]` ([columns] > 0), and the composables
- * are associated with the corresponding span based on their index.
+ * Elements in [composables] can provide their span using [SpannedGridScope.span] and have a default
+ * span of 1. Spans must be in the interval `[1, columns]` ([columns] > 0).
+ *
+ * Passing a [SpannedGridState] can be useful to get access to the [SpannedGridState.positions],
+ * representing the row and column of each item.
*
* Due to the fact that elements are seen as a linear list that's laid out in a grid, the semantics
* represent the collection as a list of elements.
@@ -101,16 +121,16 @@
columns: Int,
columnSpacing: Dp,
rowSpacing: Dp,
- spans: List<Int>,
modifier: Modifier = Modifier,
- composables: @Composable BoxScope.(spanIndex: Int) -> Unit,
+ state: SpannedGridState = rememberSpannedGridState(),
+ composables: @Composable SpannedGridScope.() -> Unit,
) {
SpannedGrid(
primarySpaces = columns,
crossAxisSpacing = columnSpacing,
mainAxisSpacing = rowSpacing,
- spans = spans,
isVertical = true,
+ state = state,
modifier = modifier,
composables = composables,
)
@@ -121,18 +141,15 @@
primarySpaces: Int,
crossAxisSpacing: Dp,
mainAxisSpacing: Dp,
- spans: List<Int>,
isVertical: Boolean,
+ state: SpannedGridState,
modifier: Modifier = Modifier,
- composables: @Composable BoxScope.(spanIndex: Int) -> Unit,
+ composables: @Composable SpannedGridScope.() -> Unit,
) {
+ state as SpannedGridStateImpl
+ SideEffect { state.setOrientation(isVertical) }
+
val crossAxisArrangement = Arrangement.spacedBy(crossAxisSpacing)
- spans.forEachIndexed { index, span ->
- check(span in 1..primarySpaces) {
- "Span out of bounds. Span at index $index has value of $span which is outside of the " +
- "expected rance of [1, $primarySpaces]"
- }
- }
if (isVertical) {
check(crossAxisSpacing >= 0.dp) { "Negative columnSpacing $crossAxisSpacing" }
@@ -142,21 +159,6 @@
check(crossAxisSpacing >= 0.dp) { "Negative rowSpacing $crossAxisSpacing" }
}
- val totalMainAxisGroups: Int =
- remember(primarySpaces, spans) {
- var currentAccumulated = 0
- var groups = 1
- spans.forEach { span ->
- if (currentAccumulated + span <= primarySpaces) {
- currentAccumulated += span
- } else {
- groups += 1
- currentAccumulated = span
- }
- }
- groups
- }
-
val slotPositionsAndSizesCache = remember {
object {
var sizes = IntArray(0)
@@ -165,25 +167,28 @@
}
Layout(
- {
- (0 until spans.size).map { spanIndex ->
- Box(
- Modifier.semantics {
- collectionItemInfo =
- if (isVertical) {
- CollectionItemInfo(spanIndex, 1, 0, 1)
- } else {
- CollectionItemInfo(0, 1, spanIndex, 1)
- }
+ { SpannedGridScope.composables() },
+ modifier.semantics { collectionInfo = CollectionInfo(state.positions.size, 1) },
+ ) { measurables, constraints ->
+ val spans =
+ measurables.fastMapIndexed { index, measurable ->
+ measurable.spannedGridParentData.span.also { span ->
+ check(span in 1..primarySpaces) {
+ "Span out of bounds. Span at index $index has value of $span which is " +
+ "outside of the expected rance of [1, $primarySpaces]"
}
- ) {
- composables(spanIndex)
}
}
- },
- modifier.semantics { collectionInfo = CollectionInfo(spans.size, 1) },
- ) { measurables, constraints ->
- check(measurables.size == spans.size)
+ var totalMainAxisGroups = 1
+ var currentAccumulated = 0
+ spans.forEach { span ->
+ if (currentAccumulated + span <= primarySpaces) {
+ currentAccumulated += span
+ } else {
+ totalMainAxisGroups += 1
+ currentAccumulated = span
+ }
+ }
val crossAxisSize = if (isVertical) constraints.maxWidth else constraints.maxHeight
check(crossAxisSize != Constraints.Infinity) { "Width must be constrained" }
if (slotPositionsAndSizesCache.sizes.size != primarySpaces) {
@@ -275,11 +280,54 @@
}
placeable.placeRelative(x, y)
}
+ state.onPlaceResults(placeables)
}
}
}
-fun makeConstraint(isVertical: Boolean, mainAxisSize: Int, crossAxisSize: Int): Constraints {
+/** Receiver scope which is used by [VerticalSpannedGrid] and [HorizontalSpannedGrid] */
+@Stable
+object SpannedGridScope {
+ fun Modifier.span(span: Int) = this then SpanElement(span)
+}
+
+/** A state object that can be hoisted to observe items positioning */
+@Stable
+sealed interface SpannedGridState {
+ data class Position(val row: Int, val column: Int)
+
+ val positions: List<Position>
+}
+
+private class SpannedGridStateImpl : SpannedGridState {
+ private val _positions = mutableStateListOf<SpannedGridState.Position>()
+ override val positions
+ get() = _positions
+
+ private var isVertical by mutableStateOf(false)
+
+ fun onPlaceResults(placeResults: List<PlaceResult>) {
+ _positions.clear()
+ _positions.addAll(
+ placeResults.fastMap { placeResult ->
+ SpannedGridState.Position(
+ row = if (isVertical) placeResult.mainAxisGroup else placeResult.slotIndex,
+ column = if (isVertical) placeResult.slotIndex else placeResult.mainAxisGroup,
+ )
+ }
+ )
+ }
+
+ fun setOrientation(isVertical: Boolean) {
+ this.isVertical = isVertical
+ }
+}
+
+private fun makeConstraint(
+ isVertical: Boolean,
+ mainAxisSize: Int,
+ crossAxisSize: Int,
+): Constraints {
return if (isVertical) {
Constraints(maxHeight = mainAxisSize, minWidth = crossAxisSize, maxWidth = crossAxisSize)
} else {
@@ -319,3 +367,27 @@
val slotIndex: Int,
val mainAxisGroup: Int,
)
+
+private val IntrinsicMeasurable.spannedGridParentData: SpannedGridParentData?
+ get() = parentData as? SpannedGridParentData
+
+private val SpannedGridParentData?.span: Int
+ get() = this?.span ?: 1
+
+private data class SpannedGridParentData(val span: Int = 1)
+
+private data class SpanElement(val span: Int) : ModifierNodeElement<SpanNode>() {
+ override fun create(): SpanNode {
+ return SpanNode(span)
+ }
+
+ override fun update(node: SpanNode) {
+ node.span = span
+ }
+}
+
+private class SpanNode(var span: Int) : ParentDataModifierNode, Modifier.Node() {
+ override fun Density.modifyParentData(parentData: Any?): Any? {
+ return ((parentData as? SpannedGridParentData) ?: SpannedGridParentData()).copy(span = span)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt b/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt
index 316964a..84c4bdf 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt
@@ -19,6 +19,7 @@
import android.service.quicksettings.Tile
import com.android.systemui.Flags
import com.android.systemui.animation.Expandable
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
import com.android.systemui.util.kotlin.pairwise
@@ -173,6 +174,7 @@
}
}
+@SysUISingleton
class TileHapticsViewModelFactoryProvider
@Inject
constructor(private val tileHapticsViewModelFactory: TileHapticsViewModel.Factory) {
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 4370abf..496c6fb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -33,7 +33,6 @@
import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent;
import com.android.keyguard.dagger.KeyguardStatusBarViewComponent;
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
-import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
import com.android.keyguard.mediator.ScreenOnCoordinator;
import com.android.systemui.CoreStartable;
import com.android.systemui.animation.ActivityTransitionAnimator;
@@ -108,8 +107,7 @@
@Module(subcomponents = {
KeyguardQsUserSwitchComponent.class,
KeyguardStatusBarViewComponent.class,
- KeyguardStatusViewComponent.class,
- KeyguardUserSwitcherComponent.class},
+ KeyguardStatusViewComponent.class},
includes = {
DeviceEntryIconTransitionModule.class,
FalsingModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
index 06da281..96b07cc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
@@ -36,7 +36,7 @@
import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.map
/** Lockscreen affordance that opens the glanceable hub. */
@SysUISingleton
@@ -60,13 +60,13 @@
get() = R.drawable.ic_widgets
override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState>
- get() = flow {
- emit(
+ get() =
+ communalInteractor.isCommunalAvailable.map { available ->
if (!communalSettingsInteractor.isV2FlagEnabled()) {
Log.i(TAG, "Button hidden on lockscreen: flag not enabled.")
KeyguardQuickAffordanceConfig.LockScreenState.Hidden
- } else if (!communalInteractor.isCommunalEnabled.value) {
- Log.i(TAG, "Button hidden on lockscreen: hub not enabled in settings.")
+ } else if (!available) {
+ Log.i(TAG, "Button hidden on lockscreen: hub not available.")
KeyguardQuickAffordanceConfig.LockScreenState.Hidden
} else {
KeyguardQuickAffordanceConfig.LockScreenState.Visible(
@@ -77,8 +77,7 @@
)
)
}
- )
- }
+ }
override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState {
return if (!communalSettingsInteractor.isV2FlagEnabled()) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
index f08576a..c774987 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
@@ -24,8 +24,8 @@
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.res.R
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
@@ -51,6 +51,7 @@
@ShadeDisplayAware private val context: Context,
private val userFileManager: UserFileManager,
private val userTracker: UserTracker,
+ private val communalSettingsInteractor: CommunalSettingsInteractor,
broadcastDispatcher: BroadcastDispatcher,
) : KeyguardQuickAffordanceSelectionManager {
@@ -102,7 +103,7 @@
// common case anyway as restoration really only happens on initial device
// setup).
emit(Unit)
- }
+ },
) { _, _ ->
}
.flatMapLatest {
@@ -128,7 +129,11 @@
override fun getSelections(): Map<String, List<String>> {
// If the custom shortcuts feature is not enabled, ignore prior selections and use defaults
- if (!context.resources.getBoolean(R.bool.custom_lockscreen_shortcuts_enabled)) {
+ // TODO(b/383391342): remove isV2FlagEnabled check and just depend on the resource
+ if (
+ !(context.resources.getBoolean(R.bool.custom_lockscreen_shortcuts_enabled) ||
+ communalSettingsInteractor.isV2FlagEnabled())
+ ) {
return defaults
}
@@ -164,10 +169,7 @@
return result
}
- override fun setSelections(
- slotId: String,
- affordanceIds: List<String>,
- ) {
+ override fun setSelections(slotId: String, affordanceIds: List<String>) {
val key = "$KEY_PREFIX_SLOT$slotId"
val value = affordanceIds.joinToString(AFFORDANCE_DELIMITER)
sharedPrefs.edit().putString(key, value).apply()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index 9c2daf5..7d8badd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -28,6 +28,7 @@
import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.devicepolicy.areKeyguardShortcutsDisabled
@@ -90,6 +91,7 @@
private val devicePolicyManager: DevicePolicyManager,
private val dockManager: DockManager,
private val biometricSettingsRepository: BiometricSettingsRepository,
+ private val communalSettingsInteractor: CommunalSettingsInteractor,
@Background private val backgroundDispatcher: CoroutineDispatcher,
@ShadeDisplayAware private val appContext: Context,
private val sceneInteractor: Lazy<SceneInteractor>,
@@ -462,7 +464,10 @@
name = Contract.FlagsTable.FLAG_NAME_CUSTOM_LOCK_SCREEN_QUICK_AFFORDANCES_ENABLED,
value =
!isFeatureDisabledByDevicePolicy() &&
- appContext.resources.getBoolean(R.bool.custom_lockscreen_shortcuts_enabled),
+ // TODO(b/383391342): remove isV2FlagEnabled check once trunkfood is reached
+ (appContext.resources.getBoolean(
+ R.bool.custom_lockscreen_shortcuts_enabled
+ ) || communalSettingsInteractor.isV2FlagEnabled()),
),
KeyguardPickerFlag(
name = Contract.FlagsTable.FLAG_NAME_CUSTOM_CLOCKS_ENABLED,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
index aa44b6d..382436c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.domain.interactor.scenetransition
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.CoreStartable
@@ -38,7 +39,6 @@
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
-import com.android.app.tracing.coroutines.launchTraced as launch
/**
* This class listens to scene framework scene transitions and manages keyguard transition framework
@@ -111,7 +111,10 @@
if (currentTransitionId == null) return
if (prevTransition !is ObservableTransitionState.Transition) return
- if (idle.currentScene == prevTransition.toContent) {
+ if (
+ idle.currentScene == prevTransition.toContent ||
+ idle.currentOverlays.contains(prevTransition.toContent)
+ ) {
finishCurrentTransition()
} else {
val targetState =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt
index b9994d7..4b30645 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.panels.ui.compose
import com.android.compose.animation.Bounceable
+import com.android.systemui.grid.ui.compose.SpannedGridState
import com.android.systemui.qs.panels.shared.model.SizedTile
import com.android.systemui.qs.panels.ui.model.GridCell
import com.android.systemui.qs.panels.ui.model.TileGridCell
@@ -37,22 +38,37 @@
val cell = this[index].first as TileGridCell
// Only look for neighbor bounceables if they are on the same row
val onLastColumn = cell.onLastColumn(cell.column, columns)
- val previousTile = getOrNull(index - 1)?.takeIf { cell.column != 0 }
- val nextTile = getOrNull(index + 1)?.takeIf { !onLastColumn }
+ val previousTile = getOrNull(index - 1)?.takeIf { it.first.row == cell.row }
+ val nextTile = getOrNull(index + 1)?.takeIf { it.first.row == cell.row }
return BounceableInfo(this[index].second, previousTile?.second, nextTile?.second, !onLastColumn)
}
-fun List<BounceableTileViewModel>.bounceableInfo(
- sizedTile: SizedTile<TileViewModel>,
- index: Int,
- column: Int,
+inline fun List<SizedTile<TileViewModel>>.forEachWithBounceables(
+ positions: List<SpannedGridState.Position>,
+ bounceables: List<BounceableTileViewModel>,
columns: Int,
-): BounceableInfo {
- // Only look for neighbor bounceables if they are on the same row
- val onLastColumn = sizedTile.onLastColumn(column, columns)
- val previousTile = getOrNull(index - 1)?.takeIf { column != 0 }
- val nextTile = getOrNull(index + 1)?.takeIf { !onLastColumn }
- return BounceableInfo(this[index], previousTile, nextTile, !onLastColumn)
+ action: (index: Int, tile: SizedTile<TileViewModel>, bounceableInfo: BounceableInfo) -> Unit,
+) {
+ this.forEachIndexed { index, tile ->
+ val position = positions.getOrNull(index)
+ val onLastColumn = position?.column == columns - tile.width
+ val previousBounceable =
+ bounceables.getOrNull(index - 1)?.takeIf {
+ position != null && positions.getOrNull(index - 1)?.row == position.row
+ }
+ val nextBounceable =
+ bounceables.getOrNull(index + 1)?.takeIf {
+ position != null && positions.getOrNull(index + 1)?.row == position.row
+ }
+ val bounceableInfo =
+ BounceableInfo(
+ bounceable = bounceables[index],
+ previousTile = previousBounceable,
+ nextTile = nextBounceable,
+ bounceEnd = !onLastColumn,
+ )
+ action(index, tile, bounceableInfo)
+ }
}
private fun <T> SizedTile<T>.onLastColumn(column: Int, columns: Int): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
index 2928ad1..ceff94a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
@@ -29,6 +29,7 @@
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.grid.ui.compose.VerticalSpannedGrid
+import com.android.systemui.grid.ui.compose.rememberSpannedGridState
import com.android.systemui.qs.composefragment.ui.GridAnchor
import com.android.systemui.qs.panels.ui.compose.infinitegrid.Tile
import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel
@@ -47,6 +48,8 @@
val bounceables = remember(sizedTiles) { List(sizedTiles.size) { BounceableTileViewModel() } }
val squishiness by viewModel.squishinessViewModel.squishiness.collectAsStateWithLifecycle()
val scope = rememberCoroutineScope()
+ val gridState = rememberSpannedGridState()
+ val positions = gridState.positions
DisposableEffect(tiles) {
val token = Any()
@@ -54,30 +57,34 @@
onDispose { tiles.forEach { it.stopListening(token) } }
}
val columns = viewModel.columns
- var cellIndex = 0
Box(modifier = modifier) {
GridAnchor()
VerticalSpannedGrid(
columns = columns,
columnSpacing = dimensionResource(R.dimen.qs_tile_margin_horizontal),
rowSpacing = dimensionResource(R.dimen.qs_tile_margin_vertical),
- spans = sizedTiles.fastMap { it.width },
+ state = gridState,
modifier = Modifier.sysuiResTag("qqs_tile_layout"),
- ) { spanIndex ->
- val it = sizedTiles[spanIndex]
- val column = cellIndex % columns
- cellIndex += it.width
- Tile(
- tile = it.tile,
- iconOnly = it.isIcon,
- modifier = Modifier.element(it.tile.spec.toElementKey(spanIndex)),
- squishiness = { squishiness },
- coroutineScope = scope,
- bounceableInfo = bounceables.bounceableInfo(it, spanIndex, column, columns),
- tileHapticsViewModelFactoryProvider = viewModel.tileHapticsViewModelFactoryProvider,
- // There should be no QuickQuickSettings when the details view is enabled.
- detailsViewModel = null,
- )
+ ) {
+ sizedTiles.forEachWithBounceables(positions, bounceables, columns) {
+ index,
+ sizedTile,
+ bounceableInfo ->
+ Tile(
+ tile = sizedTile.tile,
+ iconOnly = sizedTile.isIcon,
+ modifier =
+ Modifier.element(sizedTile.tile.spec.toElementKey(index))
+ .span(sizedTile.width),
+ squishiness = { squishiness },
+ coroutineScope = scope,
+ bounceableInfo = bounceableInfo,
+ tileHapticsViewModelFactoryProvider =
+ viewModel.tileHapticsViewModelFactoryProvider,
+ // There should be no QuickQuickSettings when the details view is enabled.
+ detailsViewModel = null,
+ )
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
index d975f10..60c78a0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
@@ -395,7 +395,7 @@
val cells =
remember(listState.tiles) {
- listState.tiles.fastMap { Pair(it, BounceableTileViewModel()) }
+ listState.tiles.fastMap { tile -> Pair(tile, BounceableTileViewModel()) }
}
TileLazyGrid(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
index 8fd99a5..8a12889 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
@@ -23,17 +23,18 @@
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
-import androidx.compose.ui.util.fastMap
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.grid.ui.compose.VerticalSpannedGrid
+import com.android.systemui.grid.ui.compose.rememberSpannedGridState
import com.android.systemui.haptics.msdl.qs.TileHapticsViewModelFactoryProvider
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QS
import com.android.systemui.qs.panels.shared.model.SizedTileImpl
import com.android.systemui.qs.panels.ui.compose.PaginatableGridLayout
import com.android.systemui.qs.panels.ui.compose.bounceableInfo
+import com.android.systemui.qs.panels.ui.compose.forEachWithBounceables
import com.android.systemui.qs.panels.ui.compose.rememberEditListState
import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel
import com.android.systemui.qs.panels.ui.viewmodel.DetailsViewModel
@@ -83,27 +84,32 @@
remember(sizedTiles) { List(sizedTiles.size) { BounceableTileViewModel() } }
val squishiness by viewModel.squishinessViewModel.squishiness.collectAsStateWithLifecycle()
val scope = rememberCoroutineScope()
- var cellIndex = 0
+ val gridState = rememberSpannedGridState()
+ val positions = gridState.positions
VerticalSpannedGrid(
columns = columns,
+ state = gridState,
columnSpacing = dimensionResource(R.dimen.qs_tile_margin_horizontal),
rowSpacing = dimensionResource(R.dimen.qs_tile_margin_vertical),
- spans = sizedTiles.fastMap { it.width },
- ) { spanIndex ->
- val it = sizedTiles[spanIndex]
- val column = cellIndex % columns
- cellIndex += it.width
- Tile(
- tile = it.tile,
- iconOnly = iconTilesViewModel.isIconTile(it.tile.spec),
- modifier = Modifier.element(it.tile.spec.toElementKey(spanIndex)),
- squishiness = { squishiness },
- tileHapticsViewModelFactoryProvider = tileHapticsViewModelFactoryProvider,
- coroutineScope = scope,
- bounceableInfo = bounceables.bounceableInfo(it, spanIndex, column, columns),
- detailsViewModel = detailsViewModel,
- )
+ ) {
+ sizedTiles.forEachWithBounceables(positions, bounceables, columns) {
+ index,
+ sizedTile,
+ bounceableInfo ->
+ Tile(
+ tile = sizedTile.tile,
+ iconOnly = iconTilesViewModel.isIconTile(sizedTile.tile.spec),
+ modifier =
+ Modifier.element(sizedTile.tile.spec.toElementKey(index))
+ .span(sizedTile.width),
+ squishiness = { squishiness },
+ tileHapticsViewModelFactoryProvider = tileHapticsViewModelFactoryProvider,
+ coroutineScope = scope,
+ bounceableInfo = bounceableInfo,
+ detailsViewModel = detailsViewModel,
+ )
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt
index 989fc0f..5ba1527 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt
@@ -22,6 +22,7 @@
import android.service.quicksettings.Tile
import com.android.internal.logging.MetricsLogger
import com.android.systemui.animation.Expandable
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
@@ -92,7 +93,8 @@
state?.apply {
this.state = tileState.activationState.legacyState
- icon = maybeLoadResourceIcon(tileState.iconRes ?: R.drawable.ic_qs_notes)
+ icon =
+ maybeLoadResourceIcon((tileState.icon as Icon.Loaded).res ?: R.drawable.ic_qs_notes)
label = tileState.label
contentDescription = tileState.contentDescription
expandedAccessibilityClassName = tileState.expandedAccessibilityClassName
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
index 34c2ec9..80d429c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
@@ -35,13 +35,13 @@
override fun map(config: QSTileConfig, data: AirplaneModeTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
- iconRes =
+ val iconRes =
if (data.isEnabled) {
R.drawable.qs_airplane_icon_on
} else {
R.drawable.qs_airplane_icon_off
}
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
secondaryLabel = resources.getStringArray(R.array.tile_states_airplane)[2]
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt
index a72992d..d56d994 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt
@@ -84,8 +84,8 @@
secondaryLabel = resources.getString(R.string.qs_alarm_tile_no_alarm)
}
}
- iconRes = R.drawable.ic_alarm
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ val iconRes = R.drawable.ic_alarm
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
sideViewIcon = QSTileState.SideViewIcon.Chevron
contentDescription = label
supportedActions = setOf(QSTileState.UserAction.CLICK)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapper.kt
index e116d8c..72759c5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapper.kt
@@ -38,10 +38,10 @@
QSTileState.build(resources, theme, config.uiConfig) {
label = resources.getString(R.string.battery_detail_switch_title)
contentDescription = label
- iconRes =
+ val iconRes =
if (data.isPowerSaving) R.drawable.qs_battery_saver_icon_on
else R.drawable.qs_battery_saver_icon_off
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
sideViewIcon = QSTileState.SideViewIcon.None
if (data.isPluggedIn) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt
index 21b9f65..e5a0fe8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt
@@ -37,8 +37,8 @@
override fun map(config: QSTileConfig, data: ColorCorrectionTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
val subtitleArray = resources.getStringArray(R.array.tile_states_color_correction)
- iconRes = R.drawable.ic_qs_color_correction
- icon = Icon.Loaded(resources.getDrawable(R.drawable.ic_qs_color_correction)!!, null)
+ val iconRes = R.drawable.ic_qs_color_correction
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
secondaryLabel = subtitleArray[2]
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
index 2dfb1fc..32ccba6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
@@ -35,14 +35,14 @@
override fun map(config: QSTileConfig, data: FlashlightTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
- iconRes =
+ val iconRes =
if (data is FlashlightTileModel.FlashlightAvailable && data.isEnabled) {
R.drawable.qs_flashlight_icon_on
} else {
R.drawable.qs_flashlight_icon_off
}
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
contentDescription = label
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt
index 7f41cbd..c571b13 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt
@@ -36,8 +36,8 @@
override fun map(config: QSTileConfig, data: FontScalingTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
- iconRes = R.drawable.ic_qs_font_scaling
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ val iconRes = R.drawable.ic_qs_font_scaling
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
contentDescription = label
activationState = QSTileState.ActivationState.ACTIVE
sideViewIcon = QSTileState.SideViewIcon.Chevron
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt
index 4c302b3..12f7149 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt
@@ -37,8 +37,8 @@
override fun map(config: QSTileConfig, data: HearingDevicesTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
label = resources.getString(R.string.quick_settings_hearing_devices_label)
- iconRes = R.drawable.qs_hearing_devices_icon
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ val iconRes = R.drawable.qs_hearing_devices_icon
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
sideViewIcon = QSTileState.SideViewIcon.Chevron
contentDescription = label
if (data.isAnyActiveHearingDevice) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt
index 1a6876d..7ad01e4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt
@@ -61,11 +61,11 @@
when (val dataIcon = data.icon) {
is InternetTileIconModel.ResourceId -> {
- iconRes = dataIcon.resId
icon =
Icon.Loaded(
resources.getDrawable(dataIcon.resId, theme),
contentDescription = null,
+ dataIcon.resId,
)
}
@@ -76,11 +76,11 @@
}
is InternetTileIconModel.Satellite -> {
- iconRes = dataIcon.resourceIcon.res // level is inferred from res
icon =
Icon.Loaded(
resources.getDrawable(dataIcon.resourceIcon.res, theme),
contentDescription = null,
+ dataIcon.resourceIcon.res,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt
index 8d35b24..05590e8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt
@@ -35,7 +35,7 @@
override fun map(config: QSTileConfig, data: ColorInversionTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
val subtitleArray = resources.getStringArray(R.array.tile_states_inversion)
-
+ val iconRes: Int
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
secondaryLabel = subtitleArray[2]
@@ -45,7 +45,7 @@
secondaryLabel = subtitleArray[1]
iconRes = R.drawable.qs_invert_colors_icon_off
}
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
contentDescription = label
supportedActions =
setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapper.kt
index 3557c1a..afb137e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapper.kt
@@ -39,6 +39,7 @@
Icon.Loaded(
resources.getDrawable(R.drawable.qs_record_issue_icon_on, theme),
null,
+ R.drawable.qs_record_issue_icon_on,
)
} else {
activationState = QSTileState.ActivationState.INACTIVE
@@ -46,6 +47,7 @@
Icon.Loaded(
resources.getDrawable(R.drawable.qs_record_issue_icon_off, theme),
null,
+ R.drawable.qs_record_issue_icon_off,
)
}
supportedActions = setOf(QSTileState.UserAction.CLICK)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
index dfc24a1..ced5a4f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
@@ -35,13 +35,13 @@
override fun map(config: QSTileConfig, data: LocationTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
- iconRes =
+ val iconRes =
if (data.isEnabled) {
R.drawable.qs_location_icon_on
} else {
R.drawable.qs_location_icon_off
}
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null)
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
label = resources.getString(R.string.quick_settings_location_label)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
index ee6b0b8..479f618 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
@@ -17,10 +17,10 @@
package com.android.systemui.qs.tiles.impl.modes.domain.interactor
import android.content.Context
+import android.graphics.drawable.Drawable
import android.os.UserHandle
import com.android.app.tracing.coroutines.flow.flowName
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.common.shared.model.asIcon
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.modes.shared.ModesUi
import com.android.systemui.modes.shared.ModesUiIcons
@@ -31,7 +31,6 @@
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
import com.android.systemui.statusbar.policy.domain.model.ActiveZenModes
-import com.android.systemui.statusbar.policy.domain.model.ZenModeInfo
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
@@ -68,46 +67,29 @@
suspend fun getCurrentTileModel() = buildTileData(zenModeInteractor.getActiveModes())
private fun buildTileData(activeModes: ActiveZenModes): ModesTileModel {
- if (ModesUiIcons.isEnabled) {
- val tileIcon = getTileIcon(activeModes.mainMode)
- return ModesTileModel(
- isActivated = activeModes.isAnyActive(),
- icon = tileIcon.icon,
- iconResId = tileIcon.resId,
- activeModes = activeModes.modeNames,
- )
- } else {
- return ModesTileModel(
- isActivated = activeModes.isAnyActive(),
- icon =
- context
- .getDrawable(ModesTile.ICON_RES_ID)!!
- .asIcon(res = ModesTile.ICON_RES_ID),
- iconResId = ModesTile.ICON_RES_ID,
- activeModes = activeModes.modeNames,
- )
- }
- }
+ val drawable: Drawable
+ val iconRes: Int?
+ val activeMode = activeModes.mainMode
- private data class TileIcon(val icon: Icon.Loaded, val resId: Int?)
-
- private fun getTileIcon(activeMode: ZenModeInfo?): TileIcon {
- return if (activeMode != null) {
+ if (ModesUiIcons.isEnabled && activeMode != null) {
// ZenIconKey.resPackage is null if its resId is a system icon.
- if (activeMode.icon.key.resPackage == null) {
- TileIcon(
- activeMode.icon.drawable.asIcon(res = activeMode.icon.key.resId),
- activeMode.icon.key.resId,
- )
- } else {
- TileIcon(activeMode.icon.drawable.asIcon(), null)
- }
+ iconRes =
+ if (activeMode.icon.key.resPackage == null) {
+ activeMode.icon.key.resId
+ } else {
+ null
+ }
+ drawable = activeMode.icon.drawable
} else {
- TileIcon(
- context.getDrawable(ModesTile.ICON_RES_ID)!!.asIcon(res = ModesTile.ICON_RES_ID),
- ModesTile.ICON_RES_ID,
- )
+ iconRes = ModesTile.ICON_RES_ID
+ drawable = context.getDrawable(iconRes)!!
}
+
+ return ModesTileModel(
+ isActivated = activeModes.isAnyActive(),
+ icon = Icon.Loaded(drawable, null, iconRes),
+ activeModes = activeModes.modeNames,
+ )
}
override fun availability(user: UserHandle): Flow<Boolean> = flowOf(ModesUi.isEnabled)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt
index db48123..d0eacbc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt
@@ -21,12 +21,10 @@
data class ModesTileModel(
val isActivated: Boolean,
val activeModes: List<String>,
- val icon: Icon.Loaded,
-
/**
- * Resource id corresponding to [icon]. Will only be present if it's know to correspond to a
- * resource with a known id in SystemUI (such as resources from `android.R`,
- * `com.android.internal.R`, or `com.android.systemui.res` itself).
+ * icon.res will only be present if it is known to correspond to a resource with a known id in
+ * SystemUI (such as resources from `android.R`, `com.android.internal.R`, or
+ * `com.android.systemui.res` itself).
*/
- val iconResId: Int? = null
+ val icon: Icon.Loaded,
)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
index 1507ef4..99ae3b8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
@@ -34,7 +34,6 @@
QSTileDataToStateMapper<ModesTileModel> {
override fun map(config: QSTileConfig, data: ModesTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
- iconRes = data.iconResId
icon = data.icon
activationState =
if (data.isActivated) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapper.kt
index 3569e4d..16b3628 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapper.kt
@@ -49,7 +49,7 @@
supportedActions =
setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
sideViewIcon = QSTileState.SideViewIcon.None
-
+ val iconRes: Int
if (data.isActivated) {
activationState = QSTileState.ActivationState.ACTIVE
iconRes = R.drawable.qs_nightlight_icon_on
@@ -58,7 +58,7 @@
iconRes = R.drawable.qs_nightlight_icon_off
}
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null)
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
secondaryLabel = getSecondaryLabel(data, resources)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapper.kt
index a543619..ecdd711 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapper.kt
@@ -35,8 +35,8 @@
) : QSTileDataToStateMapper<NotesTileModel> {
override fun map(config: QSTileConfig, data: NotesTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
- iconRes = R.drawable.ic_qs_notes
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null)
+ val iconRes = R.drawable.ic_qs_notes
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
contentDescription = label
activationState = QSTileState.ActivationState.INACTIVE
sideViewIcon = QSTileState.SideViewIcon.Chevron
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapper.kt
index 76f1e8b..5b3ea93 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapper.kt
@@ -38,8 +38,8 @@
QSTileState.build(resources, theme, config.uiConfig) {
val subtitleArray = resources.getStringArray(R.array.tile_states_onehanded)
label = resources.getString(R.string.quick_settings_onehanded_label)
- iconRes = com.android.internal.R.drawable.ic_qs_one_handed_mode
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ val iconRes = com.android.internal.R.drawable.ic_qs_one_handed_mode
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
secondaryLabel = subtitleArray[2]
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt
index c546250..21e92d3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt
@@ -38,8 +38,8 @@
QSTileState.build(resources, theme, config.uiConfig) {
label = resources.getString(R.string.qr_code_scanner_title)
contentDescription = label
- iconRes = R.drawable.ic_qr_code_scanner
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ val iconRes = R.drawable.ic_qr_code_scanner
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
sideViewIcon = QSTileState.SideViewIcon.Chevron
supportedActions = setOf(QSTileState.UserAction.CLICK)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt
index 66d0f96..66759cd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt
@@ -37,6 +37,7 @@
override fun map(config: QSTileConfig, data: ReduceBrightColorsTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
+ val iconRes: Int
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
iconRes = R.drawable.qs_extra_dim_icon_on
@@ -50,7 +51,7 @@
resources
.getStringArray(R.array.tile_states_reduce_brightness)[Tile.STATE_INACTIVE]
}
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
label =
resources.getString(com.android.internal.R.string.reduce_bright_colors_feature_name)
contentDescription = label
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt
index a014422..000c702 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt
@@ -42,7 +42,7 @@
QSTileState.build(resources, theme, config.uiConfig) {
label = resources.getString(R.string.quick_settings_rotation_unlocked_label)
contentDescription = resources.getString(R.string.accessibility_quick_settings_rotation)
-
+ val iconRes: Int
if (data.isRotationLocked) {
activationState = QSTileState.ActivationState.INACTIVE
secondaryLabel = EMPTY_SECONDARY_STRING
@@ -57,7 +57,7 @@
}
iconRes = R.drawable.qs_auto_rotate_icon_on
}
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
if (isDeviceFoldable(resources, deviceStateManager)) {
secondaryLabel = getSecondaryLabelWithPosture(activationState)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt
index aea4967..1d5cf29 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt
@@ -36,6 +36,7 @@
override fun map(config: QSTileConfig, data: DataSaverTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
with(data) {
+ val iconRes: Int
if (isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
iconRes = R.drawable.qs_data_saver_icon_on
@@ -45,7 +46,7 @@
iconRes = R.drawable.qs_data_saver_icon_off
secondaryLabel = resources.getStringArray(R.array.tile_states_saver)[1]
}
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
contentDescription = label
supportedActions =
setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt
index f3136e0..0a61e3c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt
@@ -38,7 +38,7 @@
QSTileState.build(resources, theme, config.uiConfig) {
label = resources.getString(R.string.quick_settings_screen_record_label)
supportedActions = setOf(QSTileState.UserAction.CLICK)
-
+ val iconRes: Int
when (data) {
is ScreenRecordModel.Recording -> {
activationState = QSTileState.ActivationState.ACTIVE
@@ -61,7 +61,7 @@
resources.getString(R.string.quick_settings_screen_record_start)
}
}
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
contentDescription =
if (TextUtils.isEmpty(secondaryLabel)) label
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapper.kt
index 73e61b7..f54f46c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapper.kt
@@ -50,8 +50,8 @@
contentDescription = label
supportedActions =
setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
- iconRes = sensorPrivacyTileResources.getIconRes(data.isBlocked)
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ val iconRes = sensorPrivacyTileResources.getIconRes(data.isBlocked)
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
sideViewIcon = QSTileState.SideViewIcon.None
if (data.isBlocked) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt
index e9aa46c..5933d65 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt
@@ -116,11 +116,11 @@
}
}
- iconRes =
+ val iconRes =
if (activationState == QSTileState.ActivationState.ACTIVE)
R.drawable.qs_light_dark_theme_icon_on
else R.drawable.qs_light_dark_theme_icon_off
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
supportedActions =
if (activationState == QSTileState.ActivationState.UNAVAILABLE)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapper.kt
index 6a3195a..5b462ba 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapper.kt
@@ -41,8 +41,8 @@
QSTileState.build(resources, theme, config.uiConfig) {
label = getTileLabel()!!
contentDescription = label
- iconRes = com.android.internal.R.drawable.stat_sys_managed_profile_status
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null)
+ val iconRes = com.android.internal.R.drawable.stat_sys_managed_profile_status
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
when (data) {
is WorkModeTileModel.HasActiveProfile -> {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
index 8394be5..c6af729 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
@@ -36,7 +36,6 @@
*/
data class QSTileState(
val icon: Icon?,
- val iconRes: Int?,
val label: CharSequence,
val activationState: ActivationState,
val secondaryLabel: CharSequence?,
@@ -58,7 +57,7 @@
): QSTileState {
val iconDrawable = resources.getDrawable(config.iconRes, theme)
return build(
- Icon.Loaded(iconDrawable, null),
+ Icon.Loaded(iconDrawable, null, config.iconRes),
resources.getString(config.labelRes),
builder,
)
@@ -115,7 +114,6 @@
}
class Builder(var icon: Icon?, var label: CharSequence) {
- var iconRes: Int? = null
var activationState: ActivationState = ActivationState.INACTIVE
var secondaryLabel: CharSequence? = null
var supportedActions: Set<UserAction> = setOf(UserAction.CLICK)
@@ -128,7 +126,6 @@
fun build(): QSTileState =
QSTileState(
icon,
- iconRes,
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
index 632eeef..c34edc8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
@@ -260,8 +260,8 @@
icon =
when (val stateIcon = viewModelState.icon) {
is Icon.Loaded ->
- if (viewModelState.iconRes == null) DrawableIcon(stateIcon.drawable)
- else DrawableIconWithRes(stateIcon.drawable, viewModelState.iconRes)
+ if (stateIcon.res == null) DrawableIcon(stateIcon.drawable)
+ else DrawableIconWithRes(stateIcon.drawable, stateIcon.res)
is Icon.Resource -> ResourceIcon.get(stateIcon.res)
null -> null
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 9e88583..28c675e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -58,7 +58,6 @@
import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.res.Resources;
-import android.database.ContentObserver;
import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Rect;
@@ -69,7 +68,6 @@
import android.os.Handler;
import android.os.Trace;
import android.os.UserManager;
-import android.provider.Settings;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.MathUtils;
@@ -106,10 +104,8 @@
import com.android.keyguard.KeyguardStatusViewController;
import com.android.keyguard.KeyguardUnfoldTransition;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent;
import com.android.keyguard.dagger.KeyguardStatusBarViewComponent;
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.Gefingerpoken;
@@ -221,10 +217,7 @@
import com.android.systemui.statusbar.phone.TapAgainViewController;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController;
-import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
import com.android.systemui.statusbar.policy.SplitShadeStateController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.util.Compile;
@@ -309,7 +302,6 @@
private final ShadeHeadsUpChangedListener mOnHeadsUpChangedListener =
new ShadeHeadsUpChangedListener();
private final ConfigurationListener mConfigurationListener = new ConfigurationListener();
- private final SettingsChangeObserver mSettingsChangeObserver;
private final StatusBarStateListener mStatusBarStateListener = new StatusBarStateListener();
private final NotificationPanelView mView;
private final VibratorHelper mVibratorHelper;
@@ -331,8 +323,6 @@
private final MediaHierarchyManager mMediaHierarchyManager;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
- private final KeyguardQsUserSwitchComponent.Factory mKeyguardQsUserSwitchComponentFactory;
- private final KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponentFactory;
private final KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory;
private final FragmentService mFragmentService;
private final IStatusBarService mStatusBarService;
@@ -381,8 +371,6 @@
private float mKeyguardNotificationTopPadding;
/** Current max allowed keyguard notifications determined by measuring the panel. */
private int mMaxAllowedKeyguardNotifications;
- private KeyguardQsUserSwitchController mKeyguardQsUserSwitchController;
- private KeyguardUserSwitcherController mKeyguardUserSwitcherController;
private KeyguardStatusBarViewController mKeyguardStatusBarViewController;
private KeyguardStatusViewController mKeyguardStatusViewController;
private NotificationsQuickSettingsContainer mNotificationContainerParent;
@@ -394,8 +382,6 @@
private OpenCloseListener mOpenCloseListener;
private GestureRecorder mGestureRecorder;
- private boolean mKeyguardQsUserSwitchEnabled;
- private boolean mKeyguardUserSwitcherEnabled;
private boolean mDozing;
private boolean mDozingOnDown;
private boolean mBouncerShowing;
@@ -691,8 +677,6 @@
NotificationsQSContainerController notificationsQSContainerController,
NotificationStackScrollLayoutController notificationStackScrollLayoutController,
KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory,
- KeyguardQsUserSwitchComponent.Factory keyguardQsUserSwitchComponentFactory,
- KeyguardUserSwitcherComponent.Factory keyguardUserSwitcherComponentFactory,
KeyguardStatusBarViewComponent.Factory keyguardStatusBarViewComponentFactory,
LockscreenShadeTransitionController lockscreenShadeTransitionController,
AuthController authController,
@@ -841,11 +825,8 @@
mKeyguardStatusBarViewComponentFactory = keyguardStatusBarViewComponentFactory;
mDepthController = notificationShadeDepthController;
mContentResolver = contentResolver;
- mKeyguardQsUserSwitchComponentFactory = keyguardQsUserSwitchComponentFactory;
- mKeyguardUserSwitcherComponentFactory = keyguardUserSwitcherComponentFactory;
mFragmentService = fragmentService;
mStatusBarService = statusBarService;
- mSettingsChangeObserver = new SettingsChangeObserver(handler);
mSplitShadeStateController = splitShadeStateController;
mSplitShadeEnabled =
mSplitShadeStateController.shouldUseSplitNotificationShade(mResources);
@@ -874,7 +855,6 @@
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
dynamicPrivacyController.addListener(this::onDynamicPrivacyChanged);
quickSettingsController.setExpansionHeightListener(this::onQsSetExpansionHeightCalled);
- quickSettingsController.setQsStateUpdateListener(this::onQsStateUpdated);
quickSettingsController.setApplyClippingImmediatelyListener(
this::onQsClippingImmediatelyApplied);
quickSettingsController.setFlingQsWithoutClickListener(this::onFlingQsWithoutClick);
@@ -917,7 +897,6 @@
mKeyguardUnfoldTransition = unfoldComponent.map(
SysUIUnfoldComponent::getKeyguardUnfoldTransition);
- updateUserSwitcherFlags();
mKeyguardClockInteractor = keyguardClockInteractor;
KeyguardLongPressViewBinder.bind(
mView.requireViewById(R.id.keyguard_long_press),
@@ -1009,30 +988,14 @@
void onFinishInflate() {
loadDimens();
- FrameLayout userAvatarContainer = null;
- KeyguardUserSwitcherView keyguardUserSwitcherView = null;
-
- if (mKeyguardUserSwitcherEnabled && mUserManager.isUserSwitcherEnabled(
- mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user))) {
- if (mKeyguardQsUserSwitchEnabled) {
- ViewStub stub = mView.findViewById(R.id.keyguard_qs_user_switch_stub);
- userAvatarContainer = (FrameLayout) stub.inflate();
- } else {
- ViewStub stub = mView.findViewById(R.id.keyguard_user_switcher_stub);
- keyguardUserSwitcherView = (KeyguardUserSwitcherView) stub.inflate();
- }
- }
-
mKeyguardStatusBarViewController =
mKeyguardStatusBarViewComponentFactory.build(
mView.findViewById(R.id.keyguard_header),
mShadeViewStateProvider)
.getKeyguardStatusBarViewController();
mKeyguardStatusBarViewController.init();
-
mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent);
- updateViewControllers(userAvatarContainer, keyguardUserSwitcherView);
-
+ updateStatusViewController();
mNotificationStackScrollLayoutController.setOnHeightChangedListener(
new NsslHeightChangedListener());
mNotificationStackScrollLayoutController.setOnEmptySpaceClickListener(
@@ -1225,39 +1188,6 @@
}
}
- private void updateViewControllers(
- FrameLayout userAvatarView,
- KeyguardUserSwitcherView keyguardUserSwitcherView) {
- updateStatusViewController();
- if (mKeyguardUserSwitcherController != null) {
- // Try to close the switcher so that callbacks are triggered if necessary.
- // Otherwise, NPV can get into a state where some of the views are still hidden
- mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple(false);
- }
-
- mKeyguardQsUserSwitchController = null;
- mKeyguardUserSwitcherController = null;
-
- // Re-associate the KeyguardUserSwitcherController
- if (userAvatarView != null) {
- KeyguardQsUserSwitchComponent userSwitcherComponent =
- mKeyguardQsUserSwitchComponentFactory.build(userAvatarView);
- mKeyguardQsUserSwitchController =
- userSwitcherComponent.getKeyguardQsUserSwitchController();
- mKeyguardQsUserSwitchController.init();
- mKeyguardStatusBarViewController.setKeyguardUserSwitcherEnabled(true);
- } else if (keyguardUserSwitcherView != null) {
- KeyguardUserSwitcherComponent userSwitcherComponent =
- mKeyguardUserSwitcherComponentFactory.build(keyguardUserSwitcherView);
- mKeyguardUserSwitcherController =
- userSwitcherComponent.getKeyguardUserSwitcherController();
- mKeyguardUserSwitcherController.init();
- mKeyguardStatusBarViewController.setKeyguardUserSwitcherEnabled(true);
- } else {
- mKeyguardStatusBarViewController.setKeyguardUserSwitcherEnabled(false);
- }
- }
-
/** Updates the StatusBarViewController and updates any that depend on it. */
public void updateStatusViewController() {
// Re-associate the KeyguardStatusViewController
@@ -1391,29 +1321,7 @@
// we need to update KeyguardStatusView constraints after reinflating it
updateResources();
-
- // Re-inflate the keyguard user switcher group.
- updateUserSwitcherFlags();
- boolean isUserSwitcherEnabled = mUserManager.isUserSwitcherEnabled(
- mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user));
- boolean showQsUserSwitch = mKeyguardQsUserSwitchEnabled && isUserSwitcherEnabled;
- boolean showKeyguardUserSwitcher =
- !mKeyguardQsUserSwitchEnabled
- && mKeyguardUserSwitcherEnabled
- && isUserSwitcherEnabled;
- FrameLayout userAvatarView = (FrameLayout) reInflateStub(
- R.id.keyguard_qs_user_switch_view /* viewId */,
- R.id.keyguard_qs_user_switch_stub /* stubId */,
- R.layout.keyguard_qs_user_switch /* layoutId */,
- showQsUserSwitch /* enabled */);
- KeyguardUserSwitcherView keyguardUserSwitcherView =
- (KeyguardUserSwitcherView) reInflateStub(
- R.id.keyguard_user_switcher_view /* viewId */,
- R.id.keyguard_user_switcher_stub /* stubId */,
- R.layout.keyguard_user_switcher /* layoutId */,
- showKeyguardUserSwitcher /* enabled */);
-
- updateViewControllers(userAvatarView, keyguardUserSwitcherView);
+ updateStatusViewController();
mStatusBarStateListener.onDozeAmountChanged(mStatusBarStateController.getDozeAmount(),
mStatusBarStateController.getInterpolatedDozeAmount());
@@ -1424,20 +1332,6 @@
false,
mBarState);
}
- if (mKeyguardQsUserSwitchController != null) {
- mKeyguardQsUserSwitchController.setKeyguardQsUserSwitchVisibility(
- mBarState,
- false,
- false,
- mBarState);
- }
- if (mKeyguardUserSwitcherController != null) {
- mKeyguardUserSwitcherController.setKeyguardUserSwitcherVisibility(
- mBarState,
- false,
- false,
- mBarState);
- }
}
private void attachSplitShadeMediaPlayerContainer(FrameLayout container) {
@@ -1563,7 +1457,6 @@
}
private void updateClockAppearance() {
- int userSwitcherPreferredY = mStatusBarHeaderHeightKeyguard;
boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
boolean shouldAnimateClockChange = mScreenOffAnimationController.shouldAnimateClockChange();
if (MigrateClocksToBlueprint.isEnabled()) {
@@ -1573,11 +1466,6 @@
shouldAnimateClockChange);
}
updateKeyguardStatusViewAlignment(/* animate= */true);
- int userSwitcherHeight = mKeyguardQsUserSwitchController != null
- ? mKeyguardQsUserSwitchController.getUserIconHeight() : 0;
- if (mKeyguardUserSwitcherController != null) {
- userSwitcherHeight = mKeyguardUserSwitcherController.getHeight();
- }
float expandedFraction =
mScreenOffAnimationController.shouldExpandNotifications()
? 1.0f : getExpandedFraction();
@@ -1595,8 +1483,6 @@
mStatusBarHeaderHeightKeyguard,
expandedFraction,
mKeyguardStatusViewController.getLockscreenHeight(),
- userSwitcherHeight,
- userSwitcherPreferredY,
darkAmount, mOverStretchAmount,
bypassEnabled,
mQsController.getHeaderHeight(),
@@ -1621,18 +1507,6 @@
mClockPositionResult.clockX, mClockPositionResult.clockY,
mClockPositionResult.clockScale, animateClock);
}
- if (mKeyguardQsUserSwitchController != null) {
- mKeyguardQsUserSwitchController.updatePosition(
- mClockPositionResult.clockX,
- mClockPositionResult.userSwitchY,
- animateClock);
- }
- if (mKeyguardUserSwitcherController != null) {
- mKeyguardUserSwitcherController.updatePosition(
- mClockPositionResult.clockX,
- mClockPositionResult.userSwitchY,
- animateClock);
- }
updateNotificationTranslucency();
updateClock();
}
@@ -1868,13 +1742,6 @@
mKeyguardStatusViewController
.setTranslationY(mKeyguardOnlyTransitionTranslationY, /* excludeMedia= */true);
}
-
- if (mKeyguardQsUserSwitchController != null) {
- mKeyguardQsUserSwitchController.setAlpha(alpha);
- }
- if (mKeyguardUserSwitcherController != null) {
- mKeyguardUserSwitcherController.setAlpha(alpha);
- }
}
@Override
@@ -3248,24 +3115,6 @@
}
@Override
- public void startBouncerPreHideAnimation() {
- if (mKeyguardQsUserSwitchController != null) {
- mKeyguardQsUserSwitchController.setKeyguardQsUserSwitchVisibility(
- mBarState,
- true /* keyguardFadingAway */,
- false /* goingToFullShade */,
- mBarState);
- }
- if (mKeyguardUserSwitcherController != null) {
- mKeyguardUserSwitcherController.setKeyguardUserSwitcherVisibility(
- mBarState,
- true /* keyguardFadingAway */,
- false /* goingToFullShade */,
- mBarState);
- }
- }
-
- @Override
public ShadeFoldAnimatorImpl getShadeFoldAnimator() {
return mShadeFoldAnimator;
}
@@ -3394,8 +3243,6 @@
ipw.println(mMaxAllowedKeyguardNotifications);
ipw.print("mAnimateNextPositionUpdate="); ipw.println(mAnimateNextPositionUpdate);
ipw.print("isPanelExpanded()="); ipw.println(isPanelExpanded());
- 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);
@@ -3552,31 +3399,6 @@
}
@Override
- public boolean closeUserSwitcherIfOpen() {
- if (mKeyguardUserSwitcherController != null) {
- return mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple(
- true /* animate */);
- }
- return false;
- }
-
- private void updateUserSwitcherFlags() {
- mKeyguardUserSwitcherEnabled = mResources.getBoolean(
- com.android.internal.R.bool.config_keyguardUserSwitcher);
- mKeyguardQsUserSwitchEnabled =
- mKeyguardUserSwitcherEnabled
- && mFeatureFlags.isEnabled(Flags.QS_USER_DETAIL_SHORTCUT);
- }
-
- private void registerSettingsChangeListener() {
- mContentResolver.registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.USER_SWITCHER_ENABLED),
- /* notifyForDescendants */ false,
- mSettingsChangeObserver
- );
- }
-
- @Override
public void updateSystemUiStateFlags() {
if (SysUiState.DEBUG) {
Log.d(TAG, "Updating panel sysui state flags: fullyExpanded="
@@ -4261,13 +4083,6 @@
}
}
- private void onQsStateUpdated(boolean qsExpanded, boolean isStackScrollerOverscrolling) {
- if (mKeyguardUserSwitcherController != null && qsExpanded
- && !isStackScrollerOverscrolling) {
- mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple(true);
- }
- }
-
private void onQsClippingImmediatelyApplied(boolean clipStatusView,
Rect lastQsClipBounds, int top, boolean qsFragmentCreated, boolean qsVisible) {
if (qsFragmentCreated) {
@@ -4383,44 +4198,12 @@
}
@Override
- public void onSmallestScreenWidthChanged() {
- Trace.beginSection("onSmallestScreenWidthChanged");
- debugLog("onSmallestScreenWidthChanged");
-
- // Can affect multi-user switcher visibility as it depends on screen size by default:
- // it is enabled only for devices with large screens (see config_keyguardUserSwitcher)
- boolean prevKeyguardUserSwitcherEnabled = mKeyguardUserSwitcherEnabled;
- boolean prevKeyguardQsUserSwitchEnabled = mKeyguardQsUserSwitchEnabled;
- updateUserSwitcherFlags();
- if (prevKeyguardUserSwitcherEnabled != mKeyguardUserSwitcherEnabled
- || prevKeyguardQsUserSwitchEnabled != mKeyguardQsUserSwitchEnabled) {
- reInflateViews();
- }
-
- Trace.endSection();
- }
-
- @Override
public void onDensityOrFontScaleChanged() {
debugLog("onDensityOrFontScaleChanged");
reInflateViews();
}
}
- private final class SettingsChangeObserver extends ContentObserver {
- SettingsChangeObserver(Handler handler) {
- super(handler);
- }
-
- @Override
- public void onChange(boolean selfChange) {
- debugLog("onSettingsChanged");
-
- // Can affect multi-user switcher visibility
- reInflateViews();
- }
- }
-
private final class StatusBarStateListener implements StateListener {
@Override
public void onStateChanged(int statusBarState) {
@@ -4592,12 +4375,10 @@
mConfigurationListener.onThemeChanged();
mFalsingManager.addTapListener(mFalsingTapListener);
mKeyguardIndicationController.init();
- registerSettingsChangeListener();
}
@Override
public void onViewDetachedFromWindow(View v) {
- mContentResolver.unregisterContentObserver(mSettingsChangeObserver);
mFragmentService.getFragmentHostManager(mView)
.removeTagListener(QS.TAG, mQsController.getQsFragmentListener());
mStatusBarStateController.removeCallback(mStatusBarStateListener);
@@ -4724,13 +4505,6 @@
}
mKeyguardInteractor.setAlpha(alpha);
-
- if (mKeyguardQsUserSwitchController != null) {
- mKeyguardQsUserSwitchController.setAlpha(alpha);
- }
- if (mKeyguardUserSwitcherController != null) {
- mKeyguardUserSwitcherController.setAlpha(alpha);
- }
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
index 53617d0..f65d378 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
@@ -55,19 +55,12 @@
override fun startExpandLatencyTracking() {}
- override fun startBouncerPreHideAnimation() {}
-
override fun dozeTimeTick() {}
override fun resetViews(animate: Boolean) {}
override val barState: Int = 0
- @Deprecated("Only supported by very old devices that will not adopt scenes.")
- override fun closeUserSwitcherIfOpen(): Boolean {
- return false
- }
-
override fun onBackPressed() {}
@Deprecated("According to b/318376223, shade predictive back is not be supported.")
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractor.kt
index 15ea219..faf238c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractor.kt
@@ -29,17 +29,6 @@
/** Returns whether the shade can be collapsed. */
fun canBeCollapsed(): Boolean
- /**
- * Close the keyguard user switcher if it is open and capable of closing.
- *
- * Has no effect if user switcher isn't supported, if the user switcher is already closed, or if
- * the user switcher uses "simple" mode. The simple user switcher cannot be closed.
- *
- * @return true if the keyguard user switcher was open, and is now closed
- */
- @Deprecated("Only supported by very old devices that will not adopt scenes.")
- fun closeUserSwitcherIfOpen(): Boolean
-
/** Called when Back gesture has been committed (i.e. a back event has definitely occurred) */
fun onBackPressed()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt
index f151307..884cfc10 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt
@@ -49,11 +49,6 @@
return shadeInteractor.isAnyExpanded.value && !shadeInteractor.isUserInteracting.value
}
- @Deprecated("Only supported by very old devices that will not adopt scenes.")
- override fun closeUserSwitcherIfOpen(): Boolean {
- return false
- }
-
override fun onBackPressed() {
animateCollapseQs(false)
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index a3f2c64..f1765e7 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -79,11 +79,7 @@
* The fraction between [0..1] (i.e., percentage) of screen width to consider the threshold
* between "top-left" and "top-right" for the purposes of dual-shade invocation.
*
- * When the dual-shade is not wide, this always returns 0.5 (the top edge is evenly split). On
- * wide layouts however, a larger fraction is returned because only the area of the system
- * status icons is considered top-right.
- *
- * Note that this fraction only determines the split between the absolute left and right
+ * Note that this fraction only determines the *split* between the absolute left and right
* directions. In RTL layouts, the "top-start" edge will resolve to "top-right", and "top-end"
* will resolve to "top-left".
*/
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractor.kt
index 987c016..f538d74 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractor.kt
@@ -38,9 +38,6 @@
*/
@Deprecated("Use ShadeInteractor instead") val isExpanded: Boolean
- /** Called before animating Keyguard dismissal, i.e. the animation dismissing the bouncer. */
- fun startBouncerPreHideAnimation()
-
/** Called once every minute while dozing. */
fun dozeTimeTick()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
index 2d7476c..d712ece 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
@@ -16,6 +16,7 @@
package com.android.systemui.shade.domain.interactor
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -27,7 +28,6 @@
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
-import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.withContext
class ShadeLockscreenInteractorImpl
@@ -54,10 +54,6 @@
override val isExpanded
get() = shadeInteractor.isAnyExpanded.value
- override fun startBouncerPreHideAnimation() {
- // TODO("b/324280998") Implement replacement or delete
- }
-
override fun dozeTimeTick() {
// TODO("b/383591086") Implement replacement or delete
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt
index c838c37..edf503d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt
@@ -76,10 +76,8 @@
class ShadeModeInteractorImpl
@Inject
-constructor(
- @Application applicationScope: CoroutineScope,
- private val repository: ShadeRepository,
-) : ShadeModeInteractor {
+constructor(@Application applicationScope: CoroutineScope, repository: ShadeRepository) :
+ ShadeModeInteractor {
override val isShadeLayoutWide: StateFlow<Boolean> = repository.isShadeLayoutWide
@@ -92,15 +90,7 @@
initialValue = determineShadeMode(isShadeLayoutWide.value),
)
- @FloatRange(from = 0.0, to = 1.0)
- override fun getTopEdgeSplitFraction(): Float {
- // Note: this implicitly relies on isShadeLayoutWide being hot (i.e. collected). This
- // assumption allows us to query its value on demand (during swipe source detection) instead
- // of running another infinite coroutine.
- // TODO(b/338577208): Instead of being fixed at 0.8f, this should dynamically updated based
- // on the position of system-status icons in the status bar.
- return if (repository.isShadeLayoutWide.value) 0.8f else 0.5f
- }
+ @FloatRange(from = 0.0, to = 1.0) override fun getTopEdgeSplitFraction(): Float = 0.5f
private fun determineShadeMode(isShadeLayoutWide: Boolean): ShadeMode {
return when {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/ChipDateTimeView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/ChipDateTimeView.kt
new file mode 100644
index 0000000..6ebeb84
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/ChipDateTimeView.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.chips.ui.view
+
+import android.content.Context
+import android.content.res.Configuration
+import android.util.AttributeSet
+import android.widget.DateTimeView
+
+/** A [DateTimeView] for chips in the status bar. See also: [ChipTextView]. */
+class ChipDateTimeView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
+ DateTimeView(context, attrs) {
+ private val textTruncationHelper = ChipTextTruncationHelper(this)
+
+ override fun onConfigurationChanged(newConfig: Configuration?) {
+ super.onConfigurationChanged(newConfig)
+ textTruncationHelper.onConfigurationChanged()
+ }
+
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ // Evaluate how wide the text *wants* to be if it had unlimited space. This is needed so
+ // that [textTruncationHelper.shouldShowText] works correctly.
+ super.onMeasure(textTruncationHelper.unlimitedWidthMeasureSpec.specInt, heightMeasureSpec)
+
+ if (
+ textTruncationHelper.shouldShowText(
+ desiredTextWidthPx = measuredWidth,
+ widthMeasureSpec = SysuiMeasureSpec(widthMeasureSpec),
+ )
+ ) {
+ // Show the text with the width spec specified by the helper
+ super.onMeasure(textTruncationHelper.widthMeasureSpec.specInt, heightMeasureSpec)
+ } else {
+ // Changing visibility ensures that the content description is not read aloud when the
+ // text isn't displayed.
+ visibility = GONE
+ setMeasuredDimension(0, 0)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/ChipTextTruncationHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/ChipTextTruncationHelper.kt
new file mode 100644
index 0000000..52495eb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/ChipTextTruncationHelper.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.chips.ui.view
+
+import android.view.View
+import android.view.View.MeasureSpec
+import android.widget.TextView.resolveSize
+import com.android.systemui.res.R
+
+/**
+ * Helper class to determine when a status bar chip's text should be hidden because it's too long.
+ */
+class ChipTextTruncationHelper(private val view: View) {
+ /** A measure spec for the status bar chip text with an unlimited width. */
+ val unlimitedWidthMeasureSpec =
+ SysuiMeasureSpec(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED))
+
+ /** The [MeasureSpec] that the view should actually use win [onMeasure]. */
+ lateinit var widthMeasureSpec: SysuiMeasureSpec
+
+ private var maxWidth: Int = 0
+ set(value) {
+ field = value
+ maximumWidthMeasureSpec =
+ SysuiMeasureSpec(MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST))
+ }
+
+ /** A measure spec for the status bar chip text with the correct maximum width. */
+ private lateinit var maximumWidthMeasureSpec: SysuiMeasureSpec
+
+ init {
+ maxWidth = fetchMaxWidth()
+ }
+
+ fun onConfigurationChanged() {
+ maxWidth = fetchMaxWidth()
+ }
+
+ /**
+ * Returns true if this view should show the text because there's enough room for a substantial
+ * amount of text, and returns false if this view should hide the text because the text is much
+ * too long.
+ *
+ * @param desiredTextWidthPx should be calculated by having the view measure itself with
+ * [unlimitedWidthMeasureSpec] and then sending its `measuredWidth` to this method. (This
+ * class can't compute [desiredTextWidthPx] directly because [View.onMeasure] can only be
+ * called by the view itself.)
+ * @param widthMeasureSpec the view's current and unmodified width spec
+ */
+ fun shouldShowText(desiredTextWidthPx: Int, widthMeasureSpec: SysuiMeasureSpec): Boolean {
+ // Evaluate how wide the text *can* be based on:
+ // #1: The maximum width encoded by [maxWidth]
+ val maxWidthBasedOnDimension =
+ resolveSize(desiredTextWidthPx, maximumWidthMeasureSpec.specInt)
+ // #2: The width the view is allowed to take up (If there's 2 chips, the second chip likely
+ // has < [maxWidth] room available)
+ val maxWidthBasedOnViewSpaceAvailable =
+ resolveSize(desiredTextWidthPx, widthMeasureSpec.specInt)
+
+ val enforcedTextWidth: Int
+ if (maxWidthBasedOnViewSpaceAvailable < maxWidthBasedOnDimension) {
+ // View space available takes priority
+ this.widthMeasureSpec = widthMeasureSpec
+ enforcedTextWidth = maxWidthBasedOnViewSpaceAvailable
+ } else {
+ // Enforce the maximum width
+ this.widthMeasureSpec = maximumWidthMeasureSpec
+ enforcedTextWidth = maxWidthBasedOnDimension
+ }
+
+ // Only show the text if at least 50% of it can show. (Assume that if < 50% of the text will
+ // be visible, the text will be more confusing than helpful.)
+ return desiredTextWidthPx <= enforcedTextWidth * 2
+ }
+
+ private fun fetchMaxWidth() =
+ view.context.resources.getDimensionPixelSize(R.dimen.ongoing_activity_chip_max_text_width)
+}
+
+/** A typed class for [MeasureSpec] ints. */
+data class SysuiMeasureSpec(val specInt: Int)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/ChipTextView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/ChipTextView.kt
new file mode 100644
index 0000000..3bcc9c1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/ChipTextView.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.chips.ui.view
+
+import android.content.Context
+import android.content.res.Configuration
+import android.util.AttributeSet
+import android.widget.TextView
+
+/** A [TextView] for chips in the status bar. See also: [ChipDateTimeView]. */
+class ChipTextView
+@JvmOverloads
+constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) :
+ TextView(context, attrs, defStyle) {
+ private val textTruncationHelper = ChipTextTruncationHelper(this)
+
+ override fun onConfigurationChanged(newConfig: Configuration?) {
+ super.onConfigurationChanged(newConfig)
+ textTruncationHelper.onConfigurationChanged()
+ }
+
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ // Evaluate how wide the text *wants* to be if it had unlimited space. This is needed so
+ // that [textTruncationHelper.shouldShowText] works correctly.
+ super.onMeasure(textTruncationHelper.unlimitedWidthMeasureSpec.specInt, heightMeasureSpec)
+
+ if (
+ textTruncationHelper.shouldShowText(
+ desiredTextWidthPx = measuredWidth,
+ widthMeasureSpec = SysuiMeasureSpec(widthMeasureSpec),
+ )
+ ) {
+ // Show the text with the width spec specified by the helper
+ super.onMeasure(textTruncationHelper.widthMeasureSpec.specInt, heightMeasureSpec)
+ } else {
+ // Changing visibility ensures that the content description is not read aloud when the
+ // text isn't displayed.
+ visibility = GONE
+ setMeasuredDimension(0, 0)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/commandline/CommandParser.kt b/packages/SystemUI/src/com/android/systemui/statusbar/commandline/CommandParser.kt
index de369c3..4289dab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/commandline/CommandParser.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/commandline/CommandParser.kt
@@ -74,7 +74,8 @@
*/
fun parse(args: List<String>): Boolean {
if (args.isEmpty()) {
- return false
+ // An empty args list might be valid here if there are no required inputs
+ return validateRequiredParams()
}
val iterator = args.listIterator()
@@ -268,11 +269,7 @@
_subCommands.add(new)
}
- internal fun flag(
- longName: String,
- shortName: String? = null,
- description: String = "",
- ): Flag {
+ internal fun flag(longName: String, shortName: String? = null, description: String = ""): Flag {
checkCliNames(shortName, longName)?.let {
throw IllegalArgumentException("Detected reused flag name ($it)")
}
@@ -305,9 +302,7 @@
return param
}
- internal fun <T : ParseableCommand> subCommand(
- command: T,
- ): OptionalSubCommand<T> {
+ internal fun <T : ParseableCommand> subCommand(command: T): OptionalSubCommand<T> {
checkCliNames(null, command.name)?.let {
throw IllegalArgumentException("Cannot re-use name for subcommand ($it)")
}
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 7df7ef1..254b792 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -63,6 +63,7 @@
import com.android.systemui.statusbar.phone.ui.StatusBarIconControllerImpl;
import com.android.systemui.statusbar.phone.ui.StatusBarIconList;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.wm.shell.shared.ShellTransitions;
import dagger.Binds;
import dagger.Lazy;
@@ -214,8 +215,8 @@
@Provides
@SysUISingleton
static ActivityTransitionAnimator provideActivityTransitionAnimator(
- @Main Executor mainExecutor) {
- return new ActivityTransitionAnimator(mainExecutor);
+ @Main Executor mainExecutor, ShellTransitions shellTransitions) {
+ return new ActivityTransitionAnimator(mainExecutor, shellTransitions);
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 878a4aa..70e27a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -203,18 +203,20 @@
result = inflateSmartReplyViews(result, reInflateFlags, entry, row.getContext(),
packageContext, row.getExistingSmartReplyState(), smartRepliesInflater, mLogger);
boolean isConversation = entry.getRanking().isConversation();
+ Notification.MessagingStyle messagingStyle = null;
+ if (isConversation && (AsyncHybridViewInflation.isEnabled()
+ || LockscreenOtpRedaction.isSingleLineViewEnabled())) {
+ messagingStyle = mConversationProcessor
+ .processNotification(entry, builder, mLogger);
+ }
if (AsyncHybridViewInflation.isEnabled()) {
- Notification.MessagingStyle messagingStyle = null;
- if (isConversation) {
- messagingStyle = mConversationProcessor
- .processNotification(entry, builder, mLogger);
- }
SingleLineViewModel viewModel = SingleLineViewInflater
.inflateSingleLineViewModel(
entry.getSbn().getNotification(),
messagingStyle,
builder,
- row.getContext()
+ row.getContext(),
+ false
);
// If the messagingStyle is null, we want to inflate the normal view
isConversation = viewModel.isConversation();
@@ -228,11 +230,22 @@
mLogger
);
}
-
if (LockscreenOtpRedaction.isSingleLineViewEnabled()) {
- result.mPublicInflatedSingleLineViewModel =
- SingleLineViewInflater.inflateRedactedSingleLineViewModel(row.getContext(),
- isConversation);
+ if (bindParams.redactionType == REDACTION_TYPE_SENSITIVE_CONTENT) {
+ result.mPublicInflatedSingleLineViewModel =
+ SingleLineViewInflater.inflateSingleLineViewModel(
+ entry.getSbn().getNotification(),
+ messagingStyle,
+ builder,
+ row.getContext(),
+ true);
+ } else {
+ result.mPublicInflatedSingleLineViewModel =
+ SingleLineViewInflater.inflateRedactedSingleLineViewModel(
+ row.getContext(),
+ isConversation
+ );
+ }
result.mPublicInflatedSingleLineView =
SingleLineViewInflater.inflatePublicSingleLineView(
isConversation,
@@ -1300,7 +1313,8 @@
mEntry.getSbn().getNotification(),
messagingStyle,
recoveredBuilder,
- mContext
+ mContext,
+ false
);
result.mInflatedSingleLineView =
SingleLineViewInflater.inflatePrivateSingleLineView(
@@ -1313,9 +1327,22 @@
}
if (LockscreenOtpRedaction.isSingleLineViewEnabled()) {
- result.mPublicInflatedSingleLineViewModel =
- SingleLineViewInflater.inflateRedactedSingleLineViewModel(mContext,
- isConversation);
+ if (mBindParams.redactionType == REDACTION_TYPE_SENSITIVE_CONTENT) {
+ result.mPublicInflatedSingleLineViewModel =
+ SingleLineViewInflater.inflateSingleLineViewModel(
+ mEntry.getSbn().getNotification(),
+ messagingStyle,
+ recoveredBuilder,
+ mContext,
+ true
+ );
+ } else {
+ result.mPublicInflatedSingleLineViewModel =
+ SingleLineViewInflater.inflateRedactedSingleLineViewModel(
+ mContext,
+ isConversation
+ );
+ }
result.mPublicInflatedSingleLineView =
SingleLineViewInflater.inflatePublicSingleLineView(
isConversation,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
index e4e1398..c619b17 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
@@ -718,6 +718,7 @@
messagingStyle = messagingStyle,
builder = builder,
systemUiContext = systemUiContext,
+ redactText = false,
)
} else null
@@ -727,10 +728,20 @@
reInflateFlags and FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE != 0
) {
logger.logAsyncTaskProgress(entry, "inflating public single line view model")
- SingleLineViewInflater.inflateRedactedSingleLineViewModel(
- systemUiContext,
- entry.ranking.isConversation,
- )
+ if (bindParams.redactionType == REDACTION_TYPE_SENSITIVE_CONTENT) {
+ SingleLineViewInflater.inflateSingleLineViewModel(
+ notification = entry.sbn.notification,
+ messagingStyle = messagingStyle,
+ builder = builder,
+ systemUiContext = systemUiContext,
+ redactText = true,
+ )
+ } else {
+ SingleLineViewInflater.inflateRedactedSingleLineViewModel(
+ systemUiContext,
+ entry.ranking.isConversation,
+ )
+ }
} else null
val headsUpStatusBarModel =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
index e702f10..fe2803b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
@@ -51,6 +51,7 @@
* notification, not for legacy messaging notifications
* @param builder the recovered Notification Builder
* @param systemUiContext the context of Android System UI
+ * @param redactText indicates if the text needs to be redacted
* @return the inflated SingleLineViewModel
*/
@JvmStatic
@@ -59,13 +60,21 @@
messagingStyle: MessagingStyle?,
builder: Notification.Builder,
systemUiContext: Context,
+ redactText: Boolean,
): SingleLineViewModel {
if (AsyncHybridViewInflation.isUnexpectedlyInLegacyMode()) {
return SingleLineViewModel(null, null, null)
}
peopleHelper.init(systemUiContext)
var titleText = HybridGroupManager.resolveTitle(notification)
- var contentText = HybridGroupManager.resolveText(notification)
+ var contentText =
+ if (redactText) {
+ systemUiContext.getString(
+ com.android.systemui.res.R.string.redacted_notification_single_line_text
+ )
+ } else {
+ HybridGroupManager.resolveText(notification)
+ }
if (messagingStyle == null) {
return SingleLineViewModel(
@@ -81,7 +90,7 @@
if (conversationTextData?.conversationTitle?.isNotEmpty() == true) {
titleText = conversationTextData.conversationTitle
}
- if (conversationTextData?.conversationText?.isNotEmpty() == true) {
+ if (!redactText && conversationTextData?.conversationText?.isNotEmpty() == true) {
contentText = conversationTextData.conversationText
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
index 86c7c6b..4751293 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
@@ -20,6 +20,7 @@
import android.os.UserHandle
import android.view.View
import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.animation.TransitionAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
@@ -38,7 +39,7 @@
private val statusBarStateController: SysuiStatusBarStateController,
@Main private val mainExecutor: DelayableExecutor,
activityStarterInternal: Lazy<ActivityStarterInternalImpl>,
- legacyActivityStarter: Lazy<LegacyActivityStarterInternalImpl>
+ legacyActivityStarter: Lazy<LegacyActivityStarterInternalImpl>,
) : ActivityStarter {
private val activityStarterInternal: ActivityStarterInternal =
@@ -48,10 +49,23 @@
legacyActivityStarter.get()
}
+ override fun registerTransition(
+ cookie: ActivityTransitionAnimator.TransitionCookie,
+ controllerFactory: ActivityTransitionAnimator.ControllerFactory,
+ ) {
+ if (!TransitionAnimator.longLivedReturnAnimationsEnabled()) return
+ activityStarterInternal.registerTransition(cookie, controllerFactory)
+ }
+
+ override fun unregisterTransition(cookie: ActivityTransitionAnimator.TransitionCookie) {
+ if (!TransitionAnimator.longLivedReturnAnimationsEnabled()) return
+ activityStarterInternal.unregisterTransition(cookie)
+ }
+
override fun startPendingIntentDismissingKeyguard(intent: PendingIntent) {
activityStarterInternal.startPendingIntentDismissingKeyguard(
intent = intent,
- dismissShade = true
+ dismissShade = true,
)
}
@@ -98,7 +112,7 @@
intentSentUiThreadCallback: Runnable?,
animationController: ActivityTransitionAnimator.Controller?,
fillInIntent: Intent?,
- extraOptions: Bundle?
+ extraOptions: Bundle?,
) {
activityStarterInternal.startPendingIntentDismissingKeyguard(
intent = intent,
@@ -115,7 +129,7 @@
override fun startPendingIntentMaybeDismissingKeyguard(
intent: PendingIntent,
intentSentUiThreadCallback: Runnable?,
- animationController: ActivityTransitionAnimator.Controller?
+ animationController: ActivityTransitionAnimator.Controller?,
) {
activityStarterInternal.startPendingIntentDismissingKeyguard(
intent = intent,
@@ -245,7 +259,7 @@
override fun postStartActivityDismissingKeyguard(
intent: PendingIntent,
- animationController: ActivityTransitionAnimator.Controller?
+ animationController: ActivityTransitionAnimator.Controller?,
) {
postOnUiThread {
activityStarterInternal.startPendingIntentDismissingKeyguard(
@@ -381,7 +395,7 @@
postOnUiThread {
statusBarStateController.setLeaveOpenOnKeyguardHide(true)
activityStarterInternal.executeRunnableDismissingKeyguard(
- runnable = { runnable?.let { postOnUiThread(runnable = it) } },
+ runnable = { runnable?.let { postOnUiThread(runnable = it) } }
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt
index 93ce6e8..5e427fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone
import android.app.PendingIntent
+import android.content.ComponentName
import android.content.Intent
import android.os.Bundle
import android.os.UserHandle
@@ -27,6 +28,21 @@
interface ActivityStarterInternal {
/**
+ * Registers the given [controllerFactory] for launching and closing transitions matching the
+ * [cookie] and the [ComponentName] that it contains.
+ */
+ fun registerTransition(
+ cookie: ActivityTransitionAnimator.TransitionCookie,
+ controllerFactory: ActivityTransitionAnimator.ControllerFactory,
+ )
+
+ /**
+ * Unregisters the [ActivityTransitionAnimator.Controller] previously registered containing the
+ * given [cookie]. If no such registration exists, this is a no-op.
+ */
+ fun unregisterTransition(cookie: ActivityTransitionAnimator.TransitionCookie)
+
+ /**
* Starts a pending intent after dismissing keyguard.
*
* This can be called in a background thread (to prevent calls in [ActivityIntentHelper] in the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
index f2ef2f0..33e4fed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
@@ -36,6 +36,7 @@
import com.android.systemui.Flags
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.DelegateTransitionAnimatorController
+import com.android.systemui.animation.TransitionAnimator
import com.android.systemui.assist.AssistManager
import com.android.systemui.camera.CameraIntents
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
@@ -103,6 +104,44 @@
private val centralSurfaces: CentralSurfaces?
get() = centralSurfacesOptLazy.get().getOrNull()
+ override fun registerTransition(
+ cookie: ActivityTransitionAnimator.TransitionCookie,
+ controllerFactory: ActivityTransitionAnimator.ControllerFactory,
+ ) {
+ check(TransitionAnimator.longLivedReturnAnimationsEnabled())
+
+ val factory =
+ object :
+ ActivityTransitionAnimator.ControllerFactory(
+ controllerFactory.cookie,
+ controllerFactory.component,
+ controllerFactory.launchCujType,
+ controllerFactory.returnCujType,
+ ) {
+ override fun createController(
+ forLaunch: Boolean
+ ): ActivityTransitionAnimator.Controller {
+ val baseController = controllerFactory.createController(forLaunch)
+ val rootView = baseController.transitionContainer.rootView
+ val controllerFromStatusBar: Optional<ActivityTransitionAnimator.Controller> =
+ statusBarWindowControllerStore.defaultDisplay
+ .wrapAnimationControllerIfInStatusBar(rootView, baseController)
+ return if (controllerFromStatusBar.isPresent) {
+ controllerFromStatusBar.get()
+ } else {
+ baseController
+ }
+ }
+ }
+
+ activityTransitionAnimator.register(cookie, factory)
+ }
+
+ override fun unregisterTransition(cookie: ActivityTransitionAnimator.TransitionCookie) {
+ check(TransitionAnimator.longLivedReturnAnimationsEnabled())
+ activityTransitionAnimator.unregister(cookie)
+ }
+
override fun startPendingIntentDismissingKeyguard(
intent: PendingIntent,
dismissShade: Boolean,
@@ -134,7 +173,7 @@
(skipLockscreenChecks ||
activityIntentHelper.wouldPendingShowOverLockscreen(
intent,
- lockScreenUserManager.currentUserId
+ lockScreenUserManager.currentUserId,
))
val animate =
@@ -190,7 +229,7 @@
null,
null,
null,
- options.toBundle()
+ options.toBundle(),
)
}
},
@@ -239,7 +278,7 @@
animationController: ActivityTransitionAnimator.Controller?,
customMessage: String?,
disallowEnterPictureInPictureWhileLaunching: Boolean,
- userHandle: UserHandle?
+ userHandle: UserHandle?,
) {
if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return
val userHandle: UserHandle = userHandle ?: getActivityUserHandle(intent)
@@ -280,7 +319,7 @@
activityTransitionAnimator.startIntentWithAnimation(
animController,
animate,
- intent.getPackage()
+ intent.getPackage(),
) { adapter: RemoteAnimationAdapter? ->
val options =
ActivityOptions(CentralSurfaces.getActivityOptions(displayId, adapter))
@@ -359,7 +398,7 @@
dismissShade: Boolean,
animationController: ActivityTransitionAnimator.Controller?,
showOverLockscreenWhenLocked: Boolean,
- userHandle: UserHandle?
+ userHandle: UserHandle?,
) {
if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return
val userHandle = userHandle ?: getActivityUserHandle(intent)
@@ -383,7 +422,7 @@
animationController != null &&
shouldAnimateLaunch(
isActivityIntent = true,
- showOverLockscreen = showOverLockscreenWhenLocked
+ showOverLockscreen = showOverLockscreenWhenLocked,
)
var controller: ActivityTransitionAnimator.Controller? = null
@@ -413,7 +452,7 @@
controller,
animate,
intent.getPackage(),
- showOverLockscreenWhenLocked
+ showOverLockscreenWhenLocked,
) { adapter: RemoteAnimationAdapter? ->
TaskStackBuilder.create(context)
.addNextIntent(intent)
@@ -425,7 +464,7 @@
action: ActivityStarter.OnDismissAction,
cancel: Runnable?,
afterKeyguardGone: Boolean,
- customMessage: String?
+ customMessage: String?,
) {
if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return
Log.i(TAG, "Invoking dismissKeyguardThenExecute, afterKeyguardGone: $afterKeyguardGone")
@@ -453,7 +492,7 @@
afterKeyguardGone: Boolean,
deferred: Boolean,
willAnimateOnKeyguard: Boolean,
- customMessage: String?
+ customMessage: String?,
) {
if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return
val onDismissAction: ActivityStarter.OnDismissAction =
@@ -482,12 +521,7 @@
return willAnimateOnKeyguard
}
}
- dismissKeyguardThenExecute(
- onDismissAction,
- cancelAction,
- afterKeyguardGone,
- customMessage,
- )
+ dismissKeyguardThenExecute(onDismissAction, cancelAction, afterKeyguardGone, customMessage)
}
override fun shouldAnimateLaunch(isActivityIntent: Boolean): Boolean {
@@ -565,7 +599,7 @@
val controllerFromStatusBar: Optional<ActivityTransitionAnimator.Controller> =
statusBarWindowControllerStore.defaultDisplay.wrapAnimationControllerIfInStatusBar(
rootView,
- animationController
+ animationController,
)
if (controllerFromStatusBar.isPresent) {
return controllerFromStatusBar.get()
@@ -582,7 +616,7 @@
notifShadeWindowControllerLazy.get(),
commandQueue,
displayId,
- isLaunchForActivity
+ isLaunchForActivity,
)
}
}
@@ -596,7 +630,7 @@
*/
private fun wrapAnimationControllerForLockscreen(
dismissShade: Boolean,
- animationController: ActivityTransitionAnimator.Controller?
+ animationController: ActivityTransitionAnimator.Controller?,
): ActivityTransitionAnimator.Controller? {
return animationController?.let {
object : DelegateTransitionAnimatorController(it) {
@@ -613,7 +647,7 @@
communalSceneInteractor.snapToScene(
newScene = CommunalScenes.Blank,
loggingReason = "ActivityStarterInternalImpl",
- delayMillis = ActivityTransitionAnimator.TIMINGS.totalDuration
+ delayMillis = ActivityTransitionAnimator.TIMINGS.totalDuration,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 013903a..518923e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -33,7 +33,6 @@
import com.android.systemui.res.R;
import com.android.systemui.shade.LargeScreenHeaderHelper;
import com.android.systemui.shade.ShadeViewController;
-import com.android.systemui.statusbar.policy.KeyguardUserSwitcherListView;
import javax.inject.Inject;
@@ -55,19 +54,6 @@
private int mKeyguardStatusHeight;
/**
- * Height of user avatar used by the multi-user switcher. This could either be the
- * {@link KeyguardUserSwitcherListView} when it is closed and only the current user's icon is
- * visible, or it could be height of the avatar used by the
- * {@link com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController}.
- */
- private int mUserSwitchHeight;
-
- /**
- * Preferred Y position of user avatar used by the multi-user switcher.
- */
- private int mUserSwitchPreferredY;
-
- /**
* Minimum top margin to avoid overlap with status bar or multi-user switcher avatar.
*/
private int mMinTopMargin;
@@ -184,17 +170,13 @@
* Sets up algorithm values.
*/
public void setup(int keyguardStatusBarHeaderHeight, float panelExpansion,
- int keyguardStatusHeight, int userSwitchHeight, int userSwitchPreferredY,
- float dark, float overStretchAmount, boolean bypassEnabled,
+ int keyguardStatusHeight, float dark, float overStretchAmount, boolean bypassEnabled,
int unlockedStackScrollerPadding, float qsExpansion, int cutoutTopInset,
boolean isSplitShade, float udfpsTop, float clockBottom, boolean isClockTopAligned) {
- mMinTopMargin = keyguardStatusBarHeaderHeight + Math.max(mContainerTopPadding,
- userSwitchHeight);
+ mMinTopMargin = keyguardStatusBarHeaderHeight + mContainerTopPadding;
mPanelExpansion = BouncerPanelExpansionCalculator
.getKeyguardClockScaledExpansion(panelExpansion);
mKeyguardStatusHeight = keyguardStatusHeight + mStatusViewBottomMargin;
- mUserSwitchHeight = userSwitchHeight;
- mUserSwitchPreferredY = userSwitchPreferredY;
mDarkAmount = dark;
mOverStretchAmount = overStretchAmount;
mBypassEnabled = bypassEnabled;
@@ -210,7 +192,6 @@
public void run(Result result) {
final int y = getClockY(mPanelExpansion, mDarkAmount);
result.clockY = y;
- result.userSwitchY = getUserSwitcherY(mPanelExpansion);
result.clockYFullyDozing = getClockY(
1.0f /* panelExpansion */, 1.0f /* darkAmount */);
result.clockAlpha = getClockAlpha(y);
@@ -224,7 +205,7 @@
if (mBypassEnabled) {
return mUnlockedStackScrollerPadding;
} else if (mIsSplitShade) {
- return getClockY(1.0f, mDarkAmount) + mUserSwitchHeight;
+ return getClockY(1.0f, mDarkAmount);
} else {
return getClockY(1.0f, mDarkAmount) + mKeyguardStatusHeight;
}
@@ -236,7 +217,7 @@
} else if (mIsSplitShade) {
// mCurrentBurnInOffsetY is subtracted to make notifications not follow clock adjustment
// for burn-in. It can make pulsing notification go too high and it will get clipped
- return clockYPosition - mSplitShadeTopNotificationsMargin + mUserSwitchHeight
+ return clockYPosition - mSplitShadeTopNotificationsMargin
- (int) mCurrentBurnInOffsetY;
} else {
return clockYPosition + mKeyguardStatusHeight;
@@ -251,7 +232,7 @@
if (mBypassEnabled) {
return mUnlockedStackScrollerPadding - nsslTop;
} else if (mIsSplitShade) {
- return mSplitShadeTargetTopMargin + mUserSwitchHeight - nsslTop;
+ return mSplitShadeTargetTopMargin - nsslTop;
} else {
// Non-bypass portrait shade already uses values from nsslTop
// so we don't need to subtract it here.
@@ -339,17 +320,6 @@
return (int) (MathUtils.lerp(clockY, clockYDark, darkAmount) + mOverStretchAmount);
}
- private int getUserSwitcherY(float panelExpansion) {
- float userSwitchYRegular = mUserSwitchPreferredY;
- float userSwitchYBouncer = -mKeyguardStatusHeight - mUserSwitchHeight;
-
- // Move user-switch up while collapsing the shade
- float shadeExpansion = Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(panelExpansion);
- float userSwitchY = MathUtils.lerp(userSwitchYBouncer, userSwitchYRegular, shadeExpansion);
-
- return (int) (userSwitchY + mOverStretchAmount);
- }
-
/**
* We might want to fade out the clock when the user is swiping up.
* One exception is when the bouncer will become visible, in this cause the clock
@@ -391,11 +361,6 @@
public int clockY;
/**
- * The y translation of the multi-user switch.
- */
- public int userSwitchY;
-
- /**
* The y translation of the clock when we're fully dozing.
*/
public int clockYFullyDozing;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
index d7cc65d..d7a29c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
@@ -36,6 +36,7 @@
import com.android.systemui.Flags.mediaLockscreenLaunchAnimation
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.DelegateTransitionAnimatorController
+import com.android.systemui.animation.TransitionAnimator
import com.android.systemui.assist.AssistManager
import com.android.systemui.camera.CameraIntents
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
@@ -98,6 +99,44 @@
private val centralSurfaces: CentralSurfaces?
get() = centralSurfacesOptLazy.get().getOrNull()
+ override fun registerTransition(
+ cookie: ActivityTransitionAnimator.TransitionCookie,
+ controllerFactory: ActivityTransitionAnimator.ControllerFactory,
+ ) {
+ check(TransitionAnimator.longLivedReturnAnimationsEnabled())
+
+ val factory =
+ object :
+ ActivityTransitionAnimator.ControllerFactory(
+ controllerFactory.cookie,
+ controllerFactory.component,
+ controllerFactory.launchCujType,
+ controllerFactory.returnCujType,
+ ) {
+ override fun createController(
+ forLaunch: Boolean
+ ): ActivityTransitionAnimator.Controller {
+ val baseController = controllerFactory.createController(forLaunch)
+ val rootView = baseController.transitionContainer.rootView
+ val controllerFromStatusBar: Optional<ActivityTransitionAnimator.Controller> =
+ statusBarWindowControllerStore.defaultDisplay
+ .wrapAnimationControllerIfInStatusBar(rootView, baseController)
+ return if (controllerFromStatusBar.isPresent) {
+ controllerFromStatusBar.get()
+ } else {
+ baseController
+ }
+ }
+ }
+
+ activityTransitionAnimator.register(cookie, factory)
+ }
+
+ override fun unregisterTransition(cookie: ActivityTransitionAnimator.TransitionCookie) {
+ check(TransitionAnimator.longLivedReturnAnimationsEnabled())
+ activityTransitionAnimator.unregister(cookie)
+ }
+
override fun startActivityDismissingKeyguard(
intent: Intent,
dismissShade: Boolean,
@@ -116,7 +155,7 @@
val willLaunchResolverActivity: Boolean =
activityIntentHelper.wouldLaunchResolverActivity(
intent,
- lockScreenUserManager.currentUserId
+ lockScreenUserManager.currentUserId,
)
val animate =
@@ -147,7 +186,7 @@
activityTransitionAnimator.startIntentWithAnimation(
animController,
animate,
- intent.getPackage()
+ intent.getPackage(),
) { adapter: RemoteAnimationAdapter? ->
val options =
ActivityOptions(CentralSurfaces.getActivityOptions(displayId, adapter))
@@ -259,7 +298,7 @@
(skipLockscreenChecks ||
activityIntentHelper.wouldPendingShowOverLockscreen(
intent,
- lockScreenUserManager.currentUserId
+ lockScreenUserManager.currentUserId,
))
val animate =
@@ -317,7 +356,7 @@
null,
null,
null,
- options.toBundle()
+ options.toBundle(),
)
}
},
@@ -409,7 +448,7 @@
controller,
animate,
intent.getPackage(),
- showOverLockscreenWhenLocked
+ showOverLockscreenWhenLocked,
) { adapter: RemoteAnimationAdapter? ->
TaskStackBuilder.create(context)
.addNextIntent(intent)
@@ -495,12 +534,7 @@
return willAnimateOnKeyguard
}
}
- dismissKeyguardThenExecute(
- onDismissAction,
- cancelAction,
- afterKeyguardGone,
- customMessage,
- )
+ dismissKeyguardThenExecute(onDismissAction, cancelAction, afterKeyguardGone, customMessage)
}
/**
@@ -528,7 +562,7 @@
val controllerFromStatusBar: Optional<ActivityTransitionAnimator.Controller> =
statusBarWindowControllerStore.defaultDisplay.wrapAnimationControllerIfInStatusBar(
rootView,
- animationController
+ animationController,
)
if (controllerFromStatusBar.isPresent) {
return controllerFromStatusBar.get()
@@ -545,7 +579,7 @@
notifShadeWindowControllerLazy.get(),
commandQueue,
displayId,
- isLaunchForActivity
+ isLaunchForActivity,
)
}
}
@@ -559,7 +593,7 @@
*/
private fun wrapAnimationControllerForLockscreen(
dismissShade: Boolean,
- animationController: ActivityTransitionAnimator.Controller?
+ animationController: ActivityTransitionAnimator.Controller?,
): ActivityTransitionAnimator.Controller? {
return animationController?.let {
object : DelegateTransitionAnimatorController(it) {
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 bd1360f..3749b96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -1178,7 +1178,6 @@
public void startPreHideAnimation(Runnable finishRunnable) {
if (primaryBouncerIsShowing()) {
mPrimaryBouncerInteractor.startDisappearAnimation(finishRunnable);
- mShadeLockscreenInteractor.startBouncerPreHideAnimation();
// We update the state (which will show the keyguard) only if an animation will run on
// the keyguard. If there is no animation, we wait before updating the state so that we
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index f37bc6b..4d1d64e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -37,10 +37,12 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.InitController;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.QuickSettingsController;
import com.android.systemui.shade.ShadeViewController;
@@ -59,6 +61,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.domain.interactor.NotificationAlertsInteractor;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionCondition;
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider;
@@ -69,7 +72,6 @@
import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.util.Set;
@@ -102,6 +104,7 @@
private final IStatusBarService mBarService;
private final DynamicPrivacyController mDynamicPrivacyController;
private final NotificationListContainer mNotifListContainer;
+ private final DeviceUnlockedInteractor mDeviceUnlockedInteractor;
private final QuickSettingsController mQsController;
protected boolean mVrMode;
@@ -133,7 +136,8 @@
VisualInterruptionDecisionProvider visualInterruptionDecisionProvider,
NotificationRemoteInputManager remoteInputManager,
NotificationRemoteInputManager.Callback remoteInputManagerCallback,
- NotificationListContainer notificationListContainer) {
+ NotificationListContainer notificationListContainer,
+ DeviceUnlockedInteractor deviceUnlockedInteractor) {
mActivityStarter = activityStarter;
mKeyguardStateController = keyguardStateController;
mNotificationPanel = panel;
@@ -160,6 +164,7 @@
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
mNotifListContainer = notificationListContainer;
+ mDeviceUnlockedInteractor = deviceUnlockedInteractor;
IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService(
Context.VR_SERVICE));
@@ -246,16 +251,27 @@
mPowerInteractor.wakeUpIfDozing("NOTIFICATION_CLICK", PowerManager.WAKE_REASON_GESTURE);
if (nowExpanded) {
if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
- mShadeTransitionController.goToLockedShade(clickedEntry.getRow());
- } else if (clickedEntry.isSensitive().getValue()
- && mDynamicPrivacyController.isInLockedDownShade()) {
+ mShadeTransitionController.goToLockedShade(
+ clickedEntry.getRow(), /* needsQSAnimation = */ true);
+ } else if (clickedEntry.isSensitive().getValue() && isInLockedDownShade()) {
mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
+ // launch the bouncer if the device is locked
mActivityStarter.dismissKeyguardThenExecute(() -> false /* dismissAction */
, null /* cancelRunnable */, false /* afterKeyguardGone */);
}
}
}
+ /** @return true if the Shade is shown over the Lockscreen, and the device is locked */
+ private boolean isInLockedDownShade() {
+ if (SceneContainerFlag.isEnabled()) {
+ return mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED
+ && !mDeviceUnlockedInteractor.getDeviceUnlockStatus().getValue().isUnlocked();
+ } else {
+ return mDynamicPrivacyController.isInLockedDownShade();
+ }
+ }
+
@Override
public boolean isDeviceInVrMode() {
return mVrMode;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java
deleted file mode 100644
index 3eeb59d..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.policy;
-
-import android.content.Context;
-import android.graphics.Color;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-
-import androidx.core.graphics.ColorUtils;
-
-import com.android.app.animation.Interpolators;
-import com.android.keyguard.KeyguardConstants;
-import com.android.systemui.qs.tiles.UserDetailItemView;
-import com.android.systemui.res.R;
-
-/**
- * Displays a user on the keyguard user switcher.
- */
-public class KeyguardUserDetailItemView extends UserDetailItemView {
-
- private static final String TAG = "KeyguardUserDetailItemView";
- private static final boolean DEBUG = KeyguardConstants.DEBUG;
-
- private static final int ANIMATION_DURATION_FADE_NAME = 240;
-
- private float mDarkAmount;
- private int mTextColor;
-
- public KeyguardUserDetailItemView(Context context) {
- this(context, null);
- }
-
- public KeyguardUserDetailItemView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public KeyguardUserDetailItemView(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public KeyguardUserDetailItemView(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- @Override
- protected int getFontSizeDimen() {
- return R.dimen.kg_user_switcher_text_size;
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mTextColor = mName.getCurrentTextColor();
- updateDark();
- }
-
- /**
- * Update visibility of this view.
- *
- * @param showItem If true, this item is visible on the screen to the user. Generally this
- * means that the item would be clickable. If false, item visibility will be
- * set to GONE and hidden entirely.
- * @param showTextName Whether or not the name should be shown next to the icon. If false,
- * only the icon is shown.
- * @param animate Whether the transition should be animated. Note, this only applies to
- * animating the text name. The item itself will not animate (i.e. fade in/out).
- * Instead, we delegate that to the parent view.
- */
- void updateVisibilities(boolean showItem, boolean showTextName, boolean animate) {
- if (DEBUG) {
- Log.d(TAG, String.format("updateVisibilities itemIsShown=%b nameIsShown=%b animate=%b",
- showItem, showTextName, animate));
- }
-
- getBackground().setAlpha((showItem && showTextName) ? 255 : 0);
-
- if (showItem) {
- if (showTextName) {
- mName.setVisibility(View.VISIBLE);
- if (animate) {
- mName.setAlpha(0f);
- mName.animate()
- .alpha(1f)
- .setDuration(ANIMATION_DURATION_FADE_NAME)
- .setInterpolator(Interpolators.ALPHA_IN);
- } else {
- mName.setAlpha(1f);
- }
- } else {
- if (animate) {
- mName.setVisibility(View.VISIBLE);
- mName.setAlpha(1f);
- mName.animate()
- .alpha(0f)
- .setDuration(ANIMATION_DURATION_FADE_NAME)
- .setInterpolator(Interpolators.ALPHA_OUT)
- .withEndAction(() -> {
- mName.setVisibility(View.GONE);
- mName.setAlpha(1f);
- });
- } else {
- mName.setVisibility(View.GONE);
- mName.setAlpha(1f);
- }
- }
- setVisibility(View.VISIBLE);
- setAlpha(1f);
- } else {
- // If item isn't shown, don't animate. The parent class will animate the view instead
- setVisibility(View.GONE);
- setAlpha(1f);
- mName.setVisibility(showTextName ? View.VISIBLE : View.GONE);
- mName.setAlpha(1f);
- }
- }
-
- /**
- * Set the amount (ratio) that the device has transitioned to doze.
- *
- * @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
- */
- public void setDarkAmount(float darkAmount) {
- if (mDarkAmount == darkAmount) {
- return;
- }
- mDarkAmount = darkAmount;
- updateDark();
- }
-
- private void updateDark() {
- final int blendedTextColor = ColorUtils.blendARGB(mTextColor, Color.WHITE, mDarkAmount);
- mName.setTextColor(blendedTextColor);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
deleted file mode 100644
index 770f441..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
+++ /dev/null
@@ -1,565 +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.policy;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.database.DataSetObserver;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.os.UserHandle;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.app.animation.Interpolators;
-import com.android.keyguard.KeyguardConstants;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.keyguard.KeyguardVisibilityHelper;
-import com.android.keyguard.dagger.KeyguardUserSwitcherScope;
-import com.android.settingslib.drawable.CircleFramedDrawable;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.res.R;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.notification.AnimatableProperty;
-import com.android.systemui.statusbar.notification.PropertyAnimator;
-import com.android.systemui.statusbar.notification.stack.AnimationProperties;
-import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
-import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
-import com.android.systemui.user.data.source.UserRecord;
-import com.android.systemui.util.ViewController;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.inject.Inject;
-
-/**
- * Manages the user switcher on the Keyguard.
- */
-@KeyguardUserSwitcherScope
-@Deprecated
-public class KeyguardUserSwitcherController extends ViewController<KeyguardUserSwitcherView> {
-
- private static final String TAG = "KeyguardUserSwitcherController";
- private static final boolean DEBUG = KeyguardConstants.DEBUG;
-
- private static final AnimationProperties ANIMATION_PROPERTIES =
- new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-
- private final Context mContext;
- private final UserSwitcherController mUserSwitcherController;
- private final ScreenLifecycle mScreenLifecycle;
- private final KeyguardUserAdapter mAdapter;
- private final KeyguardStateController mKeyguardStateController;
- private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- protected final SysuiStatusBarStateController mStatusBarStateController;
- private final KeyguardVisibilityHelper mKeyguardVisibilityHelper;
- private ObjectAnimator mBgAnimator;
- private final KeyguardUserSwitcherScrim mBackground;
-
- // Child views of KeyguardUserSwitcherView
- private KeyguardUserSwitcherListView mListView;
-
- // State info for the user switcher
- private boolean mUserSwitcherOpen;
- private int mCurrentUserId = UserHandle.USER_NULL;
- private int mBarState;
- private float mDarkAmount;
-
- private final KeyguardUpdateMonitorCallback mInfoCallback =
- new KeyguardUpdateMonitorCallback() {
- @Override
- public void onKeyguardVisibilityChanged(boolean visible) {
- if (DEBUG) Log.d(TAG, String.format("onKeyguardVisibilityChanged %b", visible));
- // Any time the keyguard is hidden, try to close the user switcher menu to
- // restore keyguard to the default state
- if (!visible) {
- closeSwitcherIfOpenAndNotSimple(false);
- }
- }
-
- @Override
- public void onUserSwitching(int userId) {
- closeSwitcherIfOpenAndNotSimple(false);
- }
- };
-
- private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
- @Override
- public void onScreenTurnedOff() {
- if (DEBUG) Log.d(TAG, "onScreenTurnedOff");
- closeSwitcherIfOpenAndNotSimple(false);
- }
- };
-
- private final StatusBarStateController.StateListener mStatusBarStateListener =
- new StatusBarStateController.StateListener() {
- @Override
- public void onStateChanged(int newState) {
- if (DEBUG) Log.d(TAG, String.format("onStateChanged: newState=%d", newState));
-
- boolean goingToFullShade = mStatusBarStateController.goingToFullShade();
- boolean keyguardFadingAway = mKeyguardStateController.isKeyguardFadingAway();
- int oldState = mBarState;
- mBarState = newState;
-
- if (mStatusBarStateController.goingToFullShade()
- || mKeyguardStateController.isKeyguardFadingAway()) {
- closeSwitcherIfOpenAndNotSimple(true);
- }
-
- setKeyguardUserSwitcherVisibility(
- newState,
- keyguardFadingAway,
- goingToFullShade,
- oldState);
- }
-
- @Override
- public void onDozeAmountChanged(float linearAmount, float amount) {
- if (DEBUG) {
- Log.d(TAG, String.format("onDozeAmountChanged: linearAmount=%f amount=%f",
- linearAmount, amount));
- }
- setDarkAmount(amount);
- }
- };
-
- @Inject
- public KeyguardUserSwitcherController(
- KeyguardUserSwitcherView keyguardUserSwitcherView,
- Context context,
- @Main Resources resources,
- LayoutInflater layoutInflater,
- ScreenLifecycle screenLifecycle,
- UserSwitcherController userSwitcherController,
- KeyguardStateController keyguardStateController,
- SysuiStatusBarStateController statusBarStateController,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- DozeParameters dozeParameters,
- ScreenOffAnimationController screenOffAnimationController) {
- super(keyguardUserSwitcherView);
- if (DEBUG) Log.d(TAG, "New KeyguardUserSwitcherController");
- mContext = context;
- mScreenLifecycle = screenLifecycle;
- mUserSwitcherController = userSwitcherController;
- mKeyguardStateController = keyguardStateController;
- mStatusBarStateController = statusBarStateController;
- mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mAdapter = new KeyguardUserAdapter(mContext, resources, layoutInflater,
- mUserSwitcherController, this);
- mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView,
- keyguardStateController, dozeParameters,
- screenOffAnimationController, /* animateYPos= */ false,
- /* logBuffer= */ null);
- mBackground = new KeyguardUserSwitcherScrim(context);
- }
-
- @Override
- protected void onInit() {
- super.onInit();
-
- if (DEBUG) Log.d(TAG, "onInit");
-
- mListView = mView.findViewById(R.id.keyguard_user_switcher_list);
-
- mView.setOnTouchListener((v, event) -> {
- if (!isListAnimating()) {
- // Hide switcher if it didn't handle the touch event (and block the event from
- // going through).
- return closeSwitcherIfOpenAndNotSimple(true);
- }
- return false;
- });
- }
-
- @Override
- protected void onViewAttached() {
- if (DEBUG) Log.d(TAG, "onViewAttached");
- mAdapter.registerDataSetObserver(mDataSetObserver);
- mAdapter.notifyDataSetChanged();
- mKeyguardUpdateMonitor.registerCallback(mInfoCallback);
- mStatusBarStateController.addCallback(mStatusBarStateListener);
- mScreenLifecycle.addObserver(mScreenObserver);
- if (isSimpleUserSwitcher()) {
- // Don't use the background for the simple user switcher
- setUserSwitcherOpened(true /* open */, true /* animate */);
- } else {
- mView.addOnLayoutChangeListener(mBackground);
- mView.setBackground(mBackground);
- mBackground.setAlpha(0);
- }
- }
-
- @Override
- protected void onViewDetached() {
- if (DEBUG) Log.d(TAG, "onViewDetached");
-
- // Detaching the view will always close the switcher
- closeSwitcherIfOpenAndNotSimple(false);
-
- mAdapter.unregisterDataSetObserver(mDataSetObserver);
- mKeyguardUpdateMonitor.removeCallback(mInfoCallback);
- mStatusBarStateController.removeCallback(mStatusBarStateListener);
- mScreenLifecycle.removeObserver(mScreenObserver);
- mView.removeOnLayoutChangeListener(mBackground);
- mView.setBackground(null);
- mBackground.setAlpha(0);
- }
-
- /**
- * Returns {@code true} if the user switcher should be open by default on the lock screen.
- *
- * @see android.os.UserManager#isUserSwitcherEnabled()
- */
- public boolean isSimpleUserSwitcher() {
- return mUserSwitcherController.isSimpleUserSwitcher();
- }
-
- public int getHeight() {
- return mListView.getHeight();
- }
-
- /**
- * @param animate if the transition should be animated
- * @return true if the switcher state changed
- */
- public boolean closeSwitcherIfOpenAndNotSimple(boolean animate) {
- if (isUserSwitcherOpen() && !isSimpleUserSwitcher()) {
- setUserSwitcherOpened(false /* open */, animate);
- return true;
- }
- return false;
- }
-
- public final DataSetObserver mDataSetObserver = new DataSetObserver() {
- @Override
- public void onChanged() {
- refreshUserList();
- }
- };
-
- void refreshUserList() {
- final int childCount = mListView.getChildCount();
- final int adapterCount = mAdapter.getCount();
- final int count = Math.max(childCount, adapterCount);
-
- if (DEBUG) {
- Log.d(TAG, String.format("refreshUserList childCount=%d adapterCount=%d", childCount,
- adapterCount));
- }
-
- boolean foundCurrentUser = false;
- for (int i = 0; i < count; i++) {
- if (i < adapterCount) {
- View oldView = null;
- if (i < childCount) {
- oldView = mListView.getChildAt(i);
- }
- KeyguardUserDetailItemView newView = (KeyguardUserDetailItemView)
- mAdapter.getView(i, oldView, mListView);
- UserRecord userTag =
- (UserRecord) newView.getTag();
- if (userTag.isCurrent) {
- if (i != 0) {
- Log.w(TAG, "Current user is not the first view in the list");
- }
- foundCurrentUser = true;
- mCurrentUserId = userTag.info.id;
- // Current user is always visible
- newView.updateVisibilities(true /* showItem */,
- mUserSwitcherOpen /* showTextName */, false /* animate */);
- } else {
- // Views for non-current users are always expanded (e.g. they should the name
- // next to the user icon). However, they could be hidden entirely if the list
- // is closed.
- newView.updateVisibilities(mUserSwitcherOpen /* showItem */,
- true /* showTextName */, false /* animate */);
- }
- newView.setDarkAmount(mDarkAmount);
- if (oldView == null) {
- // We ran out of existing views. Add it at the end.
- mListView.addView(newView);
- } else if (oldView != newView) {
- // We couldn't rebind the view. Replace it.
- mListView.replaceView(newView, i);
- }
- } else {
- mListView.removeLastView();
- }
- }
- if (!foundCurrentUser) {
- Log.w(TAG, "Current user is not listed");
- mCurrentUserId = UserHandle.USER_NULL;
- }
- }
-
- /**
- * Set the visibility of the keyguard user switcher view based on some new state.
- */
- public void setKeyguardUserSwitcherVisibility(
- int statusBarState,
- boolean keyguardFadingAway,
- boolean goingToFullShade,
- int oldStatusBarState) {
- mKeyguardVisibilityHelper.setViewVisibility(
- statusBarState, keyguardFadingAway, goingToFullShade, oldStatusBarState);
- }
-
- /**
- * Update position of the view with an optional animation
- */
- public void updatePosition(int x, int y, boolean animate) {
- PropertyAnimator.setProperty(mListView, AnimatableProperty.Y, y, ANIMATION_PROPERTIES,
- animate);
- PropertyAnimator.setProperty(mListView, AnimatableProperty.TRANSLATION_X, -Math.abs(x),
- ANIMATION_PROPERTIES, animate);
-
- Rect r = new Rect();
- mListView.getDrawingRect(r);
- mView.offsetDescendantRectToMyCoords(mListView, r);
- mBackground.setGradientCenter(
- (int) (mListView.getTranslationX() + r.left + r.width() / 2),
- (int) (mListView.getTranslationY() + r.top + r.height() / 2));
- }
-
- /**
- * Set keyguard user switcher view alpha.
- */
- public void setAlpha(float alpha) {
- if (!mKeyguardVisibilityHelper.isVisibilityAnimating()) {
- mView.setAlpha(alpha);
- }
- }
-
- /**
- * Set the amount (ratio) that the device has transitioned to doze.
- *
- * @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
- */
- private void setDarkAmount(float darkAmount) {
- boolean isFullyDozed = darkAmount == 1;
- if (darkAmount == mDarkAmount) {
- return;
- }
- mDarkAmount = darkAmount;
- mListView.setDarkAmount(darkAmount);
- if (isFullyDozed) {
- closeSwitcherIfOpenAndNotSimple(false);
- }
- }
-
- private boolean isListAnimating() {
- return mKeyguardVisibilityHelper.isVisibilityAnimating() || mListView.isAnimating();
- }
-
- /**
- * NOTE: switcher state is updated before animations finish.
- *
- * @param animate true to animate transition. The user switcher state (i.e.
- * {@link #isUserSwitcherOpen()}) is updated before animation is finished.
- */
- private void setUserSwitcherOpened(boolean open, boolean animate) {
- if (DEBUG) {
- Log.d(TAG,
- String.format("setUserSwitcherOpened: %b -> %b (animate=%b)",
- mUserSwitcherOpen, open, animate));
- }
- mUserSwitcherOpen = open;
- updateVisibilities(animate);
- }
-
- private void updateVisibilities(boolean animate) {
- if (DEBUG) Log.d(TAG, String.format("updateVisibilities: animate=%b", animate));
- if (mBgAnimator != null) {
- mBgAnimator.cancel();
- }
-
- if (mUserSwitcherOpen) {
- mBgAnimator = ObjectAnimator.ofInt(mBackground, "alpha", 0, 255);
- mBgAnimator.setDuration(400);
- mBgAnimator.setInterpolator(Interpolators.ALPHA_IN);
- mBgAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mBgAnimator = null;
- }
- });
- mBgAnimator.start();
- } else {
- mBgAnimator = ObjectAnimator.ofInt(mBackground, "alpha", 255, 0);
- mBgAnimator.setDuration(400);
- mBgAnimator.setInterpolator(Interpolators.ALPHA_OUT);
- mBgAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mBgAnimator = null;
- }
- });
- mBgAnimator.start();
- }
- mListView.updateVisibilities(mUserSwitcherOpen, animate);
- }
-
- private boolean isUserSwitcherOpen() {
- return mUserSwitcherOpen;
- }
-
- static class KeyguardUserAdapter extends
- BaseUserSwitcherAdapter implements View.OnClickListener {
-
- private final Context mContext;
- private final Resources mResources;
- private final LayoutInflater mLayoutInflater;
- private KeyguardUserSwitcherController mKeyguardUserSwitcherController;
- private View mCurrentUserView;
- // List of users where the first entry is always the current user
- private ArrayList<UserRecord> mUsersOrdered = new ArrayList<>();
-
- KeyguardUserAdapter(Context context, Resources resources, LayoutInflater layoutInflater,
- UserSwitcherController controller,
- KeyguardUserSwitcherController keyguardUserSwitcherController) {
- super(controller);
- mContext = context;
- mResources = resources;
- mLayoutInflater = layoutInflater;
- mKeyguardUserSwitcherController = keyguardUserSwitcherController;
- }
-
- @Override
- public void notifyDataSetChanged() {
- // At this point, value of isSimpleUserSwitcher() may have changed in addition to the
- // data set
- refreshUserOrder();
- super.notifyDataSetChanged();
- }
-
- void refreshUserOrder() {
- List<UserRecord> users = super.getUsers();
- mUsersOrdered = new ArrayList<>(users.size());
- for (int i = 0; i < users.size(); i++) {
- UserRecord record = users.get(i);
- if (record.isCurrent) {
- mUsersOrdered.add(0, record);
- } else {
- mUsersOrdered.add(record);
- }
- }
- }
-
- @Override
- protected ArrayList<UserRecord> getUsers() {
- return mUsersOrdered;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- UserRecord item = getItem(position);
- return createUserDetailItemView(convertView, parent, item);
- }
-
- KeyguardUserDetailItemView convertOrInflate(View convertView, ViewGroup parent) {
- if (!(convertView instanceof KeyguardUserDetailItemView)
- || !(convertView.getTag() instanceof UserRecord)) {
- convertView = mLayoutInflater.inflate(
- R.layout.keyguard_user_switcher_item, parent, false);
- }
- return (KeyguardUserDetailItemView) convertView;
- }
-
- KeyguardUserDetailItemView createUserDetailItemView(View convertView, ViewGroup parent,
- UserRecord item) {
- KeyguardUserDetailItemView v = convertOrInflate(convertView, parent);
- v.setOnClickListener(this);
-
- String name = getName(mContext, item);
- if (item.picture == null) {
- v.bind(name, getDrawable(item).mutate(), item.resolveId());
- } else {
- int avatarSize =
- (int) mResources.getDimension(R.dimen.kg_framed_avatar_size);
- Drawable drawable = new CircleFramedDrawable(item.picture, avatarSize);
- drawable.setColorFilter(
- item.isSwitchToEnabled ? null : getDisabledUserAvatarColorFilter());
- v.bind(name, drawable, item.info.id);
- }
- v.setActivated(item.isCurrent);
- v.setDisabledByAdmin(item.isDisabledByAdmin());
- v.setEnabled(item.isSwitchToEnabled);
- UserSwitcherController.setSelectableAlpha(v);
-
- if (item.isCurrent) {
- mCurrentUserView = v;
- }
- v.setTag(item);
- return v;
- }
-
- private Drawable getDrawable(UserRecord item) {
- Drawable drawable;
- if (item.isCurrent && item.isGuest) {
- drawable = mContext.getDrawable(R.drawable.ic_avatar_guest_user);
- } else {
- drawable = getIconDrawable(mContext, item);
- }
-
- int iconColorRes;
- if (item.isSwitchToEnabled) {
- iconColorRes = R.color.kg_user_switcher_avatar_icon_color;
- } else {
- iconColorRes = R.color.kg_user_switcher_restricted_avatar_icon_color;
- }
- drawable.setTint(mResources.getColor(iconColorRes, mContext.getTheme()));
-
- Drawable bg = mContext.getDrawable(com.android.settingslib.R.drawable.user_avatar_bg);
- drawable = new LayerDrawable(new Drawable[]{bg, drawable});
- return drawable;
- }
-
- @Override
- public void onClick(View v) {
- UserRecord user = (UserRecord) v.getTag();
-
- if (mKeyguardUserSwitcherController.isListAnimating()) {
- return;
- }
-
- if (mKeyguardUserSwitcherController.isUserSwitcherOpen()) {
- if (!user.isCurrent || user.isGuest) {
- onUserListItemClicked(user);
- } else {
- mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple(
- true /* animate */);
- }
- } else {
- // If switcher is closed, tapping anywhere in the view will open it
- mKeyguardUserSwitcherController.setUserSwitcherOpened(
- true /* open */, true /* animate */);
- }
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java
deleted file mode 100644
index 363b06a..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java
+++ /dev/null
@@ -1,151 +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.policy;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-
-import com.android.app.animation.Interpolators;
-import com.android.keyguard.AlphaOptimizedLinearLayout;
-import com.android.keyguard.KeyguardConstants;
-import com.android.settingslib.animation.AppearAnimationUtils;
-import com.android.settingslib.animation.DisappearAnimationUtils;
-
-/**
- * The container for the user switcher on Keyguard.
- */
-public class KeyguardUserSwitcherListView extends AlphaOptimizedLinearLayout {
-
- private static final String TAG = "KeyguardUserSwitcherListView";
- private static final boolean DEBUG = KeyguardConstants.DEBUG;
-
- private boolean mAnimating;
- private final AppearAnimationUtils mAppearAnimationUtils;
- private final DisappearAnimationUtils mDisappearAnimationUtils;
-
- public KeyguardUserSwitcherListView(Context context, AttributeSet attrs) {
- super(context, attrs);
- mAppearAnimationUtils = new AppearAnimationUtils(context,
- AppearAnimationUtils.DEFAULT_APPEAR_DURATION,
- -0.5f /* translationScaleFactor */,
- 0.5f /* delayScaleFactor */,
- Interpolators.FAST_OUT_SLOW_IN);
- mDisappearAnimationUtils = new DisappearAnimationUtils(context,
- AppearAnimationUtils.DEFAULT_APPEAR_DURATION,
- 0.2f /* translationScaleFactor */,
- 0.2f /* delayScaleFactor */,
- Interpolators.FAST_OUT_SLOW_IN_REVERSE);
- }
-
- /**
- * Set the amount (ratio) that the device has transitioned to doze.
- *
- * @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
- */
- void setDarkAmount(float darkAmount) {
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View v = getChildAt(i);
- if (v instanceof KeyguardUserDetailItemView) {
- ((KeyguardUserDetailItemView) v).setDarkAmount(darkAmount);
- }
- }
- }
-
- boolean isAnimating() {
- return mAnimating;
- }
-
- /**
- * Update visibilities of this view and child views for when the user list is open or closed.
- * If closed, this hides everything but the first item (which is always the current user).
- */
- void updateVisibilities(boolean open, boolean animate) {
- if (DEBUG) {
- Log.d(TAG, String.format("updateVisibilities: open=%b animate=%b childCount=%d",
- open, animate, getChildCount()));
- }
-
- mAnimating = false;
-
- int childCount = getChildCount();
- KeyguardUserDetailItemView[] userItemViews = new KeyguardUserDetailItemView[childCount];
- for (int i = 0; i < childCount; i++) {
- userItemViews[i] = (KeyguardUserDetailItemView) getChildAt(i);
- userItemViews[i].clearAnimation();
- if (i == 0) {
- // The first child is always the current user.
- userItemViews[i].updateVisibilities(true /* showItem */, open /* showTextName */,
- animate);
- userItemViews[i].setClickable(true);
- } else {
- // Update clickable state immediately so that the menu feels more responsive
- userItemViews[i].setClickable(open);
- // when opening we need to make views visible beforehand so they can be animated
- if (open) {
- userItemViews[i].updateVisibilities(true /* showItem */,
- true /* showTextName */, false /* animate */);
- }
-
- }
- }
-
- if (animate && userItemViews.length > 1) {
- // AnimationUtils will immediately hide/show the first item in the array. Since the
- // first view is the current user, we want to manage its visibility separately.
- // Set first item to null so AnimationUtils ignores it.
- userItemViews[0] = null;
-
- setClipChildren(false);
- setClipToPadding(false);
- mAnimating = true;
- (open ? mAppearAnimationUtils : mDisappearAnimationUtils)
- .startAnimation(userItemViews, () -> {
- setClipChildren(true);
- setClipToPadding(true);
- mAnimating = false;
- if (!open) {
- // after closing we hide children so that height of this view is correct
- for (int i = 1; i < userItemViews.length; i++) {
- userItemViews[i].updateVisibilities(false /* showItem */,
- true /* showTextName */, false /* animate */);
- }
- }
- });
- }
- }
-
- /**
- * Replaces the view at the specified position in the group.
- *
- * @param index the position in the group of the view to remove
- */
- void replaceView(KeyguardUserDetailItemView newView, int index) {
- removeViewAt(index);
- addView(newView, index);
- }
-
- /**
- * Removes the last view in the group.
- */
- void removeLastView() {
- int lastIndex = getChildCount() - 1;
- removeViewAt(lastIndex);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java
deleted file mode 100644
index 5ed207cc3..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.policy;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.RadialGradient;
-import android.graphics.Rect;
-import android.graphics.Shader;
-import android.graphics.drawable.Drawable;
-import android.view.View;
-
-import com.android.systemui.res.R;
-
-/**
- * Gradient background for the user switcher on Keyguard.
- */
-public class KeyguardUserSwitcherScrim extends Drawable
- implements View.OnLayoutChangeListener {
-
- private static final float OUTER_EXTENT = 2.5f;
- private static final float INNER_EXTENT = 0.25f;
-
- private int mDarkColor;
- private int mAlpha = 255;
- private Paint mRadialGradientPaint = new Paint();
- private int mCircleX;
- private int mCircleY;
- private int mSize;
-
- public KeyguardUserSwitcherScrim(Context context) {
- mDarkColor = context.getColor(
- R.color.keyguard_user_switcher_background_gradient_color);
- }
-
- @Override
- public void draw(Canvas canvas) {
- if (mAlpha == 0) {
- return;
- }
- Rect bounds = getBounds();
- canvas.drawRect(bounds.left, bounds.top, bounds.right, bounds.bottom, mRadialGradientPaint);
- }
-
- @Override
- public void setAlpha(int alpha) {
- mAlpha = alpha;
- updatePaint();
- invalidateSelf();
- }
-
- @Override
- public int getAlpha() {
- return mAlpha;
- }
-
- @Override
- public void setColorFilter(ColorFilter colorFilter) {
- }
-
- @Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
- }
-
- @Override
- public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
- int oldTop, int oldRight, int oldBottom) {
- if (left != oldLeft || top != oldTop || right != oldRight || bottom != oldBottom) {
- int width = right - left;
- int height = bottom - top;
- mSize = Math.max(width, height);
- updatePaint();
- }
- }
-
- private void updatePaint() {
- if (mSize == 0) {
- return;
- }
- float outerRadius = mSize * OUTER_EXTENT;
- mRadialGradientPaint.setShader(
- new RadialGradient(mCircleX, mCircleY, outerRadius,
- new int[] { Color.argb(
- (int) (Color.alpha(mDarkColor) * mAlpha / 255f), 0, 0, 0),
- Color.TRANSPARENT },
- new float[] { Math.max(0f, INNER_EXTENT / OUTER_EXTENT), 1f },
- Shader.TileMode.CLAMP));
- }
-
- /**
- * Sets the center of the radial gradient used as a background
- *
- * @param x
- * @param y
- */
- public void setGradientCenter(int x, int y) {
- mCircleX = x;
- mCircleY = y;
- updatePaint();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherView.java
deleted file mode 100644
index 3f0e23f..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherView.java
+++ /dev/null
@@ -1,31 +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.policy;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.FrameLayout;
-
-/**
- * The container for the user switcher on Keyguard.
- */
-public class KeyguardUserSwitcherView extends FrameLayout {
-
- public KeyguardUserSwitcherView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
index f04fb2c..1ae5682 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
@@ -24,16 +24,16 @@
import androidx.annotation.LayoutRes
import androidx.compose.ui.util.fastForEachIndexed
import androidx.constraintlayout.motion.widget.MotionLayout
-import androidx.constraintlayout.widget.ConstraintSet
import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.FloatValueHolder
import androidx.dynamicanimation.animation.SpringAnimation
import androidx.dynamicanimation.animation.SpringForce
import com.android.internal.R as internalR
import com.android.systemui.res.R
-import com.android.systemui.util.children
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
import com.android.systemui.volume.dialog.ringer.ui.util.VolumeDialogRingerDrawerTransitionListener
+import com.android.systemui.volume.dialog.ringer.ui.util.updateCloseState
+import com.android.systemui.volume.dialog.ringer.ui.util.updateOpenState
import com.android.systemui.volume.dialog.ringer.ui.viewmodel.RingerButtonUiModel
import com.android.systemui.volume.dialog.ringer.ui.viewmodel.RingerButtonViewModel
import com.android.systemui.volume.dialog.ringer.ui.viewmodel.RingerDrawerState
@@ -93,6 +93,7 @@
}
drawerContainer.setTransitionListener(ringerDrawerTransitionListener)
volumeDialogBackgroundView.background = volumeDialogBackgroundView.background.mutate()
+
viewModel.ringerViewModel
.onEach { ringerState ->
when (ringerState) {
@@ -110,7 +111,10 @@
unselectedButtonUiModel,
)
ringerDrawerTransitionListener.setProgressChangeEnabled(true)
- drawerContainer.closeDrawer(uiModel.currentButtonIndex)
+ drawerContainer.closeDrawer(
+ uiModel.currentButtonIndex,
+ ringerState.orientation,
+ )
}
is RingerDrawerState.Closed -> {
@@ -125,8 +129,10 @@
unselectedButtonUiModel,
onProgressChanged = { progress, isReverse ->
// Let's make button progress when switching matches
- // motionLayout transition progress. When full radius,
- // progress is 0.0. When small radius, progress is 1.0.
+ // motionLayout transition progress. When full
+ // radius,
+ // progress is 0.0. When small radius, progress is
+ // 1.0.
backgroundAnimationProgress =
if (isReverse) {
1F - progress
@@ -147,7 +153,10 @@
true
)
}
- drawerContainer.closeDrawer(uiModel.currentButtonIndex)
+ drawerContainer.closeDrawer(
+ uiModel.currentButtonIndex,
+ ringerState.orientation,
+ )
}
}
}
@@ -167,6 +176,7 @@
} else {
ringerDrawerTransitionListener.setProgressChangeEnabled(true)
}
+ updateOpenState(drawerContainer, ringerState.orientation)
drawerContainer.transitionToState(
R.id.volume_dialog_ringer_drawer_open
)
@@ -223,23 +233,30 @@
// We only need to execute on roundness animation end and volume dialog background
// progress update once because these changes should be applied once on volume dialog
// background and ringer drawer views.
- selectedButton.animateTo(
- selectedButtonUiModel,
- if (uiModel.currentButtonIndex == count - 1) {
- onProgressChanged
- } else {
- { _, _ -> }
- },
- roundnessAnimationEndListener,
- )
- unselectedButton.animateTo(
- unselectedButtonUiModel,
- if (previousIndex == count - 1) {
- onProgressChanged
- } else {
- { _, _ -> }
- },
- )
+ val selectedCornerRadius = (selectedButton.background as GradientDrawable).cornerRadius
+ if (selectedCornerRadius.toInt() != selectedButtonUiModel.cornerRadius) {
+ selectedButton.animateTo(
+ selectedButtonUiModel,
+ if (uiModel.currentButtonIndex == count - 1) {
+ onProgressChanged
+ } else {
+ { _, _ -> }
+ },
+ roundnessAnimationEndListener,
+ )
+ }
+ val unselectedCornerRadius =
+ (unselectedButton.background as GradientDrawable).cornerRadius
+ if (unselectedCornerRadius.toInt() != unselectedButtonUiModel.cornerRadius) {
+ unselectedButton.animateTo(
+ unselectedButtonUiModel,
+ if (previousIndex == count - 1) {
+ onProgressChanged
+ } else {
+ { _, _ -> }
+ },
+ )
+ }
} else {
bindButtons(viewModel, uiModel, onAnimationEnd)
}
@@ -318,107 +335,16 @@
inflater.inflate(viewLayoutId, this, true)
getChildAt(childCount - 1).id = View.generateViewId()
}
- cloneConstraintSet(R.id.volume_dialog_ringer_drawer_open)
- .adjustOpenConstraintsForDrawer(this)
}
}
}
- private fun MotionLayout.closeDrawer(selectedIndex: Int) {
+ private fun MotionLayout.closeDrawer(selectedIndex: Int, orientation: Int) {
setTransition(R.id.close_to_open_transition)
- cloneConstraintSet(R.id.volume_dialog_ringer_drawer_close)
- .adjustClosedConstraintsForDrawer(selectedIndex, this)
+ updateCloseState(this, selectedIndex, orientation)
transitionToState(R.id.volume_dialog_ringer_drawer_close)
}
- private fun ConstraintSet.adjustOpenConstraintsForDrawer(motionLayout: MotionLayout) {
- motionLayout.children.forEachIndexed { index, button ->
- setButtonPositionConstraints(motionLayout, index, button)
- setAlpha(button.id, 1.0F)
- constrainWidth(
- button.id,
- motionLayout.context.resources.getDimensionPixelSize(
- R.dimen.volume_dialog_ringer_drawer_button_size
- ),
- )
- constrainHeight(
- button.id,
- motionLayout.context.resources.getDimensionPixelSize(
- R.dimen.volume_dialog_ringer_drawer_button_size
- ),
- )
- if (index != motionLayout.childCount - 1) {
- setMargin(
- button.id,
- ConstraintSet.BOTTOM,
- motionLayout.context.resources.getDimensionPixelSize(
- R.dimen.volume_dialog_components_spacing
- ),
- )
- }
- }
- motionLayout.updateState(R.id.volume_dialog_ringer_drawer_open, this)
- }
-
- private fun ConstraintSet.adjustClosedConstraintsForDrawer(
- selectedIndex: Int,
- motionLayout: MotionLayout,
- ) {
- motionLayout.children.forEachIndexed { index, button ->
- setButtonPositionConstraints(motionLayout, index, button)
- constrainWidth(
- button.id,
- motionLayout.context.resources.getDimensionPixelSize(
- R.dimen.volume_dialog_ringer_drawer_button_size
- ),
- )
- if (selectedIndex != motionLayout.childCount - index - 1) {
- setAlpha(button.id, 0.0F)
- constrainHeight(button.id, 0)
- setMargin(button.id, ConstraintSet.BOTTOM, 0)
- } else {
- setAlpha(button.id, 1.0F)
- constrainHeight(
- button.id,
- motionLayout.context.resources.getDimensionPixelSize(
- R.dimen.volume_dialog_ringer_drawer_button_size
- ),
- )
- }
- }
- motionLayout.updateState(R.id.volume_dialog_ringer_drawer_close, this)
- }
-
- private fun ConstraintSet.setButtonPositionConstraints(
- motionLayout: MotionLayout,
- index: Int,
- button: View,
- ) {
- if (motionLayout.getChildAt(index - 1) == null) {
- connect(button.id, ConstraintSet.TOP, motionLayout.id, ConstraintSet.TOP)
- } else {
- connect(
- button.id,
- ConstraintSet.TOP,
- motionLayout.getChildAt(index - 1).id,
- ConstraintSet.BOTTOM,
- )
- }
-
- if (motionLayout.getChildAt(index + 1) == null) {
- connect(button.id, ConstraintSet.BOTTOM, motionLayout.id, ConstraintSet.BOTTOM)
- } else {
- connect(
- button.id,
- ConstraintSet.BOTTOM,
- motionLayout.getChildAt(index + 1).id,
- ConstraintSet.TOP,
- )
- }
- connect(button.id, ConstraintSet.START, motionLayout.id, ConstraintSet.START)
- connect(button.id, ConstraintSet.END, motionLayout.id, ConstraintSet.END)
- }
-
private suspend fun ImageButton.animateTo(
ringerButtonUiModel: RingerButtonUiModel,
onProgressChanged: (Float, Boolean) -> Unit = { _, _ -> },
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt
new file mode 100644
index 0000000..25ba1bd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.volume.dialog.ringer.ui.util
+
+import android.content.res.Configuration.ORIENTATION_LANDSCAPE
+import android.content.res.Configuration.ORIENTATION_PORTRAIT
+import android.view.View
+import androidx.constraintlayout.motion.widget.MotionLayout
+import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.res.R
+import com.android.systemui.util.children
+
+fun updateOpenState(ringerDrawer: MotionLayout, orientation: Int) {
+ val openSet = ringerDrawer.cloneConstraintSet(R.id.volume_dialog_ringer_drawer_open)
+ openSet.adjustOpenConstraintsForDrawer(ringerDrawer, orientation)
+ ringerDrawer.updateState(R.id.volume_dialog_ringer_drawer_open, openSet)
+}
+
+fun updateCloseState(ringerDrawer: MotionLayout, selectedIndex: Int, orientation: Int) {
+ val closeSet = ringerDrawer.cloneConstraintSet(R.id.volume_dialog_ringer_drawer_close)
+ closeSet.adjustClosedConstraintsForDrawer(ringerDrawer, selectedIndex, orientation)
+ ringerDrawer.updateState(R.id.volume_dialog_ringer_drawer_close, closeSet)
+}
+
+private fun ConstraintSet.setButtonPositionPortraitConstraints(
+ motionLayout: MotionLayout,
+ index: Int,
+ button: View,
+) {
+ if (motionLayout.getChildAt(index - 1) == null) {
+ connect(button.id, ConstraintSet.TOP, motionLayout.id, ConstraintSet.TOP)
+ } else {
+ connect(
+ button.id,
+ ConstraintSet.TOP,
+ motionLayout.getChildAt(index - 1).id,
+ ConstraintSet.BOTTOM,
+ )
+ }
+
+ if (motionLayout.getChildAt(index + 1) == null) {
+ connect(button.id, ConstraintSet.BOTTOM, motionLayout.id, ConstraintSet.BOTTOM)
+ } else {
+ connect(
+ button.id,
+ ConstraintSet.BOTTOM,
+ motionLayout.getChildAt(index + 1).id,
+ ConstraintSet.TOP,
+ )
+ }
+ connect(button.id, ConstraintSet.START, motionLayout.id, ConstraintSet.START)
+ connect(button.id, ConstraintSet.END, motionLayout.id, ConstraintSet.END)
+ clear(button.id, ConstraintSet.LEFT)
+ clear(button.id, ConstraintSet.RIGHT)
+}
+
+private fun ConstraintSet.setButtonPositionLandscapeConstraints(
+ motionLayout: MotionLayout,
+ index: Int,
+ button: View,
+) {
+ if (motionLayout.getChildAt(index - 1) == null) {
+ connect(button.id, ConstraintSet.LEFT, motionLayout.id, ConstraintSet.LEFT)
+ } else {
+ connect(
+ button.id,
+ ConstraintSet.LEFT,
+ motionLayout.getChildAt(index - 1).id,
+ ConstraintSet.RIGHT,
+ )
+ }
+ if (motionLayout.getChildAt(index + 1) == null) {
+ connect(button.id, ConstraintSet.RIGHT, motionLayout.id, ConstraintSet.RIGHT)
+ } else {
+ connect(
+ button.id,
+ ConstraintSet.RIGHT,
+ motionLayout.getChildAt(index + 1).id,
+ ConstraintSet.LEFT,
+ )
+ }
+ connect(button.id, ConstraintSet.TOP, motionLayout.id, ConstraintSet.TOP)
+ connect(button.id, ConstraintSet.BOTTOM, motionLayout.id, ConstraintSet.BOTTOM)
+ clear(button.id, ConstraintSet.START)
+ clear(button.id, ConstraintSet.END)
+}
+
+private fun ConstraintSet.adjustOpenConstraintsForDrawer(
+ motionLayout: MotionLayout,
+ lastOrientation: Int,
+) {
+ motionLayout.children.forEachIndexed { index, button ->
+ setAlpha(button.id, 1.0F)
+ constrainWidth(
+ button.id,
+ motionLayout.context.resources.getDimensionPixelSize(
+ R.dimen.volume_dialog_ringer_drawer_button_size
+ ),
+ )
+ constrainHeight(
+ button.id,
+ motionLayout.context.resources.getDimensionPixelSize(
+ R.dimen.volume_dialog_ringer_drawer_button_size
+ ),
+ )
+ when (lastOrientation) {
+ ORIENTATION_LANDSCAPE -> {
+ setButtonPositionLandscapeConstraints(motionLayout, index, button)
+ if (index != motionLayout.childCount - 1) {
+ setMargin(
+ button.id,
+ ConstraintSet.RIGHT,
+ motionLayout.context.resources.getDimensionPixelSize(
+ R.dimen.volume_dialog_components_spacing
+ ),
+ )
+ } else {
+ setMargin(button.id, ConstraintSet.RIGHT, 0)
+ }
+ setMargin(button.id, ConstraintSet.BOTTOM, 0)
+ }
+ ORIENTATION_PORTRAIT -> {
+ setButtonPositionPortraitConstraints(motionLayout, index, button)
+ if (index != motionLayout.childCount - 1) {
+ setMargin(
+ button.id,
+ ConstraintSet.BOTTOM,
+ motionLayout.context.resources.getDimensionPixelSize(
+ R.dimen.volume_dialog_components_spacing
+ ),
+ )
+ } else {
+ setMargin(button.id, ConstraintSet.BOTTOM, 0)
+ }
+ setMargin(button.id, ConstraintSet.RIGHT, 0)
+ }
+ }
+ }
+}
+
+private fun ConstraintSet.adjustClosedConstraintsForDrawer(
+ motionLayout: MotionLayout,
+ selectedIndex: Int,
+ lastOrientation: Int,
+) {
+ motionLayout.children.forEachIndexed { index, button ->
+ setMargin(button.id, ConstraintSet.RIGHT, 0)
+ setMargin(button.id, ConstraintSet.BOTTOM, 0)
+ when (lastOrientation) {
+ ORIENTATION_LANDSCAPE -> {
+ setButtonPositionLandscapeConstraints(motionLayout, index, button)
+ if (selectedIndex != motionLayout.childCount - index - 1) {
+ setAlpha(button.id, 0.0F)
+ constrainWidth(button.id, 0)
+ } else {
+ setAlpha(button.id, 1.0F)
+ constrainWidth(
+ button.id,
+ motionLayout.context.resources.getDimensionPixelSize(
+ R.dimen.volume_dialog_ringer_drawer_button_size
+ ),
+ )
+ }
+ constrainHeight(
+ button.id,
+ motionLayout.context.resources.getDimensionPixelSize(
+ R.dimen.volume_dialog_ringer_drawer_button_size
+ ),
+ )
+ }
+ ORIENTATION_PORTRAIT -> {
+ setButtonPositionPortraitConstraints(motionLayout, index, button)
+ if (selectedIndex != motionLayout.childCount - index - 1) {
+ setAlpha(button.id, 0.0F)
+ constrainHeight(button.id, 0)
+ } else {
+ setAlpha(button.id, 1.0F)
+ constrainHeight(
+ button.id,
+ motionLayout.context.resources.getDimensionPixelSize(
+ R.dimen.volume_dialog_ringer_drawer_button_size
+ ),
+ )
+ }
+ constrainWidth(
+ button.id,
+ motionLayout.context.resources.getDimensionPixelSize(
+ R.dimen.volume_dialog_ringer_drawer_button_size
+ ),
+ )
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerViewModelState.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerViewModelState.kt
index 78b00af..50898b6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerViewModelState.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerViewModelState.kt
@@ -19,7 +19,8 @@
/** Models ringer view model state. */
sealed class RingerViewModelState {
- data class Available(val uiModel: RingerViewModel) : RingerViewModelState()
+ data class Available(val uiModel: RingerViewModel, val orientation: Int) :
+ RingerViewModelState()
data object Unavailable : RingerViewModelState()
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt
index 627d75e..eec64d9 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt
@@ -33,6 +33,8 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.onConfigChanged
import com.android.systemui.volume.Events
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
@@ -48,6 +50,7 @@
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -65,19 +68,29 @@
private val vibrator: VibratorHelper,
private val volumeDialogLogger: VolumeDialogLogger,
private val visibilityInteractor: VolumeDialogVisibilityInteractor,
+ configurationController: ConfigurationController,
) {
private val drawerState = MutableStateFlow<RingerDrawerState>(RingerDrawerState.Initial)
+ private val orientation: StateFlow<Int> =
+ configurationController.onConfigChanged
+ .map { it.orientation }
+ .stateIn(
+ coroutineScope,
+ SharingStarted.Eagerly,
+ applicationContext.resources.configuration.orientation,
+ )
val ringerViewModel: StateFlow<RingerViewModelState> =
combine(
soundPolicyInteractor.isZenMuted(AudioStream(STREAM_RING)),
ringerInteractor.ringerModel,
drawerState,
- ) { isZenMuted, ringerModel, state ->
+ orientation,
+ ) { isZenMuted, ringerModel, state, orientation ->
level = ringerModel.level
levelMax = ringerModel.levelMax
- ringerModel.toViewModel(state, isZenMuted)
+ ringerModel.toViewModel(state, isZenMuted, orientation)
}
.flowOn(backgroundDispatcher)
.stateIn(coroutineScope, SharingStarted.Eagerly, RingerViewModelState.Unavailable)
@@ -133,6 +146,7 @@
private fun VolumeDialogRingerModel.toViewModel(
drawerState: RingerDrawerState,
isZenMuted: Boolean,
+ orientation: Int,
): RingerViewModelState {
val currentIndex = availableModes.indexOf(currentRingerMode)
if (currentIndex == -1) {
@@ -149,7 +163,8 @@
currentButtonIndex = currentIndex,
selectedButton = it,
drawerState = drawerState,
- )
+ ),
+ orientation,
)
} ?: RingerViewModelState.Unavailable
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt
index c0c525b..88af210 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt
@@ -17,6 +17,7 @@
package com.android.systemui.volume.dialog.sliders.dagger
import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType
+import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogOverscrollViewBinder
import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderHapticsViewBinder
import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderTouchesViewBinder
import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderViewBinder
@@ -37,6 +38,8 @@
fun sliderHapticsViewBinder(): VolumeDialogSliderHapticsViewBinder
+ fun overscrollViewBinder(): VolumeDialogOverscrollViewBinder
+
@Subcomponent.Factory
interface Factory {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/data/repository/VolumeDialogSliderTouchEventsRepository.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/data/repository/VolumeDialogSliderTouchEventsRepository.kt
index adc2383..82885d6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/data/repository/VolumeDialogSliderTouchEventsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/data/repository/VolumeDialogSliderTouchEventsRepository.kt
@@ -16,7 +16,6 @@
package com.android.systemui.volume.dialog.sliders.data.repository
-import android.annotation.SuppressLint
import android.view.MotionEvent
import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
import javax.inject.Inject
@@ -27,7 +26,6 @@
@VolumeDialogSliderScope
class VolumeDialogSliderTouchEventsRepository @Inject constructor() {
- @SuppressLint("SharedFlowCreation")
private val mutableSliderTouchEvents: MutableStateFlow<MotionEvent?> = MutableStateFlow(null)
val sliderTouchEvent: Flow<MotionEvent> = mutableSliderTouchEvents.filterNotNull()
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
index 2967fe8..04dc80c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
@@ -17,14 +17,18 @@
package com.android.systemui.volume.dialog.sliders.domain.interactor
import com.android.systemui.plugins.VolumeDialogController
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogStateInteractor
import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.stateIn
/** Operates a state of particular slider of the Volume Dialog. */
@VolumeDialogSliderScope
@@ -32,6 +36,7 @@
@Inject
constructor(
private val sliderType: VolumeDialogSliderType,
+ @VolumeDialog private val coroutineScope: CoroutineScope,
volumeDialogStateInteractor: VolumeDialogStateInteractor,
private val volumeDialogController: VolumeDialogController,
) {
@@ -47,7 +52,8 @@
}
}
}
- .distinctUntilChanged()
+ .stateIn(coroutineScope, SharingStarted.Eagerly, null)
+ .filterNotNull()
fun setStreamVolume(userLevel: Int) {
with(volumeDialogController) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractor.kt
index c904ac5..690f9ef 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractor.kt
@@ -63,7 +63,7 @@
LinkedHashSet(sliderTypes)
}
.runningReduce { sliderTypes, newSliderTypes ->
- newSliderTypes.apply { addAll(sliderTypes) }
+ sliderTypes.apply { addAll(newSliderTypes) }
}
.map { sliderTypes ->
VolumeDialogSlidersModel(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogOverscrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogOverscrollViewBinder.kt
new file mode 100644
index 0000000..8109b50
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogOverscrollViewBinder.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.volume.dialog.sliders.ui
+
+import android.view.View
+import androidx.dynamicanimation.animation.FloatValueHolder
+import androidx.dynamicanimation.animation.SpringAnimation
+import androidx.dynamicanimation.animation.SpringForce
+import com.android.systemui.res.R
+import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
+import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogOverscrollViewModel
+import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogOverscrollViewModel.OverscrollEventModel
+import com.google.android.material.slider.Slider
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+
+@VolumeDialogSliderScope
+class VolumeDialogOverscrollViewBinder
+@Inject
+constructor(private val viewModel: VolumeDialogOverscrollViewModel) {
+
+ /**
+ * [viewsToAnimate] is an array of [View] to be affected by the overscroll animation. [view] is
+ * NOT animated by default.
+ */
+ fun CoroutineScope.bind(view: View, viewsToAnimate: Array<View>) {
+ val animationValueHolder = FloatValueHolder(0f)
+ val animation: SpringAnimation =
+ SpringAnimation(animationValueHolder)
+ .setSpring(
+ SpringForce(0f).apply {
+ stiffness = 800f
+ dampingRatio = 0.6f
+ }
+ )
+ .addUpdateListener { _, value, _ -> viewsToAnimate.setTranslationY(value) }
+
+ view.requireViewById<Slider>(R.id.volume_dialog_slider).addOnChangeListener { s, value, _ ->
+ viewModel.setSlider(value = value, min = s.valueFrom, max = s.valueTo)
+ }
+
+ viewModel.overscrollEvent
+ .onEach { event ->
+ when (event) {
+ is OverscrollEventModel.Animate -> {
+ animation.animateToFinalPosition(event.targetOffsetPx)
+ }
+ is OverscrollEventModel.Move -> {
+ animation.cancel()
+ viewsToAnimate.setTranslationY(event.touchOffsetPx)
+ animationValueHolder.value = event.touchOffsetPx
+ }
+ }
+ }
+ .launchIn(this)
+ }
+}
+
+private fun Array<View>.setTranslationY(translation: Float) {
+ for (viewToAnimate in this) {
+ viewToAnimate.translationY = translation
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
index faf06b94..a7ffcd7 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
@@ -55,22 +55,21 @@
viewModel.setStreamVolume(value.roundToInt(), fromUser)
}
- viewModel.state.onEach { it.bindToSlider(sliderView) }.launchIn(this)
+ viewModel.state.onEach { sliderView.setModel(it) }.launchIn(this)
}
@SuppressLint("UseCompatLoadingForDrawables")
- private suspend fun VolumeDialogSliderStateModel.bindToSlider(slider: Slider) {
- with(slider) {
- valueFrom = minValue
- valueTo = maxValue
- // coerce the current value to the new value range before animating it
- value = value.coerceIn(valueFrom, valueTo)
- setValueAnimated(
- value,
- jankListenerFactory.update(this, PROGRESS_CHANGE_ANIMATION_DURATION_MS),
- )
- trackIconActiveEnd = context.getDrawable(iconRes)
- }
+ private suspend fun Slider.setModel(model: VolumeDialogSliderStateModel) {
+ valueFrom = model.minValue
+ valueTo = model.maxValue
+ // coerce the current value to the new value range before animating it. This prevents
+ // animating from the value that is outside of current [valueFrom, valueTo].
+ value = value.coerceIn(valueFrom, valueTo)
+ setValueAnimated(
+ model.value,
+ jankListenerFactory.update(this, PROGRESS_CHANGE_ANIMATION_DURATION_MS),
+ )
+ trackIconActiveEnd = context.getDrawable(model.iconRes)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
index c9b5259..f066b56 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
@@ -40,9 +40,17 @@
view.requireViewById(R.id.volume_dialog_floating_sliders_container)
val mainSliderContainer: View =
view.requireViewById(R.id.volume_dialog_main_slider_container)
+ val background: View = view.requireViewById(R.id.volume_dialog_background)
+ val settingsButton: View = view.requireViewById(R.id.volume_dialog_settings)
+ val ringerDrawer: View = view.requireViewById(R.id.volume_ringer_drawer)
+
viewModel.sliders
.onEach { uiModel ->
- bindSlider(uiModel.sliderComponent, mainSliderContainer)
+ bindSlider(
+ uiModel.sliderComponent,
+ mainSliderContainer,
+ arrayOf(mainSliderContainer, background, settingsButton, ringerDrawer),
+ )
val floatingSliderViewBinders = uiModel.floatingSliderComponent
floatingSlidersContainer.ensureChildCount(
@@ -50,7 +58,8 @@
count = floatingSliderViewBinders.size,
)
floatingSliderViewBinders.fastForEachIndexed { index, sliderComponent ->
- bindSlider(sliderComponent, floatingSlidersContainer.getChildAt(index))
+ val sliderContainer = floatingSlidersContainer.getChildAt(index)
+ bindSlider(sliderComponent, sliderContainer, arrayOf(sliderContainer))
}
}
.launchIn(this)
@@ -59,10 +68,12 @@
private fun CoroutineScope.bindSlider(
component: VolumeDialogSliderComponent,
sliderContainer: View,
+ viewsToAnimate: Array<View>,
) {
with(component.sliderViewBinder()) { bind(sliderContainer) }
with(component.sliderTouchesViewBinder()) { bind(sliderContainer) }
with(component.sliderHapticsViewBinder()) { bind(sliderContainer) }
+ with(component.overscrollViewBinder()) { bind(sliderContainer, viewsToAnimate) }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogOverscrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogOverscrollViewModel.kt
new file mode 100644
index 0000000..0d41860
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogOverscrollViewModel.kt
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.volume.dialog.sliders.ui.viewmodel
+
+import android.content.Context
+import android.view.MotionEvent
+import android.view.animation.PathInterpolator
+import com.android.systemui.res.R
+import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
+import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInputEventsInteractor
+import com.android.systemui.volume.dialog.sliders.shared.model.SliderInputEvent
+import javax.inject.Inject
+import kotlin.math.abs
+import kotlin.math.sign
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.transform
+
+@VolumeDialogSliderScope
+@OptIn(ExperimentalCoroutinesApi::class)
+class VolumeDialogOverscrollViewModel
+@Inject
+constructor(
+ context: Context,
+ private val inputEventsInteractor: VolumeDialogSliderInputEventsInteractor,
+) {
+
+ /**
+ * This is the ratio between the pointer distance and the dialog offset. The pointer has to
+ * travel this distance for a single point of an offset.
+ *
+ * When greater than 1 this makes the dialog to follow the touch behind.
+ */
+ private val offsetToTranslationRatio: Float = 3f
+ private val maxDeviation: Float =
+ context.resources
+ .getDimensionPixelSize(R.dimen.volume_dialog_slider_max_deviation)
+ .toFloat()
+ private val offsetInterpolator = PathInterpolator(0.15f, 0.00f, 0.20f, 1.00f)
+
+ private val sliderValue = MutableStateFlow<Slider?>(null)
+
+ val overscrollEvent: Flow<OverscrollEventModel> =
+ sliderValue
+ .filterNotNull()
+ .map { slider ->
+ when (slider.value) {
+ slider.min -> 1f
+ slider.max -> -1f
+ else -> 0f
+ }
+ }
+ .distinctUntilChanged()
+ .flatMapLatest { direction ->
+ if (direction == 0f) {
+ flowOf(OverscrollEventModel.Animate(0f))
+ } else {
+ overscrollEvents(direction)
+ }
+ }
+
+ fun setSlider(value: Float, min: Float, max: Float) {
+ sliderValue.value = Slider(value = value, min = min, max = max)
+ }
+
+ /**
+ * Returns a flow that for each another [MotionEvent] it receives maps into a path from the
+ * first event.
+ *
+ * Emits [OverscrollEventModel.Move] that follows the [SliderInputEvent.Touch] from the pointer
+ * down position. Emits [OverscrollEventModel.Animate] when the gesture is terminated to create
+ * a spring-back effect.
+ */
+ private fun overscrollEvents(direction: Float): Flow<OverscrollEventModel> {
+ var startPosition: Float? = null
+ return inputEventsInteractor.event
+ .mapNotNull { (it as? SliderInputEvent.Touch)?.event }
+ .transform { touchEvent ->
+ // Skip events from inside the slider bounds for the case when the user adjusts
+ // slider
+ // towards max when the slider is already on max value.
+ if (touchEvent.isFinalEvent()) {
+ startPosition = null
+ emit(OverscrollEventModel.Animate(0f))
+ return@transform
+ }
+ val currentStartPosition = startPosition
+ val newPosition: Float = touchEvent.rawY
+ if (currentStartPosition == null) {
+ startPosition = newPosition
+ } else {
+ val offset = (newPosition - currentStartPosition) / offsetToTranslationRatio
+ val interpolatedOffset =
+ if (areOfTheSameSign(direction, offset)) {
+ sign(offset) *
+ (maxDeviation *
+ offsetInterpolator.getInterpolation(
+ (abs(offset)) / maxDeviation
+ ))
+ } else {
+ 0f
+ }
+ emit(OverscrollEventModel.Move(interpolatedOffset))
+ }
+ }
+ }
+
+ /** @return true when the [MotionEvent] indicates the end of the gesture. */
+ private fun MotionEvent.isFinalEvent(): Boolean {
+ return actionMasked == MotionEvent.ACTION_UP || actionMasked == MotionEvent.ACTION_CANCEL
+ }
+
+ /** Models overscroll event */
+ sealed interface OverscrollEventModel {
+
+ /** Notifies the consumed to move by the [touchOffsetPx]. */
+ data class Move(val touchOffsetPx: Float) : OverscrollEventModel
+
+ /** Notifies the consume to animate to the [targetOffsetPx]. */
+ data class Animate(val targetOffsetPx: Float) : OverscrollEventModel
+ }
+
+ private data class Slider(val value: Float, val min: Float, val max: Float)
+}
+
+private fun areOfTheSameSign(lhs: Float, rhs: Float): Boolean =
+ when {
+ lhs < 0 -> rhs < 0
+ lhs > 0 -> rhs > 0
+ else -> rhs == 0f
+ }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index 67e03e4..82bf5e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -34,6 +34,7 @@
import com.android.systemui.SystemUIAppComponentFactoryBase
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.dock.DockManagerFake
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
@@ -133,6 +134,7 @@
.thenReturn(FakeSharedPreferences())
},
userTracker = userTracker,
+ communalSettingsInteractor = kosmos.communalSettingsInteractor,
broadcastDispatcher = fakeBroadcastDispatcher,
)
val remoteUserSelectionManager =
@@ -203,6 +205,7 @@
biometricSettingsRepository = biometricSettingsRepository,
backgroundDispatcher = testDispatcher,
appContext = mContext,
+ communalSettingsInteractor = kosmos.communalSettingsInteractor,
sceneInteractor = { kosmos.sceneInteractor },
)
underTest.previewManager =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index 21dd5bc..111d819 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -20,7 +20,6 @@
import android.app.admin.DevicePolicyManager
import android.content.Intent
import android.os.UserHandle
-import androidx.test.filters.FlakyTest
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
@@ -30,6 +29,7 @@
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.dock.DockManagerFake
import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.FakeFeatureFlags
@@ -64,6 +64,7 @@
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Before
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
@@ -82,7 +83,7 @@
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
@DisableSceneContainer
-@FlakyTest(bugId = 292574995, detail = "NullPointer on MockMakerTypeMockability.mockable()")
+@Ignore("b/292574995 NullPointer on MockMakerTypeMockability.mockable()")
class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
companion object {
@@ -270,6 +271,7 @@
.thenReturn(FakeSharedPreferences())
},
userTracker = userTracker,
+ communalSettingsInteractor = kosmos.communalSettingsInteractor,
broadcastDispatcher = fakeBroadcastDispatcher,
)
val remoteUserSelectionManager =
@@ -320,6 +322,7 @@
biometricSettingsRepository = biometricSettingsRepository,
backgroundDispatcher = testDispatcher,
appContext = mContext,
+ communalSettingsInteractor = kosmos.communalSettingsInteractor,
sceneInteractor = { kosmos.sceneInteractor },
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
index 1ce128c..8c00047 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
@@ -30,6 +30,7 @@
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.dock.DockManagerFake
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.FakeFeatureFlags
@@ -273,6 +274,7 @@
.thenReturn(FakeSharedPreferences())
},
userTracker = userTracker,
+ communalSettingsInteractor = kosmos.communalSettingsInteractor,
broadcastDispatcher = fakeBroadcastDispatcher,
)
val remoteUserSelectionManager =
@@ -323,6 +325,7 @@
biometricSettingsRepository = biometricSettingsRepository,
backgroundDispatcher = testDispatcher,
appContext = mContext,
+ communalSettingsInteractor = kosmos.communalSettingsInteractor,
sceneInteractor = { kosmos.sceneInteractor },
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
index 3364528..84976a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
@@ -29,6 +29,7 @@
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dock.DockManagerFake
import com.android.systemui.flags.FakeFeatureFlags
@@ -216,6 +217,7 @@
.thenReturn(FakeSharedPreferences())
},
userTracker = userTracker,
+ communalSettingsInteractor = kosmos.communalSettingsInteractor,
broadcastDispatcher = fakeBroadcastDispatcher,
)
val remoteUserSelectionManager =
@@ -295,6 +297,7 @@
biometricSettingsRepository = biometricSettingsRepository,
backgroundDispatcher = kosmos.testDispatcher,
appContext = mContext,
+ communalSettingsInteractor = kosmos.communalSettingsInteractor,
sceneInteractor = { kosmos.sceneInteractor },
),
keyguardInteractor = keyguardInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt
index 503fa78..1eb88c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt
@@ -89,6 +89,7 @@
messagingStyle = null,
builder = notificationBuilder,
systemUiContext = context,
+ redactText = false,
)
// WHEN: binds the viewHolder
@@ -149,6 +150,7 @@
messagingStyle = style,
builder = notificationBuilder,
systemUiContext = context,
+ redactText = false,
)
// WHEN: binds the view
SingleLineViewBinder.bind(viewModel, view)
@@ -197,6 +199,7 @@
messagingStyle = null,
builder = notificationBuilder,
systemUiContext = context,
+ redactText = false,
)
// WHEN: binds the view with the view model
SingleLineViewBinder.bind(viewModel, view)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt
index d366632..ef70e27 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt
@@ -379,7 +379,8 @@
this,
if (isConversation) messagingStyle else null,
builder,
- context
+ context,
+ false
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index eb1e28b..9d6eb79 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -65,7 +65,6 @@
private float mPanelExpansion;
private int mKeyguardStatusBarHeaderHeight;
private int mKeyguardStatusHeight;
- private int mUserSwitchHeight;
private float mDark;
private float mQsExpansion;
private int mCutoutTopInset = 0;
@@ -317,30 +316,6 @@
}
@Test
- public void notifPaddingAccountsForMultiUserSwitcherInSplitShade() {
- setSplitShadeTopMargin(100);
- mUserSwitchHeight = 150;
- givenLockScreen();
- mIsSplitShade = true;
- // WHEN the position algorithm is run
- positionClock();
- // THEN the notif padding is split shade top margin + user switch height
- assertThat(mClockPosition.stackScrollerPadding).isEqualTo(250);
- }
-
- @Test
- public void clockDoesntAccountForMultiUserSwitcherInSplitShade() {
- setSplitShadeTopMargin(100);
- mUserSwitchHeight = 150;
- givenLockScreen();
- mIsSplitShade = true;
- // WHEN the position algorithm is run
- positionClock();
- // THEN clockY = split shade top margin
- assertThat(mClockPosition.clockY).isEqualTo(100);
- }
-
- @Test
public void notifPaddingExpandedAlignedWithClockInSplitShadeMode() {
givenLockScreen();
mIsSplitShade = true;
@@ -370,9 +345,7 @@
mIsSplitShade = false;
mBypassEnabled = false;
- // mMinTopMargin = 100 = 80 + max(20, 0)
- mKeyguardStatusBarHeaderHeight = 80;
- mUserSwitchHeight = 20;
+ mKeyguardStatusBarHeaderHeight = 100;
when(mResources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin))
.thenReturn(0);
@@ -636,8 +609,6 @@
mKeyguardStatusBarHeaderHeight,
mPanelExpansion,
mKeyguardStatusHeight,
- mUserSwitchHeight,
- 0 /* userSwitchPreferredY */,
mDark,
ZERO_DRAG,
mBypassEnabled,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
index bfc4248..89aad4b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
@@ -99,10 +99,6 @@
}
suspend fun Kosmos.setCommunalV2Available(available: Boolean) {
- setCommunalV2ConfigEnabled(true)
- setCommunalEnabled(available)
- with(fakeKeyguardRepository) {
- setIsEncryptedOrLockdown(!available)
- setKeyguardShowing(available)
- }
+ setCommunalV2ConfigEnabled(available)
+ setCommunalAvailable(available)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfigKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfigKosmos.kt
new file mode 100644
index 0000000..5683248
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfigKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.quickaffordance
+
+import android.content.applicationContext
+import com.android.systemui.communal.data.repository.communalSceneRepository
+import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+
+val Kosmos.glanceableHubQuickAffordanceConfig by
+ Kosmos.Fixture {
+ GlanceableHubQuickAffordanceConfig(
+ context = applicationContext,
+ communalInteractor = communalInteractor,
+ communalSceneRepository = communalSceneRepository,
+ communalSettingsInteractor = communalSettingsInteractor,
+ sceneInteractor = sceneInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/LocalUserSelectionManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/LocalUserSelectionManagerKosmos.kt
index 21d1a76..328338b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/LocalUserSelectionManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/LocalUserSelectionManagerKosmos.kt
@@ -18,6 +18,7 @@
import android.content.applicationContext
import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.settings.userFileManager
import com.android.systemui.settings.userTracker
@@ -28,6 +29,7 @@
context = applicationContext,
userFileManager = userFileManager,
userTracker = userTracker,
+ communalSettingsInteractor = communalSettingsInteractor,
broadcastDispatcher = broadcastDispatcher,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorKosmos.kt
index 009d17e..3b1199a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorKosmos.kt
@@ -21,6 +21,7 @@
import com.android.internal.widget.lockPatternUtils
import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
import com.android.systemui.animation.dialogTransitionAnimator
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.dock.dockManager
import com.android.systemui.flags.featureFlagsClassic
import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
@@ -52,6 +53,7 @@
devicePolicyManager = devicePolicyManager,
dockManager = dockManager,
biometricSettingsRepository = biometricSettingsRepository,
+ communalSettingsInteractor = communalSettingsInteractor,
backgroundDispatcher = testDispatcher,
appContext = applicationContext,
sceneInteractor = { sceneInteractor },
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/ActivityStarterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/ActivityStarterKosmos.kt
index 0ec8d49..49bbbef 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/ActivityStarterKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/ActivityStarterKosmos.kt
@@ -17,6 +17,6 @@
package com.android.systemui.plugins
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.util.mockito.mock
+import org.mockito.kotlin.mock
var Kosmos.activityStarter by Kosmos.Fixture { mock<ActivityStarter>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt
index ab1c181..aa29808 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt
@@ -45,7 +45,6 @@
other ?: return
}
check("icon").that(actual.icon).isEqualTo(other.icon)
- check("iconRes").that(actual.iconRes).isEqualTo(other.iconRes)
check("label").that(actual.label).isEqualTo(other.label)
check("activationState").that(actual.activationState).isEqualTo(other.activationState)
check("secondaryLabel").that(actual.secondaryLabel).isEqualTo(other.secondaryLabel)
diff --git a/core/tests/coretests/src/android/graphics/GraphicsTests.java b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/NotificationShadeWindowViewKosmos.kt
similarity index 62%
copy from core/tests/coretests/src/android/graphics/GraphicsTests.java
copy to packages/SystemUI/tests/utils/src/com/android/systemui/shade/NotificationShadeWindowViewKosmos.kt
index 70f5976..18c44ba 100644
--- a/core/tests/coretests/src/android/graphics/GraphicsTests.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/NotificationShadeWindowViewKosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open 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,9 @@
* limitations under the License.
*/
-package android.graphics;
+package com.android.systemui.shade
-import junit.framework.TestSuite;
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
-public class GraphicsTests {
- public static TestSuite suite() {
- TestSuite suite = new TestSuite(GraphicsTests.class.getName());
-
- suite.addTestSuite(BitmapTest.class);
- return suite;
- }
-}
+var Kosmos.notificationShadeWindowView by Kosmos.Fixture { mock<NotificationShadeWindowView>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionControllerKosmos.kt
index e5a75d5..9f4091c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionControllerKosmos.kt
@@ -18,8 +18,14 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.util.mockito.mock
+import org.mockito.kotlin.mock
+
+var Kosmos.lockscreenShadeKeyguardTransitionController by Fixture {
+ mock<LockscreenShadeKeyguardTransitionController>()
+}
var Kosmos.lockscreenShadeKeyguardTransitionControllerFactory by Fixture {
- mock<LockscreenShadeKeyguardTransitionController.Factory>()
+ LockscreenShadeKeyguardTransitionController.Factory {
+ lockscreenShadeKeyguardTransitionController
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerKosmos.kt
index 2767980..fc52e45 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerKosmos.kt
@@ -18,8 +18,12 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.util.mockito.mock
+import org.mockito.kotlin.mock
+
+var Kosmos.lockscreenShadeQsTransitionController by Fixture {
+ mock<LockscreenShadeQsTransitionController>()
+}
var Kosmos.lockscreenShadeQsTransitionControllerFactory by Fixture {
- mock<LockscreenShadeQsTransitionController.Factory>()
+ LockscreenShadeQsTransitionController.Factory { lockscreenShadeQsTransitionController }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt
index e4a3896..1a451ce 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt
@@ -36,7 +36,7 @@
import com.android.systemui.statusbar.policy.configurationController
import com.android.systemui.statusbar.policy.splitShadeStateController
-val Kosmos.lockscreenShadeTransitionController by Fixture {
+var Kosmos.lockscreenShadeTransitionController by Fixture {
LockscreenShadeTransitionController(
statusBarStateController = sysuiStatusBarStateController,
logger = lsShadeTransitionLogger,
@@ -62,6 +62,6 @@
splitShadeStateController = splitShadeStateController,
shadeLockscreenInteractorLazy = { shadeLockscreenInteractor },
naturalScrollingSettingObserver = naturalScrollingSettingObserver,
- lazyQSSceneAdapter = { qsSceneAdapter }
+ lazyQSSceneAdapter = { qsSceneAdapter },
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerKosmos.kt
index 43e39c0..8a68eef 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerKosmos.kt
@@ -18,7 +18,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.util.mockito.mock
+import org.mockito.kotlin.mock
var Kosmos.singleShadeLockScreenOverScrollerFactory by Fixture {
mock<SingleShadeLockScreenOverScroller.Factory>()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerKosmos.kt
index 017371a..e491dff 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerKosmos.kt
@@ -18,8 +18,10 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.util.mockito.mock
+import org.mockito.kotlin.mock
+
+var Kosmos.splitShadeLockScreenOverScroller by Fixture { mock<SplitShadeLockScreenOverScroller>() }
var Kosmos.splitShadeLockScreenOverScrollerFactory by Fixture {
- mock<SplitShadeLockScreenOverScroller.Factory>()
+ SplitShadeLockScreenOverScroller.Factory { _, _ -> splitShadeLockScreenOverScroller }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/VisualInterruptionDecisionProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/VisualInterruptionDecisionProviderKosmos.kt
new file mode 100644
index 0000000..360e9e9
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/VisualInterruptionDecisionProviderKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider
+import org.mockito.kotlin.mock
+
+val Kosmos.visualInterruptionDecisionProvider by
+ Kosmos.Fixture { mock<VisualInterruptionDecisionProvider>() }
diff --git a/core/tests/coretests/src/android/graphics/GraphicsTests.java b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorKosmos.kt
similarity index 62%
rename from core/tests/coretests/src/android/graphics/GraphicsTests.java
rename to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorKosmos.kt
index 70f5976..768952d 100644
--- a/core/tests/coretests/src/android/graphics/GraphicsTests.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorKosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open 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,9 @@
* limitations under the License.
*/
-package android.graphics;
+package com.android.systemui.statusbar.notification.domain.interactor
-import junit.framework.TestSuite;
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
-public class GraphicsTests {
- public static TestSuite suite() {
- TestSuite suite = new TestSuite(GraphicsTests.class.getName());
-
- suite.addTestSuite(BitmapTest.class);
- return suite;
- }
-}
+val Kosmos.notificationAlertsInteractor by Kosmos.Fixture { mock<NotificationAlertsInteractor>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/KeyguardStateControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/KeyguardStateControllerKosmos.kt
index f19ac1e..26642d4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/KeyguardStateControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/KeyguardStateControllerKosmos.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.policy
import com.android.systemui.kosmos.Kosmos
-import org.mockito.Mockito.mock
+import org.mockito.kotlin.mock
var Kosmos.keyguardStateController: KeyguardStateController by
- Kosmos.Fixture { mock(KeyguardStateController::class.java) }
+ Kosmos.Fixture { mock<KeyguardStateController>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelKosmos.kt
index 34661ce..4fda95b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelKosmos.kt
@@ -22,6 +22,7 @@
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.statusbar.notification.domain.interactor.notificationsSoundPolicyInteractor
+import com.android.systemui.statusbar.policy.configurationController
import com.android.systemui.volume.dialog.domain.interactor.volumeDialogVisibilityInteractor
import com.android.systemui.volume.dialog.ringer.domain.volumeDialogRingerInteractor
import com.android.systemui.volume.dialog.shared.volumeDialogLogger
@@ -37,5 +38,6 @@
vibrator = vibratorHelper,
volumeDialogLogger = volumeDialogLogger,
visibilityInteractor = volumeDialogVisibilityInteractor,
+ configurationController = configurationController,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorKosmos.kt
index 423100a..44917dd 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorKosmos.kt
@@ -17,6 +17,7 @@
package com.android.systemui.volume.dialog.sliders.domain.interactor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.plugins.volumeDialogController
import com.android.systemui.volume.dialog.domain.interactor.volumeDialogStateInteractor
import com.android.systemui.volume.dialog.sliders.domain.model.volumeDialogSliderType
@@ -25,6 +26,7 @@
Kosmos.Fixture {
VolumeDialogSliderInteractor(
volumeDialogSliderType,
+ applicationCoroutineScope,
volumeDialogStateInteractor,
volumeDialogController,
)
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index c15cf34..6858e29 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -31,6 +31,7 @@
per-file *TimeUpdate* = file:/services/core/java/com/android/server/timezonedetector/OWNERS
per-file DynamicSystemService.java = file:/packages/DynamicSystemInstallationService/OWNERS
per-file GestureLauncherService.java = file:platform/packages/apps/EmergencyInfo:/OWNERS
+per-file GestureLauncherService.java = file:/INPUT_OWNERS
per-file MmsServiceBroker.java = file:/telephony/OWNERS
per-file NetIdManager.java = file:/services/core/java/com/android/server/net/OWNERS
per-file PackageWatchdog.java = file:/services/core/java/com/android/server/crashrecovery/OWNERS
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 719928b..09440e0 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -5266,6 +5266,22 @@
}
}
+ @Override
+ public void onNullBinding(ComponentName name) {
+ IAccountManagerResponse response = getResponseAndClose();
+ if (response != null) {
+ try {
+ response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
+ "disconnected");
+ } catch (RemoteException e) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Session.onNullBinding: "
+ + "caught RemoteException while responding", e);
+ }
+ }
+ }
+ }
+
public abstract void run() throws RemoteException;
public void onTimedOut() {
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 4944caf..4b8770b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_RECONFIGURATION;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_HEALTH;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION;
@@ -31,6 +32,7 @@
import static com.android.server.am.BroadcastConstants.getDeviceConfigBoolean;
import android.annotation.NonNull;
+import android.app.ActivityManagerInternal;
import android.app.ActivityThread;
import android.app.ForegroundServiceTypePolicy;
import android.content.ComponentName;
@@ -55,6 +57,7 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalServices;
import dalvik.annotation.optimization.NeverCompile;
@@ -181,6 +184,12 @@
static final String KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION =
"follow_up_oomadj_update_wait_duration";
+ /*
+ * Oom score cutoff beyond which any process that does not have the CPU_TIME capability will be
+ * frozen.
+ */
+ static final String KEY_FREEZER_CUTOFF_ADJ = "freezer_cutoff_adj";
+
private static final int DEFAULT_MAX_CACHED_PROCESSES = 1024;
private static final boolean DEFAULT_PRIORITIZE_ALARM_BROADCASTS = true;
private static final long DEFAULT_FGSERVICE_MIN_SHOWN_TIME = 2*1000;
@@ -267,6 +276,9 @@
*/
private static final long DEFAULT_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION = 1000L;
+ /** The default value to {@link #KEY_FREEZER_CUTOFF_ADJ} */
+ private static final int DEFAULT_FREEZER_CUTOFF_ADJ = ProcessList.CACHED_APP_MIN_ADJ;
+
/**
* Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED}
*/
@@ -1171,6 +1183,14 @@
DEFAULT_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION;
/**
+ * The cutoff adj for the freezer, app processes with adj greater than this value will be
+ * eligible for the freezer.
+ *
+ * @see #KEY_FREEZER_CUTOFF_ADJ
+ */
+ public int FREEZER_CUTOFF_ADJ = DEFAULT_FREEZER_CUTOFF_ADJ;
+
+ /**
* Indicates whether PSS profiling in AppProfiler is disabled or not.
*/
static final String KEY_DISABLE_APP_PROFILER_PSS_PROFILING =
@@ -1194,6 +1214,7 @@
new OnPropertiesChangedListener() {
@Override
public void onPropertiesChanged(Properties properties) {
+ boolean oomAdjusterConfigUpdated = false;
for (String name : properties.getKeyset()) {
if (name == null) {
return;
@@ -1372,6 +1393,11 @@
case KEY_TIERED_CACHED_ADJ_UI_TIER_SIZE:
updateUseTieredCachedAdj();
break;
+ case KEY_FREEZER_CUTOFF_ADJ:
+ FREEZER_CUTOFF_ADJ = properties.getInt(KEY_FREEZER_CUTOFF_ADJ,
+ DEFAULT_FREEZER_CUTOFF_ADJ);
+ oomAdjusterConfigUpdated = true;
+ break;
case KEY_DISABLE_APP_PROFILER_PSS_PROFILING:
updateDisableAppProfilerPssProfiling();
break;
@@ -1389,6 +1415,13 @@
break;
}
}
+ if (oomAdjusterConfigUpdated) {
+ final ActivityManagerInternal ami = LocalServices.getService(
+ ActivityManagerInternal.class);
+ if (ami != null) {
+ ami.updateOomAdj(OOM_ADJ_REASON_RECONFIGURATION);
+ }
+ }
}
};
@@ -2534,6 +2567,9 @@
pw.print(" "); pw.print(KEY_ENABLE_NEW_OOMADJ);
pw.print("="); pw.println(ENABLE_NEW_OOMADJ);
+ pw.print(" "); pw.print(KEY_FREEZER_CUTOFF_ADJ);
+ pw.print("="); pw.println(FREEZER_CUTOFF_ADJ);
+
pw.print(" "); pw.print(KEY_DISABLE_APP_PROFILER_PSS_PROFILING);
pw.print("="); pw.println(APP_PROFILER_PSS_PROFILING_DISABLED);
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 9a63546..cbebc90 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -449,6 +449,8 @@
return runSetAppZygotePreloadTimeout(pw);
case "set-media-foreground-service":
return runSetMediaForegroundService(pw);
+ case "clear-bad-process":
+ return runClearBadProcess(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -4276,6 +4278,27 @@
return 0;
}
+ int runClearBadProcess(PrintWriter pw) throws RemoteException {
+ final String processName = getNextArgRequired();
+ int userId = UserHandle.USER_CURRENT;
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ if ("--user".equals(opt)) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ } else {
+ getErrPrintWriter().println("Error: unknown option " + opt);
+ return -1;
+ }
+ }
+ if (userId == UserHandle.USER_CURRENT) {
+ userId = mInternal.getCurrentUserId();
+ }
+
+ pw.println("Clearing '" + processName + "' in u" + userId + " from bad processes list");
+ mInternal.mAppErrors.clearBadProcessForUser(processName, userId);
+ return 0;
+ }
+
private Resources getResources(PrintWriter pw) throws RemoteException {
// system resources does not contain all the device configuration, construct it manually.
Configuration config = mInterface.getConfiguration();
@@ -4717,6 +4740,8 @@
pw.println(" set-media-foreground-service inactive|active [--user USER_ID] <PACKAGE>"
+ " <NOTIFICATION_ID>");
pw.println(" Set an app's media service inactive or active.");
+ pw.println(" clear-bad-process [--user USER_ID] <PROCESS_NAME>");
+ pw.println(" Clears a process from the bad processes list.");
Intent.printIntentArgsHelp(pw, "");
}
}
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index b7a5f3e..2fbf05e 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -373,6 +373,24 @@
}
}
+ void clearBadProcessForUser(final String processName, final int userId) {
+ synchronized (mBadProcessLock) {
+ final ProcessMap<BadProcessInfo> badProcesses = new ProcessMap<>();
+ badProcesses.putAll(mBadProcesses);
+ final SparseArray<BadProcessInfo> uids = badProcesses.get(processName);
+ if (uids == null) {
+ return;
+ }
+ for (int i = uids.size() - 1; i >= 0; --i) {
+ final int uid = uids.keyAt(i);
+ if (userId == UserHandle.USER_ALL || userId == UserHandle.getUserId(uid)) {
+ badProcesses.remove(processName, uid);
+ }
+ }
+ mBadProcesses = badProcesses;
+ }
+ }
+
void markBadProcess(final String processName, final int uid, BadProcessInfo info) {
synchronized (mBadProcessLock) {
final ProcessMap<BadProcessInfo> badProcesses = new ProcessMap<>();
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 2f5362f..d335529 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -25,9 +25,11 @@
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_COMPONENT_DISABLED;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_EXECUTING_SERVICE;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_FINISH_RECEIVER;
+import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_FOLLOW_UP;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_GET_PROVIDER;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_PROCESS_BEGIN;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_PROCESS_END;
+import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_RECONFIGURATION;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_REMOVE_PROVIDER;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_REMOVE_TASK;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_RESTRICTION_CHANGE;
@@ -203,6 +205,10 @@
FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON_V2__UFR_RESTRICTION_CHANGE;
static final int UNFREEZE_REASON_COMPONENT_DISABLED =
FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON_V2__UFR_COMPONENT_DISABLED;
+ static final int UNFREEZE_REASON_OOM_ADJ_FOLLOW_UP =
+ FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON_V2__UFR_OOM_ADJ_FOLLOW_UP;
+ static final int UNFREEZE_REASON_OOM_ADJ_RECONFIGURATION =
+ FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON_V2__UFR_OOM_ADJ_RECONFIGURATION;
@IntDef(prefix = {"UNFREEZE_REASON_"}, value = {
UNFREEZE_REASON_NONE,
@@ -234,6 +240,8 @@
UNFREEZE_REASON_EXECUTING_SERVICE,
UNFREEZE_REASON_RESTRICTION_CHANGE,
UNFREEZE_REASON_COMPONENT_DISABLED,
+ UNFREEZE_REASON_OOM_ADJ_FOLLOW_UP,
+ UNFREEZE_REASON_OOM_ADJ_RECONFIGURATION,
})
@Retention(RetentionPolicy.SOURCE)
public @interface UnfreezeReason {}
@@ -2451,8 +2459,8 @@
synchronized (mAm.mPidsSelfLocked) {
pr = mAm.mPidsSelfLocked.get(blocked);
}
- if (pr != null
- && pr.mState.getCurAdj() < ProcessList.FREEZER_CUTOFF_ADJ) {
+ if (pr != null && pr.mState.getCurAdj()
+ < mAm.mConstants.FREEZER_CUTOFF_ADJ) {
Slog.d(TAG_AM, app.processName + " (" + pid + ") blocks "
+ pr.processName + " (" + blocked + ")");
// Found at least one blocked non-cached process
@@ -2539,6 +2547,10 @@
return UNFREEZE_REASON_RESTRICTION_CHANGE;
case OOM_ADJ_REASON_COMPONENT_DISABLED:
return UNFREEZE_REASON_COMPONENT_DISABLED;
+ case OOM_ADJ_REASON_FOLLOW_UP:
+ return UNFREEZE_REASON_OOM_ADJ_FOLLOW_UP;
+ case OOM_ADJ_REASON_RECONFIGURATION:
+ return UNFREEZE_REASON_OOM_ADJ_RECONFIGURATION;
default:
return UNFREEZE_REASON_NONE;
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index aadf6f6..9c569db 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -55,6 +55,7 @@
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_NONE;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_PROCESS_BEGIN;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_PROCESS_END;
+import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_RECONFIGURATION;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_REMOVE_PROVIDER;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_REMOVE_TASK;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_RESTRICTION_CHANGE;
@@ -105,7 +106,6 @@
import static com.android.server.am.ProcessList.CACHED_APP_MAX_ADJ;
import static com.android.server.am.ProcessList.CACHED_APP_MIN_ADJ;
import static com.android.server.am.ProcessList.FOREGROUND_APP_ADJ;
-import static com.android.server.am.ProcessList.FREEZER_CUTOFF_ADJ;
import static com.android.server.am.ProcessList.HEAVY_WEIGHT_APP_ADJ;
import static com.android.server.am.ProcessList.HOME_APP_ADJ;
import static com.android.server.am.ProcessList.INVALID_ADJ;
@@ -232,6 +232,8 @@
return AppProtoEnums.OOM_ADJ_REASON_COMPONENT_DISABLED;
case OOM_ADJ_REASON_FOLLOW_UP:
return AppProtoEnums.OOM_ADJ_REASON_FOLLOW_UP;
+ case OOM_ADJ_REASON_RECONFIGURATION:
+ return AppProtoEnums.OOM_ADJ_REASON_RECONFIGURATION;
default:
return AppProtoEnums.OOM_ADJ_REASON_UNKNOWN_TO_PROTO;
}
@@ -288,6 +290,8 @@
return OOM_ADJ_REASON_METHOD + "_componentDisabled";
case OOM_ADJ_REASON_FOLLOW_UP:
return OOM_ADJ_REASON_METHOD + "_followUp";
+ case OOM_ADJ_REASON_RECONFIGURATION:
+ return OOM_ADJ_REASON_METHOD + "_reconfiguration";
default:
return "_unknown";
}
@@ -4079,7 +4083,7 @@
}
// Reasons to freeze:
- if (proc.mState.getCurAdj() >= FREEZER_CUTOFF_ADJ) {
+ if (proc.mState.getCurAdj() >= mConstants.FREEZER_CUTOFF_ADJ) {
// Oomscore is in a high enough state, it is safe to freeze.
return true;
}
@@ -4098,9 +4102,8 @@
final ProcessCachedOptimizerRecord opt = app.mOptRecord;
final ProcessStateRecord state = app.mState;
if (Flags.traceUpdateAppFreezeStateLsp()) {
- final boolean oomAdjChanged =
- (state.getCurAdj() >= FREEZER_CUTOFF_ADJ ^ oldOomAdj >= FREEZER_CUTOFF_ADJ)
- || oldOomAdj == UNKNOWN_ADJ;
+ final boolean oomAdjChanged = (state.getCurAdj() >= mConstants.FREEZER_CUTOFF_ADJ
+ ^ oldOomAdj >= mConstants.FREEZER_CUTOFF_ADJ) || oldOomAdj == UNKNOWN_ADJ;
final boolean shouldNotFreezeChanged = opt.shouldNotFreezeAdjSeq() == mAdjSeq;
final boolean hasCpuCapability =
(PROCESS_CAPABILITY_CPU_TIME & app.mState.getCurCapability())
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 70febcd..bddde9d 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -383,12 +383,6 @@
private static final long LMKD_RECONNECT_DELAY_MS = 1000;
/**
- * The cuttoff adj for the freezer, app processes with adj greater than this value will be
- * eligible for the freezer.
- */
- static final int FREEZER_CUTOFF_ADJ = CACHED_APP_MIN_ADJ;
-
- /**
* Apps have no access to the private data directories of any other app, even if the other
* app has made them world-readable.
*/
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 98f738c..49149e1 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1699,7 +1699,7 @@
return mService.mOomAdjuster.mCachedAppOptimizer.useFreezer()
&& !mOptRecord.isFreezeExempt()
&& !mOptRecord.shouldNotFreeze()
- && mState.getCurAdj() >= ProcessList.FREEZER_CUTOFF_ADJ;
+ && mState.getCurAdj() >= mService.mConstants.FREEZER_CUTOFF_ADJ;
}
public void forEachConnectionHost(Consumer<ProcessRecord> consumer) {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 06c586f..295e044 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -3191,7 +3191,7 @@
resolveProxyPackageName, proxyAttributionTag, proxyVirtualDeviceId,
Process.INVALID_UID, null, null,
Context.DEVICE_ID_DEFAULT, proxyFlags, !isProxyTrusted,
- "proxy " + message, shouldCollectMessage);
+ "proxy " + message, shouldCollectMessage, 1);
if (proxyReturn.getOpMode() != AppOpsManager.MODE_ALLOWED) {
return new SyncNotedAppOp(proxyReturn.getOpMode(), code, proxiedAttributionTag,
proxiedPackageName);
@@ -3210,7 +3210,20 @@
return noteOperationUnchecked(code, proxiedUid, resolveProxiedPackageName,
proxiedAttributionTag, proxiedVirtualDeviceId, proxyUid, resolveProxyPackageName,
proxyAttributionTag, proxyVirtualDeviceId, proxiedFlags, shouldCollectAsyncNotedOp,
- message, shouldCollectMessage);
+ message, shouldCollectMessage, 1);
+ }
+
+ @Override
+ public void noteOperationsInBatch(Map batchedNoteOps) {
+ for (var entry : ((Map<AppOpsManager.NotedOp, Integer>) batchedNoteOps).entrySet()) {
+ AppOpsManager.NotedOp notedOp = entry.getKey();
+ int notedCount = entry.getValue();
+ mCheckOpsDelegateDispatcher.noteOperation(
+ notedOp.getOp(), notedOp.getUid(), notedOp.getPackageName(),
+ notedOp.getAttributionTag(), notedOp.getVirtualDeviceId(),
+ notedOp.getShouldCollectAsyncNotedOp(), notedOp.getMessage(),
+ notedOp.getShouldCollectMessage(), notedCount);
+ }
}
@Override
@@ -3228,7 +3241,7 @@
}
return mCheckOpsDelegateDispatcher.noteOperation(code, uid, packageName,
attributionTag, Context.DEVICE_ID_DEFAULT, shouldCollectAsyncNotedOp, message,
- shouldCollectMessage);
+ shouldCollectMessage, 1);
}
@Override
@@ -3237,13 +3250,12 @@
String message, boolean shouldCollectMessage) {
return mCheckOpsDelegateDispatcher.noteOperation(code, uid, packageName,
attributionTag, virtualDeviceId, shouldCollectAsyncNotedOp, message,
- shouldCollectMessage);
+ shouldCollectMessage, 1);
}
private SyncNotedAppOp noteOperationImpl(int code, int uid, @Nullable String packageName,
- @Nullable String attributionTag, int virtualDeviceId,
- boolean shouldCollectAsyncNotedOp, @Nullable String message,
- boolean shouldCollectMessage) {
+ @Nullable String attributionTag, int virtualDeviceId, boolean shouldCollectAsyncNotedOp,
+ @Nullable String message, boolean shouldCollectMessage, int notedCount) {
String resolvedPackageName;
if (!shouldUseNewCheckOp()) {
verifyIncomingUid(uid);
@@ -3278,14 +3290,14 @@
return noteOperationUnchecked(code, uid, resolvedPackageName, attributionTag,
virtualDeviceId, Process.INVALID_UID, null, null,
Context.DEVICE_ID_DEFAULT, AppOpsManager.OP_FLAG_SELF, shouldCollectAsyncNotedOp,
- message, shouldCollectMessage);
+ message, shouldCollectMessage, notedCount);
}
private SyncNotedAppOp noteOperationUnchecked(int code, int uid, @NonNull String packageName,
@Nullable String attributionTag, int virtualDeviceId, int proxyUid,
String proxyPackageName, @Nullable String proxyAttributionTag, int proxyVirtualDeviceId,
@OpFlags int flags, boolean shouldCollectAsyncNotedOp, @Nullable String message,
- boolean shouldCollectMessage) {
+ boolean shouldCollectMessage, int notedCount) {
PackageVerificationResult pvr;
try {
pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName);
@@ -3388,11 +3400,11 @@
virtualDeviceId, flags, AppOpsManager.MODE_ALLOWED);
attributedOp.accessed(proxyUid, proxyPackageName, proxyAttributionTag,
- getPersistentId(proxyVirtualDeviceId), uidState.getState(), flags);
+ getPersistentId(proxyVirtualDeviceId), uidState.getState(), flags, notedCount);
if (shouldCollectAsyncNotedOp) {
collectAsyncNotedOp(uid, packageName, code, attributionTag, flags, message,
- shouldCollectMessage);
+ shouldCollectMessage, notedCount);
}
return new SyncNotedAppOp(AppOpsManager.MODE_ALLOWED, code, attributionTag,
@@ -3551,7 +3563,7 @@
*/
private void collectAsyncNotedOp(int uid, @NonNull String packageName, int opCode,
@Nullable String attributionTag, @OpFlags int flags, @NonNull String message,
- boolean shouldCollectMessage) {
+ boolean shouldCollectMessage, int notedCount) {
Objects.requireNonNull(message);
int callingUid = Binder.getCallingUid();
@@ -3559,42 +3571,51 @@
final long token = Binder.clearCallingIdentity();
try {
synchronized (this) {
- Pair<String, Integer> key = getAsyncNotedOpsKey(packageName, uid);
-
- RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key);
- AsyncNotedAppOp asyncNotedOp = new AsyncNotedAppOp(opCode, callingUid,
- attributionTag, message, System.currentTimeMillis());
- final boolean[] wasNoteForwarded = {false};
-
if ((flags & (OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED)) != 0
&& shouldCollectMessage) {
reportRuntimeAppOpAccessMessageAsyncLocked(uid, packageName, opCode,
attributionTag, message);
}
- if (callbacks != null) {
+ Pair<String, Integer> key = getAsyncNotedOpsKey(packageName, uid);
+ RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key);
+ if (callbacks == null) {
+ return;
+ }
+
+ final boolean[] wasNoteForwarded = {false};
+ if (Flags.rateLimitBatchedNoteOpAsyncCallbacksEnabled()) {
+ notedCount = 1;
+ }
+
+ for (int i = 0; i < notedCount; i++) {
+ AsyncNotedAppOp asyncNotedOp = new AsyncNotedAppOp(opCode, callingUid,
+ attributionTag, message, System.currentTimeMillis());
+ wasNoteForwarded[0] = false;
callbacks.broadcast((cb) -> {
try {
cb.opNoted(asyncNotedOp);
wasNoteForwarded[0] = true;
} catch (RemoteException e) {
Slog.e(TAG,
- "Could not forward noteOp of " + opCode + " to " + packageName
+ "Could not forward noteOp of " + opCode + " to "
+ + packageName
+ "/" + uid + "(" + attributionTag + ")", e);
}
});
- }
- if (!wasNoteForwarded[0]) {
- ArrayList<AsyncNotedAppOp> unforwardedOps = mUnforwardedAsyncNotedOps.get(key);
- if (unforwardedOps == null) {
- unforwardedOps = new ArrayList<>(1);
- mUnforwardedAsyncNotedOps.put(key, unforwardedOps);
- }
+ if (!wasNoteForwarded[0]) {
+ ArrayList<AsyncNotedAppOp> unforwardedOps = mUnforwardedAsyncNotedOps.get(
+ key);
+ if (unforwardedOps == null) {
+ unforwardedOps = new ArrayList<>(1);
+ mUnforwardedAsyncNotedOps.put(key, unforwardedOps);
+ }
- unforwardedOps.add(asyncNotedOp);
- if (unforwardedOps.size() > MAX_UNFORWARDED_OPS) {
- unforwardedOps.remove(0);
+ unforwardedOps.add(asyncNotedOp);
+ if (unforwardedOps.size() > MAX_UNFORWARDED_OPS) {
+ unforwardedOps.remove(0);
+ }
}
}
}
@@ -4026,7 +4047,7 @@
if (shouldCollectAsyncNotedOp && !isRestricted) {
collectAsyncNotedOp(uid, packageName, code, attributionTag, AppOpsManager.OP_FLAG_SELF,
- message, shouldCollectMessage);
+ message, shouldCollectMessage, 1);
}
return new SyncNotedAppOp(isRestricted ? MODE_IGNORED : MODE_ALLOWED, code, attributionTag,
@@ -7574,34 +7595,36 @@
public SyncNotedAppOp noteOperation(int code, int uid, String packageName,
String attributionTag, int virtualDeviceId, boolean shouldCollectAsyncNotedOp,
- String message, boolean shouldCollectMessage) {
+ String message, boolean shouldCollectMessage, int notedCount) {
if (mPolicy != null) {
if (mCheckOpsDelegate != null) {
return mPolicy.noteOperation(code, uid, packageName, attributionTag,
virtualDeviceId, shouldCollectAsyncNotedOp, message,
- shouldCollectMessage, this::noteDelegateOperationImpl
+ shouldCollectMessage, notedCount, this::noteDelegateOperationImpl
);
} else {
return mPolicy.noteOperation(code, uid, packageName, attributionTag,
virtualDeviceId, shouldCollectAsyncNotedOp, message,
- shouldCollectMessage, AppOpsService.this::noteOperationImpl
+ shouldCollectMessage, notedCount, AppOpsService.this::noteOperationImpl
);
}
} else if (mCheckOpsDelegate != null) {
return noteDelegateOperationImpl(code, uid, packageName, attributionTag,
- virtualDeviceId, shouldCollectAsyncNotedOp, message, shouldCollectMessage);
+ virtualDeviceId, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+ notedCount);
}
return noteOperationImpl(code, uid, packageName, attributionTag,
- virtualDeviceId, shouldCollectAsyncNotedOp, message, shouldCollectMessage);
+ virtualDeviceId, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+ notedCount);
}
private SyncNotedAppOp noteDelegateOperationImpl(int code, int uid,
@Nullable String packageName, @Nullable String featureId, int virtualDeviceId,
boolean shouldCollectAsyncNotedOp, @Nullable String message,
- boolean shouldCollectMessage) {
+ boolean shouldCollectMessage, int notedCount) {
return mCheckOpsDelegate.noteOperation(code, uid, packageName, featureId,
virtualDeviceId, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
- AppOpsService.this::noteOperationImpl
+ notedCount, AppOpsService.this::noteOperationImpl
);
}
diff --git a/services/core/java/com/android/server/appop/AttributedOp.java b/services/core/java/com/android/server/appop/AttributedOp.java
index 314664b..4d114b4 100644
--- a/services/core/java/com/android/server/appop/AttributedOp.java
+++ b/services/core/java/com/android/server/appop/AttributedOp.java
@@ -100,10 +100,12 @@
* @param proxyDeviceId The device Id of the proxy
* @param uidState UID state of the app noteOp/startOp was called for
* @param flags OpFlags of the call
+ * @param accessCount The number of times the op is accessed
*/
public void accessed(int proxyUid, @Nullable String proxyPackageName,
@Nullable String proxyAttributionTag, @Nullable String proxyDeviceId,
- @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags) {
+ @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags,
+ int accessCount) {
long accessTime = System.currentTimeMillis();
accessed(accessTime, -1, proxyUid, proxyPackageName, proxyAttributionTag, proxyDeviceId,
uidState, flags);
@@ -111,7 +113,7 @@
mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
parent.packageName, persistentDeviceId, tag, uidState, flags, accessTime,
AppOpsManager.ATTRIBUTION_FLAGS_NONE, AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE,
- DiscreteRegistry.ACCESS_TYPE_NOTE_OP);
+ DiscreteRegistry.ACCESS_TYPE_NOTE_OP, accessCount);
}
/**
@@ -255,7 +257,7 @@
if (isStarted) {
mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
parent.packageName, persistentDeviceId, tag, uidState, flags, startTime,
- attributionFlags, attributionChainId, DiscreteRegistry.ACCESS_TYPE_START_OP);
+ attributionFlags, attributionChainId, DiscreteRegistry.ACCESS_TYPE_START_OP, 1);
}
}
@@ -451,7 +453,7 @@
mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
parent.packageName, persistentDeviceId, tag, event.getUidState(),
event.getFlags(), startTime, event.getAttributionFlags(),
- event.getAttributionChainId(), DiscreteRegistry.ACCESS_TYPE_RESUME_OP);
+ event.getAttributionChainId(), DiscreteRegistry.ACCESS_TYPE_RESUME_OP, 1);
if (shouldSendActive) {
mAppOpsService.scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
parent.packageName, tag, event.getVirtualDeviceId(), true,
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 6b02538..5e67f26 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -475,7 +475,7 @@
@NonNull String deviceId, @Nullable String attributionTag, @UidState int uidState,
@OpFlags int flags, long accessTime,
@AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId,
- @DiscreteRegistry.AccessType int accessType) {
+ @DiscreteRegistry.AccessType int accessType, int accessCount) {
synchronized (mInMemoryLock) {
if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
if (!isPersistenceInitializedMLocked()) {
@@ -484,7 +484,7 @@
}
getUpdatedPendingHistoricalOpsMLocked(
System.currentTimeMillis()).increaseAccessCount(op, uid, packageName,
- attributionTag, uidState, flags, 1);
+ attributionTag, uidState, flags, accessCount);
mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op,
attributionTag, flags, uidState, accessTime, -1, attributionFlags,
diff --git a/services/core/java/com/android/server/audio/FadeOutManager.java b/services/core/java/com/android/server/audio/FadeOutManager.java
index 4d5bce5..fedfe51 100644
--- a/services/core/java/com/android/server/audio/FadeOutManager.java
+++ b/services/core/java/com/android/server/audio/FadeOutManager.java
@@ -199,7 +199,9 @@
for (AudioPlaybackConfiguration apc : players) {
final VolumeShaper.Configuration volShaper =
mFadeConfigurations.getFadeOutVolumeShaperConfig(apc.getAudioAttributes());
- fa.addFade(apc, /* skipRamp= */ false, volShaper);
+ if (volShaper != null) {
+ fa.addFade(apc, /* skipRamp= */ false, volShaper);
+ }
}
}
}
@@ -249,7 +251,7 @@
final VolumeShaper.Configuration volShaper =
mFadeConfigurations.getFadeOutVolumeShaperConfig(apc.getAudioAttributes());
final FadedOutApp fa = mUidToFadedAppsMap.get(apc.getClientUid());
- if (fa == null) {
+ if (fa == null || volShaper == null) {
return;
}
fa.addFade(apc, /* skipRamp= */ true, volShaper);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 5740e16..f145a47 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -21,8 +21,11 @@
import static android.Manifest.permission.ADD_TRUSTED_DISPLAY;
import static android.Manifest.permission.CAPTURE_SECURE_VIDEO_OUTPUT;
import static android.Manifest.permission.CAPTURE_VIDEO_OUTPUT;
+import static android.Manifest.permission.CONFIGURE_WIFI_DISPLAY;
+import static android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.MANAGE_DISPLAYS;
+import static android.Manifest.permission.MODIFY_HDR_CONVERSION_MODE;
import static android.Manifest.permission.RESTRICT_DISPLAY_MODES;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
@@ -119,6 +122,7 @@
import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
+import android.os.IBinder.FrozenStateChangeCallback;
import android.os.IThermalService;
import android.os.Looper;
import android.os.Message;
@@ -272,6 +276,7 @@
private static final int MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE = 7;
private static final int MSG_DELIVER_DISPLAY_GROUP_EVENT = 8;
private static final int MSG_RECEIVED_DEVICE_STATE = 9;
+ private static final int MSG_DISPATCH_PENDING_PROCESS_EVENTS = 10;
private static final int[] EMPTY_ARRAY = new int[0];
private static final HdrConversionMode HDR_CONVERSION_MODE_UNSUPPORTED = new HdrConversionMode(
HDR_CONVERSION_UNSUPPORTED);
@@ -286,7 +291,6 @@
private InputManagerInternal mInputManagerInternal;
private ActivityManagerInternal mActivityManagerInternal;
private final UidImportanceListener mUidImportanceListener = new UidImportanceListener();
- private final DisplayFrozenProcessListener mDisplayFrozenProcessListener;
@Nullable
private IMediaProjectionManager mProjectionService;
@@ -630,7 +634,6 @@
mFlags = injector.getFlags();
mHandler = new DisplayManagerHandler(displayThreadLooper);
mHandlerExecutor = new HandlerExecutor(mHandler);
- mDisplayFrozenProcessListener = new DisplayFrozenProcessListener();
mUiHandler = UiThread.getHandler();
mDisplayDeviceRepo = new DisplayDeviceRepository(mSyncRoot, mPersistentDataStore);
mLogicalDisplayMapper = new LogicalDisplayMapper(mContext,
@@ -1165,31 +1168,11 @@
}
}
- private class DisplayFrozenProcessListener
- implements ActivityManagerInternal.FrozenProcessListener {
- public void onProcessFrozen(int pid) {
- synchronized (mSyncRoot) {
- CallbackRecord callback = mCallbacks.get(pid);
- if (callback == null) {
- return;
- }
- callback.setFrozen(true);
- }
- }
-
- public void onProcessUnfrozen(int pid) {
- // First, see if there is a callback associated with this pid. If there's no
- // callback, then there is nothing to do.
- CallbackRecord callback;
- synchronized (mSyncRoot) {
- callback = mCallbacks.get(pid);
- if (callback == null) {
- return;
- }
- callback.setFrozen(false);
- }
- // Attempt to dispatch pending events if the process is coming out of frozen.
+ private void dispatchPendingProcessEvents(@NonNull Object cb) {
+ if (cb instanceof CallbackRecord callback) {
callback.dispatchPending();
+ } else {
+ Slog.wtf(TAG, "not a callback: " + cb);
}
}
@@ -4052,6 +4035,9 @@
deliverDisplayGroupEvent(msg.arg1, msg.arg2);
break;
+ case MSG_DISPATCH_PENDING_PROCESS_EVENTS:
+ dispatchPendingProcessEvents(msg.obj);
+ break;
}
}
}
@@ -4119,7 +4105,7 @@
}
}
- private final class CallbackRecord implements DeathRecipient {
+ private final class CallbackRecord implements DeathRecipient, FrozenStateChangeCallback {
public final int mPid;
public final int mUid;
private final IDisplayManagerCallback mCallback;
@@ -4142,6 +4128,8 @@
private boolean mCached;
@GuardedBy("mCallback")
private boolean mFrozen;
+ @GuardedBy("mCallback")
+ private boolean mAlive;
CallbackRecord(int pid, int uid, @NonNull IDisplayManagerCallback callback,
@InternalEventFlag long internalEventFlagsMask) {
@@ -4151,18 +4139,20 @@
mInternalEventFlagsMask = new AtomicLong(internalEventFlagsMask);
mCached = false;
mFrozen = false;
+ mAlive = true;
if (deferDisplayEventsWhenFrozen()) {
- // Some CallbackRecords are registered very early in system boot, before
- // mActivityManagerInternal is initialized. If mActivityManagerInternal is null,
- // do not register the frozen process listener. However, do verify that all such
- // registrations are for the self pid (which can never be frozen, so the frozen
- // process listener does not matter).
- if (mActivityManagerInternal != null) {
- mActivityManagerInternal.addFrozenProcessListener(pid, mHandlerExecutor,
- mDisplayFrozenProcessListener);
- } else if (Process.myPid() != pid) {
- Slog.e(TAG, "DisplayListener registered too early");
+ try {
+ callback.asBinder().addFrozenStateChangeCallback(this);
+ } catch (UnsupportedOperationException e) {
+ // Ignore the exception. The callback is not supported on this platform or on
+ // this binder. The callback is never supported for local binders. There is
+ // no error: the UID importance listener will still operate. A log message is
+ // provided for debug.
+ Slog.v(TAG, "FrozenStateChangeCallback not supported for pid " + mPid);
+ } catch (RemoteException e) {
+ // This is unexpected. Just give up.
+ throw new RuntimeException(e);
}
}
@@ -4187,7 +4177,7 @@
*/
@GuardedBy("mCallback")
private boolean hasPendingAndIsReadyLocked() {
- return isReadyLocked() && mPendingEvents != null && !mPendingEvents.isEmpty();
+ return isReadyLocked() && mPendingEvents != null && !mPendingEvents.isEmpty() && mAlive;
}
/**
@@ -4195,7 +4185,7 @@
* receive events and there are pending events to be delivered.
* This is only used if {@link deferDisplayEventsWhenFrozen()} is true.
*/
- public boolean setFrozen(boolean frozen) {
+ private boolean setFrozen(boolean frozen) {
synchronized (mCallback) {
mFrozen = frozen;
return hasPendingAndIsReadyLocked();
@@ -4216,6 +4206,9 @@
@Override
public void binderDied() {
+ synchronized (mCallback) {
+ mAlive = false;
+ }
if (DEBUG || extraLogging(mPackageName)) {
Slog.d(TAG, "Display listener for pid " + mPid + " died.");
}
@@ -4226,6 +4219,14 @@
onCallbackDied(this);
}
+ @Override
+ public void onFrozenStateChanged(@NonNull IBinder who, int state) {
+ if (setFrozen(state == FrozenStateChangeCallback.STATE_FROZEN)) {
+ Message msg = mHandler.obtainMessage(MSG_DISPATCH_PENDING_PROCESS_EVENTS, this);
+ mHandler.sendMessage(msg);
+ }
+ }
+
/**
* @return {@code false} if RemoteException happens; otherwise {@code true} for
* success. This returns true even if the event was deferred because the remote client is
@@ -4392,7 +4393,7 @@
// This is only used if {@link deferDisplayEventsWhenFrozen()} is true.
public boolean dispatchPending() {
synchronized (mCallback) {
- if (mPendingEvents == null || mPendingEvents.isEmpty()) {
+ if (mPendingEvents == null || mPendingEvents.isEmpty() || !mAlive) {
return true;
}
if (!isReadyLocked()) {
@@ -4609,13 +4610,13 @@
}
}
+ @EnforcePermission(CONFIGURE_WIFI_DISPLAY)
@Override // Binder call
public void connectWifiDisplay(String address) {
+ connectWifiDisplay_enforcePermission();
if (address == null) {
throw new IllegalArgumentException("address must not be null");
}
- mContext.enforceCallingOrSelfPermission(Manifest.permission.CONFIGURE_WIFI_DISPLAY,
- "Permission required to connect to a wifi display");
final long token = Binder.clearCallingIdentity();
try {
@@ -4640,13 +4641,13 @@
}
}
+ @EnforcePermission(CONFIGURE_WIFI_DISPLAY)
@Override // Binder call
public void renameWifiDisplay(String address, String alias) {
+ renameWifiDisplay_enforcePermission();
if (address == null) {
throw new IllegalArgumentException("address must not be null");
}
- mContext.enforceCallingOrSelfPermission(Manifest.permission.CONFIGURE_WIFI_DISPLAY,
- "Permission required to rename to a wifi display");
final long token = Binder.clearCallingIdentity();
try {
@@ -4656,13 +4657,13 @@
}
}
+ @EnforcePermission(CONFIGURE_WIFI_DISPLAY)
@Override // Binder call
public void forgetWifiDisplay(String address) {
+ forgetWifiDisplay_enforcePermission();
if (address == null) {
throw new IllegalArgumentException("address must not be null");
}
- mContext.enforceCallingOrSelfPermission(Manifest.permission.CONFIGURE_WIFI_DISPLAY,
- "Permission required to forget to a wifi display");
final long token = Binder.clearCallingIdentity();
try {
@@ -5006,7 +5007,7 @@
}
}
- @EnforcePermission(android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS)
+ @EnforcePermission(CONTROL_DISPLAY_BRIGHTNESS)
@Override
public BrightnessInfo getBrightnessInfo(int displayId) {
getBrightnessInfo_enforcePermission();
@@ -5037,7 +5038,7 @@
}
}
- @EnforcePermission(android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS)
+ @EnforcePermission(CONTROL_DISPLAY_BRIGHTNESS)
@Override // Binder call
public void setTemporaryBrightness(int displayId, float brightness) {
setTemporaryBrightness_enforcePermission();
@@ -5052,7 +5053,7 @@
}
}
- @EnforcePermission(android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS)
+ @EnforcePermission(CONTROL_DISPLAY_BRIGHTNESS)
@Override // Binder call
public void setBrightness(int displayId, float brightness) {
setBrightness_enforcePermission();
@@ -5076,12 +5077,11 @@
}
}
+ @EnforcePermission(CONTROL_DISPLAY_BRIGHTNESS)
@Override // Binder call
public float getBrightness(int displayId) {
+ getBrightness_enforcePermission();
float brightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS,
- "Permission required to set the display's brightness");
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
@@ -5096,7 +5096,7 @@
return brightness;
}
- @EnforcePermission(android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS)
+ @EnforcePermission(CONTROL_DISPLAY_BRIGHTNESS)
@Override // Binder call
public void setTemporaryAutoBrightnessAdjustment(float adjustment) {
setTemporaryAutoBrightnessAdjustment_enforcePermission();
@@ -5171,14 +5171,13 @@
}
}
+ @EnforcePermission(MODIFY_HDR_CONVERSION_MODE)
@Override // Binder call
public void setHdrConversionMode(HdrConversionMode hdrConversionMode) {
+ setHdrConversionMode_enforcePermission();
if (!mIsHdrOutputControlEnabled) {
return;
}
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.MODIFY_HDR_CONVERSION_MODE,
- "Permission required to set the HDR conversion mode.");
final long token = Binder.clearCallingIdentity();
try {
setHdrConversionModeInternal(hdrConversionMode);
@@ -5342,7 +5341,7 @@
? ddc.getHdrBrightnessData().highestHdrSdrRatio : 1;
}
- @EnforcePermission(android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS)
+ @EnforcePermission(CONTROL_DISPLAY_BRIGHTNESS)
@Override // Binder call
public float[] getDozeBrightnessSensorValueToBrightness(int displayId) {
getDozeBrightnessSensorValueToBrightness_enforcePermission();
@@ -5355,7 +5354,7 @@
return ddc.getDozeBrightnessSensorValueToBrightness();
}
- @EnforcePermission(android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS)
+ @EnforcePermission(CONTROL_DISPLAY_BRIGHTNESS)
@Override // Binder call
public float getDefaultDozeBrightness(int displayId) {
getDefaultDozeBrightness_enforcePermission();
@@ -6064,6 +6063,7 @@
* Return the value of the pause
*/
private static boolean deferDisplayEventsWhenFrozen() {
- return com.android.server.am.Flags.deferDisplayEventsWhenFrozen();
+ return android.os.Flags.binderFrozenStateChangeCallback()
+ && com.android.server.am.Flags.deferDisplayEventsWhenFrozen();
}
}
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index 3358f72..5f97410 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -96,7 +96,7 @@
name: "display_topology"
namespace: "display_manager"
description: "Display topology for moving cursors and windows between extended displays"
- bug: "278199220"
+ bug: "364906028"
is_fixed_read_only: true
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index 477660d..4f3aa06 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -492,8 +492,8 @@
}
if (getCurToken() != null) {
- removeCurrentToken();
mService.resetSystemUiLocked(this);
+ removeCurrentToken();
mAutofillController.onResetSystemUi();
}
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index 3528d3d..8a35006 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -487,6 +487,20 @@
ProviderInfo resolveContentProvider(@NonNull String name,
@PackageManager.ResolveInfoFlagsBits long flags, @UserIdInt int userId, int callingUid);
+ /**
+ * Resolves a ContentProvider on behalf of a UID
+ * @param name Authority of the content provider
+ * @param flags option flags to modify the data returned.
+ * @param userId Current user ID
+ * @param filterCallingUid UID of the caller who's access to the content provider
+ * is to be checked
+ * @return
+ */
+ @Nullable
+ ProviderInfo resolveContentProviderForUid(@NonNull String name,
+ @PackageManager.ResolveInfoFlagsBits long flags, @UserIdInt int userId,
+ int filterCallingUid);
+
@Nullable
ProviderInfo getGrantImplicitAccessProviderInfo(int recipientUid,
@NonNull String visibleAuthority);
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index be2f58d..3861762 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -4749,6 +4749,38 @@
@Nullable
@Override
+ public ProviderInfo resolveContentProviderForUid(@NonNull String name,
+ @PackageManager.ResolveInfoFlagsBits long flags, @UserIdInt int userId,
+ int filterCallingUid) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.RESOLVE_COMPONENT_FOR_UID,
+ "resolveContentProviderForUid");
+
+ int callingUid = Binder.getCallingUid();
+ int filterUserId = UserHandle.getUserId(filterCallingUid);
+ enforceCrossUserPermission(callingUid, filterUserId, false, false,
+ "resolveContentProviderForUid");
+
+ // Real callingUid should be able to see filterCallingUid
+ if (filterAppAccess(filterCallingUid, callingUid)) {
+ return null;
+ }
+
+ ProviderInfo pInfo = resolveContentProvider(name, flags, userId, filterCallingUid);
+ if (pInfo == null) {
+ return null;
+ }
+ // Real callingUid should be able to see the ContentProvider accessible to filterCallingUid
+ ProviderInfo pInfo2 = resolveContentProvider(name, flags, userId, callingUid);
+ if (pInfo2 != null
+ && Objects.equals(pInfo.name, pInfo2.name)
+ && Objects.equals(pInfo.authority, pInfo2.authority)) {
+ return pInfo;
+ }
+ return null;
+ }
+
+ @Nullable
+ @Override
public ProviderInfo resolveContentProvider(@NonNull String name,
@PackageManager.ResolveInfoFlagsBits long flags, @UserIdInt int userId,
int callingUid) {
diff --git a/services/core/java/com/android/server/pm/IPackageManagerBase.java b/services/core/java/com/android/server/pm/IPackageManagerBase.java
index f05c54d..b11d349 100644
--- a/services/core/java/com/android/server/pm/IPackageManagerBase.java
+++ b/services/core/java/com/android/server/pm/IPackageManagerBase.java
@@ -1129,6 +1129,12 @@
}
@Override
+ public final ProviderInfo resolveContentProviderForUid(String name,
+ @PackageManager.ResolveInfoFlagsBits long flags, int userId, int filterCallingUid) {
+ return snapshot().resolveContentProviderForUid(name, flags, userId, filterCallingUid);
+ }
+
+ @Override
@Deprecated
public final void resetApplicationPreferences(int userId) {
mPreferredActivityHelper.resetApplicationPreferences(userId);
diff --git a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
index e9cb279..e989d68 100644
--- a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
+++ b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
@@ -40,7 +40,7 @@
import com.android.internal.util.function.DodecFunction;
import com.android.internal.util.function.HexConsumer;
import com.android.internal.util.function.HexFunction;
-import com.android.internal.util.function.OctFunction;
+import com.android.internal.util.function.NonaFunction;
import com.android.internal.util.function.QuadFunction;
import com.android.internal.util.function.TriFunction;
import com.android.internal.util.function.UndecFunction;
@@ -351,22 +351,22 @@
@Override
public SyncNotedAppOp noteOperation(int code, int uid, @Nullable String packageName,
@Nullable String featureId, int virtualDeviceId, boolean shouldCollectAsyncNotedOp,
- @Nullable String message, boolean shouldCollectMessage,
- @NonNull OctFunction<Integer, Integer, String, String, Integer, Boolean, String,
- Boolean, SyncNotedAppOp> superImpl) {
+ @Nullable String message, boolean shouldCollectMessage, int notedCount,
+ @NonNull NonaFunction<Integer, Integer, String, String, Integer, Boolean, String,
+ Boolean, Integer, SyncNotedAppOp> superImpl) {
if (uid == mDelegateAndOwnerUid && isDelegateOp(code)) {
final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid),
Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
try {
return superImpl.apply(code, shellUid, SHELL_PKG, featureId, virtualDeviceId,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage);
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage, notedCount);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
return superImpl.apply(code, uid, packageName, featureId, virtualDeviceId,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage);
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage, notedCount);
}
@Override
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index 3f9144f..dea52fd 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -53,7 +53,7 @@
import com.android.internal.util.function.DodecFunction;
import com.android.internal.util.function.HexConsumer;
import com.android.internal.util.function.HexFunction;
-import com.android.internal.util.function.OctFunction;
+import com.android.internal.util.function.NonaFunction;
import com.android.internal.util.function.QuadFunction;
import com.android.internal.util.function.UndecFunction;
import com.android.server.LocalServices;
@@ -248,11 +248,12 @@
public SyncNotedAppOp noteOperation(int code, int uid, @Nullable String packageName,
@Nullable String attributionTag, int virtualDeviceId,
boolean shouldCollectAsyncNotedOp, @Nullable String message,
- boolean shouldCollectMessage, @NonNull OctFunction<Integer, Integer, String, String,
- Integer, Boolean, String, Boolean, SyncNotedAppOp> superImpl) {
+ boolean shouldCollectMessage, int notedCount,
+ @NonNull NonaFunction<Integer, Integer, String, String,
+ Integer, Boolean, String, Boolean, Integer, SyncNotedAppOp> superImpl) {
return superImpl.apply(resolveDatasourceOp(code, uid, packageName, attributionTag),
resolveUid(code, uid), packageName, attributionTag, virtualDeviceId,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage);
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage, notedCount);
}
@Override
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 8346112..a0bc77e 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 static com.android.server.power.hint.Flags.powerhintThreadCleanup;
import static com.android.server.power.hint.Flags.resetOnForkEnabled;
+import android.Manifest;
import android.adpf.ISessionManager;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -59,6 +60,9 @@
import android.os.ServiceManager;
import android.os.SessionCreationConfig;
import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.system.Os;
+import android.system.OsConstants;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -79,7 +83,10 @@
import com.android.server.power.hint.HintManagerService.AppHintSession.SessionModes;
import com.android.server.utils.Slogf;
+import java.io.BufferedReader;
import java.io.FileDescriptor;
+import java.io.FileReader;
+import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.ArrayList;
@@ -95,6 +102,8 @@
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/** An hint service implementation that runs in System Server process. */
public final class HintManagerService extends SystemService {
@@ -103,10 +112,10 @@
private static final int EVENT_CLEAN_UP_UID = 3;
@VisibleForTesting static final int CLEAN_UP_UID_DELAY_MILLIS = 1000;
- // The minimum interval between the headroom calls as rate limiting.
- private static final int DEFAULT_GPU_HEADROOM_INTERVAL_MILLIS = 1000;
- private static final int DEFAULT_CPU_HEADROOM_INTERVAL_MILLIS = 1000;
+ // example: cpu 2255 34 2290 22625563 6290 127 456
+ private static final Pattern PROC_STAT_CPU_TIME_TOTAL_PATTERN =
+ Pattern.compile("cpu\\s+(?<user>[0-9]+)\\s(?<nice>[0-9]+).+");
@VisibleForTesting final long mHintSessionPreferredRate;
@@ -192,10 +201,26 @@
private static final String PROPERTY_HWUI_ENABLE_HINT_MANAGER = "debug.hwui.use_hint_manager";
private static final String PROPERTY_USE_HAL_HEADROOMS = "persist.hms.use_hal_headrooms";
private static final String PROPERTY_CHECK_HEADROOM_TID = "persist.hms.check_headroom_tid";
- private static final String PROPERTY_CHECK_HEADROOM_AFFINITY = "persist.hms.check_affinity";
+ private static final String PROPERTY_CHECK_HEADROOM_AFFINITY =
+ "persist.hms.check_headroom_affinity";
+ private static final String PROPERTY_CHECK_HEADROOM_PROC_STAT_MIN_MILLIS =
+ "persist.hms.check_headroom_proc_stat_min_millis";
private Boolean mFMQUsesIntegratedEventFlag = false;
private final Object mCpuHeadroomLock = new Object();
+ @VisibleForTesting
+ final float mJiffyMillis;
+ private final int mCheckHeadroomProcStatMinMillis;
+ @GuardedBy("mCpuHeadroomLock")
+ private long mLastCpuUserModeTimeCheckedMillis = 0;
+ @GuardedBy("mCpuHeadroomLock")
+ private long mLastCpuUserModeJiffies = 0;
+ @GuardedBy("mCpuHeadroomLock")
+ private final Map<Integer, Long> mUidToLastUserModeJiffies;
+ @VisibleForTesting
+ private String mProcStatFilePathOverride = null;
+ @VisibleForTesting
+ private boolean mEnforceCpuHeadroomUserModeCpuTimeCheck = false;
private ISessionManager mSessionManager;
@@ -310,8 +335,16 @@
new GpuHeadroomParamsInternal().calculationWindowMillis;
if (mSupportInfo.headroom.isCpuSupported) {
mCpuHeadroomCache = new HeadroomCache<>(2, mSupportInfo.headroom.cpuMinIntervalMillis);
+ mUidToLastUserModeJiffies = new ArrayMap<>();
+ long jiffyHz = Os.sysconf(OsConstants._SC_CLK_TCK);
+ mJiffyMillis = 1000.0f / jiffyHz;
+ mCheckHeadroomProcStatMinMillis = SystemProperties.getInt(
+ PROPERTY_CHECK_HEADROOM_PROC_STAT_MIN_MILLIS, 50);
} else {
mCpuHeadroomCache = null;
+ mUidToLastUserModeJiffies = null;
+ mJiffyMillis = 0.0f;
+ mCheckHeadroomProcStatMinMillis = 0;
}
if (mSupportInfo.headroom.isGpuSupported) {
mGpuHeadroomCache = new HeadroomCache<>(2, mSupportInfo.headroom.gpuMinIntervalMillis);
@@ -370,6 +403,12 @@
return supportInfo;
}
+ @VisibleForTesting
+ void setProcStatPathOverride(String override) {
+ mProcStatFilePathOverride = override;
+ mEnforceCpuHeadroomUserModeCpuTimeCheck = true;
+ }
+
private ServiceThread createCleanUpThread() {
final ServiceThread handlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_LOWEST, true /*allowIo*/);
@@ -851,6 +890,11 @@
mChannelMap.remove(uid);
}
}
+ synchronized (mCpuHeadroomLock) {
+ if (mSupportInfo.headroom.isCpuSupported && mUidToLastUserModeJiffies != null) {
+ mUidToLastUserModeJiffies.remove(uid);
+ }
+ }
});
}
@@ -1230,7 +1274,7 @@
// Only call into AM if the tid is either isolated or invalid
if (isolatedPids == null) {
// To avoid deadlock, do not call into AMS if the call is from system.
- if (uid == Process.SYSTEM_UID) {
+ if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) {
return tid;
}
isolatedPids = mAmInternal.getIsolatedProcesses(uid);
@@ -1485,14 +1529,17 @@
throw new UnsupportedOperationException();
}
checkCpuHeadroomParams(params);
+ final int uid = Binder.getCallingUid();
+ final int pid = Binder.getCallingPid();
final CpuHeadroomParams halParams = new CpuHeadroomParams();
- halParams.tids = new int[]{Binder.getCallingPid()};
+ halParams.tids = new int[]{pid};
halParams.calculationType = params.calculationType;
halParams.calculationWindowMillis = params.calculationWindowMillis;
if (params.usesDeviceHeadroom) {
halParams.tids = new int[]{};
} else if (params.tids != null && params.tids.length > 0) {
- if (SystemProperties.getBoolean(PROPERTY_CHECK_HEADROOM_TID, true)) {
+ if (UserHandle.getAppId(uid) != Process.SYSTEM_UID && SystemProperties.getBoolean(
+ PROPERTY_CHECK_HEADROOM_TID, true)) {
final int tgid = Process.getThreadGroupLeader(Binder.getCallingPid());
for (int tid : params.tids) {
if (Process.getThreadGroupLeader(tid) != tgid) {
@@ -1515,6 +1562,20 @@
if (res != null) return res;
}
}
+ final boolean shouldCheckUserModeCpuTime =
+ mEnforceCpuHeadroomUserModeCpuTimeCheck
+ || (UserHandle.getAppId(uid) != Process.SYSTEM_UID
+ && mContext.checkCallingPermission(
+ Manifest.permission.DEVICE_POWER)
+ == PackageManager.PERMISSION_DENIED);
+
+ if (shouldCheckUserModeCpuTime) {
+ synchronized (mCpuHeadroomLock) {
+ if (!checkPerUidUserModeCpuTimeElapsedLocked(uid)) {
+ return null;
+ }
+ }
+ }
// return from HAL directly
try {
final CpuHeadroomResult result = mPowerHal.getCpuHeadroom(halParams);
@@ -1528,6 +1589,11 @@
mCpuHeadroomCache.add(halParams, result);
}
}
+ if (shouldCheckUserModeCpuTime) {
+ synchronized (mCpuHeadroomLock) {
+ mUidToLastUserModeJiffies.put(uid, mLastCpuUserModeJiffies);
+ }
+ }
return result;
} catch (RemoteException e) {
Slog.e(TAG, "Failed to get CPU headroom from Power HAL", e);
@@ -1556,6 +1622,40 @@
}
}
+ // check if there has been sufficient user mode cpu time elapsed since last call
+ // from the same uid
+ @GuardedBy("mCpuHeadroomLock")
+ private boolean checkPerUidUserModeCpuTimeElapsedLocked(int uid) {
+ // skip checking proc stat if it's within mCheckHeadroomProcStatMinMillis
+ if (System.currentTimeMillis() - mLastCpuUserModeTimeCheckedMillis
+ > mCheckHeadroomProcStatMinMillis) {
+ try {
+ mLastCpuUserModeJiffies = getUserModeJiffies();
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to get user mode CPU time", e);
+ return false;
+ }
+ mLastCpuUserModeTimeCheckedMillis = System.currentTimeMillis();
+ }
+ if (mUidToLastUserModeJiffies.containsKey(uid)) {
+ long uidLastUserModeJiffies = mUidToLastUserModeJiffies.get(uid);
+ if ((mLastCpuUserModeJiffies - uidLastUserModeJiffies) * mJiffyMillis
+ < mSupportInfo.headroom.cpuMinIntervalMillis) {
+ Slog.w(TAG, "UID " + uid + " is requesting CPU headroom too soon");
+ Slog.d(TAG, "UID " + uid + " last request at "
+ + uidLastUserModeJiffies * mJiffyMillis
+ + "ms with device currently at "
+ + mLastCpuUserModeJiffies * mJiffyMillis
+ + "ms, the interval: "
+ + (mLastCpuUserModeJiffies - uidLastUserModeJiffies)
+ * mJiffyMillis + "ms is less than require minimum interval "
+ + mSupportInfo.headroom.cpuMinIntervalMillis + "ms");
+ return false;
+ }
+ }
+ return true;
+ }
+
private void checkCpuHeadroomParams(CpuHeadroomParamsInternal params) {
boolean calculationTypeMatched = false;
try {
@@ -1731,6 +1831,27 @@
}
}
+ private long getUserModeJiffies() throws IOException {
+ String filePath =
+ mProcStatFilePathOverride == null ? "/proc/stat" : mProcStatFilePathOverride;
+ try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ Matcher matcher = PROC_STAT_CPU_TIME_TOTAL_PATTERN.matcher(line.trim());
+ if (matcher.find()) {
+ long userJiffies = Long.parseLong(matcher.group("user"));
+ long niceJiffies = Long.parseLong(matcher.group("nice"));
+ Slog.d(TAG,
+ "user: " + userJiffies + " nice: " + niceJiffies
+ + " total " + (userJiffies + niceJiffies));
+ reader.close();
+ return userJiffies + niceJiffies;
+ }
+ }
+ }
+ throw new IllegalStateException("Can't find cpu line in " + filePath);
+ }
+
private boolean checkGraphicsPipelineValid(SessionCreationConfig creationConfig, int uid) {
if (creationConfig.modesToEnable == null) {
return true;
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 1bed48a..2e6be5b 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -1251,12 +1251,12 @@
// should document in PackageInstaller.SessionParams#setEnableRollback
// After enabling and committing any rollback, observe packages and
// prepare to rollback if packages crashes too frequently.
- mPackageWatchdog.startExplicitHealthCheck(mPackageHealthObserver,
- rollback.getPackageNames(), mRollbackLifetimeDurationInMillis);
+ mPackageWatchdog.startExplicitHealthCheck(rollback.getPackageNames(),
+ mRollbackLifetimeDurationInMillis, mPackageHealthObserver);
}
} else {
- mPackageWatchdog.startExplicitHealthCheck(mPackageHealthObserver,
- rollback.getPackageNames(), mRollbackLifetimeDurationInMillis);
+ mPackageWatchdog.startExplicitHealthCheck(rollback.getPackageNames(),
+ mRollbackLifetimeDurationInMillis, mPackageHealthObserver);
}
runExpiration();
}
diff --git a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
index 19eba5f..90c2216 100644
--- a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
+++ b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
@@ -51,8 +51,11 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
import com.android.server.wm.utils.InsetUtils;
+import com.android.window.flags.Flags;
import java.io.PrintWriter;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
/**
* Base class for a Snapshot controller
@@ -148,43 +151,60 @@
protected abstract Rect getLetterboxInsets(ActivityRecord topActivity);
/**
- * This is different than {@link #recordSnapshotInner(TYPE)} because it doesn't store
- * the snapshot to the cache and returns the TaskSnapshot immediately.
- *
- * This is only used for testing so the snapshot content can be verified.
+ * This is different than {@link #recordSnapshotInner(TYPE, boolean, Consumer)} because it
+ * doesn't store the snapshot to the cache and returns the TaskSnapshot immediately.
*/
@VisibleForTesting
- TaskSnapshot captureSnapshot(TYPE source) {
- final TaskSnapshot snapshot;
+ SnapshotSupplier captureSnapshot(TYPE source, boolean allowAppTheme) {
+ final SnapshotSupplier supplier = new SnapshotSupplier();
switch (getSnapshotMode(source)) {
- case SNAPSHOT_MODE_NONE:
- return null;
case SNAPSHOT_MODE_APP_THEME:
- snapshot = drawAppThemeSnapshot(source);
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "drawAppThemeSnapshot");
+ if (Flags.excludeDrawingAppThemeSnapshotFromLock()) {
+ if (allowAppTheme) {
+ supplier.setSupplier(drawAppThemeSnapshot(source));
+ }
+ } else {
+ final Supplier<TaskSnapshot> original = drawAppThemeSnapshot(source);
+ final TaskSnapshot snapshot = original != null ? original.get() : null;
+ supplier.setSnapshot(snapshot);
+ }
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
break;
case SNAPSHOT_MODE_REAL:
- snapshot = snapshot(source);
+ supplier.setSnapshot(snapshot(source));
break;
default:
- snapshot = null;
break;
}
- return snapshot;
+ return supplier;
}
- final TaskSnapshot recordSnapshotInner(TYPE source) {
+ /**
+ * @param allowAppTheme If true, allows to draw app theme snapshot when it's not allowed to take
+ * a real screenshot, but create a fake representation of the app.
+ * @param inLockConsumer Extra task to do in WM lock when first get the snapshot object.
+ */
+ final SnapshotSupplier recordSnapshotInner(TYPE source, boolean allowAppTheme,
+ @Nullable Consumer<TaskSnapshot> inLockConsumer) {
if (shouldDisableSnapshots()) {
return null;
}
- final TaskSnapshot snapshot = captureSnapshot(source);
- if (snapshot == null) {
- return null;
- }
- mCache.putSnapshot(source, snapshot);
- return snapshot;
+ final SnapshotSupplier supplier = captureSnapshot(source, allowAppTheme);
+ supplier.setConsumer(t -> {
+ synchronized (mService.mGlobalLock) {
+ if (!source.isAttached()) {
+ return;
+ }
+ mCache.putSnapshot(source, t);
+ if (inLockConsumer != null) {
+ inLockConsumer.accept(t);
+ }
+ }
+ });
+ return supplier;
}
- @VisibleForTesting
int getSnapshotMode(TYPE source) {
final int type = source.getActivityType();
if (type == ACTIVITY_TYPE_RECENTS || type == ACTIVITY_TYPE_DREAM) {
@@ -400,7 +420,7 @@
* If we are not allowed to take a real screenshot, this attempts to represent the app as best
* as possible by using the theme's window background.
*/
- private TaskSnapshot drawAppThemeSnapshot(TYPE source) {
+ private Supplier<TaskSnapshot> drawAppThemeSnapshot(TYPE source) {
final ActivityRecord topActivity = getTopActivity(source);
if (topActivity == null) {
return null;
@@ -432,26 +452,46 @@
decorPainter.setInsets(systemBarInsets);
decorPainter.drawDecors(c /* statusBarExcludeFrame */, null /* alreadyDrawFrame */);
node.end(c);
- final Bitmap hwBitmap = ThreadedRenderer.createHardwareBitmap(node, width, height);
- if (hwBitmap == null) {
- return null;
- }
+
final Rect contentInsets = new Rect(systemBarInsets);
final Rect letterboxInsets = getLetterboxInsets(topActivity);
InsetUtils.addInsets(contentInsets, letterboxInsets);
- // Note, the app theme snapshot is never translucent because we enforce a non-translucent
- // color above
- final TaskSnapshot taskSnapshot = new TaskSnapshot(
- System.currentTimeMillis() /* id */,
- SystemClock.elapsedRealtimeNanos() /* captureTime */,
- topActivity.mActivityComponent, hwBitmap.getHardwareBuffer(),
- hwBitmap.getColorSpace(), mainWindow.getConfiguration().orientation,
- mainWindow.getWindowConfiguration().getRotation(), new Point(taskWidth, taskHeight),
- contentInsets, letterboxInsets, false /* isLowResolution */,
- false /* isRealSnapshot */, source.getWindowingMode(),
- attrs.insetsFlags.appearance, false /* isTranslucent */, false /* hasImeSurface */,
- topActivity.getConfiguration().uiMode /* uiMode */);
- return validateSnapshot(taskSnapshot);
+
+ final TaskSnapshot.Builder builder = new TaskSnapshot.Builder();
+ builder.setIsRealSnapshot(false);
+ builder.setId(System.currentTimeMillis());
+ builder.setContentInsets(contentInsets);
+ builder.setLetterboxInsets(letterboxInsets);
+
+ builder.setTopActivityComponent(topActivity.mActivityComponent);
+ // Note, the app theme snapshot is never translucent because we enforce a
+ // non-translucent color above.
+ builder.setIsTranslucent(false);
+ builder.setWindowingMode(source.getWindowingMode());
+ builder.setAppearance(attrs.insetsFlags.appearance);
+ builder.setUiMode(topActivity.getConfiguration().uiMode);
+
+ builder.setRotation(mainWindow.getWindowConfiguration().getRotation());
+ builder.setOrientation(mainWindow.getConfiguration().orientation);
+ builder.setTaskSize(new Point(taskWidth, taskHeight));
+ builder.setCaptureTime(SystemClock.elapsedRealtimeNanos());
+
+ return () -> {
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "drawAppThemeSnapshot_acquire");
+ // Do not hold WM lock when calling to render thread.
+ final Bitmap hwBitmap = ThreadedRenderer.createHardwareBitmap(node, width,
+ height);
+ if (hwBitmap == null) {
+ return null;
+ }
+ builder.setSnapshot(hwBitmap.getHardwareBuffer());
+ builder.setColorSpace(hwBitmap.getColorSpace());
+ return validateSnapshot(builder.build());
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ }
+ };
}
static Rect getSystemBarInsets(Rect frame, InsetsState state) {
@@ -482,4 +522,45 @@
pw.println(prefix + "mSnapshotEnabled=" + mSnapshotEnabled);
mCache.dump(pw, prefix);
}
+
+ static class SnapshotSupplier implements Supplier<TaskSnapshot> {
+
+ private TaskSnapshot mSnapshot;
+ private boolean mHasSet;
+ private Consumer<TaskSnapshot> mConsumer;
+ private Supplier<TaskSnapshot> mSupplier;
+
+ /** Callback when the snapshot is get for the first time. */
+ void setConsumer(@NonNull Consumer<TaskSnapshot> consumer) {
+ mConsumer = consumer;
+ }
+
+ void setSupplier(@NonNull Supplier<TaskSnapshot> createSupplier) {
+ mSupplier = createSupplier;
+ }
+
+ void setSnapshot(TaskSnapshot snapshot) {
+ mSnapshot = snapshot;
+ }
+
+ void handleSnapshot() {
+ if (mHasSet) {
+ return;
+ }
+ mHasSet = true;
+ if (mSnapshot == null) {
+ mSnapshot = mSupplier != null ? mSupplier.get() : null;
+ }
+ if (mConsumer != null && mSnapshot != null) {
+ mConsumer.accept(mSnapshot);
+ }
+ }
+
+ @Override
+ @Nullable
+ public TaskSnapshot get() {
+ handleSnapshot();
+ return mSnapshot;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 83b273c..b71256d 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3227,7 +3227,7 @@
return false;
}
// If the user preference respects aspect ratio, then it becomes non-resizable.
- return mAppCompatController.getAppCompatOverrides().getAppCompatAspectRatioOverrides()
+ return mAppCompatController.getAppCompatAspectRatioOverrides()
.userPreferenceCompatibleWithNonResizability();
}
@@ -3898,11 +3898,18 @@
final TaskFragment taskFragment = getTaskFragment();
if (next != null && taskFragment != null && taskFragment.isEmbedded()) {
final TaskFragment organized = taskFragment.getOrganizedTaskFragment();
- final TaskFragment adjacent =
- organized != null ? organized.getAdjacentTaskFragment() : null;
- if (adjacent != null && next.isDescendantOf(adjacent)
- && organized.topRunningActivity() == null) {
- delayRemoval = organized.isDelayLastActivityRemoval();
+ if (Flags.allowMultipleAdjacentTaskFragments()) {
+ delayRemoval = organized != null
+ && organized.topRunningActivity() == null
+ && organized.isDelayLastActivityRemoval()
+ && organized.forOtherAdjacentTaskFragments(next::isDescendantOf);
+ } else {
+ final TaskFragment adjacent =
+ organized != null ? organized.getAdjacentTaskFragment() : null;
+ if (adjacent != null && next.isDescendantOf(adjacent)
+ && organized.topRunningActivity() == null) {
+ delayRemoval = organized.isDelayLastActivityRemoval();
+ }
}
}
@@ -4880,15 +4887,25 @@
* @see #canShowWhenLocked(ActivityRecord)
*/
boolean canShowWhenLocked() {
- final TaskFragment taskFragment = getTaskFragment();
- if (taskFragment != null && taskFragment.getAdjacentTaskFragment() != null
- && taskFragment.isEmbedded()) {
- final TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment();
- final ActivityRecord r = adjacentTaskFragment.getTopNonFinishingActivity();
- return canShowWhenLocked(this) && canShowWhenLocked(r);
- } else {
- return canShowWhenLocked(this);
+ if (!canShowWhenLocked(this)) {
+ return false;
}
+ final TaskFragment taskFragment = getTaskFragment();
+ if (taskFragment == null || !taskFragment.hasAdjacentTaskFragment()
+ || !taskFragment.isEmbedded()) {
+ // No embedded adjacent that need to be checked.
+ return true;
+ }
+
+ // Make sure the embedded adjacent can also be shown.
+ if (!Flags.allowMultipleAdjacentTaskFragments()) {
+ final ActivityRecord adjacentActivity = taskFragment.getAdjacentTaskFragment()
+ .getTopNonFinishingActivity();
+ return canShowWhenLocked(adjacentActivity);
+ }
+ final boolean hasAdjacentNotAllowToShow = taskFragment.forOtherAdjacentTaskFragments(
+ adjacentTF -> !canShowWhenLocked(adjacentTF.getTopNonFinishingActivity()));
+ return !hasAdjacentNotAllowToShow;
}
/**
@@ -8528,8 +8545,7 @@
// If activity in fullscreen mode is letterboxed because of fixed orientation then bounds
// are already calculated in resolveFixedOrientationConfiguration.
// Don't apply aspect ratio if app is overridden to fullscreen by device user/manufacturer.
- if (Flags.immersiveAppRepositioning()
- && !mAppCompatController.getAppCompatAspectRatioPolicy()
+ if (!mAppCompatController.getAppCompatAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio()
&& !mAppCompatController.getAppCompatAspectRatioOverrides()
.hasFullscreenOverride()) {
@@ -8551,18 +8567,6 @@
computeConfigByResolveHint(resolvedConfig, newParentConfiguration);
}
}
- // If activity in fullscreen mode is letterboxed because of fixed orientation then bounds
- // are already calculated in resolveFixedOrientationConfiguration, or if in size compat
- // mode, it should already be calculated in resolveSizeCompatModeConfiguration.
- // Don't apply aspect ratio if app is overridden to fullscreen by device user/manufacturer.
- if (!Flags.immersiveAppRepositioning()
- && !mAppCompatController.getAppCompatAspectRatioPolicy()
- .isLetterboxedForFixedOrientationAndAspectRatio()
- && !scmPolicy.isInSizeCompatModeForBounds()
- && !mAppCompatController.getAppCompatAspectRatioOverrides()
- .hasFullscreenOverride()) {
- resolveAspectRatioRestriction(newParentConfiguration);
- }
if (isFixedOrientationLetterboxAllowed
|| scmPolicy.hasAppCompatDisplayInsetsWithoutInheritance()
@@ -8819,9 +8823,6 @@
}
boolean isImmersiveMode(@NonNull Rect parentBounds) {
- if (!Flags.immersiveAppRepositioning()) {
- return false;
- }
if (!mResolveConfigHint.mUseOverrideInsetsForConfig
&& mWmService.mFlags.mInsetsDecoupledConfiguration) {
return false;
@@ -10355,7 +10356,9 @@
}
if (!isVisibleRequested()) {
// TODO(b/294925498): Remove this finishing check once we have accurate ready tracking.
- if (task != null && task.getPausingActivity() == this) {
+ if (task != null && task.getPausingActivity() == this
+ // Display is asleep, so nothing will be visible anyways.
+ && !mDisplayContent.isSleeping()) {
// Visibility of starting activities isn't calculated until pause-complete, so if
// this is not paused yet, don't consider it ready.
return false;
diff --git a/services/core/java/com/android/server/wm/ActivityRefresher.java b/services/core/java/com/android/server/wm/ActivityRefresher.java
index ed8b689..597f75a 100644
--- a/services/core/java/com/android/server/wm/ActivityRefresher.java
+++ b/services/core/java/com/android/server/wm/ActivityRefresher.java
@@ -115,8 +115,8 @@
private boolean shouldRefreshActivity(@NonNull ActivityRecord activity,
@NonNull Configuration newConfig, @NonNull Configuration lastReportedConfig) {
return mWmService.mAppCompatConfiguration.isCameraCompatRefreshEnabled()
- && activity.mAppCompatController.getAppCompatOverrides()
- .getAppCompatCameraOverrides().shouldRefreshActivityForCameraCompat()
+ && activity.mAppCompatController.getAppCompatCameraOverrides()
+ .shouldRefreshActivityForCameraCompat()
&& ArrayUtils.find(mEvaluators.toArray(), evaluator ->
((Evaluator) evaluator)
.shouldRefreshActivity(activity, newConfig, lastReportedConfig)) != null;
diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotController.java b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
index 9aaa0e1..cfd3248 100644
--- a/services/core/java/com/android/server/wm/ActivitySnapshotController.java
+++ b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
@@ -38,6 +38,7 @@
import java.io.File;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.function.Supplier;
/**
* When an app token becomes invisible, we take a snapshot (bitmap) and put it into our cache.
@@ -355,7 +356,9 @@
final int[] mixedCode = new int[size];
if (size == 1) {
final ActivityRecord singleActivity = activity.get(0);
- final TaskSnapshot snapshot = recordSnapshotInner(singleActivity);
+ final Supplier<TaskSnapshot> supplier = recordSnapshotInner(singleActivity,
+ false /* allowAppTheme */, null /* inLockConsumer */);
+ final TaskSnapshot snapshot = supplier != null ? supplier.get() : null;
if (snapshot != null) {
mixedCode[0] = getSystemHashCode(singleActivity);
addUserSavedFile(singleActivity.mUserId, snapshot, mixedCode);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 5eee8ec..290f155 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -314,6 +314,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.function.Supplier;
/**
* System service for managing activities and their containers (task, displays,... ).
@@ -4038,6 +4039,7 @@
mAmInternal.enforceCallingPermission(READ_FRAME_BUFFER, "takeTaskSnapshot()");
final long ident = Binder.clearCallingIdentity();
try {
+ final Supplier<TaskSnapshot> supplier;
synchronized (mGlobalLock) {
final Task task = mRootWindowContainer.anyTaskForId(taskId,
MATCH_ATTACHED_TASK_OR_RECENT_TASKS);
@@ -4050,11 +4052,13 @@
// be retrieved by recents. While if updateCache is false, the real snapshot will
// always be taken and the snapshot won't be put into SnapshotPersister.
if (updateCache) {
- return mWindowManager.mTaskSnapshotController.recordSnapshot(task);
+ supplier = mWindowManager.mTaskSnapshotController
+ .getRecordSnapshotSupplier(task);
} else {
return mWindowManager.mTaskSnapshotController.snapshot(task);
}
}
+ return supplier != null ? supplier.get() : null;
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -6403,6 +6407,7 @@
@Override
public boolean shuttingDown(boolean booted, int timeout) {
mShuttingDown = true;
+ mWindowManager.mSnapshotController.mTaskSnapshotController.prepareShutdown();
synchronized (mGlobalLock) {
mRootWindowContainer.prepareForShutdown();
updateEventDispatchingLocked(booted);
diff --git a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
index e8eae4f..6a0de98 100644
--- a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE;
@@ -36,6 +37,8 @@
import android.content.res.Configuration;
import android.graphics.Rect;
+import com.android.window.flags.Flags;
+
/**
* Encapsulate app compat policy logic related to aspect ratio.
*/
@@ -239,7 +242,14 @@
|| AppCompatUtils.isInVrUiMode(mActivityRecord.getConfiguration())
// TODO(b/232898850): Always respect aspect ratio requests.
// Don't set aspect ratio for activity in ActivityEmbedding split.
- || (organizedTf != null && !organizedTf.fillsParent())) {
+ || (organizedTf != null && !organizedTf.fillsParent())
+ // Don't set aspect ratio for resizeable activities in freeform.
+ // {@link ActivityRecord#shouldCreateAppCompatDisplayInsets()} will be false for
+ // both activities that are naturally resizeable and activities that have been
+ // forced resizeable.
+ || (Flags.ignoreAspectRatioRestrictionsForResizeableFreeformActivities()
+ && task.getWindowingMode() == WINDOWING_MODE_FREEFORM
+ && !mActivityRecord.shouldCreateAppCompatDisplayInsets())) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/AppCompatController.java b/services/core/java/com/android/server/wm/AppCompatController.java
index 330283f..4433d64 100644
--- a/services/core/java/com/android/server/wm/AppCompatController.java
+++ b/services/core/java/com/android/server/wm/AppCompatController.java
@@ -123,11 +123,6 @@
}
@NonNull
- AppCompatOverrides getAppCompatOverrides() {
- return mAppCompatOverrides;
- }
-
- @NonNull
AppCompatOrientationOverrides getAppCompatOrientationOverrides() {
return mAppCompatOverrides.getAppCompatOrientationOverrides();
}
diff --git a/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java b/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java
index 4e390df..e929fb4 100644
--- a/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java
@@ -281,7 +281,6 @@
mActivityRecord.mWmService.mTransactionFactory,
reachabilityPolicy, letterboxOverrides,
this::getLetterboxParentSurface);
- mLetterbox.attachInput(w);
mActivityRecord.mAppCompatController.getAppCompatReachabilityPolicy()
.setLetterboxInnerBoundsSupplier(mLetterbox::getInnerFrame);
}
@@ -335,7 +334,7 @@
}
start(winHint);
if (isRunning() && mLetterbox.needsApplySurfaceChanges()) {
- mLetterbox.applySurfaceChanges(t, inputT);
+ mLetterbox.applySurfaceChanges(t, inputT, winHint);
}
}
diff --git a/services/core/java/com/android/server/wm/AppCompatReachabilityOverrides.java b/services/core/java/com/android/server/wm/AppCompatReachabilityOverrides.java
index caff96b..4fac81b 100644
--- a/services/core/java/com/android/server/wm/AppCompatReachabilityOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatReachabilityOverrides.java
@@ -35,8 +35,6 @@
import android.content.res.Configuration;
import android.graphics.Rect;
-import com.android.window.flags.Flags;
-
/**
* Encapsulate overrides and configurations about app compat reachability.
*/
@@ -157,33 +155,27 @@
}
/**
- * @return {@value true} if the vertical reachability should be allowed in case of
+ * @return {@code true} if the vertical reachability should be allowed in case of
* thin letterboxing.
*/
boolean allowVerticalReachabilityForThinLetterbox() {
- if (!Flags.disableThinLetterboxingPolicy()) {
- return true;
- }
// When the flag is enabled we allow vertical reachability only if the
// app is not thin letterboxed vertically.
return !isVerticalThinLetterboxed();
}
/**
- * @return {@value true} if the horizontal reachability should be enabled in case of
+ * @return {@code true} if the horizontal reachability should be enabled in case of
* thin letterboxing.
*/
boolean allowHorizontalReachabilityForThinLetterbox() {
- if (!Flags.disableThinLetterboxingPolicy()) {
- return true;
- }
// When the flag is enabled we allow horizontal reachability only if the
// app is not thin pillarboxed.
return !isHorizontalThinLetterboxed();
}
/**
- * @return {@value true} if the resulting app is letterboxed in a way defined as thin.
+ * @return {@code true} if the resulting app is letterboxed in a way defined as thin.
*/
boolean isVerticalThinLetterboxed() {
final int thinHeight = mAppCompatConfiguration.getThinLetterboxHeightPx();
@@ -200,7 +192,7 @@
}
/**
- * @return {@value true} if the resulting app is pillarboxed in a way defined as thin.
+ * @return {@code true} if the resulting app is pillarboxed in a way defined as thin.
*/
boolean isHorizontalThinLetterboxed() {
final int thinWidth = mAppCompatConfiguration.getThinLetterboxWidthPx();
diff --git a/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java b/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
index f3b043b..d278dc3 100644
--- a/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
@@ -28,8 +28,6 @@
import android.content.res.Configuration;
import android.graphics.Rect;
-import com.android.window.flags.Flags;
-
import java.io.PrintWriter;
import java.util.function.DoubleSupplier;
@@ -202,9 +200,7 @@
// saved here before resolved bounds are overridden below.
final AppCompatAspectRatioPolicy aspectRatioPolicy = mActivityRecord.mAppCompatController
.getAppCompatAspectRatioPolicy();
- final boolean useResolvedBounds = Flags.immersiveAppRepositioning()
- ? aspectRatioPolicy.isAspectRatioApplied()
- : aspectRatioPolicy.isLetterboxedForFixedOrientationAndAspectRatio();
+ final boolean useResolvedBounds = aspectRatioPolicy.isAspectRatioApplied();
final Rect containerBounds = useResolvedBounds
? new Rect(resolvedBounds)
: newParentConfiguration.windowConfiguration.getBounds();
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 1a7c6b7..fc0df64 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -111,9 +111,7 @@
}
void onEmbeddedWindowGestureTransferred(@NonNull WindowState host) {
- if (Flags.disallowAppProgressEmbeddedWindow()) {
- mNavigationMonitor.onEmbeddedWindowGestureTransferred(host);
- }
+ mNavigationMonitor.onEmbeddedWindowGestureTransferred(host);
}
/**
@@ -215,7 +213,7 @@
infoBuilder.setFocusedTaskId(currentTask.mTaskId);
}
boolean transferGestureToEmbedded = false;
- if (Flags.disallowAppProgressEmbeddedWindow() && embeddedWindows != null) {
+ if (embeddedWindows != null) {
for (int i = embeddedWindows.size() - 1; i >= 0; --i) {
if (embeddedWindows.get(i).mGestureToEmbedded) {
transferGestureToEmbedded = true;
@@ -997,11 +995,9 @@
/**
* Handle the pending animation when the running transition finished, all the visibility change
* has applied so ready to start pending predictive back animation.
- * @param targets The final animation targets derived in transition.
* @param finishedTransition The finished transition target.
*/
- void onTransitionFinish(ArrayList<Transition.ChangeInfo> targets,
- @NonNull Transition finishedTransition) {
+ void onTransitionFinish(@NonNull Transition finishedTransition) {
if (isMonitoringPrepareTransition(finishedTransition)) {
if (mAnimationHandler.mPrepareCloseTransition == null) {
clearBackAnimations(true /* cancel */);
@@ -1049,14 +1045,6 @@
return;
}
- // Ensure the final animation targets which hidden by transition could be visible.
- for (int i = 0; i < targets.size(); i++) {
- final WindowContainer wc = targets.get(i).mContainer;
- if (wc.mSurfaceControl != null) {
- wc.prepareSurfaces();
- }
- }
-
// The pending builder could be cleared due to prepareSurfaces
// => updateNonSystemOverlayWindowsVisibilityIfNeeded
// => setForceHideNonSystemOverlayWindowIfNeeded
diff --git a/services/core/java/com/android/server/wm/CameraStateMonitor.java b/services/core/java/com/android/server/wm/CameraStateMonitor.java
index 3aa3558..0027992 100644
--- a/services/core/java/com/android/server/wm/CameraStateMonitor.java
+++ b/services/core/java/com/android/server/wm/CameraStateMonitor.java
@@ -110,8 +110,10 @@
}
void startListeningToCameraState() {
- mCameraManager.registerAvailabilityCallback(
- mWmService.mContext.getMainExecutor(), mAvailabilityCallback);
+ if (mCameraManager != null) {
+ mCameraManager.registerAvailabilityCallback(
+ mWmService.mContext.getMainExecutor(), mAvailabilityCallback);
+ }
mIsRunning = true;
}
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index 258a87e..3c60d82 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -289,7 +289,8 @@
transaction.setAlpha(surfaceControl, mDragState.mOriginalAlpha);
transaction.show(surfaceControl);
displayContent.reparentToOverlay(transaction, surfaceControl);
- mDragState.updateDragSurfaceLocked(true, touchX, touchY);
+ mDragState.updateDragSurfaceLocked(true /* keepHandling */,
+ displayContent.getDisplayId(), touchX, touchY);
if (SHOW_LIGHT_TRANSACTIONS) {
Slog.i(TAG_WM, "<<< CLOSE TRANSACTION performDrag");
}
@@ -483,10 +484,11 @@
* Handles motion events.
* @param keepHandling Whether if the drag operation is continuing or this is the last motion
* event.
+ * @param displayId id of the display the X,Y coordinate is n.
* @param newX X coordinate value in dp in the screen coordinate
* @param newY Y coordinate value in dp in the screen coordinate
*/
- void handleMotionEvent(boolean keepHandling, float newX, float newY) {
+ void handleMotionEvent(boolean keepHandling, int displayId, float newX, float newY) {
synchronized (mService.mGlobalLock) {
if (!dragDropActiveLocked()) {
// The drag has ended but the clean-up message has not been processed by
@@ -495,7 +497,7 @@
return;
}
- mDragState.updateDragSurfaceLocked(keepHandling, newX, newY);
+ mDragState.updateDragSurfaceLocked(keepHandling, displayId, newX, newY);
}
}
diff --git a/services/core/java/com/android/server/wm/DragInputEventReceiver.java b/services/core/java/com/android/server/wm/DragInputEventReceiver.java
index 5372d8b..8f4548f 100644
--- a/services/core/java/com/android/server/wm/DragInputEventReceiver.java
+++ b/services/core/java/com/android/server/wm/DragInputEventReceiver.java
@@ -22,13 +22,13 @@
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;
import static android.view.MotionEvent.BUTTON_STYLUS_PRIMARY;
+
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.os.Looper;
import android.util.Slog;
import android.view.InputChannel;
-import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputEventReceiver;
import android.view.MotionEvent;
@@ -63,6 +63,7 @@
return;
}
final MotionEvent motionEvent = (MotionEvent) event;
+ final int displayId = motionEvent.getDisplayId();
final float newX = motionEvent.getRawX();
final float newY = motionEvent.getRawY();
final boolean isStylusButtonDown =
@@ -102,7 +103,8 @@
return;
}
- mDragDropController.handleMotionEvent(!mMuteInput /* keepHandling */, newX, newY);
+ mDragDropController.handleMotionEvent(!mMuteInput /* keepHandling */, displayId, newX,
+ newY);
handled = true;
} catch (Exception e) {
Slog.e(TAG_WM, "Exception caught by drag handleMotion", e);
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 1c4e487..3a0e41a 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -113,8 +113,8 @@
boolean mRelinquishDragSurfaceToDropTarget;
float mAnimatedScale = 1.0f;
float mOriginalAlpha;
- float mOriginalX, mOriginalY;
- float mCurrentX, mCurrentY;
+ float mOriginalDisplayX, mOriginalDisplayY;
+ float mCurrentDisplayX, mCurrentDisplayY;
float mThumbOffsetX, mThumbOffsetY;
InputInterceptor mInputInterceptor;
ArrayList<WindowState> mNotifiedWindows;
@@ -230,22 +230,22 @@
if (mDragInProgress) {
if (DEBUG_DRAG) Slog.d(TAG_WM, "Broadcasting DRAG_ENDED");
for (WindowState ws : mNotifiedWindows) {
- float x = 0;
- float y = 0;
+ float inWindowX = 0;
+ float inWindowY = 0;
SurfaceControl dragSurface = null;
if (!mDragResult && (ws.mSession.mPid == mPid)) {
// Report unconsumed drop location back to the app that started the drag.
- x = ws.translateToWindowX(mCurrentX);
- y = ws.translateToWindowY(mCurrentY);
+ inWindowX = ws.translateToWindowX(mCurrentDisplayX);
+ inWindowY = ws.translateToWindowY(mCurrentDisplayY);
if (relinquishDragSurfaceToDragSource()) {
// If requested (and allowed), report the drag surface back to the app
// starting the drag to handle the return animation
dragSurface = mSurfaceControl;
}
}
- DragEvent event = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED, x, y,
- mThumbOffsetX, mThumbOffsetY, mFlags, null, null, null, dragSurface, null,
- mDragResult);
+ DragEvent event = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED, inWindowX,
+ inWindowY, mThumbOffsetX, mThumbOffsetY, mFlags, null, null, null,
+ dragSurface, null, mDragResult);
try {
if (DEBUG_DRAG) Slog.d(TAG_WM, "Sending DRAG_ENDED to " + ws);
ws.mClient.dispatchDragEvent(event);
@@ -297,70 +297,71 @@
}
/**
- * Creates the drop event for this drag gesture. If `touchedWin` is null, then the drop event
- * will be created for dispatching to the unhandled drag and the drag surface will be provided
- * as a part of the dispatched event.
+ * Creates the drop event for dispatching to the unhandled drag.
+ * TODO(b/384841906): Update `inWindowX` and `inWindowY` to be display-coordinate.
*/
- private DragEvent createDropEvent(float x, float y, @Nullable WindowState touchedWin,
- boolean includePrivateInfo) {
- if (touchedWin != null) {
- final int targetUserId = UserHandle.getUserId(touchedWin.getOwningUid());
- final DragAndDropPermissionsHandler dragAndDropPermissions;
- if ((mFlags & View.DRAG_FLAG_GLOBAL) != 0 && (mFlags & DRAG_FLAGS_URI_ACCESS) != 0
- && mData != null) {
- dragAndDropPermissions = new DragAndDropPermissionsHandler(mService.mGlobalLock,
- mData,
- mUid,
- touchedWin.getOwningPackage(),
- mFlags & DRAG_FLAGS_URI_PERMISSIONS,
- mSourceUserId,
- targetUserId);
- } else {
- dragAndDropPermissions = null;
- }
- if (mSourceUserId != targetUserId) {
- if (mData != null) {
- mData.fixUris(mSourceUserId);
- }
- }
- final boolean targetInterceptsGlobalDrag = targetInterceptsGlobalDrag(touchedWin);
- return obtainDragEvent(DragEvent.ACTION_DROP, x, y, mDataDescription, mData,
- /* includeDragSurface= */ targetInterceptsGlobalDrag,
- /* includeDragFlags= */ targetInterceptsGlobalDrag,
- dragAndDropPermissions);
+ private DragEvent createUnhandledDropEvent(float inWindowX, float inWindowY) {
+ return obtainDragEvent(DragEvent.ACTION_DROP, inWindowX, inWindowY, mDataDescription, mData,
+ /* includeDragSurface= */ true,
+ /* includeDragFlags= */ true, null /* dragAndDropPermissions */);
+ }
+
+ /**
+ * Creates the drop event for this drag gesture.
+ */
+ private DragEvent createDropEvent(float inWindowX, float inWindowY, WindowState touchedWin) {
+ final int targetUserId = UserHandle.getUserId(touchedWin.getOwningUid());
+ final DragAndDropPermissionsHandler dragAndDropPermissions;
+ if ((mFlags & View.DRAG_FLAG_GLOBAL) != 0 && (mFlags & DRAG_FLAGS_URI_ACCESS) != 0
+ && mData != null) {
+ dragAndDropPermissions = new DragAndDropPermissionsHandler(mService.mGlobalLock, mData,
+ mUid, touchedWin.getOwningPackage(), mFlags & DRAG_FLAGS_URI_PERMISSIONS,
+ mSourceUserId, targetUserId);
} else {
- return obtainDragEvent(DragEvent.ACTION_DROP, x, y, mDataDescription, mData,
- /* includeDragSurface= */ includePrivateInfo,
- /* includeDragFlags= */ includePrivateInfo,
- null /* dragAndDropPermissions */);
+ dragAndDropPermissions = null;
}
+ if (mSourceUserId != targetUserId) {
+ if (mData != null) {
+ mData.fixUris(mSourceUserId);
+ }
+ }
+ final boolean targetInterceptsGlobalDrag = targetInterceptsGlobalDrag(touchedWin);
+ return obtainDragEvent(DragEvent.ACTION_DROP, inWindowX, inWindowY, mDataDescription, mData,
+ /* includeDragSurface= */ targetInterceptsGlobalDrag,
+ /* includeDragFlags= */ targetInterceptsGlobalDrag, dragAndDropPermissions);
}
/**
* Notify the drop target and tells it about the data. If the drop event is not sent to the
* target, invokes {@code endDragLocked} after the unhandled drag listener gets a chance to
* handle the drop.
+ * @param inWindowX if `token` refers to a dragEvent-accepting window, `inWindowX` will be
+ * inside the window, else values might be invalid (0, 0).
+ * @param inWindowY if `token` refers to a dragEvent-accepting window, `inWindowY` will be
+ * inside the window, else values might be invalid (0, 0).
*/
- boolean reportDropWindowLock(IBinder token, float x, float y) {
+ boolean reportDropWindowLock(IBinder token, float inWindowX, float inWindowY) {
if (mAnimator != null) {
return false;
}
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "DragDropController#DROP");
- return reportDropWindowLockInner(token, x, y);
+ return reportDropWindowLockInner(token, inWindowX, inWindowY);
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
- private boolean reportDropWindowLockInner(IBinder token, float x, float y) {
+ private boolean reportDropWindowLockInner(IBinder token, float inWindowX, float inWindowY) {
if (mAnimator != null) {
return false;
}
final WindowState touchedWin = mService.mInputToWindowMap.get(token);
- final DragEvent unhandledDropEvent = createDropEvent(x, y, null /* touchedWin */,
- true /* includePrivateInfo */);
+ // TODO(b/384841906): The x, y here when sent to a window and unhandled, will still be
+ // relative to the window it was originally sent to. Need to update this to actually be
+ // display-coordinate.
+ final DragEvent unhandledDropEvent = createUnhandledDropEvent(inWindowX, inWindowY);
if (!isWindowNotified(touchedWin)) {
// Delegate to the unhandled drag listener as a first pass
if (mDragDropController.notifyUnhandledDrop(unhandledDropEvent, "unhandled-drop")) {
@@ -381,7 +382,7 @@
if (DEBUG_DRAG) Slog.d(TAG_WM, "Sending DROP to " + touchedWin);
final IBinder clientToken = touchedWin.mClient.asBinder();
- final DragEvent event = createDropEvent(x, y, touchedWin, false /* includePrivateInfo */);
+ final DragEvent event = createDropEvent(inWindowX, inWindowY, touchedWin);
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "DragDropController#dispatchDrop");
touchedWin.mClient.dispatchDragEvent(event);
@@ -486,8 +487,8 @@
*/
void broadcastDragStartedLocked(final float touchX, final float touchY) {
Trace.instant(TRACE_TAG_WINDOW_MANAGER, "DragDropController#DRAG_STARTED");
- mOriginalX = mCurrentX = touchX;
- mOriginalY = mCurrentY = touchY;
+ mOriginalDisplayX = mCurrentDisplayX = touchX;
+ mOriginalDisplayY = mCurrentDisplayY = touchY;
// Cache a base-class instance of the clip metadata so that parceling
// works correctly in calling out to the apps.
@@ -636,7 +637,7 @@
if (isWindowNotified(newWin)) {
return;
}
- sendDragStartedLocked(newWin, mCurrentX, mCurrentY,
+ sendDragStartedLocked(newWin, mCurrentDisplayX, mCurrentDisplayY,
containsApplicationExtras(mDataDescription));
}
}
@@ -685,12 +686,21 @@
mAnimator = createCancelAnimationLocked();
}
- void updateDragSurfaceLocked(boolean keepHandling, float x, float y) {
+ /**
+ * Updates the position of the drag surface.
+ *
+ * @param keepHandling whether to keep handling the drag.
+ * @param displayId the display ID of the drag surface.
+ * @param displayX the x-coordinate of the drag surface in the display's coordinate frame.
+ * @param displayY the y-coordinate of the drag surface in the display's coordinate frame.
+ */
+ void updateDragSurfaceLocked(boolean keepHandling, int displayId, float displayX,
+ float displayY) {
if (mAnimator != null) {
return;
}
- mCurrentX = x;
- mCurrentY = y;
+ mCurrentDisplayX = displayX;
+ mCurrentDisplayY = displayY;
if (!keepHandling) {
return;
@@ -700,9 +710,10 @@
if (SHOW_LIGHT_TRANSACTIONS) {
Slog.i(TAG_WM, ">>> OPEN TRANSACTION notifyMoveLocked");
}
- mTransaction.setPosition(mSurfaceControl, x - mThumbOffsetX, y - mThumbOffsetY).apply();
- ProtoLog.i(WM_SHOW_TRANSACTIONS, "DRAG %s: pos=(%d,%d)", mSurfaceControl,
- (int) (x - mThumbOffsetX), (int) (y - mThumbOffsetY));
+ mTransaction.setPosition(mSurfaceControl, displayX - mThumbOffsetX,
+ displayY - mThumbOffsetY).apply();
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, "DRAG %s: displayId=%d, pos=(%d,%d)", mSurfaceControl,
+ displayId, (int) (displayX - mThumbOffsetX), (int) (displayY - mThumbOffsetY));
}
/**
@@ -713,6 +724,12 @@
return mDragInProgress;
}
+ /**
+ * `x` and `y` here varies between local window coordinate, relative coordinate to another
+ * window and local display coordinate, all depending on the `action`. Please take a look
+ * at the callers to determine the type.
+ * TODO(b/384845022): Properly document the events sent based on the event type.
+ */
private DragEvent obtainDragEvent(int action, float x, float y, ClipDescription description,
ClipData data, boolean includeDragSurface, boolean includeDragFlags,
IDragAndDropPermissions dragAndDropPermissions) {
@@ -728,34 +745,34 @@
final long duration;
if (mCallingTaskIdToHide != -1) {
animator = ValueAnimator.ofPropertyValuesHolder(
- PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_X, mCurrentX, mCurrentX),
- PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_Y, mCurrentY, mCurrentY),
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_X, mCurrentDisplayX,
+ mCurrentDisplayX),
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_Y, mCurrentDisplayY,
+ mCurrentDisplayY),
PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_SCALE, mAnimatedScale,
mAnimatedScale),
PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_ALPHA, mOriginalAlpha, 0f));
duration = MIN_ANIMATION_DURATION_MS;
} else {
animator = ValueAnimator.ofPropertyValuesHolder(
- PropertyValuesHolder.ofFloat(
- ANIMATED_PROPERTY_X, mCurrentX - mThumbOffsetX,
- mOriginalX - mThumbOffsetX),
- PropertyValuesHolder.ofFloat(
- ANIMATED_PROPERTY_Y, mCurrentY - mThumbOffsetY,
- mOriginalY - mThumbOffsetY),
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_X,
+ mCurrentDisplayX - mThumbOffsetX, mOriginalDisplayX - mThumbOffsetX),
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_Y,
+ mCurrentDisplayY - mThumbOffsetY, mOriginalDisplayY - mThumbOffsetY),
PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_SCALE, mAnimatedScale,
mAnimatedScale),
- PropertyValuesHolder.ofFloat(
- ANIMATED_PROPERTY_ALPHA, mOriginalAlpha, mOriginalAlpha / 2));
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_ALPHA, mOriginalAlpha,
+ mOriginalAlpha / 2));
- final float translateX = mOriginalX - mCurrentX;
- final float translateY = mOriginalY - mCurrentY;
+ final float translateX = mOriginalDisplayX - mCurrentDisplayX;
+ final float translateY = mOriginalDisplayY - mCurrentDisplayY;
// Adjust the duration to the travel distance.
final double travelDistance = Math.sqrt(
translateX * translateX + translateY * translateY);
- final double displayDiagonal =
- Math.sqrt(mDisplaySize.x * mDisplaySize.x + mDisplaySize.y * mDisplaySize.y);
- duration = MIN_ANIMATION_DURATION_MS + (long) (travelDistance / displayDiagonal
- * (MAX_ANIMATION_DURATION_MS - MIN_ANIMATION_DURATION_MS));
+ final double displayDiagonal = Math.sqrt(
+ mDisplaySize.x * mDisplaySize.x + mDisplaySize.y * mDisplaySize.y);
+ duration = MIN_ANIMATION_DURATION_MS + (long) (travelDistance / displayDiagonal * (
+ MAX_ANIMATION_DURATION_MS - MIN_ANIMATION_DURATION_MS));
}
final AnimationListener listener = new AnimationListener();
@@ -771,18 +788,20 @@
private ValueAnimator createCancelAnimationLocked() {
final ValueAnimator animator;
if (mCallingTaskIdToHide != -1) {
- animator = ValueAnimator.ofPropertyValuesHolder(
- PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_X, mCurrentX, mCurrentX),
- PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_Y, mCurrentY, mCurrentY),
+ animator = ValueAnimator.ofPropertyValuesHolder(
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_X, mCurrentDisplayX,
+ mCurrentDisplayX),
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_Y, mCurrentDisplayY,
+ mCurrentDisplayY),
PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_SCALE, mAnimatedScale,
mAnimatedScale),
PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_ALPHA, mOriginalAlpha, 0f));
} else {
animator = ValueAnimator.ofPropertyValuesHolder(
- PropertyValuesHolder.ofFloat(
- ANIMATED_PROPERTY_X, mCurrentX - mThumbOffsetX, mCurrentX),
- PropertyValuesHolder.ofFloat(
- ANIMATED_PROPERTY_Y, mCurrentY - mThumbOffsetY, mCurrentY),
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_X,
+ mCurrentDisplayX - mThumbOffsetX, mCurrentDisplayX),
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_Y,
+ mCurrentDisplayY - mThumbOffsetY, mCurrentDisplayY),
PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_SCALE, mAnimatedScale, 0),
PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_ALPHA, mOriginalAlpha, 0));
}
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index cf145f9..ce85184 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -374,12 +374,6 @@
void notifyControlChanged(InsetsControlTarget target, InsetsSourceProvider provider) {
addToPendingControlMaps(target, provider);
notifyPendingInsetsControlChanged();
-
- if (android.view.inputmethod.Flags.refactorInsetsController()) {
- notifyInsetsChanged();
- mDisplayContent.updateSystemGestureExclusion();
- mDisplayContent.getDisplayPolicy().updateSystemBarAttributes();
- }
}
void notifySurfaceTransactionReady(InsetsSourceProvider provider, long id, boolean ready) {
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index ca47133..29c0c7b 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -22,6 +22,7 @@
import static android.window.TaskConstants.TASK_CHILD_LAYER_TASK_OVERLAY;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
@@ -174,11 +175,12 @@
public void destroy() {
mOuter.setEmpty();
mInner.setEmpty();
-
+ final SurfaceControl.Transaction tx = mTransactionFactory.get();
for (LetterboxSurface surface : mSurfaces) {
- surface.remove();
+ surface.remove(tx);
}
- mFullWindowSurface.remove();
+ mFullWindowSurface.remove(tx);
+ tx.apply();
}
/** Returns whether a call to {@link #applySurfaceChanges} would change the surface. */
@@ -196,30 +198,19 @@
/** Applies surface changes such as colour, window crop, position and input info. */
public void applySurfaceChanges(@NonNull SurfaceControl.Transaction t,
- @NonNull SurfaceControl.Transaction inputT) {
+ @NonNull SurfaceControl.Transaction inputT, @NonNull WindowState windowState) {
if (useFullWindowSurface()) {
+ for (LetterboxSurface surface : mSurfaces) {
+ surface.remove(t);
+ }
+ mFullWindowSurface.attachInput(windowState);
mFullWindowSurface.applySurfaceChanges(t, inputT);
-
- for (LetterboxSurface surface : mSurfaces) {
- surface.remove();
- }
} else {
+ mFullWindowSurface.remove(t);
for (LetterboxSurface surface : mSurfaces) {
+ surface.attachInput(windowState);
surface.applySurfaceChanges(t, inputT);
}
-
- mFullWindowSurface.remove();
- }
- }
-
- /** Enables touches to slide into other neighboring surfaces. */
- void attachInput(WindowState win) {
- if (useFullWindowSurface()) {
- mFullWindowSurface.attachInput(win);
- } else {
- for (LetterboxSurface surface : mSurfaces) {
- surface.attachInput(win);
- }
}
}
@@ -358,9 +349,10 @@
private final Rect mLayoutFrameGlobal = new Rect();
private final Rect mLayoutFrameRelative = new Rect();
+ @Nullable
private InputInterceptor mInputInterceptor;
- public LetterboxSurface(String type) {
+ LetterboxSurface(@NonNull String type) {
mType = type;
}
@@ -394,28 +386,28 @@
t.setLayer(mInputSurface, TASK_CHILD_LAYER_TASK_OVERLAY);
}
- void attachInput(WindowState win) {
- if (mInputInterceptor != null) {
- mInputInterceptor.dispose();
+ void attachInput(@NonNull WindowState windowState) {
+ if (mInputInterceptor != null || windowState.mDisplayContent == null) {
+ return;
}
// TODO(b/371179559): only detect double tap on LB surfaces not used for cutout area.
// Potentially, the input interceptor may still be needed for slippery feature.
- mInputInterceptor = new InputInterceptor("Letterbox_" + mType + "_", win);
+ mInputInterceptor = new InputInterceptor("Letterbox_" + mType + "_", windowState);
}
- public void remove() {
- if (mSurface != null) {
- mTransactionFactory.get().remove(mSurface).apply();
- mSurface = null;
- }
- if (mInputSurface != null) {
- mTransactionFactory.get().remove(mInputSurface).apply();
- mInputSurface = null;
- }
+ void remove(@NonNull SurfaceControl.Transaction t) {
if (mInputInterceptor != null) {
mInputInterceptor.dispose();
mInputInterceptor = null;
}
+ if (mSurface != null) {
+ t.remove(mSurface);
+ }
+ if (mInputSurface != null) {
+ t.remove(mInputSurface);
+ }
+ mInputSurface = null;
+ mSurface = null;
}
public int getWidth() {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 3d28685..4f36476 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1941,7 +1941,8 @@
if (Flags.enableTopVisibleRootTaskPerUserTracking()) {
final IntArray visibleRootTasks = new IntArray();
forAllRootTasks(rootTask -> {
- if (mCurrentUser == rootTask.mUserId && rootTask.isVisibleRequested()) {
+ if ((mCurrentUser == rootTask.mUserId || rootTask.showForAllUsers())
+ && rootTask.isVisible()) {
visibleRootTasks.add(rootTask.getRootTaskId());
}
}, /* traverseTopToBottom */ false);
@@ -2045,6 +2046,11 @@
if (Flags.enableTopVisibleRootTaskPerUserTracking()) {
final IntArray rootTasks = mUserVisibleRootTasks.get(userId, new IntArray());
+ // If root task already exists in the list, move it to the top instead.
+ final int rootTaskIndex = rootTasks.indexOf(rootTask.getRootTaskId());
+ if (rootTaskIndex != -1) {
+ rootTasks.remove(rootTaskIndex);
+ }
rootTasks.add(rootTask.getRootTaskId());
mUserVisibleRootTasks.put(userId, rootTasks);
} else {
@@ -2926,7 +2932,6 @@
}
void prepareForShutdown() {
- mWindowManager.mSnapshotController.mTaskSnapshotController.prepareShutdown();
for (int i = 0; i < getChildCount(); i++) {
createSleepToken("shutdown", getChildAt(i).mDisplayId);
}
diff --git a/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java b/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java
index 38e0115..efc68aa 100644
--- a/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java
+++ b/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java
@@ -95,8 +95,9 @@
if (mediaProjectionInfo.getLaunchCookie() == null) {
mRecordedWC = (WindowContainer) mWms.mRoot.getDefaultDisplay();
} else {
- mRecordedWC = mWms.mRoot.getActivity(activity -> activity.mLaunchCookie
- == mediaProjectionInfo.getLaunchCookie().binder).getTask();
+ final ActivityRecord matchingActivity = mWms.mRoot.getActivity(activity ->
+ activity.mLaunchCookie == mediaProjectionInfo.getLaunchCookie().binder);
+ mRecordedWC = matchingActivity != null ? matchingActivity.getTask() : null;
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 9062afb..d92301b 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3707,10 +3707,21 @@
// Boost the adjacent TaskFragment for dimmer if needed.
final TaskFragment taskFragment = wc.asTaskFragment();
- if (taskFragment != null && taskFragment.isEmbedded()) {
- final TaskFragment adjacentTf = taskFragment.getAdjacentTaskFragment();
- if (adjacentTf != null && adjacentTf.shouldBoostDimmer()) {
- adjacentTf.assignLayer(t, layer++);
+ if (taskFragment != null && taskFragment.isEmbedded()
+ && taskFragment.hasAdjacentTaskFragment()) {
+ if (Flags.allowMultipleAdjacentTaskFragments()) {
+ final int[] nextLayer = { layer };
+ taskFragment.forOtherAdjacentTaskFragments(adjacentTf -> {
+ if (adjacentTf.shouldBoostDimmer()) {
+ adjacentTf.assignLayer(t, nextLayer[0]++);
+ }
+ });
+ layer = nextLayer[0];
+ } else {
+ final TaskFragment adjacentTf = taskFragment.getAdjacentTaskFragment();
+ if (adjacentTf.shouldBoostDimmer()) {
+ adjacentTf.assignLayer(t, layer++);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 9564c59..3d0b41b 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1045,7 +1045,7 @@
+ adjacentFlagRootTask);
}
- if (adjacentFlagRootTask.getAdjacentTaskFragment() == null) {
+ if (!adjacentFlagRootTask.hasAdjacentTaskFragment()) {
throw new UnsupportedOperationException(
"Can't set non-adjacent root as launch adjacent flag root tr="
+ adjacentFlagRootTask);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 38a2ebe..7d300e98 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -36,7 +36,9 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
import com.android.server.wm.BaseAppSnapshotPersister.PersistInfoProvider;
+import com.android.window.flags.Flags;
+import java.util.ArrayList;
import java.util.Set;
/**
@@ -154,6 +156,8 @@
* The attributes of task snapshot are based on task configuration. But sometimes the
* configuration may have been changed during a transition, so supply the ChangeInfo that
* stored the previous appearance of the closing task.
+ *
+ * The snapshot won't be created immediately if it should be captured as fake snapshot.
*/
void recordSnapshot(Task task, Transition.ChangeInfo changeInfo) {
mCurrentChangeInfo = changeInfo;
@@ -164,13 +168,35 @@
}
}
- TaskSnapshot recordSnapshot(Task task) {
- final TaskSnapshot snapshot = recordSnapshotInner(task);
- if (snapshot != null && !task.isActivityTypeHome()) {
- mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
- task.onSnapshotChanged(snapshot);
+ void recordSnapshot(Task task) {
+ if (shouldDisableSnapshots()) {
+ return;
}
- return snapshot;
+ final SnapshotSupplier supplier = getRecordSnapshotSupplier(task);
+ if (supplier == null) {
+ return;
+ }
+ final int mode = getSnapshotMode(task);
+ if (Flags.excludeDrawingAppThemeSnapshotFromLock() && mode == SNAPSHOT_MODE_APP_THEME) {
+ mService.mH.post(supplier::handleSnapshot);
+ } else {
+ supplier.handleSnapshot();
+ }
+ }
+
+ /**
+ * Note that the snapshot is not created immediately, if the returned supplier is non-null, the
+ * caller must call {@link AbsAppSnapshotController.SnapshotSupplier#get} or
+ * {@link AbsAppSnapshotController.SnapshotSupplier#handleSnapshot} to complete the entire
+ * record request.
+ */
+ SnapshotSupplier getRecordSnapshotSupplier(Task task) {
+ return recordSnapshotInner(task, true /* allowAppTheme */, snapshot -> {
+ if (!task.isActivityTypeHome()) {
+ mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
+ task.onSnapshotChanged(snapshot);
+ }
+ });
}
/**
@@ -328,27 +354,38 @@
* Record task snapshots before shutdown.
*/
void prepareShutdown() {
- if (!com.android.window.flags.Flags.recordTaskSnapshotsBeforeShutdown()) {
+ if (!Flags.recordTaskSnapshotsBeforeShutdown()) {
return;
}
- // Make write items run in a batch.
- mPersister.mSnapshotPersistQueue.setPaused(true);
- mPersister.mSnapshotPersistQueue.prepareShutdown();
- for (int i = 0; i < mService.mRoot.getChildCount(); i++) {
- mService.mRoot.getChildAt(i).forAllLeafTasks(task -> {
- if (task.isVisible() && !task.isActivityTypeHome()) {
- final TaskSnapshot snapshot = captureSnapshot(task);
- if (snapshot != null) {
- mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
+ final ArrayList<SnapshotSupplier> supplierArrayList = new ArrayList<>();
+ synchronized (mService.mGlobalLock) {
+ // Make write items run in a batch.
+ mPersister.mSnapshotPersistQueue.setPaused(true);
+ mPersister.mSnapshotPersistQueue.prepareShutdown();
+ for (int i = 0; i < mService.mRoot.getChildCount(); i++) {
+ mService.mRoot.getChildAt(i).forAllLeafTasks(task -> {
+ if (task.isVisible() && !task.isActivityTypeHome()) {
+ final SnapshotSupplier supplier = captureSnapshot(task,
+ true /* allowAppTheme */);
+ if (supplier != null) {
+ supplier.setConsumer(t ->
+ mPersister.persistSnapshot(task.mTaskId, task.mUserId, t));
+ supplierArrayList.add(supplier);
+ }
}
- }
- }, true /* traverseTopToBottom */);
+ }, true /* traverseTopToBottom */);
+ }
}
- mPersister.mSnapshotPersistQueue.setPaused(false);
+ for (int i = supplierArrayList.size() - 1; i >= 0; --i) {
+ supplierArrayList.get(i).handleSnapshot();
+ }
+ synchronized (mService.mGlobalLock) {
+ mPersister.mSnapshotPersistQueue.setPaused(false);
+ }
}
void waitFlush(long timeout) {
- if (!com.android.window.flags.Flags.recordTaskSnapshotsBeforeShutdown()) {
+ if (!Flags.recordTaskSnapshotsBeforeShutdown()) {
return;
}
mPersister.mSnapshotPersistQueue.waitFlush(timeout);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 1f539a1..a3d71db 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1589,7 +1589,7 @@
cleanUpInternal();
// Handle back animation if it's already started.
- mController.mAtm.mBackNavigationController.onTransitionFinish(mTargets, this);
+ mController.mAtm.mBackNavigationController.onTransitionFinish(this);
mController.mFinishingTransition = null;
mController.mSnapshotController.onTransitionFinish(mType, mTargets);
// Resume snapshot persist thread after snapshot controller analysis this transition.
@@ -2542,15 +2542,16 @@
// TaskFragment doesn't contain occluded ActivityRecord.
return true;
}
- final TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment();
- if (adjacentTaskFragment != null) {
- // When the TaskFragment has an adjacent TaskFragment, sibling behind them should be
- // hidden unless any of them are translucent.
- return adjacentTaskFragment.isTranslucentForTransition();
- } else {
+ if (!taskFragment.hasAdjacentTaskFragment()) {
// Non-filling without adjacent is considered as translucent.
return !wc.fillsParent();
}
+ // When the TaskFragment has an adjacent TaskFragment, sibling behind them should be
+ // hidden unless any of them are translucent.
+ if (!Flags.allowMultipleAdjacentTaskFragments()) {
+ return taskFragment.getAdjacentTaskFragment().isTranslucentForTransition();
+ }
+ return taskFragment.forOtherAdjacentTaskFragments(TaskFragment::isTranslucentForTransition);
}
private void updatePriorVisibility() {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index db62ceb..04650b9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -10084,14 +10084,16 @@
TaskSnapshot taskSnapshot;
final long token = Binder.clearCallingIdentity();
try {
+ final Supplier<TaskSnapshot> supplier;
synchronized (mGlobalLock) {
Task task = mRoot.anyTaskForId(taskId, MATCH_ATTACHED_TASK_OR_RECENT_TASKS);
if (task == null) {
throw new IllegalArgumentException(
"Failed to find matching task for taskId=" + taskId);
}
- taskSnapshot = mTaskSnapshotController.captureSnapshot(task);
+ supplier = mTaskSnapshotController.captureSnapshot(task, true /* allowAppTheme */);
}
+ taskSnapshot = supplier != null ? supplier.get() : null;
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 4078726..9ab9a8f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -3121,10 +3121,10 @@
if (com.android.ranging.flags.Flags.rangingStackEnabled()) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_UWB)
|| context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_WIFI_RTT)
+ PackageManager.FEATURE_WIFI_AWARE)
|| (com.android.ranging.flags.Flags.rangingCsEnabled()
&& context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_BLUETOOTH_LE_CHANNEL_SOUNDING))) {
+ PackageManager.FEATURE_BLUETOOTH_LE))) {
t.traceBegin("RangingService");
// TODO: b/375264320 - Remove after RELEASE_RANGING_STACK is ramped to next.
try {
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
index d2c91ff..232bb83 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
@@ -286,14 +286,21 @@
return@forEach
}
var newFlags = oldFlags
+ val isSystemOrInstalled =
+ packageState.isSystem || packageState.getUserStateOrDefault(userId).isInstalled
newFlags =
if (
- newFlags.hasBits(PermissionFlags.ROLE) ||
- newFlags.hasBits(PermissionFlags.PREGRANT)
+ isSystemOrInstalled && (
+ newFlags.hasBits(PermissionFlags.ROLE) ||
+ newFlags.hasBits(PermissionFlags.PREGRANT)
+ )
) {
newFlags or PermissionFlags.RUNTIME_GRANTED
} else {
- newFlags andInv PermissionFlags.RUNTIME_GRANTED
+ newFlags andInv (
+ PermissionFlags.RUNTIME_GRANTED or PermissionFlags.ROLE or
+ PermissionFlags.PREGRANT
+ )
}
newFlags = newFlags andInv USER_SETTABLE_MASK
if (newFlags.hasBits(PermissionFlags.LEGACY_GRANTED)) {
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionResetTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionResetTest.kt
index 1237095..8b357862d 100644
--- a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionResetTest.kt
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionResetTest.kt
@@ -72,7 +72,8 @@
} else {
mockPackageState(
APP_ID_1,
- mockAndroidPackage(PACKAGE_NAME_1, requestedPermissions = setOf(PERMISSION_NAME_0))
+ mockAndroidPackage(PACKAGE_NAME_1, requestedPermissions = setOf(PERMISSION_NAME_0)),
+ true
)
}
setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0, oldFlags)
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayEventDeliveryTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayEventDeliveryTest.java
index d00e2c6..1f45792 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayEventDeliveryTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayEventDeliveryTest.java
@@ -33,6 +33,7 @@
import android.content.Intent;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
+import android.os.BinderProxy;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -290,11 +291,15 @@
}
/**
- * Return true if the freezer is enabled on this platform.
+ * Return true if the freezer is enabled on this platform and if freezer notifications are
+ * supported. It is not enough to test that the freezer notification feature is enabled
+ * because some devices do not have the necessary kernel support.
*/
private boolean isAppFreezerEnabled() {
try {
- return mActivityManager.getService().isAppFreezerEnabled();
+ return mActivityManager.getService().isAppFreezerEnabled()
+ && android.os.Flags.binderFrozenStateChangeCallback()
+ && BinderProxy.isFrozenStateChangeCallbackSupported();
} catch (Exception e) {
Log.e(TAG, "isAppFreezerEnabled() failed: " + e);
return false;
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index b7b4f04..a9ad435 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -23,12 +23,14 @@
import static android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS;
import static android.Manifest.permission.MANAGE_DISPLAYS;
import static android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE;
+import static android.hardware.display.DisplayManager.SWITCHING_TYPE_NONE;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED;
+import static android.hardware.display.HdrConversionMode.HDR_CONVERSION_SYSTEM;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
import static android.provider.Settings.Secure.MIRROR_BUILT_IN_DISPLAY;
import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY;
@@ -123,6 +125,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.os.UserManager;
import android.os.test.FakePermissionEnforcer;
import android.platform.test.flag.junit.SetFlagsRule;
@@ -444,8 +447,6 @@
when(mContext.getResources()).thenReturn(mResources);
mUserManager = Mockito.spy(mContext.getSystemService(UserManager.class));
- mPermissionEnforcer.grant(CONTROL_DISPLAY_BRIGHTNESS);
- mPermissionEnforcer.grant(MODIFY_USER_PREFERRED_DISPLAY_MODE);
doReturn(Context.PERMISSION_ENFORCER_SERVICE).when(mContext).getSystemServiceName(
eq(PermissionEnforcer.class));
doReturn(mPermissionEnforcer).when(mContext).getSystemService(
@@ -2576,11 +2577,11 @@
new HdrConversionMode(HdrConversionMode.HDR_CONVERSION_FORCE, 2),
new HdrConversionMode(HdrConversionMode.HDR_CONVERSION_FORCE, 3));
assertEquals(
- new HdrConversionMode(HdrConversionMode.HDR_CONVERSION_SYSTEM),
- new HdrConversionMode(HdrConversionMode.HDR_CONVERSION_SYSTEM));
+ new HdrConversionMode(HDR_CONVERSION_SYSTEM),
+ new HdrConversionMode(HDR_CONVERSION_SYSTEM));
assertNotEquals(
new HdrConversionMode(HdrConversionMode.HDR_CONVERSION_FORCE, 2),
- new HdrConversionMode(HdrConversionMode.HDR_CONVERSION_SYSTEM));
+ new HdrConversionMode(HDR_CONVERSION_SYSTEM));
}
@Test
@@ -2601,7 +2602,7 @@
+ "HDR_CONVERSION_SYSTEM",
IllegalArgumentException.class,
() -> displayManager.setHdrConversionModeInternal(new HdrConversionMode(
- HdrConversionMode.HDR_CONVERSION_SYSTEM,
+ HDR_CONVERSION_SYSTEM,
Display.HdrCapabilities.HDR_TYPE_DOLBY_VISION)));
}
@@ -2678,7 +2679,7 @@
displayManager.setUserDisabledHdrTypesInternal(new int [0]);
displayManager.setAreUserDisabledHdrTypesAllowedInternal(true);
displayManager.setHdrConversionModeInternal(
- new HdrConversionMode(HdrConversionMode.HDR_CONVERSION_SYSTEM));
+ new HdrConversionMode(HDR_CONVERSION_SYSTEM));
assertEquals(1, mAllowedHdrOutputTypes.length);
assertTrue(Display.HdrCapabilities.HDR_TYPE_DOLBY_VISION == mAllowedHdrOutputTypes[0]);
@@ -2732,7 +2733,7 @@
assertTrue(logicalDisplay.getDisplayInfoLocked().isForceSdr);
displayManager.setHdrConversionModeInternal(
- new HdrConversionMode(HdrConversionMode.HDR_CONVERSION_SYSTEM));
+ new HdrConversionMode(HDR_CONVERSION_SYSTEM));
assertFalse(logicalDisplay.getDisplayInfoLocked().isForceSdr);
}
@@ -3360,6 +3361,7 @@
@Test
public void testOnUserSwitching_UpdatesBrightness() {
+ mPermissionEnforcer.grant(CONTROL_DISPLAY_BRIGHTNESS);
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mShortMockedInjector);
DisplayManagerInternal localService = displayManager.new LocalService();
@@ -3411,6 +3413,7 @@
@Test
public void testOnUserSwitching_brightnessForNewUserIsDefault() {
+ mPermissionEnforcer.grant(CONTROL_DISPLAY_BRIGHTNESS);
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mShortMockedInjector);
DisplayManagerInternal localService = displayManager.new LocalService();
@@ -3439,7 +3442,8 @@
}
@Test
- public void testResolutionChangeGetsBackedUp_FeatureFlagFalse() throws Exception {
+ public void testResolutionChangeGetsBackedUp_FeatureFlagFalse() {
+ mPermissionEnforcer.grant(MODIFY_USER_PREFERRED_DISPLAY_MODE);
when(mMockFlags.isResolutionBackupRestoreEnabled()).thenReturn(false);
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mBasicInjector);
@@ -3465,6 +3469,7 @@
@Test
public void testBrightnessUpdates() {
+ mPermissionEnforcer.grant(CONTROL_DISPLAY_BRIGHTNESS);
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mShortMockedInjector);
DisplayManagerInternal localService = displayManager.new LocalService();
@@ -3533,6 +3538,7 @@
@Test
public void testResolutionChangeGetsBackedUp() throws Exception {
+ mPermissionEnforcer.grant(MODIFY_USER_PREFERRED_DISPLAY_MODE);
when(mMockFlags.isResolutionBackupRestoreEnabled()).thenReturn(true);
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mBasicInjector);
@@ -3911,9 +3917,9 @@
waitForIdleHandler(handler);
// Create a default display device
- createFakeDisplayDevice(displayManager, new float[] {60f}, Display.TYPE_INTERNAL);
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
// Create a non-default display device
- createFakeDisplayDevice(displayManager, new float[] {60f}, Display.TYPE_EXTERNAL);
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_EXTERNAL);
Settings.Secure.putInt(mContext.getContentResolver(), MIRROR_BUILT_IN_DISPLAY, 1);
final ContentObserver observer = displayManager.getSettingsObserver();
@@ -3923,6 +3929,300 @@
assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_BASIC_CHANGED);
}
+ @Test
+ public void startWifiDisplayScan_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ assertThrows(SecurityException.class, displayManagerBinderService::startWifiDisplayScan);
+ }
+
+ @Test
+ public void stopWifiDisplayScan_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ assertThrows(SecurityException.class, displayManagerBinderService::stopWifiDisplayScan);
+ }
+
+ @Test
+ public void connectWifiDisplay_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ assertThrows(SecurityException.class,
+ () -> displayManagerBinderService.connectWifiDisplay("someAddress"));
+ }
+
+ @Test
+ public void renameWifiDisplay_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ assertThrows(SecurityException.class,
+ () -> displayManagerBinderService.renameWifiDisplay("someAddress", "someAlias"));
+ }
+
+ @Test
+ public void forgetWifiDisplay_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ assertThrows(SecurityException.class,
+ () -> displayManagerBinderService.forgetWifiDisplay("someAddress"));
+ }
+
+ @Test
+ public void pauseWifiDisplay_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ assertThrows(SecurityException.class, displayManagerBinderService::pauseWifiDisplay);
+ }
+
+ @Test
+ public void resumeWifiDisplay_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ assertThrows(SecurityException.class, displayManagerBinderService::resumeWifiDisplay);
+ }
+
+ @Test
+ public void setUserDisabledHdrTypes_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ assertThrows(SecurityException.class, () ->
+ displayManagerBinderService.setUserDisabledHdrTypes(new int[0]));
+ }
+
+ @Test
+ public void setAreUserDisabledHdrTypesAllowed_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ assertThrows(SecurityException.class, () ->
+ displayManagerBinderService.setAreUserDisabledHdrTypesAllowed(true));
+ }
+
+ @Test
+ public void requestColorMode_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ assertThrows(SecurityException.class, () -> displayManagerBinderService.requestColorMode(
+ Display.DEFAULT_DISPLAY, Display.COLOR_MODE_DEFAULT));
+ }
+
+ @Test
+ public void getBrightnessEvents_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ assertThrows(SecurityException.class, () ->
+ displayManagerBinderService.getBrightnessEvents("somePackage"));
+ }
+
+ @Test
+ public void getAmbientBrightnessStats_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ assertThrows(SecurityException.class,
+ displayManagerBinderService::getAmbientBrightnessStats);
+ }
+
+ @Test
+ public void setBrightnessConfigurationForUser_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ assertThrows(SecurityException.class, () ->
+ displayManagerBinderService.setBrightnessConfigurationForUser(
+ new BrightnessConfiguration.Builder(/* lux= */ new float[]{0, 100},
+ /* nits= */ new float[]{100, 200}).build(), UserHandle.USER_SYSTEM,
+ "somePackage"));
+ }
+
+ @Test
+ public void setBrightnessConfigurationForDisplay_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ assertThrows(SecurityException.class, () ->
+ displayManagerBinderService.setBrightnessConfigurationForDisplay(
+ new BrightnessConfiguration.Builder(/* lux= */ new float[]{0, 100},
+ /* nits= */ new float[]{100, 200}).build(), "uniqueId",
+ UserHandle.USER_SYSTEM, "somePackage"));
+ }
+
+ @Test
+ public void getBrightnessConfigurationForDisplay_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ assertThrows(SecurityException.class, () ->
+ displayManagerBinderService.getBrightnessConfigurationForDisplay("uniqueId",
+ UserHandle.USER_SYSTEM));
+ }
+
+ @Test
+ public void getDefaultBrightnessConfiguration_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ assertThrows(SecurityException.class,
+ displayManagerBinderService::getDefaultBrightnessConfiguration);
+ }
+
+ @Test
+ public void getBrightnessInfo_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ assertThrows(SecurityException.class, () ->
+ displayManagerBinderService.getBrightnessInfo(Display.DEFAULT_DISPLAY));
+ }
+
+ @Test
+ public void setTemporaryBrightness_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ assertThrows(SecurityException.class, () ->
+ displayManagerBinderService.setTemporaryBrightness(Display.DEFAULT_DISPLAY, 0.3f));
+ }
+
+ @Test
+ public void setBrightness_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ assertThrows(SecurityException.class, () ->
+ displayManagerBinderService.setBrightness(Display.DEFAULT_DISPLAY, 0.3f));
+ }
+
+ @Test
+ public void getBrightness_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ assertThrows(SecurityException.class, () ->
+ displayManagerBinderService.getBrightness(Display.DEFAULT_DISPLAY));
+ }
+
+ @Test
+ public void setTemporaryAutoBrightnessAdjustment_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ assertThrows(SecurityException.class, () ->
+ displayManagerBinderService.setTemporaryAutoBrightnessAdjustment(0.1f));
+ }
+
+ @Test
+ public void setUserPreferredDisplayMode_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ assertThrows(SecurityException.class, () -> displayManagerBinderService
+ .setUserPreferredDisplayMode(Display.DEFAULT_DISPLAY, new Display.Mode(
+ /* width= */ 800, /* height= */ 600, /* refreshRate= */ 60)));
+ }
+
+ @Test
+ public void setHdrConversionMode_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ assertThrows(SecurityException.class, () -> displayManagerBinderService
+ .setHdrConversionMode(new HdrConversionMode(HDR_CONVERSION_SYSTEM)));
+ }
+
+ @Test
+ public void setShouldAlwaysRespectAppRequestedMode_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ assertThrows(SecurityException.class, () -> displayManagerBinderService
+ .setShouldAlwaysRespectAppRequestedMode(true));
+ }
+
+ @Test
+ public void shouldAlwaysRespectAppRequestedMode_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ assertThrows(SecurityException.class,
+ displayManagerBinderService::shouldAlwaysRespectAppRequestedMode);
+ }
+
+ @Test
+ public void setRefreshRateSwitchingType_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ assertThrows(SecurityException.class, () -> displayManagerBinderService
+ .setRefreshRateSwitchingType(SWITCHING_TYPE_NONE));
+ }
+
+ @Test
+ public void requestDisplayModes_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ assertThrows(SecurityException.class, () -> displayManagerBinderService
+ .requestDisplayModes(new Binder(), Display.DEFAULT_DISPLAY, new int[0]));
+ }
+
+ @Test
+ public void getDozeBrightnessSensorValueToBrightness_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ assertThrows(SecurityException.class, () -> displayManagerBinderService
+ .getDozeBrightnessSensorValueToBrightness(Display.DEFAULT_DISPLAY));
+ }
+
+ @Test
+ public void getDefaultDozeBrightness_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ assertThrows(SecurityException.class, () -> displayManagerBinderService
+ .getDefaultDozeBrightness(Display.DEFAULT_DISPLAY));
+ }
+
private void initDisplayPowerController(DisplayManagerInternal localService) {
localService.initPowerManagement(new DisplayManagerInternal.DisplayPowerCallbacks() {
@Override
diff --git a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
index 35f421e..de6f9bd 100644
--- a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
+++ b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
@@ -78,12 +78,15 @@
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.Log;
+import androidx.test.InstrumentationRegistry;
+
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.power.hint.HintManagerService.AppHintSession;
import com.android.server.power.hint.HintManagerService.Injector;
import com.android.server.power.hint.HintManagerService.NativeWrapper;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -93,6 +96,8 @@
import org.mockito.stubbing.Answer;
import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
@@ -111,6 +116,7 @@
*/
public class HintManagerServiceTest {
private static final String TAG = "HintManagerServiceTest";
+ private List<File> mFilesCreated = new ArrayList<>();
private static WorkDuration makeWorkDuration(
long timestamp, long duration, long workPeriodStartTime,
@@ -192,9 +198,9 @@
mSupportInfo.sessionTags = -1;
mSupportInfo.headroom = new SupportInfo.HeadroomSupportInfo();
mSupportInfo.headroom.isCpuSupported = true;
- mSupportInfo.headroom.cpuMinIntervalMillis = 2000;
+ mSupportInfo.headroom.cpuMinIntervalMillis = 1000;
mSupportInfo.headroom.isGpuSupported = true;
- mSupportInfo.headroom.gpuMinIntervalMillis = 2000;
+ mSupportInfo.headroom.gpuMinIntervalMillis = 1000;
mSupportInfo.compositionData = new SupportInfo.CompositionDataSupportInfo();
return mSupportInfo;
}
@@ -243,6 +249,13 @@
LocalServices.addService(ActivityManagerInternal.class, mAmInternalMock);
}
+ @After
+ public void tearDown() {
+ for (File file : mFilesCreated) {
+ file.delete();
+ }
+ }
+
/**
* Mocks the creation calls, but without support for new createHintSessionWithConfig method
*/
@@ -1327,6 +1340,58 @@
});
}
+ @Test
+ public void testCpuHeadroomCpuProcStatPath() throws Exception {
+ File dir = InstrumentationRegistry.getTargetContext().getFilesDir();
+ dir.mkdir();
+ String procStatFileStr = "mock_proc_stat";
+ File file = new File(dir, procStatFileStr);
+ mFilesCreated.add(file);
+ try (FileOutputStream output = new FileOutputStream(file)) {
+ output.write("cpu 2000 3000 4000 0 0 0 0 0 0 0".getBytes());
+ }
+ HintManagerService service = createService();
+ service.setProcStatPathOverride(file.getPath());
+
+ CpuHeadroomParamsInternal params1 = new CpuHeadroomParamsInternal();
+ CpuHeadroomParams halParams1 = new CpuHeadroomParams();
+ halParams1.calculationType = CpuHeadroomParams.CalculationType.MIN;
+ halParams1.tids = new int[]{Process.myPid()};
+
+ float headroom1 = 0.1f;
+ CpuHeadroomResult halRet1 = CpuHeadroomResult.globalHeadroom(headroom1);
+ when(mIPowerMock.getCpuHeadroom(eq(halParams1))).thenReturn(halRet1);
+ clearInvocations(mIPowerMock);
+ assertEquals(halRet1, service.getBinderServiceInstance().getCpuHeadroom(params1));
+ verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams1));
+ // expire the cache but cpu proc hasn't changed so we expect no value return
+ Thread.sleep(1100);
+ clearInvocations(mIPowerMock);
+ assertEquals(null, service.getBinderServiceInstance().getCpuHeadroom(params1));
+ verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams1));
+
+ // update user jiffies with 500 equivalent jiffies, which is not sufficient cpu time
+ Thread.sleep(1100);
+ try (FileOutputStream output = new FileOutputStream(file)) {
+ output.write(("cpu " + (2000 + (int) (500 / service.mJiffyMillis))
+ + " 3000 4000 0 0 0 0 0 0 0").getBytes());
+ }
+ clearInvocations(mIPowerMock);
+ assertEquals(null, service.getBinderServiceInstance().getCpuHeadroom(params1));
+ verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams1));
+
+ // update nice jiffies with 600 equivalent jiffies, now it exceeds 1000ms requirement
+ Thread.sleep(1100);
+ try (FileOutputStream output = new FileOutputStream(file)) {
+ output.write(("cpu " + (2000 + (int) (500 / service.mJiffyMillis))
+ + " " + +(3000 + (int) (600 / service.mJiffyMillis))
+ + " 4000 0 0 0 0 0 0 0").getBytes());
+ }
+ clearInvocations(mIPowerMock);
+ assertEquals(halRet1, service.getBinderServiceInstance().getCpuHeadroom(params1));
+ verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams1));
+ }
+
@Test
@EnableFlags({Flags.FLAG_CPU_HEADROOM_AFFINITY_CHECK})
@@ -1397,8 +1462,8 @@
verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams3));
verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams4));
- // after 1 more second it should be served with cache still
- Thread.sleep(1000);
+ // after 500ms more it should be served with cache
+ Thread.sleep(500);
clearInvocations(mIPowerMock);
assertEquals(halRet1, service.getBinderServiceInstance().getCpuHeadroom(params1));
assertEquals(halRet2, service.getBinderServiceInstance().getCpuHeadroom(params2));
@@ -1410,8 +1475,8 @@
verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams3));
verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams4));
- // after 2+ seconds it should be served from HAL as it exceeds 2000 millis interval
- Thread.sleep(1100);
+ // after 1+ seconds it should be served from HAL as it exceeds 1000 millis interval
+ Thread.sleep(600);
clearInvocations(mIPowerMock);
assertEquals(halRet1, service.getBinderServiceInstance().getCpuHeadroom(params1));
assertEquals(halRet2, service.getBinderServiceInstance().getCpuHeadroom(params2));
@@ -1519,8 +1584,8 @@
verify(mIPowerMock, times(0)).getGpuHeadroom(eq(halParams1));
verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2));
- // after 1 more second it should be served with cache still
- Thread.sleep(1000);
+ // after 500ms it should be served with cache
+ Thread.sleep(500);
clearInvocations(mIPowerMock);
assertEquals(halRet1, service.getBinderServiceInstance().getGpuHeadroom(params1));
assertEquals(halRet2, service.getBinderServiceInstance().getGpuHeadroom(params2));
@@ -1528,8 +1593,8 @@
verify(mIPowerMock, times(0)).getGpuHeadroom(eq(halParams1));
verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2));
- // after 2+ seconds it should be served from HAL as it exceeds 2000 millis interval
- Thread.sleep(1100);
+ // after 1+ seconds it should be served from HAL as it exceeds 1000 millis interval
+ Thread.sleep(600);
clearInvocations(mIPowerMock);
assertEquals(halRet1, service.getBinderServiceInstance().getGpuHeadroom(params1));
assertEquals(halRet2, service.getBinderServiceInstance().getGpuHeadroom(params2));
diff --git a/services/tests/servicestests/src/com/android/server/OWNERS b/services/tests/servicestests/src/com/android/server/OWNERS
index d8a9400..69feb1d 100644
--- a/services/tests/servicestests/src/com/android/server/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/OWNERS
@@ -6,5 +6,6 @@
per-file *Network* = file:/services/core/java/com/android/server/net/OWNERS
per-file BatteryServiceTest.java = file:platform/hardware/interfaces:/health/OWNERS
per-file GestureLauncherServiceTest.java = file:platform/packages/apps/EmergencyInfo:/OWNERS
+per-file GestureLauncherServiceTest.java = file:/INPUT_OWNERS
per-file PinnerServiceTest.java = file:/apct-tests/perftests/OWNERS
per-file SecurityStateTest.java = file:/SECURITY_STATE_OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java b/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java
index b1df0f1..c7a06b8 100644
--- a/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java
@@ -31,6 +31,7 @@
import android.os.test.TestLooper;
import android.provider.Settings;
import android.security.advancedprotection.AdvancedProtectionFeature;
+import android.security.advancedprotection.AdvancedProtectionManager;
import android.security.advancedprotection.IAdvancedProtectionCallback;
import androidx.annotation.NonNull;
@@ -54,7 +55,8 @@
private Context mContext;
private AdvancedProtectionService.AdvancedProtectionStore mStore;
private TestLooper mLooper;
- AdvancedProtectionFeature mFeature = new AdvancedProtectionFeature("test-id");
+ AdvancedProtectionFeature mTestFeature2g = new AdvancedProtectionFeature(
+ AdvancedProtectionManager.FEATURE_ID_DISALLOW_CELLULAR_2G);
@Before
public void setup() throws Settings.SettingNotFoundException {
@@ -105,7 +107,7 @@
@NonNull
@Override
public AdvancedProtectionFeature getFeature() {
- return mFeature;
+ return mTestFeature2g;
}
@Override
@@ -135,7 +137,7 @@
@NonNull
@Override
public AdvancedProtectionFeature getFeature() {
- return mFeature;
+ return mTestFeature2g;
}
@Override
@@ -165,7 +167,7 @@
@NonNull
@Override
public AdvancedProtectionFeature getFeature() {
- return mFeature;
+ return mTestFeature2g;
}
@Override
@@ -238,8 +240,10 @@
@Test
public void testGetFeatures() {
- AdvancedProtectionFeature feature1 = new AdvancedProtectionFeature("id-1");
- AdvancedProtectionFeature feature2 = new AdvancedProtectionFeature("id-2");
+ AdvancedProtectionFeature feature1 = new AdvancedProtectionFeature(
+ AdvancedProtectionManager.FEATURE_ID_DISALLOW_CELLULAR_2G);
+ AdvancedProtectionFeature feature2 = new AdvancedProtectionFeature(
+ AdvancedProtectionManager.FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES);
AdvancedProtectionHook hook = new AdvancedProtectionHook(mContext, true) {
@NonNull
@Override
@@ -268,8 +272,10 @@
@Test
public void testGetFeatures_featureNotAvailable() {
- AdvancedProtectionFeature feature1 = new AdvancedProtectionFeature("id-1");
- AdvancedProtectionFeature feature2 = new AdvancedProtectionFeature("id-2");
+ AdvancedProtectionFeature feature1 = new AdvancedProtectionFeature(
+ AdvancedProtectionManager.FEATURE_ID_DISALLOW_CELLULAR_2G);
+ AdvancedProtectionFeature feature2 = new AdvancedProtectionFeature(
+ AdvancedProtectionManager.FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES);
AdvancedProtectionHook hook = new AdvancedProtectionHook(mContext, true) {
@NonNull
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java
index a7fc10f..948371f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java
@@ -29,6 +29,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.never;
@@ -253,7 +254,11 @@
*/
@Test
public void testSkipRecordActivity() {
- doReturn(createSnapshot()).when(mActivitySnapshotController).recordSnapshotInner(any());
+ final AbsAppSnapshotController.SnapshotSupplier supplier =
+ new AbsAppSnapshotController.SnapshotSupplier();
+ supplier.setSupplier(this::createSnapshot);
+ doReturn(supplier).when(mActivitySnapshotController).recordSnapshotInner(
+ any(), anyBoolean(), any());
final Task task = createTask(mDisplayContent);
mSnapshotPersistQueue.setPaused(true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java
index 8747cfa..9d191ce 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java
@@ -335,8 +335,7 @@
}
private AppCompatOrientationOverrides getTopOrientationOverrides() {
- return activity().top().mAppCompatController.getAppCompatOverrides()
- .getAppCompatOrientationOverrides();
+ return activity().top().mAppCompatController.getAppCompatOrientationOverrides();
}
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
index 90bf5f0..a21ab5d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
@@ -601,8 +601,7 @@
}
private AppCompatOrientationOverrides getTopOrientationOverrides() {
- return activity().top().mAppCompatController.getAppCompatOverrides()
- .getAppCompatOrientationOverrides();
+ return activity().top().mAppCompatController.getAppCompatOrientationOverrides();
}
private AppCompatOrientationPolicy getTopAppCompatOrientationPolicy() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityOverridesTest.java
index 1edbcd5..463254c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityOverridesTest.java
@@ -23,14 +23,10 @@
import android.compat.testing.PlatformCompatChangeRule;
import android.graphics.Rect;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import androidx.annotation.NonNull;
-import com.android.window.flags.Flags;
-
import junit.framework.Assert;
import org.junit.Rule;
@@ -125,8 +121,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_DISABLE_THIN_LETTERBOXING_POLICY)
- public void testAllowReachabilityForThinLetterboxWithFlagEnabled() {
+ public void testAllowReachabilityForThinLetterbox_disableForThinLetterboxing() {
runTestScenario((robot) -> {
robot.activity().createActivityWithComponent();
@@ -142,24 +137,6 @@
});
}
- @Test
- @DisableFlags(Flags.FLAG_DISABLE_THIN_LETTERBOXING_POLICY)
- public void testAllowReachabilityForThinLetterboxWithFlagDisabled() {
- runTestScenario((robot) -> {
- robot.activity().createActivityWithComponent();
-
- robot.configureIsVerticalThinLetterboxed(/* isThin */ true);
- robot.checkAllowVerticalReachabilityForThinLetterbox(/* expected */ true);
- robot.configureIsHorizontalThinLetterboxed(/* isThin */ true);
- robot.checkAllowHorizontalReachabilityForThinLetterbox(/* expected */ true);
-
- robot.configureIsVerticalThinLetterboxed(/* isThin */ false);
- robot.checkAllowVerticalReachabilityForThinLetterbox(/* expected */ true);
- robot.configureIsHorizontalThinLetterboxed(/* isThin */ false);
- robot.checkAllowHorizontalReachabilityForThinLetterbox(/* expected */ true);
- });
- }
-
/**
* Runs a test scenario providing a Robot.
*/
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 429a396a..de4b6fa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -150,8 +150,8 @@
mProcess).build();
// Use a new TestIWindow so we don't collect events for other windows
- final WindowState window = createWindow(null, TYPE_BASE_APPLICATION, activity, name,
- ownerId, false, new TestIWindow());
+ final WindowState window = newWindowBuilder(name, TYPE_BASE_APPLICATION).setWindowToken(
+ activity).setOwnerId(ownerId).setClientWindow(new TestIWindow()).build();
InputChannel channel = new InputChannel();
window.openInputChannel(channel);
window.mHasSurface = true;
@@ -249,7 +249,7 @@
mTarget.mDeferDragStateClosed = true;
mTarget.reportDropWindow(mWindow.mInputChannelToken, 0, 0);
// Verify the drop event includes the drag surface
- mTarget.handleMotionEvent(false, 0, 0);
+ mTarget.handleMotionEvent(false, mWindow.getDisplayId(), 0, 0);
final DragEvent dropEvent = dragEvents.get(dragEvents.size() - 1);
assertTrue(dropEvent.getDragSurface() != null);
@@ -296,7 +296,7 @@
0).getClipData().willParcelWithActivityInfo());
mTarget.reportDropWindow(globalInterceptWindow.mInputChannelToken, 0, 0);
- mTarget.handleMotionEvent(false, 0, 0);
+ mTarget.handleMotionEvent(false, globalInterceptWindow.getDisplayId(), 0, 0);
mToken = globalInterceptWindow.mClient.asBinder();
// Verify the drop event is only sent for the global intercept window
@@ -334,8 +334,8 @@
try {
mTarget.mDeferDragStateClosed = true;
mTarget.reportDropWindow(mWindow.mInputChannelToken, 0, 0);
- // // Verify the drop event does not have the drag flags
- mTarget.handleMotionEvent(false, 0, 0);
+ // Verify the drop event does not have the drag flags
+ mTarget.handleMotionEvent(false, mWindow.getDisplayId(), 0, 0);
final DragEvent dropEvent = dragEvents.get(dragEvents.size() - 1);
assertTrue(dropEvent.getDragFlags() == (View.DRAG_FLAG_GLOBAL
| View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG));
@@ -520,7 +520,7 @@
// Verify after consuming that the drag surface is relinquished
mTarget.reportDropWindow(otherWindow.mInputChannelToken, 0, 0);
- mTarget.handleMotionEvent(false, 0, 0);
+ mTarget.handleMotionEvent(false, otherWindow.getDisplayId(), 0, 0);
mToken = otherWindow.mClient.asBinder();
mTarget.reportDropResult(otherIWindow, true);
@@ -551,7 +551,7 @@
// Verify after consuming that the drag surface is relinquished
mTarget.reportDropWindow(otherWindow.mInputChannelToken, 0, 0);
- mTarget.handleMotionEvent(false, 0, 0);
+ mTarget.handleMotionEvent(false, otherWindow.getDisplayId(), 0, 0);
mToken = otherWindow.mClient.asBinder();
mTarget.reportDropResult(otherIWindow, false);
@@ -586,7 +586,8 @@
ClipData.newPlainText("label", "Test"), () -> {
// Trigger an unhandled drop and verify the global drag listener was called
mTarget.reportDropWindow(mWindow.mInputChannelToken, invalidXY, invalidXY);
- mTarget.handleMotionEvent(false /* keepHandling */, invalidXY, invalidXY);
+ mTarget.handleMotionEvent(false /* keepHandling */, mWindow.getDisplayId(),
+ invalidXY, invalidXY);
mTarget.reportDropResult(mWindow.mClient, false);
mTarget.onUnhandledDropCallback(true);
mToken = null;
@@ -610,7 +611,8 @@
ClipData.newPlainText("label", "Test"), () -> {
// Trigger an unhandled drop and verify the global drag listener was called
mTarget.reportDropWindow(mock(IBinder.class), invalidXY, invalidXY);
- mTarget.handleMotionEvent(false /* keepHandling */, invalidXY, invalidXY);
+ mTarget.handleMotionEvent(false /* keepHandling */, mWindow.getDisplayId(),
+ invalidXY, invalidXY);
mTarget.onUnhandledDropCallback(true);
mToken = null;
try {
@@ -632,7 +634,8 @@
startDrag(View.DRAG_FLAG_GLOBAL, ClipData.newPlainText("label", "Test"), () -> {
// Trigger an unhandled drop and verify the global drag listener was not called
mTarget.reportDropWindow(mock(IBinder.class), invalidXY, invalidXY);
- mTarget.handleMotionEvent(false /* keepHandling */, invalidXY, invalidXY);
+ mTarget.handleMotionEvent(false /* keepHandling */, mDisplayContent.getDisplayId(),
+ invalidXY, invalidXY);
mToken = null;
try {
verify(listener, never()).onUnhandledDrop(any(), any());
@@ -654,7 +657,8 @@
ClipData.newPlainText("label", "Test"), () -> {
// Trigger an unhandled drop and verify the global drag listener was called
mTarget.reportDropWindow(mock(IBinder.class), invalidXY, invalidXY);
- mTarget.handleMotionEvent(false /* keepHandling */, invalidXY, invalidXY);
+ mTarget.handleMotionEvent(false /* keepHandling */,
+ mDisplayContent.getDisplayId(), invalidXY, invalidXY);
// Verify that the unhandled drop listener callback timeout has been scheduled
final Handler handler = mTarget.getHandler();
@@ -673,7 +677,8 @@
private void doDragAndDrop(int flags, ClipData data, float dropX, float dropY) {
startDrag(flags, data, () -> {
mTarget.reportDropWindow(mWindow.mInputChannelToken, dropX, dropY);
- mTarget.handleMotionEvent(false /* keepHandling */, dropX, dropY);
+ mTarget.handleMotionEvent(false /* keepHandling */, mWindow.getDisplayId(), dropX,
+ dropY);
mToken = mWindow.mClient.asBinder();
});
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxAttachInputTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxAttachInputTest.java
index 7e1de47..51e0240 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxAttachInputTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxAttachInputTest.java
@@ -62,6 +62,8 @@
private Letterbox mLetterbox;
private LetterboxTest.SurfaceControlMocker mSurfaces;
+ private WindowState mWindowState;
+
@Before
public void setUp() throws Exception {
mSurfaces = new LetterboxTest.SurfaceControlMocker();
@@ -72,6 +74,7 @@
doReturn(false).when(letterboxOverrides).hasWallpaperBackgroundForLetterbox();
doReturn(0).when(letterboxOverrides).getLetterboxWallpaperBlurRadiusPx();
doReturn(0.5f).when(letterboxOverrides).getLetterboxWallpaperDarkScrimAlpha();
+ mWindowState = createWindowState();
mLetterbox = new Letterbox(mSurfaces, StubTransaction::new,
mock(AppCompatReachabilityPolicy.class), letterboxOverrides,
() -> mock(SurfaceControl.class));
@@ -83,7 +86,6 @@
public void testSurface_createdHasSlipperyInput_scrollingFromLetterboxDisabled() {
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
- attachInput();
applySurfaceChanges();
assertNotNull(mSurfaces.top);
@@ -100,7 +102,6 @@
public void testInputSurface_notCreated_scrollingFromLetterboxDisabled() {
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
- attachInput();
applySurfaceChanges();
assertNull(mSurfaces.topInput);
@@ -111,7 +112,6 @@
public void testSurface_createdHasNoInput_scrollingFromLetterboxEnabled() {
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
- attachInput();
applySurfaceChanges();
assertNotNull(mSurfaces.top);
@@ -124,7 +124,6 @@
public void testInputSurface_createdHasSpyInput_scrollingFromLetterboxEnabled() {
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
- attachInput();
applySurfaceChanges();
assertNotNull(mSurfaces.topInput);
@@ -141,7 +140,6 @@
public void testInputSurfaceOrigin_applied_scrollingFromLetterboxEnabled() {
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
- attachInput();
applySurfaceChanges();
verify(mTransaction).setPosition(mSurfaces.topInput, -1000, -2000);
@@ -152,7 +150,6 @@
public void testInputSurfaceOrigin_changeCausesReapply_scrollingFromLetterboxEnabled() {
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
- attachInput();
applySurfaceChanges();
clearInvocations(mTransaction);
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(0, 0));
@@ -166,13 +163,12 @@
private void applySurfaceChanges() {
mLetterbox.applySurfaceChanges(/* syncTransaction */ mTransaction,
- /* pendingTransaction */ mTransaction);
+ /* pendingTransaction */ mTransaction, mWindowState);
}
- private void attachInput() {
+ private WindowState createWindowState() {
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams();
final WindowToken windowToken = createTestWindowToken(0, mDisplayContent);
- WindowState windowState = createWindowState(attrs, windowToken);
- mLetterbox.attachInput(windowState);
+ return createWindowState(attrs, windowToken);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
index 0baa517..a51a44f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -70,6 +70,7 @@
private SurfaceControl mParentSurface = mock(SurfaceControl.class);
private AppCompatLetterboxOverrides mLetterboxOverrides;
+ private WindowState mWindowState;
@Before
public void setUp() throws Exception {
@@ -81,6 +82,7 @@
doReturn(false).when(mLetterboxOverrides).hasWallpaperBackgroundForLetterbox();
doReturn(0).when(mLetterboxOverrides).getLetterboxWallpaperBlurRadiusPx();
doReturn(0.5f).when(mLetterboxOverrides).getLetterboxWallpaperDarkScrimAlpha();
+ mWindowState = mock(WindowState.class);
mLetterbox = new Letterbox(mSurfaces, StubTransaction::new,
mock(AppCompatReachabilityPolicy.class), mLetterboxOverrides, () -> mParentSurface);
mTransaction = spy(StubTransaction.class);
@@ -320,7 +322,7 @@
private void applySurfaceChanges() {
mLetterbox.applySurfaceChanges(/* syncTransaction */ mTransaction,
- /* pendingTransaction */ mTransaction);
+ /* pendingTransaction */ mTransaction, mWindowState);
}
static class SurfaceControlMocker implements Supplier<SurfaceControl.Builder> {
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 699ed02..7e62b89 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -1348,13 +1348,19 @@
WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
doReturn(rootTask3).when(mRootWindowContainer).getTopDisplayFocusedRootTask();
- // Set up user ids and visibility
+ // Set up child tasks inside root tasks and set some of them visible
+ final Task task1 = new TaskBuilder(mSupervisor).setOnTop(true).setParentTask(
+ rootTask1).build();
+ final Task task2 = new TaskBuilder(mSupervisor).setOnTop(true).setParentTask(
+ rootTask2).build();
+ final Task task3 = new TaskBuilder(mSupervisor).setOnTop(true).setParentTask(
+ rootTask3).build();
rootTask1.mUserId = mRootWindowContainer.mCurrentUser;
rootTask2.mUserId = mRootWindowContainer.mCurrentUser;
rootTask3.mUserId = mRootWindowContainer.mCurrentUser;
- rootTask1.mVisibleRequested = false;
- rootTask2.mVisibleRequested = true;
- rootTask3.mVisibleRequested = true;
+ doReturn(false).when(task1).isVisible();
+ doReturn(true).when(task2).isVisible();
+ doReturn(true).when(task3).isVisible();
// Switch to a different user
int currentUser = mRootWindowContainer.mCurrentUser;
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 201ff51..6a738ae5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -391,7 +391,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_IMMERSIVE_APP_REPOSITIONING)
@DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED})
public void testRepositionLandscapeImmersiveAppWithDisplayCutout() {
final int dw = 2100;
@@ -3783,7 +3782,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_IMMERSIVE_APP_REPOSITIONING)
public void testImmersiveLetterboxAlignedToBottom_OverlappingNavbar() {
assertLandscapeActivityAlignedToBottomWithNavbar(true /* immersive */);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 0cd036f..19c1ce2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -741,16 +741,16 @@
// Not allowed because TaskFragments are not organized by the caller organizer.
assertApplyTransactionDisallowed(mTransaction);
- assertNull(mTaskFragment.getAdjacentTaskFragment());
- assertNull(taskFragment2.getAdjacentTaskFragment());
+ assertFalse(mTaskFragment.hasAdjacentTaskFragment());
+ assertFalse(taskFragment2.hasAdjacentTaskFragment());
mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
"Test:TaskFragmentOrganizer" /* processName */);
// Not allowed because TaskFragment2 is not organized by the caller organizer.
assertApplyTransactionDisallowed(mTransaction);
- assertNull(mTaskFragment.getAdjacentTaskFragment());
- assertNull(taskFragment2.getAdjacentTaskFragment());
+ assertFalse(mTaskFragment.hasAdjacentTaskFragment());
+ assertFalse(taskFragment2.hasAdjacentTaskFragment());
mTaskFragment.onTaskFragmentOrganizerRemoved();
taskFragment2.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
@@ -758,14 +758,14 @@
// Not allowed because mTaskFragment is not organized by the caller organizer.
assertApplyTransactionDisallowed(mTransaction);
- assertNull(mTaskFragment.getAdjacentTaskFragment());
- assertNull(taskFragment2.getAdjacentTaskFragment());
+ assertFalse(mTaskFragment.hasAdjacentTaskFragment());
+ assertFalse(taskFragment2.hasAdjacentTaskFragment());
mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
"Test:TaskFragmentOrganizer" /* processName */);
assertApplyTransactionAllowed(mTransaction);
- assertEquals(taskFragment2, mTaskFragment.getAdjacentTaskFragment());
+ assertTrue(mTaskFragment.isAdjacentTo(taskFragment2));
}
@Test
@@ -790,14 +790,14 @@
// Not allowed because TaskFragment is not organized by the caller organizer.
assertApplyTransactionDisallowed(mTransaction);
- assertEquals(taskFragment2, mTaskFragment.getAdjacentTaskFragment());
+ assertTrue(mTaskFragment.isAdjacentTo(taskFragment2));
mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
"Test:TaskFragmentOrganizer" /* processName */);
assertApplyTransactionAllowed(mTransaction);
- assertNull(mTaskFragment.getAdjacentTaskFragment());
- assertNull(taskFragment2.getAdjacentTaskFragment());
+ assertFalse(mTaskFragment.hasAdjacentTaskFragment());
+ assertFalse(taskFragment2.hasAdjacentTaskFragment());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index dafa96f..35a2546 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -365,7 +365,7 @@
assertEquals(taskFragmentBounds, activity.getBounds());
assertEquals(WINDOWING_MODE_MULTI_WINDOW, activity.getWindowingMode());
- assertEquals(taskFragment1, taskFragment0.getAdjacentTaskFragment());
+ assertTrue(taskFragment0.isAdjacentTo(taskFragment1));
assertEquals(taskFragment1, taskFragment0.getCompanionTaskFragment());
assertNotEquals(TaskFragmentAnimationParams.DEFAULT, taskFragment0.getAnimationParams());
@@ -381,7 +381,7 @@
assertEquals(taskBounds, taskFragment0.getBounds());
assertEquals(taskBounds, activity.getBounds());
assertEquals(Configuration.EMPTY, taskFragment0.getRequestedOverrideConfiguration());
- assertNull(taskFragment0.getAdjacentTaskFragment());
+ assertFalse(taskFragment0.hasAdjacentTaskFragment());
assertNull(taskFragment0.getCompanionTaskFragment());
assertEquals(TaskFragmentAnimationParams.DEFAULT, taskFragment0.getAnimationParams());
// Because the whole Task is entering PiP, no need to record for future reparent.
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index 6655932..c6b2a6b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -33,6 +33,7 @@
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -45,12 +46,15 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.util.ArraySet;
import android.window.TaskSnapshot;
import androidx.test.filters.SmallTest;
+import com.android.window.flags.Flags;
+
import com.google.android.collect.Sets;
import org.junit.Test;
@@ -285,4 +289,27 @@
assertFalse(success);
}
+
+ @Test
+ @EnableFlags(Flags.FLAG_EXCLUDE_DRAWING_APP_THEME_SNAPSHOT_FROM_LOCK)
+ public void testRecordTaskSnapshot() {
+ spyOn(mWm.mTaskSnapshotController.mCache);
+ spyOn(mWm.mTaskSnapshotController);
+ doReturn(false).when(mWm.mTaskSnapshotController).shouldDisableSnapshots();
+
+ final WindowState normalWindow = createWindow(null,
+ FIRST_APPLICATION_WINDOW, mDisplayContent, "normalWindow");
+ final TaskSnapshot snapshot = new TaskSnapshotPersisterTestBase.TaskSnapshotBuilder()
+ .setTopActivityComponent(normalWindow.mActivityRecord.mActivityComponent).build();
+ doReturn(snapshot).when(mWm.mTaskSnapshotController).snapshot(any());
+ final Task task = normalWindow.mActivityRecord.getTask();
+ mWm.mTaskSnapshotController.recordSnapshot(task);
+ verify(mWm.mTaskSnapshotController.mCache).putSnapshot(eq(task), any());
+ clearInvocations(mWm.mTaskSnapshotController.mCache);
+
+ normalWindow.mAttrs.flags |= FLAG_SECURE;
+ mWm.mTaskSnapshotController.recordSnapshot(task);
+ waitHandlerIdle(mWm.mH);
+ verify(mWm.mTaskSnapshotController.mCache).putSnapshot(eq(task), any());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index da4c522..1281be51 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -909,8 +909,8 @@
WindowContainerTransaction wct = new WindowContainerTransaction();
wct.setAdjacentRoots(info1.token, info2.token);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
- assertEquals(task1.getAdjacentTaskFragment(), task2);
- assertEquals(task2.getAdjacentTaskFragment(), task1);
+ assertTrue(task1.isAdjacentTo(task2));
+ assertTrue(task2.isAdjacentTo(task1));
wct = new WindowContainerTransaction();
wct.setLaunchAdjacentFlagRoot(info1.token);
@@ -921,8 +921,8 @@
wct.clearAdjacentRoots(info1.token);
wct.clearLaunchAdjacentFlagRoot(info1.token);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
- assertEquals(task1.getAdjacentTaskFragment(), null);
- assertEquals(task2.getAdjacentTaskFragment(), null);
+ assertFalse(task1.hasAdjacentTaskFragment());
+ assertFalse(task2.hasAdjacentTaskFragment());
assertEquals(dc.getDefaultTaskDisplayArea().mLaunchAdjacentFlagRootTask, null);
}
diff --git a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
index 49616c3..8ac3433 100644
--- a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
@@ -765,14 +765,14 @@
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(e);
}
- watchdog.registerHealthObserver(rollbackObserver, mTestExecutor);
+ watchdog.registerHealthObserver(mTestExecutor, rollbackObserver);
return rollbackObserver;
}
RescuePartyObserver setUpRescuePartyObserver(PackageWatchdog watchdog) {
setCrashRecoveryPropRescueBootCount(0);
RescuePartyObserver rescuePartyObserver = spy(RescuePartyObserver.getInstance(mSpyContext));
assertFalse(RescueParty.isRebootPropertySet());
- watchdog.registerHealthObserver(rescuePartyObserver, mTestExecutor);
+ watchdog.registerHealthObserver(mTestExecutor, rescuePartyObserver);
return rescuePartyObserver;
}
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index 928e232..1c50cb1 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -54,7 +54,6 @@
import android.provider.DeviceConfig;
import android.util.AtomicFile;
import android.util.LongArrayQueue;
-import android.util.Slog;
import android.util.Xml;
import androidx.test.InstrumentationRegistry;
@@ -231,8 +230,8 @@
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.registerHealthObserver(observer, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, observer);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
@@ -248,10 +247,10 @@
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
- watchdog.registerHealthObserver(observer1, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.registerHealthObserver(observer2, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer2, Arrays.asList(APP_A, APP_B), SHORT_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, observer1);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer1);
+ watchdog.registerHealthObserver(mTestExecutor, observer2);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A, APP_B), SHORT_DURATION, observer2);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE),
new VersionedPackage(APP_B, VERSION_CODE)),
@@ -268,8 +267,8 @@
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.registerHealthObserver(observer, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, observer);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer);
watchdog.unregisterHealthObserver(observer);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
@@ -285,10 +284,10 @@
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
- watchdog.registerHealthObserver(observer1, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.registerHealthObserver(observer2, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer2, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, observer1);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer1);
+ watchdog.registerHealthObserver(mTestExecutor, observer2);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer2);
watchdog.unregisterHealthObserver(observer2);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
@@ -305,8 +304,8 @@
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.registerHealthObserver(observer, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, observer);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer);
moveTimeForwardAndDispatch(SHORT_DURATION);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
@@ -322,10 +321,10 @@
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
- watchdog.registerHealthObserver(observer1, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.registerHealthObserver(observer2, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer2, Arrays.asList(APP_A), LONG_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, observer1);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer1);
+ watchdog.registerHealthObserver(mTestExecutor, observer2);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), LONG_DURATION, observer2);
moveTimeForwardAndDispatch(SHORT_DURATION);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
@@ -344,14 +343,14 @@
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
// Start observing APP_A
- watchdog.registerHealthObserver(observer, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, observer);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer);
// Then advance time half-way
moveTimeForwardAndDispatch(SHORT_DURATION / 2);
// Start observing APP_A again
- watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer);
// Then advance time such that it should have expired were it not for the second observation
moveTimeForwardAndDispatch((SHORT_DURATION / 2) + 1);
@@ -373,17 +372,17 @@
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
- watchdog1.registerHealthObserver(observer1, mTestExecutor);
- watchdog1.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog1.registerHealthObserver(observer2, mTestExecutor);
- watchdog1.startExplicitHealthCheck(observer2, Arrays.asList(APP_A, APP_B), SHORT_DURATION);
+ watchdog1.registerHealthObserver(mTestExecutor, observer1);
+ watchdog1.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer1);
+ watchdog1.registerHealthObserver(mTestExecutor, observer2);
+ watchdog1.startExplicitHealthCheck(Arrays.asList(APP_A, APP_B), SHORT_DURATION, observer2);
// Then advance time and run IO Handler so file is saved
mTestLooper.dispatchAll();
// Then start a new watchdog
PackageWatchdog watchdog2 = createWatchdog();
// Then resume observer1 and observer2
- watchdog2.registerHealthObserver(observer1, mTestExecutor);
- watchdog2.registerHealthObserver(observer2, mTestExecutor);
+ watchdog2.registerHealthObserver(mTestExecutor, observer1);
+ watchdog2.registerHealthObserver(mTestExecutor, observer2);
raiseFatalFailureAndDispatch(watchdog2,
Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE),
new VersionedPackage(APP_B, VERSION_CODE)),
@@ -405,10 +404,10 @@
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
- watchdog.registerHealthObserver(observer2, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer2, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.registerHealthObserver(observer1, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, observer2);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer2);
+ watchdog.registerHealthObserver(mTestExecutor, observer1);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer1);
// Then fail APP_A below the threshold
for (int i = 0; i < watchdog.getTriggerFailureCount() - 1; i++) {
@@ -434,10 +433,10 @@
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
- watchdog.registerHealthObserver(observer2, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer2, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.registerHealthObserver(observer1, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_B), SHORT_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, observer2);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer2);
+ watchdog.registerHealthObserver(mTestExecutor, observer1);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_B), SHORT_DURATION, observer1);
// Then fail APP_C (not observed) above the threshold
raiseFatalFailureAndDispatch(watchdog,
@@ -469,8 +468,8 @@
}
};
- watchdog.registerHealthObserver(observer, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, observer);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer);
// Then fail APP_A (different version) above the threshold
raiseFatalFailureAndDispatch(watchdog,
@@ -499,18 +498,17 @@
PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
// Start observing for all impact observers
- watchdog.registerHealthObserver(observerNone, mTestExecutor);
- watchdog.startExplicitHealthCheck(observerNone, Arrays.asList(APP_A, APP_B, APP_C, APP_D),
- SHORT_DURATION);
- watchdog.registerHealthObserver(observerHigh, mTestExecutor);
- watchdog.startExplicitHealthCheck(observerHigh, Arrays.asList(APP_A, APP_B, APP_C),
- SHORT_DURATION);
- watchdog.registerHealthObserver(observerMid, mTestExecutor);
- watchdog.startExplicitHealthCheck(observerMid, Arrays.asList(APP_A, APP_B),
- SHORT_DURATION);
- watchdog.registerHealthObserver(observerLow, mTestExecutor);
- watchdog.startExplicitHealthCheck(observerLow, Arrays.asList(APP_A),
- SHORT_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, observerNone);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A, APP_B, APP_C, APP_D),
+ SHORT_DURATION, observerNone);
+ watchdog.registerHealthObserver(mTestExecutor, observerHigh);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A, APP_B, APP_C), SHORT_DURATION,
+ observerHigh);
+ watchdog.registerHealthObserver(mTestExecutor, observerMid);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A, APP_B), SHORT_DURATION,
+ observerMid);
+ watchdog.registerHealthObserver(mTestExecutor, observerLow);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observerLow);
// Then fail all apps above the threshold
raiseFatalFailureAndDispatch(watchdog,
@@ -549,18 +547,17 @@
PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
// Start observing for all impact observers
- watchdog.registerHealthObserver(observerNone, mTestExecutor);
- watchdog.startExplicitHealthCheck(observerNone, Arrays.asList(APP_A, APP_B, APP_C, APP_D),
- SHORT_DURATION);
- watchdog.registerHealthObserver(observerHigh, mTestExecutor);
- watchdog.startExplicitHealthCheck(observerHigh, Arrays.asList(APP_A, APP_B, APP_C),
- SHORT_DURATION);
- watchdog.registerHealthObserver(observerMid, mTestExecutor);
- watchdog.startExplicitHealthCheck(observerMid, Arrays.asList(APP_A, APP_B),
- SHORT_DURATION);
- watchdog.registerHealthObserver(observerLow, mTestExecutor);
- watchdog.startExplicitHealthCheck(observerLow, Arrays.asList(APP_A),
- SHORT_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, observerNone);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A, APP_B, APP_C, APP_D),
+ SHORT_DURATION, observerNone);
+ watchdog.registerHealthObserver(mTestExecutor, observerHigh);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A, APP_B, APP_C), SHORT_DURATION,
+ observerHigh);
+ watchdog.registerHealthObserver(mTestExecutor, observerMid);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A, APP_B), SHORT_DURATION,
+ observerMid);
+ watchdog.registerHealthObserver(mTestExecutor, observerLow);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observerLow);
// Then fail all apps above the threshold
raiseFatalFailureAndDispatch(watchdog,
@@ -607,10 +604,10 @@
PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
// Start observing for observerFirst and observerSecond with failure handling
- watchdog.registerHealthObserver(observerFirst, mTestExecutor);
- watchdog.startExplicitHealthCheck(observerFirst, Arrays.asList(APP_A), LONG_DURATION);
- watchdog.registerHealthObserver(observerSecond, mTestExecutor);
- watchdog.startExplicitHealthCheck(observerSecond, Arrays.asList(APP_A), LONG_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, observerFirst);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), LONG_DURATION, observerFirst);
+ watchdog.registerHealthObserver(mTestExecutor, observerSecond);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), LONG_DURATION, observerSecond);
// Then fail APP_A above the threshold
raiseFatalFailureAndDispatch(watchdog,
@@ -673,10 +670,10 @@
PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
// Start observing for observerFirst and observerSecond with failure handling
- watchdog.registerHealthObserver(observerFirst, mTestExecutor);
- watchdog.startExplicitHealthCheck(observerFirst, Arrays.asList(APP_A), LONG_DURATION);
- watchdog.registerHealthObserver(observerSecond, mTestExecutor);
- watchdog.startExplicitHealthCheck(observerSecond, Arrays.asList(APP_A), LONG_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, observerFirst);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), LONG_DURATION, observerFirst);
+ watchdog.registerHealthObserver(mTestExecutor, observerSecond);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), LONG_DURATION, observerSecond);
// Then fail APP_A above the threshold
raiseFatalFailureAndDispatch(watchdog,
@@ -743,10 +740,10 @@
PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
// Start observing for observer1 and observer2 with failure handling
- watchdog.registerHealthObserver(observer2, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer2, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.registerHealthObserver(observer1, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, observer2);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer2);
+ watchdog.registerHealthObserver(mTestExecutor, observer1);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer1);
// Then fail APP_A above the threshold
raiseFatalFailureAndDispatch(watchdog,
@@ -767,10 +764,10 @@
PackageHealthObserverImpact.USER_IMPACT_LEVEL_50);
// Start observing for observer1 and observer2 with failure handling
- watchdog.registerHealthObserver(observer2, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer2, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.registerHealthObserver(observer1, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, observer2);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer2);
+ watchdog.registerHealthObserver(mTestExecutor, observer1);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer1);
// Then fail APP_A above the threshold
raiseFatalFailureAndDispatch(watchdog,
@@ -800,10 +797,10 @@
// Start observing with explicit health checks for APP_A and APP_B respectively
// with observer1 and observer2
controller.setSupportedPackages(Arrays.asList(APP_A, APP_B));
- watchdog.registerHealthObserver(observer1, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.registerHealthObserver(observer2, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer2, Arrays.asList(APP_B), SHORT_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, observer1);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer1);
+ watchdog.registerHealthObserver(mTestExecutor, observer2);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_B), SHORT_DURATION, observer2);
// Run handler so requests are dispatched to the controller
mTestLooper.dispatchAll();
@@ -819,8 +816,8 @@
// Observer3 didn't exist when we got the explicit health check above, so
// it starts out with a non-passing explicit health check and has to wait for a pass
// otherwise it would be notified of APP_A failure on expiry
- watchdog.registerHealthObserver(observer3, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer3, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, observer3);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer3);
// Then expire observers
moveTimeForwardAndDispatch(SHORT_DURATION);
@@ -850,9 +847,9 @@
// Start observing with explicit health checks for APP_A and APP_B
controller.setSupportedPackages(Arrays.asList(APP_A, APP_B, APP_C));
- watchdog.registerHealthObserver(observer, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_B), LONG_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, observer);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_B), LONG_DURATION, observer);
// Run handler so requests are dispatched to the controller
mTestLooper.dispatchAll();
@@ -888,7 +885,7 @@
// Then set new supported packages
controller.setSupportedPackages(Arrays.asList(APP_C));
// Start observing APP_A and APP_C; only APP_C has support for explicit health checks
- watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A, APP_C), SHORT_DURATION);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A, APP_C), SHORT_DURATION, observer);
// Run handler so requests/cancellations are dispatched to the controller
mTestLooper.dispatchAll();
@@ -919,8 +916,8 @@
// package observation duration == LONG_DURATION
// health check duration == SHORT_DURATION (set by default in the TestController)
controller.setSupportedPackages(Arrays.asList(APP_A));
- watchdog.registerHealthObserver(observer, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), LONG_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, observer);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), LONG_DURATION, observer);
// Then APP_A has exceeded health check duration
moveTimeForwardAndDispatch(SHORT_DURATION);
@@ -951,8 +948,8 @@
// package observation duration == SHORT_DURATION / 2
// health check duration == SHORT_DURATION (set by default in the TestController)
controller.setSupportedPackages(Arrays.asList(APP_A));
- watchdog.registerHealthObserver(observer, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), SHORT_DURATION / 2);
+ watchdog.registerHealthObserver(mTestExecutor, observer);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION / 2, observer);
// Forward time to expire the observation duration
moveTimeForwardAndDispatch(SHORT_DURATION / 2);
@@ -1025,7 +1022,7 @@
// Start observing with failure handling
TestObserver observer = new TestObserver(OBSERVER_NAME_1,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
- wd.startExplicitHealthCheck(observer, Collections.singletonList(APP_A), SHORT_DURATION);
+ wd.startExplicitHealthCheck(Collections.singletonList(APP_A), SHORT_DURATION, observer);
// Notify of NetworkStack failure
mConnectivityModuleCallbackCaptor.getValue().onNetworkStackFailure(APP_A);
@@ -1045,7 +1042,7 @@
// Start observing with failure handling
TestObserver observer = new TestObserver(OBSERVER_NAME_1,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
- wd.startExplicitHealthCheck(observer, Collections.singletonList(APP_A), SHORT_DURATION);
+ wd.startExplicitHealthCheck(Collections.singletonList(APP_A), SHORT_DURATION, observer);
// Notify of NetworkStack failure
mConnectivityModuleCallbackCaptor.getValue().onNetworkStackFailure(APP_A);
@@ -1066,8 +1063,8 @@
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.registerHealthObserver(observer, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, observer);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer);
// Fail APP_A below the threshold which should not trigger package failures
for (int i = 0; i < PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT - 1; i++) {
watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
@@ -1095,8 +1092,8 @@
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.registerHealthObserver(observer, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A, APP_B), Long.MAX_VALUE);
+ watchdog.registerHealthObserver(mTestExecutor, observer);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A, APP_B), Long.MAX_VALUE, observer);
watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_TRIGGER_FAILURE_DURATION_MS + 1);
@@ -1129,8 +1126,8 @@
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.registerHealthObserver(observer, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), -1);
+ watchdog.registerHealthObserver(mTestExecutor, observer);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), -1, observer);
// Note: Don't move too close to the expiration time otherwise the handler will be thrashed
// by PackageWatchdog#scheduleNextSyncStateLocked which keeps posting runnables with very
// small timeouts.
@@ -1152,8 +1149,8 @@
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.registerHealthObserver(observer, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), -1);
+ watchdog.registerHealthObserver(mTestExecutor, observer);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), -1, observer);
moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_OBSERVING_DURATION_MS + 1);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
@@ -1175,8 +1172,8 @@
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.registerHealthObserver(observer, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), Long.MAX_VALUE);
+ watchdog.registerHealthObserver(mTestExecutor, observer);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), Long.MAX_VALUE, observer);
// Raise 2 failures at t=0 and t=900 respectively
watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
@@ -1203,10 +1200,10 @@
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
- watchdog.registerHealthObserver(observer1, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.registerHealthObserver(observer2, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer2, Arrays.asList(APP_B), SHORT_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, observer1);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer1);
+ watchdog.registerHealthObserver(mTestExecutor, observer2);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_B), SHORT_DURATION, observer2);
raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
VERSION_CODE)), PackageWatchdog.FAILURE_REASON_APP_CRASH);
@@ -1225,8 +1222,8 @@
PackageWatchdog watchdog = createWatchdog();
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
- watchdog.registerHealthObserver(observer1, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, observer1);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer1);
raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
VERSION_CODE)), PackageWatchdog.FAILURE_REASON_NATIVE_CRASH);
@@ -1246,8 +1243,8 @@
persistentObserver.setPersistent(true);
persistentObserver.setMayObservePackages(true);
- watchdog.registerHealthObserver(persistentObserver, mTestExecutor);
- watchdog.startExplicitHealthCheck(persistentObserver, Arrays.asList(APP_B), SHORT_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, persistentObserver);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_B), SHORT_DURATION, persistentObserver);
raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN);
@@ -1265,8 +1262,8 @@
persistentObserver.setPersistent(true);
persistentObserver.setMayObservePackages(false);
- watchdog.registerHealthObserver(persistentObserver, mTestExecutor);
- watchdog.startExplicitHealthCheck(persistentObserver, Arrays.asList(APP_B), SHORT_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, persistentObserver);
+ watchdog.startExplicitHealthCheck(Arrays.asList(APP_B), SHORT_DURATION, persistentObserver);
raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN);
@@ -1277,11 +1274,10 @@
/** Ensure that boot loop mitigation is done when the number of boots meets the threshold. */
@Test
public void testBootLoopDetection_meetsThreshold() {
- Slog.w("hrm1243", "I should definitely be here try 1 ");
mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
PackageWatchdog watchdog = createWatchdog();
TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1);
- watchdog.registerHealthObserver(bootObserver, mTestExecutor);
+ watchdog.registerHealthObserver(mTestExecutor, bootObserver);
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
watchdog.noteBoot();
}
@@ -1293,7 +1289,7 @@
public void testBootLoopDetection_meetsThresholdRecoverability() {
PackageWatchdog watchdog = createWatchdog();
TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1);
- watchdog.registerHealthObserver(bootObserver, mTestExecutor);
+ watchdog.registerHealthObserver(mTestExecutor, bootObserver);
for (int i = 0; i < 15; i++) {
watchdog.noteBoot();
}
@@ -1309,7 +1305,7 @@
public void testBootLoopDetection_doesNotMeetThreshold() {
PackageWatchdog watchdog = createWatchdog();
TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1);
- watchdog.registerHealthObserver(bootObserver, mTestExecutor);
+ watchdog.registerHealthObserver(mTestExecutor, bootObserver);
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT - 1; i++) {
watchdog.noteBoot();
}
@@ -1326,7 +1322,7 @@
PackageWatchdog watchdog = createWatchdog();
TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
- watchdog.registerHealthObserver(bootObserver, mTestExecutor);
+ watchdog.registerHealthObserver(mTestExecutor, bootObserver);
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT - 1; i++) {
watchdog.noteBoot();
}
@@ -1345,8 +1341,8 @@
bootObserver1.setImpact(PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
TestObserver bootObserver2 = new TestObserver(OBSERVER_NAME_2);
bootObserver2.setImpact(PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
- watchdog.registerHealthObserver(bootObserver1, mTestExecutor);
- watchdog.registerHealthObserver(bootObserver2, mTestExecutor);
+ watchdog.registerHealthObserver(mTestExecutor, bootObserver1);
+ watchdog.registerHealthObserver(mTestExecutor, bootObserver2);
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
watchdog.noteBoot();
}
@@ -1362,8 +1358,8 @@
bootObserver1.setImpact(PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
TestObserver bootObserver2 = new TestObserver(OBSERVER_NAME_2);
bootObserver2.setImpact(PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
- watchdog.registerHealthObserver(bootObserver1, mTestExecutor);
- watchdog.registerHealthObserver(bootObserver2, mTestExecutor);
+ watchdog.registerHealthObserver(mTestExecutor, bootObserver1);
+ watchdog.registerHealthObserver(mTestExecutor, bootObserver2);
for (int i = 0; i < 15; i++) {
watchdog.noteBoot();
}
@@ -1380,7 +1376,7 @@
mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
PackageWatchdog watchdog = createWatchdog();
TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1);
- watchdog.registerHealthObserver(bootObserver, mTestExecutor);
+ watchdog.registerHealthObserver(mTestExecutor, bootObserver);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; j++) {
watchdog.noteBoot();
@@ -1403,7 +1399,7 @@
PackageWatchdog watchdog = createWatchdog();
TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
- watchdog.registerHealthObserver(bootObserver, mTestExecutor);
+ watchdog.registerHealthObserver(mTestExecutor, bootObserver);
for (int j = 0; j < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT - 1; j++) {
watchdog.noteBoot();
}
@@ -1431,8 +1427,8 @@
public void testNullFailedPackagesList() {
PackageWatchdog watchdog = createWatchdog();
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
- watchdog.registerHealthObserver(observer1, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer1, List.of(APP_A), LONG_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, observer1);
+ watchdog.startExplicitHealthCheck(List.of(APP_A), LONG_DURATION, observer1);
raiseFatalFailureAndDispatch(watchdog, null, PackageWatchdog.FAILURE_REASON_APP_CRASH);
assertThat(observer1.mMitigatedPackages).isEmpty();
@@ -1450,18 +1446,18 @@
PackageWatchdog watchdog = createWatchdog(testController, true);
TestObserver testObserver1 = new TestObserver(OBSERVER_NAME_1);
- watchdog.registerHealthObserver(testObserver1, mTestExecutor);
- watchdog.startExplicitHealthCheck(testObserver1, List.of(APP_A), LONG_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, testObserver1);
+ watchdog.startExplicitHealthCheck(List.of(APP_A), LONG_DURATION, testObserver1);
mTestLooper.dispatchAll();
TestObserver testObserver2 = new TestObserver(OBSERVER_NAME_2);
- watchdog.registerHealthObserver(testObserver2, mTestExecutor);
- watchdog.startExplicitHealthCheck(testObserver2, List.of(APP_B), LONG_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, testObserver2);
+ watchdog.startExplicitHealthCheck(List.of(APP_B), LONG_DURATION, testObserver2);
mTestLooper.dispatchAll();
TestObserver testObserver3 = new TestObserver(OBSERVER_NAME_3);
- watchdog.registerHealthObserver(testObserver3, mTestExecutor);
- watchdog.startExplicitHealthCheck(testObserver3, List.of(APP_C), LONG_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, testObserver3);
+ watchdog.startExplicitHealthCheck(List.of(APP_C), LONG_DURATION, testObserver3);
mTestLooper.dispatchAll();
watchdog.unregisterHealthObserver(testObserver1);
@@ -1493,15 +1489,15 @@
public void testFailureHistoryIsPreserved() {
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.registerHealthObserver(observer, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer, List.of(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(mTestExecutor, observer);
+ watchdog.startExplicitHealthCheck(List.of(APP_A), SHORT_DURATION, observer);
for (int i = 0; i < PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT - 1; i++) {
watchdog.notifyPackageFailure(List.of(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
}
mTestLooper.dispatchAll();
assertThat(observer.mMitigatedPackages).isEmpty();
- watchdog.startExplicitHealthCheck(observer, List.of(APP_A), LONG_DURATION);
+ watchdog.startExplicitHealthCheck(List.of(APP_A), LONG_DURATION, observer);
watchdog.notifyPackageFailure(List.of(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
mTestLooper.dispatchAll();
@@ -1516,9 +1512,9 @@
public void testMitigationSlidingWindow() {
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.registerHealthObserver(observer, mTestExecutor);
- watchdog.startExplicitHealthCheck(observer, List.of(APP_A),
- PackageWatchdog.DEFAULT_OBSERVING_DURATION_MS * 2);
+ watchdog.registerHealthObserver(mTestExecutor, observer);
+ watchdog.startExplicitHealthCheck(List.of(APP_A),
+ PackageWatchdog.DEFAULT_OBSERVING_DURATION_MS * 2, observer);
raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,