Merge "Use aconfig flag for communal hub" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index b924ac8..c8046ab 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -152,6 +152,11 @@
java_aconfig_library {
name: "android.nfc.flags-aconfig-java",
aconfig_declarations: "android.nfc.flags-aconfig",
+ min_sdk_version: "VanillaIceCream",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.nfcservices",
+ ],
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
@@ -289,6 +294,12 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+rust_aconfig_library {
+ name: "libandroid_security_flags_rust",
+ crate_name: "android_security_flags",
+ aconfig_declarations: "android.security.flags-aconfig",
+}
+
// Package Manager
aconfig_declarations {
name: "android.content.pm.flags-aconfig",
diff --git a/ProtoLibraries.bp b/ProtoLibraries.bp
index 45bb161..e7adf20 100644
--- a/ProtoLibraries.bp
+++ b/ProtoLibraries.bp
@@ -77,6 +77,42 @@
output_extension: "proto.h",
}
+// ==== nfc framework java library ==============================
+gensrcs {
+ name: "framework-nfc-javastream-protos",
+
+ tools: [
+ "aprotoc",
+ "protoc-gen-javastream",
+ "soong_zip",
+ ],
+
+ cmd: "mkdir -p $(genDir)/$(in) " +
+ "&& $(location aprotoc) " +
+ " --plugin=$(location protoc-gen-javastream) " +
+ " --javastream_out=$(genDir)/$(in) " +
+ " -Iexternal/protobuf/src " +
+ " -I . " +
+ " $(in) " +
+ "&& $(location soong_zip) -jar -o $(out) -C $(genDir)/$(in) -D $(genDir)/$(in)",
+
+ srcs: [
+ "core/proto/android/app/pendingintent.proto",
+ "core/proto/android/content/component_name.proto",
+ "core/proto/android/content/intent.proto",
+ "core/proto/android/nfc/*.proto",
+ "core/proto/android/os/patternmatcher.proto",
+ "core/proto/android/os/persistablebundle.proto",
+ "core/proto/android/privacy.proto",
+ ],
+
+ data: [
+ ":libprotobuf-internal-protos",
+ ],
+
+ output_extension: "srcjar",
+}
+
// ==== java proto host library ==============================
java_library_host {
name: "platformprotos",
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 a143d6f..bbe1485 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1555,7 +1555,7 @@
private final Predicate<Integer> mIsUidActivePredicate = this::isUidActive;
- public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
+ public int scheduleAsPackage(JobInfo job, JobWorkItem work, int callingUid, String packageName,
int userId, @Nullable String namespace, String tag) {
// Rate limit excessive schedule() calls.
final String servicePkg = job.getService().getPackageName();
@@ -1608,12 +1608,12 @@
mQuotaTracker.noteEvent(userId, pkg, QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG);
}
- if (mActivityManagerInternal.isAppStartModeDisabled(uId, servicePkg)) {
- Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
+ if (mActivityManagerInternal.isAppStartModeDisabled(callingUid, servicePkg)) {
+ Slog.w(TAG, "Not scheduling job for " + callingUid + ":" + job.toString()
+ " -- package not allowed to start");
Counter.logIncrementWithUid(
"job_scheduler.value_cntr_w_uid_schedule_failure_app_start_mode_disabled",
- uId);
+ callingUid);
return JobScheduler.RESULT_FAILURE;
}
@@ -1623,7 +1623,7 @@
job.getEstimatedNetworkDownloadBytes()));
sInitialJobEstimatedNetworkUploadKBLogger.logSample(
safelyScaleBytesToKBForHistogram(job.getEstimatedNetworkUploadBytes()));
- sJobMinimumChunkKBLogger.logSampleWithUid(uId,
+ sJobMinimumChunkKBLogger.logSampleWithUid(callingUid,
safelyScaleBytesToKBForHistogram(job.getMinimumNetworkChunkBytes()));
if (work != null) {
sInitialJwiEstimatedNetworkDownloadKBLogger.logSample(
@@ -1632,7 +1632,7 @@
sInitialJwiEstimatedNetworkUploadKBLogger.logSample(
safelyScaleBytesToKBForHistogram(
work.getEstimatedNetworkUploadBytes()));
- sJwiMinimumChunkKBLogger.logSampleWithUid(uId,
+ sJwiMinimumChunkKBLogger.logSampleWithUid(callingUid,
safelyScaleBytesToKBForHistogram(
work.getMinimumNetworkChunkBytes()));
}
@@ -1640,11 +1640,12 @@
if (work != null) {
Counter.logIncrementWithUid(
- "job_scheduler.value_cntr_w_uid_job_work_items_enqueued", uId);
+ "job_scheduler.value_cntr_w_uid_job_work_items_enqueued", callingUid);
}
synchronized (mLock) {
- final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, namespace, job.getId());
+ final JobStatus toCancel =
+ mJobs.getJobByUidAndJobId(callingUid, namespace, job.getId());
if (work != null && toCancel != null) {
// Fast path: we are adding work to an existing job, and the JobInfo is not
@@ -1664,7 +1665,7 @@
// TODO(273758274): improve JobScheduler's resilience and memory management
if (toCancel.getWorkCount() >= mConstants.MAX_NUM_PERSISTED_JOB_WORK_ITEMS
&& toCancel.isPersisted()) {
- Slog.w(TAG, "Too many JWIs for uid " + uId);
+ Slog.w(TAG, "Too many JWIs for uid " + callingUid);
throw new IllegalStateException("Apps may not persist more than "
+ mConstants.MAX_NUM_PERSISTED_JOB_WORK_ITEMS
+ " JobWorkItems per job");
@@ -1682,7 +1683,8 @@
| JobStatus.INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ);
}
mJobs.touchJob(toCancel);
- sEnqueuedJwiHighWaterMarkLogger.logSampleWithUid(uId, toCancel.getWorkCount());
+ sEnqueuedJwiHighWaterMarkLogger
+ .logSampleWithUid(callingUid, toCancel.getWorkCount());
// If any of work item is enqueued when the source is in the foreground,
// exempt the entire job.
@@ -1692,8 +1694,8 @@
}
}
- JobStatus jobStatus =
- JobStatus.createFromJobInfo(job, uId, packageName, userId, namespace, tag);
+ JobStatus jobStatus = JobStatus.createFromJobInfo(
+ job, callingUid, packageName, userId, namespace, tag);
// Return failure early if expedited job quota used up.
if (jobStatus.isRequestedExpeditedJob()) {
@@ -1702,7 +1704,7 @@
&& !mQuotaController.isWithinEJQuotaLocked(jobStatus))) {
Counter.logIncrementWithUid(
"job_scheduler.value_cntr_w_uid_schedule_failure_ej_out_of_quota",
- uId);
+ callingUid);
return JobScheduler.RESULT_FAILURE;
}
}
@@ -1716,10 +1718,10 @@
if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
// Jobs on behalf of others don't apply to the per-app job cap
if (packageName == null) {
- if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
- Slog.w(TAG, "Too many jobs for uid " + uId);
+ if (mJobs.countJobsForUid(callingUid) > MAX_JOBS_PER_APP) {
+ Slog.w(TAG, "Too many jobs for uid " + callingUid);
Counter.logIncrementWithUid(
- "job_scheduler.value_cntr_w_uid_max_scheduling_limit_hit", uId);
+ "job_scheduler.value_cntr_w_uid_max_scheduling_limit_hit", callingUid);
throw new IllegalStateException("Apps may not schedule more than "
+ MAX_JOBS_PER_APP + " distinct jobs");
}
@@ -1743,7 +1745,7 @@
// TODO(273758274): improve JobScheduler's resilience and memory management
if (work != null && toCancel.isPersisted()
&& toCancel.getWorkCount() >= mConstants.MAX_NUM_PERSISTED_JOB_WORK_ITEMS) {
- Slog.w(TAG, "Too many JWIs for uid " + uId);
+ Slog.w(TAG, "Too many JWIs for uid " + callingUid);
throw new IllegalStateException("Apps may not persist more than "
+ mConstants.MAX_NUM_PERSISTED_JOB_WORK_ITEMS
+ " JobWorkItems per job");
@@ -1759,13 +1761,14 @@
if (work != null) {
// If work has been supplied, enqueue it into the new job.
jobStatus.enqueueWorkLocked(work);
- sEnqueuedJwiHighWaterMarkLogger.logSampleWithUid(uId, jobStatus.getWorkCount());
+ sEnqueuedJwiHighWaterMarkLogger
+ .logSampleWithUid(callingUid, jobStatus.getWorkCount());
}
- final int sourceUid = uId;
+ final int sourceUid = jobStatus.getSourceUid();
FrameworkStatsLog.write(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
jobStatus.isProxyJob()
- ? new int[]{sourceUid, jobStatus.getUid()} : new int[]{sourceUid},
+ ? new int[]{sourceUid, callingUid} : new int[]{sourceUid},
// Given that the source tag is set by the calling app, it should be connected
// to the calling app in the attribution for a proxied job.
jobStatus.isProxyJob()
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
index d48d84b..1fdf906 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
@@ -75,6 +75,7 @@
import java.util.concurrent.CountDownLatch;
import java.util.function.Consumer;
import java.util.function.Predicate;
+import java.util.regex.Pattern;
/**
* Maintains the master list of jobs that the job scheduler is tracking. These jobs are compared by
@@ -99,6 +100,8 @@
private static final long SCHEDULED_JOB_HIGH_WATER_MARK_PERIOD_MS = 30 * 60_000L;
@VisibleForTesting
static final String JOB_FILE_SPLIT_PREFIX = "jobs_";
+ private static final Pattern SPLIT_FILE_PATTERN =
+ Pattern.compile("^" + JOB_FILE_SPLIT_PREFIX + "\\d+.xml$");
private static final int ALL_UIDS = -1;
@VisibleForTesting
static final int INVALID_UID = -2;
@@ -1121,6 +1124,11 @@
int numDuplicates = 0;
synchronized (mLock) {
for (File file : files) {
+ if (!file.getName().equals("jobs.xml")
+ && !SPLIT_FILE_PATTERN.matcher(file.getName()).matches()) {
+ // Skip temporary or other files.
+ continue;
+ }
final AtomicFile aFile = createJobFile(file);
try (FileInputStream fis = aFile.openRead()) {
jobs = readJobMapImpl(fis, rtcGood, nowElapsed);
diff --git a/api/ApiDocs.bp b/api/ApiDocs.bp
index e162100..e086bfe 100644
--- a/api/ApiDocs.bp
+++ b/api/ApiDocs.bp
@@ -139,9 +139,22 @@
// using droiddoc
/////////////////////////////////////////////////////////////////////
-framework_docs_only_args = " -android -manifest $(location :frameworks-base-core-AndroidManifest.xml) " +
+// doclava contains checks for a few issues that are have been migrated to metalava.
+// disable them in doclava, to avoid mistriggering or double triggering.
+ignore_doclava_errors_checked_by_metalava = "" +
+ "-hide 111 " + // HIDDEN_SUPERCLASS
+ "-hide 113 " + // DEPRECATION_MISMATCH
+ "-hide 125 " + // REQUIRES_PERMISSION
+ "-hide 126 " + // BROADCAST_BEHAVIOR
+ "-hide 127 " + // SDK_CONSTANT
+ "-hide 128 " // TODO
+
+framework_docs_only_args = "-android " +
+ "-manifest $(location :frameworks-base-core-AndroidManifest.xml) " +
"-metalavaApiSince " +
- "-werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 " +
+ "-werror " +
+ "-lerror " +
+ ignore_doclava_errors_checked_by_metalava +
"-overview $(location :frameworks-base-java-overview) " +
// Federate Support Library references against local API file.
"-federate SupportLib https://developer.android.com " +
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 7e41660..7e78185 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -739,7 +739,9 @@
"android_stubs_current_contributions",
"android_system_stubs_current_contributions",
"android_test_frameworks_core_stubs_current_contributions",
- "stub-annotation-defaults",
+ ],
+ libs: [
+ "stub-annotations",
],
api_contributions: [
"api-stubs-docs-non-updatable.api.contribution",
diff --git a/core/api/current.txt b/core/api/current.txt
index 8c2ba08..7d5417d 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9533,6 +9533,7 @@
method public int getId();
method public int getSystemDataSyncFlags();
method @FlaggedApi("android.companion.association_tag") @Nullable public String getTag();
+ method public boolean isSelfManaged();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.companion.AssociationInfo> CREATOR;
}
@@ -18551,12 +18552,12 @@
ctor @Deprecated public BiometricPrompt.CryptoObject(@NonNull android.security.identity.IdentityCredential);
ctor public BiometricPrompt.CryptoObject(@NonNull android.security.identity.PresentationSession);
ctor @FlaggedApi("android.hardware.biometrics.add_key_agreement_crypto_object") public BiometricPrompt.CryptoObject(@NonNull javax.crypto.KeyAgreement);
- method public javax.crypto.Cipher getCipher();
+ method @Nullable public javax.crypto.Cipher getCipher();
method @Deprecated @Nullable public android.security.identity.IdentityCredential getIdentityCredential();
method @FlaggedApi("android.hardware.biometrics.add_key_agreement_crypto_object") @Nullable public javax.crypto.KeyAgreement getKeyAgreement();
- method public javax.crypto.Mac getMac();
+ method @Nullable public javax.crypto.Mac getMac();
method @Nullable public android.security.identity.PresentationSession getPresentationSession();
- method public java.security.Signature getSignature();
+ method @Nullable public java.security.Signature getSignature();
}
}
@@ -23779,6 +23780,8 @@
field public static final int TYPE_DOCK = 13; // 0xd
field public static final int TYPE_GROUP = 2000; // 0x7d0
field public static final int TYPE_HDMI = 9; // 0x9
+ field @FlaggedApi("com.android.media.flags.enable_audio_policies_device_and_bluetooth_controller") public static final int TYPE_HDMI_ARC = 10; // 0xa
+ field @FlaggedApi("com.android.media.flags.enable_audio_policies_device_and_bluetooth_controller") public static final int TYPE_HDMI_EARC = 29; // 0x1d
field public static final int TYPE_HEARING_AID = 23; // 0x17
field public static final int TYPE_REMOTE_AUDIO_VIDEO_RECEIVER = 1003; // 0x3eb
field public static final int TYPE_REMOTE_CAR = 1008; // 0x3f0
@@ -45706,6 +45709,7 @@
field public static final int TYPE_IMS = 64; // 0x40
field public static final int TYPE_MCX = 1024; // 0x400
field public static final int TYPE_MMS = 2; // 0x2
+ field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final int TYPE_RCS = 32768; // 0x8000
field public static final int TYPE_SUPL = 4; // 0x4
field public static final int TYPE_VSIM = 4096; // 0x1000
field public static final int TYPE_XCAP = 2048; // 0x800
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 70f5e2f..ac61107 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3149,7 +3149,6 @@
public final class AssociationInfo implements android.os.Parcelable {
method @NonNull public String getPackageName();
- method public boolean isSelfManaged();
}
public final class CompanionDeviceManager {
@@ -9641,6 +9640,7 @@
method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void registerControllerAlwaysOnListener(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.ControllerAlwaysOnListener);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean removeNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler);
method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean setControllerAlwaysOn(boolean);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setReaderMode(boolean);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int setTagIntentAppPreferenceForUser(int, @NonNull String, boolean);
method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void unregisterControllerAlwaysOnListener(@NonNull android.nfc.NfcAdapter.ControllerAlwaysOnListener);
field public static final int TAG_INTENT_APP_PREF_RESULT_PACKAGE_NOT_FOUND = -1; // 0xffffffff
@@ -10482,7 +10482,7 @@
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS, android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public boolean isUserNameSet();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isUserOfType(@NonNull String);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isUserUnlockingOrUnlocked(@NonNull android.os.UserHandle);
- method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.MANAGE_USERS"}) public boolean isUserVisible();
+ method public boolean isUserVisible();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean removeUser(@NonNull android.os.UserHandle);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public int removeUserWhenPossible(@NonNull android.os.UserHandle, boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public void setBootUser(@NonNull android.os.UserHandle);
@@ -14717,6 +14717,7 @@
field public static final String TYPE_IMS_STRING = "ims";
field public static final String TYPE_MCX_STRING = "mcx";
field public static final String TYPE_MMS_STRING = "mms";
+ field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String TYPE_RCS_STRING = "rcs";
field public static final String TYPE_SUPL_STRING = "supl";
field public static final String TYPE_VSIM_STRING = "vsim";
field public static final String TYPE_XCAP_STRING = "xcap";
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 0293f66..ddb221f 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -14,6 +14,15 @@
hdrs: ["android/hardware/HardwareBuffer.aidl"],
}
+// TODO (b/303286040): Remove this once |ENABLE_NFC_MAINLINE_FLAG| is rolled out
+filegroup {
+ name: "framework-core-nfc-infcadapter-sources",
+ srcs: [
+ "android/nfc/INfcAdapter.aidl",
+ ],
+ visibility: ["//frameworks/base/services/core"],
+}
+
filegroup {
name: "framework-core-sources",
srcs: [
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index a3b82e9..d7d6546 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -161,12 +161,6 @@
*/
boolean isWallpaperBackupEligible(int which, int userId);
- /*
- * Keyguard: register a callback for being notified that lock-state relevant
- * wallpaper content has changed.
- */
- boolean setLockWallpaperCallback(IWallpaperManagerCallback cb);
-
/**
* Returns the colors used by the lock screen or system wallpaper.
*
@@ -253,13 +247,6 @@
boolean isStaticWallpaper(int which);
/**
- * Temporary method for project b/197814683.
- * Return true if the lockscreen wallpaper always uses a WallpaperService, not a static image.
- * @hide
- */
- boolean isLockscreenLiveWallpaperEnabled();
-
- /**
* Temporary method for project b/270726737.
* Return true if the wallpaper supports different crops for different display dimensions.
* @hide
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index e7a5b72..d660078 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -313,7 +313,6 @@
private final Context mContext;
private final boolean mWcgEnabled;
private final ColorManagementProxy mCmProxy;
- private static Boolean sIsLockscreenLiveWallpaperEnabled = null;
private static Boolean sIsMultiCropEnabled = null;
/**
@@ -841,29 +840,14 @@
}
/**
+ * TODO (b/305908217) remove
* Temporary method for project b/197814683.
* @return true if the lockscreen wallpaper always uses a wallpaperService, not a static image
* @hide
*/
@TestApi
public boolean isLockscreenLiveWallpaperEnabled() {
- return isLockscreenLiveWallpaperEnabledHelper();
- }
-
- private static boolean isLockscreenLiveWallpaperEnabledHelper() {
- if (sGlobals == null) {
- sIsLockscreenLiveWallpaperEnabled = SystemProperties.getBoolean(
- "persist.wm.debug.lockscreen_live_wallpaper", true);
- }
- if (sIsLockscreenLiveWallpaperEnabled == null) {
- try {
- sIsLockscreenLiveWallpaperEnabled =
- sGlobals.mService.isLockscreenLiveWallpaperEnabled();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- return sIsLockscreenLiveWallpaperEnabled;
+ return true;
}
/**
@@ -2446,12 +2430,7 @@
*/
@RequiresPermission(android.Manifest.permission.SET_WALLPAPER)
public void clearWallpaper() {
- if (isLockscreenLiveWallpaperEnabled()) {
- clearWallpaper(FLAG_LOCK | FLAG_SYSTEM, mContext.getUserId());
- return;
- }
- clearWallpaper(FLAG_LOCK, mContext.getUserId());
- clearWallpaper(FLAG_SYSTEM, mContext.getUserId());
+ clearWallpaper(FLAG_LOCK | FLAG_SYSTEM, mContext.getUserId());
}
/**
@@ -2787,11 +2766,7 @@
*/
@RequiresPermission(android.Manifest.permission.SET_WALLPAPER)
public void clear() throws IOException {
- if (isLockscreenLiveWallpaperEnabled()) {
- clear(FLAG_SYSTEM | FLAG_LOCK);
- return;
- }
- setStream(openDefaultWallpaper(mContext, FLAG_SYSTEM), null, false);
+ clear(FLAG_SYSTEM | FLAG_LOCK);
}
/**
@@ -2816,16 +2791,7 @@
*/
@RequiresPermission(android.Manifest.permission.SET_WALLPAPER)
public void clear(@SetWallpaperFlags int which) throws IOException {
- if (isLockscreenLiveWallpaperEnabled()) {
- clearWallpaper(which, mContext.getUserId());
- return;
- }
- if ((which & FLAG_SYSTEM) != 0) {
- clear();
- }
- if ((which & FLAG_LOCK) != 0) {
- clearWallpaper(FLAG_LOCK, mContext.getUserId());
- }
+ clearWallpaper(which, mContext.getUserId());
}
/**
@@ -2840,16 +2806,12 @@
public static InputStream openDefaultWallpaper(Context context, @SetWallpaperFlags int which) {
final String whichProp;
final int defaultResId;
- if (which == FLAG_LOCK && !isLockscreenLiveWallpaperEnabledHelper()) {
- /* Factory-default lock wallpapers are not yet supported
- whichProp = PROP_LOCK_WALLPAPER;
- defaultResId = com.android.internal.R.drawable.default_lock_wallpaper;
- */
- return null;
- } else {
- whichProp = PROP_WALLPAPER;
- defaultResId = com.android.internal.R.drawable.default_wallpaper;
- }
+ /* Factory-default lock wallpapers are not yet supported.
+ whichProp = which == FLAG_LOCK ? PROP_LOCK_WALLPAPER : PROP_WALLPAPER;
+ defaultResId = which == FLAG_LOCK ? R.drawable.default_lock_wallpaper : ....
+ */
+ whichProp = PROP_WALLPAPER;
+ defaultResId = R.drawable.default_wallpaper;
final String path = SystemProperties.get(whichProp);
final InputStream wallpaperInputStream = getWallpaperInputStream(path);
if (wallpaperInputStream != null) {
@@ -2988,25 +2950,6 @@
}
/**
- * Register a callback for lock wallpaper observation. Only the OS may use this.
- *
- * @return true on success; false on error.
- * @hide
- */
- public boolean setLockWallpaperCallback(IWallpaperManagerCallback callback) {
- if (sGlobals.mService == null) {
- Log.w(TAG, "WallpaperService not running");
- throw new RuntimeException(new DeadSystemException());
- }
-
- try {
- return sGlobals.mService.setLockWallpaperCallback(callback);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Is the current system wallpaper eligible for backup?
*
* Only the OS itself may use this method.
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index c145c02..f99615f 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -19,4 +19,11 @@
namespace: "enterprise"
description: "Add feature to track required changes for enabled V2 of auto-capturing of onboarding bug reports."
bug: "302517677"
-}
\ No newline at end of file
+}
+
+flag {
+ name: "cross_user_suspension_enabled"
+ namespace: "enterprise"
+ description: "Allow holders of INTERACT_ACROSS_USERS_FULL to suspend apps in different users."
+ bug: "263464464"
+}
diff --git a/core/java/android/app/usage/flags.aconfig b/core/java/android/app/usage/flags.aconfig
index f401a76..4f1c65b 100644
--- a/core/java/android/app/usage/flags.aconfig
+++ b/core/java/android/app/usage/flags.aconfig
@@ -13,3 +13,11 @@
description: "Feature flag for the new REPORT_USAGE_STATS permission."
bug: "296056771"
}
+
+flag {
+ name: "use_dedicated_handler_thread"
+ namespace: "backstage_power"
+ description: "Flag to use a dedicated thread for usage event process"
+ is_fixed_read_only: true
+ bug: "299336442"
+}
diff --git a/core/java/android/companion/AssociationInfo.java b/core/java/android/companion/AssociationInfo.java
index 8b09bdf..161fa79 100644
--- a/core/java/android/companion/AssociationInfo.java
+++ b/core/java/android/companion/AssociationInfo.java
@@ -206,9 +206,8 @@
/**
* @return whether the association is managed by the companion application it belongs to.
* @see AssociationRequest.Builder#setSelfManaged(boolean)
- * @hide
*/
- @SystemApi
+ @SuppressLint("UnflaggedApi") // promoting from @SystemApi
public boolean isSelfManaged() {
return mSelfManaged;
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index bb9cc0b..7b6bad3 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4292,6 +4292,14 @@
"com.android.intent.action.SHOW_BRIGHTNESS_DIALOG";
/**
+ * Intent Extra: holds boolean that determines whether brightness dialog is full width when
+ * in landscape mode.
+ * @hide
+ */
+ public static final String EXTRA_BRIGHTNESS_DIALOG_IS_FULL_WIDTH =
+ "android.intent.extra.BRIGHTNESS_DIALOG_IS_FULL_WIDTH";
+
+ /**
* Activity Action: Shows the contrast setting dialog.
* @hide
*/
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index b37da84..b15c9e4 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1480,7 +1480,6 @@
INSTALL_ALLOW_DOWNGRADE,
INSTALL_STAGED,
INSTALL_REQUEST_UPDATE_OWNERSHIP,
- INSTALL_IGNORE_DEXOPT_PROFILE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface InstallFlags {}
@@ -1713,18 +1712,6 @@
public static final int INSTALL_ARCHIVED = 1 << 27;
/**
- * If set, all dexopt profiles are ignored by dexopt during the installation, including the
- * profile in the DM file and the profile embedded in the APK file. If an invalid profile is
- * provided during installation, no warning will be reported by {@code adb install}.
- *
- * This option does not affect later dexopt operations (e.g., background dexopt and manual `pm
- * compile` invocations).
- *
- * @hide
- */
- public static final int INSTALL_IGNORE_DEXOPT_PROFILE = 1 << 28;
-
- /**
* Flag parameter for {@link #installPackage} to force a non-staged update of an APEX. This is
* a development-only feature and should not be used on end user devices.
*
diff --git a/core/java/android/hardware/HardwareBuffer.aidl b/core/java/android/hardware/HardwareBuffer.aidl
index 1333f0d..a9742cb 100644
--- a/core/java/android/hardware/HardwareBuffer.aidl
+++ b/core/java/android/hardware/HardwareBuffer.aidl
@@ -16,4 +16,4 @@
package android.hardware;
-@JavaOnlyStableParcelable @NdkOnlyStableParcelable parcelable HardwareBuffer ndk_header "android/hardware_buffer_aidl.h";
+@JavaOnlyStableParcelable @NdkOnlyStableParcelable @RustOnlyStableParcelable parcelable HardwareBuffer ndk_header "android/hardware_buffer_aidl.h" rust_type "nativewindow::HardwareBuffer";
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 490ff64..7a43286 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -805,7 +805,7 @@
* Get {@link Signature} object.
* @return {@link Signature} object or null if this doesn't contain one.
*/
- public Signature getSignature() {
+ public @Nullable Signature getSignature() {
return super.getSignature();
}
@@ -813,7 +813,7 @@
* Get {@link Cipher} object.
* @return {@link Cipher} object or null if this doesn't contain one.
*/
- public Cipher getCipher() {
+ public @Nullable Cipher getCipher() {
return super.getCipher();
}
@@ -821,7 +821,7 @@
* Get {@link Mac} object.
* @return {@link Mac} object or null if this doesn't contain one.
*/
- public Mac getMac() {
+ public @Nullable Mac getMac() {
return super.getMac();
}
diff --git a/core/java/android/hardware/biometrics/CryptoObject.java b/core/java/android/hardware/biometrics/CryptoObject.java
index 6ac1efb..39fbe83 100644
--- a/core/java/android/hardware/biometrics/CryptoObject.java
+++ b/core/java/android/hardware/biometrics/CryptoObject.java
@@ -20,6 +20,7 @@
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.security.identity.IdentityCredential;
import android.security.identity.PresentationSession;
import android.security.keystore2.AndroidKeyStoreProvider;
@@ -33,20 +34,35 @@
/**
* A wrapper class for the crypto objects supported by BiometricPrompt and FingerprintManager.
* Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac},
- * {@link IdentityCredential}, and {@link PresentationSession} objects.
+ * {@link KeyAgreement}, {@link IdentityCredential}, and {@link PresentationSession} objects.
* @hide
*/
public class CryptoObject {
private final Object mCrypto;
+ /**
+ * Create from a {@link Signature} object.
+ *
+ * @param signature a {@link Signature} object.
+ */
public CryptoObject(@NonNull Signature signature) {
mCrypto = signature;
}
+ /**
+ * Create from a {@link Cipher} object.
+ *
+ * @param cipher a {@link Cipher} object.
+ */
public CryptoObject(@NonNull Cipher cipher) {
mCrypto = cipher;
}
+ /**
+ * Create from a {@link Mac} object.
+ *
+ * @param mac a {@link Mac} object.
+ */
public CryptoObject(@NonNull Mac mac) {
mCrypto = mac;
}
@@ -62,10 +78,20 @@
mCrypto = credential;
}
+ /**
+ * Create from a {@link PresentationSession} object.
+ *
+ * @param session a {@link PresentationSession} object.
+ */
public CryptoObject(@NonNull PresentationSession session) {
mCrypto = session;
}
+ /**
+ * Create from a {@link KeyAgreement} object.
+ *
+ * @param keyAgreement a {@link KeyAgreement} object.
+ */
@FlaggedApi(FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT)
public CryptoObject(@NonNull KeyAgreement keyAgreement) {
mCrypto = keyAgreement;
@@ -75,7 +101,7 @@
* Get {@link Signature} object.
* @return {@link Signature} object or null if this doesn't contain one.
*/
- public Signature getSignature() {
+ public @Nullable Signature getSignature() {
return mCrypto instanceof Signature ? (Signature) mCrypto : null;
}
@@ -83,7 +109,7 @@
* Get {@link Cipher} object.
* @return {@link Cipher} object or null if this doesn't contain one.
*/
- public Cipher getCipher() {
+ public @Nullable Cipher getCipher() {
return mCrypto instanceof Cipher ? (Cipher) mCrypto : null;
}
@@ -91,7 +117,7 @@
* Get {@link Mac} object.
* @return {@link Mac} object or null if this doesn't contain one.
*/
- public Mac getMac() {
+ public @Nullable Mac getMac() {
return mCrypto instanceof Mac ? (Mac) mCrypto : null;
}
@@ -101,7 +127,7 @@
* @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
*/
@Deprecated
- public IdentityCredential getIdentityCredential() {
+ public @Nullable IdentityCredential getIdentityCredential() {
return mCrypto instanceof IdentityCredential ? (IdentityCredential) mCrypto : null;
}
@@ -109,16 +135,18 @@
* Get {@link PresentationSession} object.
* @return {@link PresentationSession} object or null if this doesn't contain one.
*/
- public PresentationSession getPresentationSession() {
+ public @Nullable PresentationSession getPresentationSession() {
return mCrypto instanceof PresentationSession ? (PresentationSession) mCrypto : null;
}
/**
- * Get {@link KeyAgreement} object.
+ * Get {@link KeyAgreement} object. A key-agreement protocol is a protocol whereby
+ * two or more parties can agree on a shared secret using public key cryptography.
+ *
* @return {@link KeyAgreement} object or null if this doesn't contain one.
*/
@FlaggedApi(FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT)
- public KeyAgreement getKeyAgreement() {
+ public @Nullable KeyAgreement getKeyAgreement() {
return mCrypto instanceof KeyAgreement ? (KeyAgreement) mCrypto : null;
}
diff --git a/core/java/android/nfc/Constants.java b/core/java/android/nfc/Constants.java
new file mode 100644
index 0000000..f768330
--- /dev/null
+++ b/core/java/android/nfc/Constants.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+/**
+ * @hide
+ * TODO(b/303286040): Holds @hide API constants. Formalize these APIs.
+ */
+public final class Constants {
+ private Constants() { }
+
+ public static final String SETTINGS_SECURE_NFC_PAYMENT_FOREGROUND = "nfc_payment_foreground";
+ public static final String SETTINGS_SECURE_NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
+ public static final String FEATURE_NFC_ANY = "android.hardware.nfc.any";
+}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 4658630..4a7bd3f 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -24,6 +24,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.UserIdInt;
import android.app.Activity;
@@ -37,6 +38,7 @@
import android.nfc.tech.Ndef;
import android.nfc.tech.NfcA;
import android.nfc.tech.NfcF;
+import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -1594,6 +1596,40 @@
mNfcActivityManager.disableReaderMode(activity);
}
+ // Flags arguments to NFC adapter to enable/disable NFC
+ private static final int DISABLE_POLLING_FLAGS = 0x1000;
+ private static final int ENABLE_POLLING_FLAGS = 0x0000;
+
+ /**
+ * Privileged API to enable disable reader polling.
+ * Note: Use with caution! The app is responsible for ensuring that the polling state is
+ * returned to normal.
+ *
+ * @see #enableReaderMode(Activity, ReaderCallback, int, Bundle) for more detailed
+ * documentation.
+ *
+ * @param enablePolling whether to enable or disable polling.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @SuppressLint("VisiblySynchronized")
+ public void setReaderMode(boolean enablePolling) {
+ synchronized (NfcAdapter.class) {
+ if (!sHasNfcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ }
+ Binder token = new Binder();
+ int flags = enablePolling ? ENABLE_POLLING_FLAGS : DISABLE_POLLING_FLAGS;
+ try {
+ NfcAdapter.sService.setReaderMode(token, null, flags, null);
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ }
+ }
+
/**
* Manually invoke Android Beam to share data.
*
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index 4909b08..32c2a1b 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -26,6 +26,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.nfc.Constants;
import android.nfc.INfcCardEmulation;
import android.nfc.NfcAdapter;
import android.os.RemoteException;
@@ -274,7 +275,7 @@
try {
preferForeground = Settings.Secure.getInt(
contextAsUser.getContentResolver(),
- Settings.Secure.NFC_PAYMENT_FOREGROUND) != 0;
+ Constants.SETTINGS_SECURE_NFC_PAYMENT_FOREGROUND) != 0;
} catch (SettingNotFoundException e) {
}
return preferForeground;
diff --git a/core/java/android/os/OomKillRecord.java b/core/java/android/os/OomKillRecord.java
index 151a65f..ca1d49a 100644
--- a/core/java/android/os/OomKillRecord.java
+++ b/core/java/android/os/OomKillRecord.java
@@ -15,10 +15,15 @@
*/
package android.os;
+import com.android.internal.util.FrameworkStatsLog;
/**
+ * Activity manager communication with kernel out-of-memory (OOM) data handling
+ * and statsd atom logging.
+ *
* Expected data to get back from the OOM event's file.
- * Note that this should be equivalent to the struct <b>OomKill</b> inside
+ * Note that this class fields' should be equivalent to the struct
+ * <b>OomKill</b> inside
* <pre>
* system/memory/libmeminfo/libmemevents/include/memevents.h
* </pre>
@@ -41,6 +46,18 @@
this.mOomScoreAdj = oomScoreAdj;
}
+ /**
+ * Logs the event when the kernel OOM killer claims a victims to reduce
+ * memory pressure.
+ * KernelOomKillOccurred = 754
+ */
+ public void logKillOccurred() {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.KERNEL_OOM_KILL_OCCURRED,
+ mUid, mPid, mOomScoreAdj, mTimeStampInMillis,
+ mProcessName);
+ }
+
public long getTimestampMilli() {
return mTimeStampInMillis;
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 4c8ef97..9034ff1 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -3330,7 +3330,10 @@
*
* @return whether the context user is running in the foreground.
*/
- @UserHandleAware
+ @UserHandleAware(
+ requiresAnyOfPermissionsIfNotCaller = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS})
public boolean isUserForeground() {
try {
return mService.isUserForeground(mUserId);
@@ -3404,11 +3407,10 @@
* @hide
*/
@SystemApi
- @UserHandleAware
- @RequiresPermission(anyOf = {
- "android.permission.INTERACT_ACROSS_USERS",
- "android.permission.MANAGE_USERS"
- })
+ @UserHandleAware(
+ requiresAnyOfPermissionsIfNotCaller = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS})
public boolean isUserVisible() {
try {
return mService.isUserVisible(mUserId);
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index a391571..27ad45d 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -3196,6 +3196,16 @@
public static final String ALWAYS_ON = "always_on";
/**
+ * The infrastructure bitmask which the APN can be used on. For example, some APNs can only
+ * be used when the device is on cellular, on satellite, or both. The default value is
+ * 1 (INFRASTRUCTURE_CELLULAR).
+ *
+ * <P>Type: INTEGER</P>
+ * @hide
+ */
+ public static final String INFRASTRUCTURE_BITMASK = "infrastructure_bitmask";
+
+ /**
* MVNO type:
* {@code SPN (Service Provider Name), IMSI, GID (Group Identifier Level 1)}.
* <P>Type: TEXT</P>
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 2f3d73a..42a0c9a 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -70,6 +70,7 @@
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
@@ -796,7 +797,7 @@
if (nativeObject != 0) {
// Only add valid surface controls to the registry. This is called at the end of this
// method since its information is dumped if the process threshold is reached.
- addToRegistry();
+ SurfaceControlRegistry.getProcessInstance().add(this);
}
}
@@ -1501,7 +1502,7 @@
if (mCloseGuard != null) {
mCloseGuard.warnIfOpen();
}
- removeFromRegistry();
+ SurfaceControlRegistry.getProcessInstance().remove(this);
} finally {
super.finalize();
}
@@ -1519,6 +1520,10 @@
*/
public void release() {
if (mNativeObject != 0) {
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "release", null, this, null);
+ }
mFreeNativeResources.run();
mNativeObject = 0;
mNativeHandle = 0;
@@ -1532,7 +1537,7 @@
mChoreographer = null;
}
}
- removeFromRegistry();
+ SurfaceControlRegistry.getProcessInstance().remove(this);
}
}
@@ -2765,8 +2770,10 @@
private Transaction(long nativeObject) {
mNativeObject = nativeObject;
- mFreeNativeResources =
- sRegistry.registerNativeAllocation(this, mNativeObject);
+ mFreeNativeResources = sRegistry.registerNativeAllocation(this, mNativeObject);
+ if (!SurfaceControlRegistry.sCallStackDebuggingInitialized) {
+ SurfaceControlRegistry.initializeCallStackDebugging();
+ }
}
private Transaction(Parcel in) {
@@ -2845,6 +2852,11 @@
applyResizedSurfaces();
notifyReparentedSurfaces();
nativeApplyTransaction(mNativeObject, sync, oneWay);
+
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "apply", this, null, null);
+ }
}
/**
@@ -2920,6 +2932,10 @@
@UnsupportedAppUsage
public Transaction show(SurfaceControl sc) {
checkPreconditions(sc);
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "show", this, sc, null);
+ }
nativeSetFlags(mNativeObject, sc.mNativeObject, 0, SURFACE_HIDDEN);
return this;
}
@@ -2934,6 +2950,10 @@
@UnsupportedAppUsage
public Transaction hide(SurfaceControl sc) {
checkPreconditions(sc);
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "hide", this, sc, null);
+ }
nativeSetFlags(mNativeObject, sc.mNativeObject, SURFACE_HIDDEN, SURFACE_HIDDEN);
return this;
}
@@ -2950,6 +2970,10 @@
@NonNull
public Transaction setPosition(@NonNull SurfaceControl sc, float x, float y) {
checkPreconditions(sc);
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "setPosition", this, sc, "x=" + x + " y=" + y);
+ }
nativeSetPosition(mNativeObject, sc.mNativeObject, x, y);
return this;
}
@@ -2968,6 +2992,10 @@
checkPreconditions(sc);
Preconditions.checkArgument(scaleX >= 0, "Negative value passed in for scaleX");
Preconditions.checkArgument(scaleY >= 0, "Negative value passed in for scaleY");
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "setScale", this, sc, "sx=" + scaleX + " sy=" + scaleY);
+ }
nativeSetScale(mNativeObject, sc.mNativeObject, scaleX, scaleY);
return this;
}
@@ -2985,6 +3013,10 @@
public Transaction setBufferSize(@NonNull SurfaceControl sc,
@IntRange(from = 0) int w, @IntRange(from = 0) int h) {
checkPreconditions(sc);
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "setBufferSize", this, sc, "w=" + w + " h=" + h);
+ }
mResizedSurfaces.put(sc, new Point(w, h));
return this;
}
@@ -3005,6 +3037,10 @@
public Transaction setFixedTransformHint(@NonNull SurfaceControl sc,
@Surface.Rotation int transformHint) {
checkPreconditions(sc);
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "setFixedTransformHint", this, sc, "hint=" + transformHint);
+ }
nativeSetFixedTransformHint(mNativeObject, sc.mNativeObject, transformHint);
return this;
}
@@ -3018,6 +3054,10 @@
@NonNull
public Transaction unsetFixedTransformHint(@NonNull SurfaceControl sc) {
checkPreconditions(sc);
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "unsetFixedTransformHint", this, sc, null);
+ }
nativeSetFixedTransformHint(mNativeObject, sc.mNativeObject, -1/* INVALID_ROTATION */);
return this;
}
@@ -3035,6 +3075,10 @@
public Transaction setLayer(@NonNull SurfaceControl sc,
@IntRange(from = Integer.MIN_VALUE, to = Integer.MAX_VALUE) int z) {
checkPreconditions(sc);
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "setLayer", this, sc, "z=" + z);
+ }
nativeSetLayer(mNativeObject, sc.mNativeObject, z);
return this;
}
@@ -3044,6 +3088,10 @@
*/
public Transaction setRelativeLayer(SurfaceControl sc, SurfaceControl relativeTo, int z) {
checkPreconditions(sc);
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "setRelativeLayer", this, sc, "relTo=" + relativeTo + " z=" + z);
+ }
nativeSetRelativeLayer(mNativeObject, sc.mNativeObject, relativeTo.mNativeObject, z);
return this;
}
@@ -3053,6 +3101,10 @@
*/
public Transaction setTransparentRegionHint(SurfaceControl sc, Region transparentRegion) {
checkPreconditions(sc);
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "unsetFixedTransformHint", this, sc, "region=" + transparentRegion);
+ }
nativeSetTransparentRegionHint(mNativeObject,
sc.mNativeObject, transparentRegion);
return this;
@@ -3069,6 +3121,10 @@
public Transaction setAlpha(@NonNull SurfaceControl sc,
@FloatRange(from = 0.0, to = 1.0) float alpha) {
checkPreconditions(sc);
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "setAlpha", this, sc, "alpha=" + alpha);
+ }
nativeSetAlpha(mNativeObject, sc.mNativeObject, alpha);
return this;
}
@@ -3124,6 +3180,11 @@
public Transaction setMatrix(SurfaceControl sc,
float dsdx, float dtdx, float dtdy, float dsdy) {
checkPreconditions(sc);
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "setMatrix", this, sc,
+ "dsdx=" + dsdx + " dtdx=" + dtdx + " dtdy=" + dtdy + " dsdy=" + dsdy);
+ }
nativeSetMatrix(mNativeObject, sc.mNativeObject,
dsdx, dtdx, dtdy, dsdy);
return this;
@@ -3189,6 +3250,10 @@
@UnsupportedAppUsage
public Transaction setWindowCrop(SurfaceControl sc, Rect crop) {
checkPreconditions(sc);
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "setWindowCrop", this, sc, "crop=" + crop);
+ }
if (crop != null) {
nativeSetWindowCrop(mNativeObject, sc.mNativeObject,
crop.left, crop.top, crop.right, crop.bottom);
@@ -3211,6 +3276,10 @@
*/
public @NonNull Transaction setCrop(@NonNull SurfaceControl sc, @Nullable Rect crop) {
checkPreconditions(sc);
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "setCrop", this, sc, "crop=" + crop);
+ }
if (crop != null) {
Preconditions.checkArgument(crop.isValid(), "Crop isn't valid.");
nativeSetWindowCrop(mNativeObject, sc.mNativeObject,
@@ -3233,6 +3302,10 @@
*/
public Transaction setWindowCrop(SurfaceControl sc, int width, int height) {
checkPreconditions(sc);
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "setCornerRadius", this, sc, "w=" + width + " h=" + height);
+ }
nativeSetWindowCrop(mNativeObject, sc.mNativeObject, 0, 0, width, height);
return this;
}
@@ -3247,6 +3320,10 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public Transaction setCornerRadius(SurfaceControl sc, float cornerRadius) {
checkPreconditions(sc);
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "setCornerRadius", this, sc, "cornerRadius=" + cornerRadius);
+ }
nativeSetCornerRadius(mNativeObject, sc.mNativeObject, cornerRadius);
return this;
@@ -3262,6 +3339,10 @@
*/
public Transaction setBackgroundBlurRadius(SurfaceControl sc, int radius) {
checkPreconditions(sc);
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "setBackgroundBlurRadius", this, sc, "radius=" + radius);
+ }
nativeSetBackgroundBlurRadius(mNativeObject, sc.mNativeObject, radius);
return this;
}
@@ -3318,6 +3399,10 @@
public Transaction reparent(@NonNull SurfaceControl sc,
@Nullable SurfaceControl newParent) {
checkPreconditions(sc);
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "reparent", this, sc, "newParent=" + newParent);
+ }
long otherObject = 0;
if (newParent != null) {
newParent.checkNotReleased();
@@ -3337,6 +3422,11 @@
@UnsupportedAppUsage
public Transaction setColor(SurfaceControl sc, @Size(3) float[] color) {
checkPreconditions(sc);
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "reparent", this, sc,
+ "r=" + color[0] + " g=" + color[1] + " b=" + color[2]);
+ }
nativeSetColor(mNativeObject, sc.mNativeObject, color);
return this;
}
@@ -3347,6 +3437,10 @@
*/
public Transaction unsetColor(SurfaceControl sc) {
checkPreconditions(sc);
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "unsetColor", this, sc, null);
+ }
nativeSetColor(mNativeObject, sc.mNativeObject, INVALID_COLOR);
return this;
}
@@ -3358,6 +3452,10 @@
*/
public Transaction setSecure(SurfaceControl sc, boolean isSecure) {
checkPreconditions(sc);
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "setSecure", this, sc, "secure=" + isSecure);
+ }
if (isSecure) {
nativeSetFlags(mNativeObject, sc.mNativeObject, SECURE, SECURE);
} else {
@@ -3411,6 +3509,10 @@
@NonNull
public Transaction setOpaque(@NonNull SurfaceControl sc, boolean isOpaque) {
checkPreconditions(sc);
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "setOpaque", this, sc, "opaque=" + isOpaque);
+ }
if (isOpaque) {
nativeSetFlags(mNativeObject, sc.mNativeObject, SURFACE_OPAQUE, SURFACE_OPAQUE);
} else {
@@ -3580,6 +3682,10 @@
*/
public Transaction setShadowRadius(SurfaceControl sc, float shadowRadius) {
checkPreconditions(sc);
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "setShadowRadius", this, sc, "radius=" + shadowRadius);
+ }
nativeSetShadowRadius(mNativeObject, sc.mNativeObject, shadowRadius);
return this;
}
@@ -4463,26 +4569,6 @@
return -1;
}
- /**
- * Adds this surface control to the registry for this process if it is created.
- */
- private void addToRegistry() {
- final SurfaceControlRegistry registry = SurfaceControlRegistry.getProcessInstance();
- if (registry != null) {
- registry.add(this);
- }
- }
-
- /**
- * Removes this surface control from the registry for this process.
- */
- private void removeFromRegistry() {
- final SurfaceControlRegistry registry = SurfaceControlRegistry.getProcessInstance();
- if (registry != null) {
- registry.remove(this);
- }
- }
-
// Called by native
private static void invokeReleaseCallback(Consumer<SyncFence> callback, long nativeFencePtr) {
SyncFence fence = new SyncFence(nativeFencePtr);
diff --git a/core/java/android/view/SurfaceControlRegistry.java b/core/java/android/view/SurfaceControlRegistry.java
index 0f35048..52be8f6 100644
--- a/core/java/android/view/SurfaceControlRegistry.java
+++ b/core/java/android/view/SurfaceControlRegistry.java
@@ -23,7 +23,9 @@
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.content.Context;
+import android.os.Build;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -104,6 +106,9 @@
// Number of surface controls to dump when the max threshold is exceeded
private static final int DUMP_LIMIT = 256;
+ // An instance of a registry that is a no-op
+ private static final SurfaceControlRegistry NO_OP_REGISTRY = new NoOpRegistry();
+
// Static lock, must be held for all registry operations
private static final Object sLock = new Object();
@@ -113,6 +118,22 @@
// The registry for a given process
private static volatile SurfaceControlRegistry sProcessRegistry;
+ // Whether call stack debugging has been initialized. This is evaluated only once per process
+ // instance when the first SurfaceControl.Transaction object is created
+ static boolean sCallStackDebuggingInitialized;
+
+ // Whether call stack debugging is currently enabled, ie. whether there is a valid match string
+ // for either a specific surface control name or surface control transaction method
+ static boolean sCallStackDebuggingEnabled;
+
+ // The name of the surface control to log stack traces for. Always non-null if
+ // sCallStackDebuggingEnabled is true. Can be combined with the match call.
+ private static String sCallStackDebuggingMatchName;
+
+ // The surface control transaction method name to log stack traces for. Always non-null if
+ // sCallStackDebuggingEnabled is true. Can be combined with the match name.
+ private static String sCallStackDebuggingMatchCall;
+
// Mapping of the active SurfaceControls to the elapsed time when they were registered
@GuardedBy("sLock")
private final WeakHashMap<SurfaceControl, Long> mSurfaceControls;
@@ -160,6 +181,12 @@
}
}
+ @VisibleForTesting
+ public void setCallStackDebuggingParams(String matchName, String matchCall) {
+ sCallStackDebuggingMatchName = matchName.toLowerCase();
+ sCallStackDebuggingMatchCall = matchCall.toLowerCase();
+ }
+
/**
* Creates and initializes the registry for all SurfaceControls in this process. The caller must
* hold the READ_FRAME_BUFFER permission.
@@ -196,11 +223,9 @@
* createProcessInstance(Context) was previously called from a valid caller.
* @hide
*/
- @Nullable
- @VisibleForTesting
public static SurfaceControlRegistry getProcessInstance() {
synchronized (sLock) {
- return sProcessRegistry;
+ return sProcessRegistry != null ? sProcessRegistry : NO_OP_REGISTRY;
}
}
@@ -248,6 +273,91 @@
}
/**
+ * Initializes global call stack debugging if this is a debug build and a filter is specified.
+ * This is a no-op if
+ *
+ * Usage:
+ * adb shell setprop persist.wm.debug.sc.tx.log_match_call <call or \"\" to unset>
+ * adb shell setprop persist.wm.debug.sc.tx.log_match_name <name or \"\" to unset>
+ * adb reboot
+ */
+ final static void initializeCallStackDebugging() {
+ if (sCallStackDebuggingInitialized || !Build.IS_DEBUGGABLE) {
+ // Return early if already initialized or this is not a debug build
+ return;
+ }
+
+ sCallStackDebuggingInitialized = true;
+ sCallStackDebuggingMatchCall =
+ SystemProperties.get("persist.wm.debug.sc.tx.log_match_call", null)
+ .toLowerCase();
+ sCallStackDebuggingMatchName =
+ SystemProperties.get("persist.wm.debug.sc.tx.log_match_name", null)
+ .toLowerCase();
+ // Only enable stack debugging if any of the match filters are set
+ sCallStackDebuggingEnabled = (!sCallStackDebuggingMatchCall.isEmpty()
+ || !sCallStackDebuggingMatchName.isEmpty());
+ if (sCallStackDebuggingEnabled) {
+ Log.d(TAG, "Enabling transaction call stack debugging:"
+ + " matchCall=" + sCallStackDebuggingMatchCall
+ + " matchName=" + sCallStackDebuggingMatchName);
+ }
+ }
+
+ /**
+ * Dumps the callstack if it matches the global debug properties. Caller should first verify
+ * {@link #sCallStackDebuggingEnabled} is true.
+ *
+ * @param call the name of the call
+ * @param tx (optional) the transaction associated with this call
+ * @param sc the affected surface
+ * @param details additional details to print with the stack track
+ */
+ final void checkCallStackDebugging(@NonNull String call,
+ @Nullable SurfaceControl.Transaction tx, @Nullable SurfaceControl sc,
+ @Nullable String details) {
+ if (!sCallStackDebuggingEnabled) {
+ return;
+ }
+ if (!matchesForCallStackDebugging(sc != null ? sc.getName() : null, call)) {
+ return;
+ }
+ final String txMsg = tx != null ? "tx=" + tx.getId() + " ": "";
+ final String scMsg = sc != null ? " sc=" + sc.getName() + "": "";
+ final String msg = details != null
+ ? call + " (" + txMsg + scMsg + ") " + details
+ : call + " (" + txMsg + scMsg + ")";
+ Log.e(TAG, msg, new Throwable());
+ }
+
+ /**
+ * Tests whether the given surface control name/method call matches the filters set for the
+ * call stack debugging.
+ */
+ @VisibleForTesting
+ public final boolean matchesForCallStackDebugging(@Nullable String name, @NonNull String call) {
+ final boolean matchCall = !sCallStackDebuggingMatchCall.isEmpty();
+ if (matchCall && !call.toLowerCase().contains(sCallStackDebuggingMatchCall)) {
+ // Skip if target call doesn't match requested caller
+ return false;
+ }
+ final boolean matchName = !sCallStackDebuggingMatchName.isEmpty();
+ if (matchName && (name == null
+ || !name.toLowerCase().contains(sCallStackDebuggingMatchName))) {
+ // Skip if target surface doesn't match requested surface
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns whether call stack debugging is enabled for this process.
+ */
+ final static boolean isCallStackDebuggingEnabled() {
+ return sCallStackDebuggingEnabled;
+ }
+
+ /**
* Forces the gc and finalizers to run, used prior to dumping to ensure we only dump strongly
* referenced surface controls.
*/
@@ -272,7 +382,27 @@
synchronized (sLock) {
if (sProcessRegistry != null) {
sDefaultReporter.onMaxLayersExceeded(sProcessRegistry.mSurfaceControls, limit, pw);
+ pw.println("sCallStackDebuggingInitialized=" + sCallStackDebuggingInitialized);
+ pw.println("sCallStackDebuggingEnabled=" + sCallStackDebuggingEnabled);
+ pw.println("sCallStackDebuggingMatchName=" + sCallStackDebuggingMatchName);
+ pw.println("sCallStackDebuggingMatchCall=" + sCallStackDebuggingMatchCall);
}
}
}
+
+ /**
+ * A no-op implementation of the registry.
+ */
+ private static class NoOpRegistry extends SurfaceControlRegistry {
+
+ @Override
+ public void setReportingThresholds(int maxLayersReportingThreshold,
+ int resetReportingThreshold, Reporter reporter) {}
+
+ @Override
+ void add(SurfaceControl sc) {}
+
+ @Override
+ void remove(SurfaceControl sc) {}
+ }
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 49b16c7..e9d0e4c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -19,8 +19,6 @@
import static android.content.res.Resources.ID_NULL;
import static android.os.Trace.TRACE_TAG_APP;
import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP;
-import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
-import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_BOUNDS;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
@@ -29,7 +27,6 @@
import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH;
import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE;
import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API;
-import static android.view.flags.Flags.toolkitSetFrameRate;
import static android.view.flags.Flags.viewVelocityApi;
import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS;
@@ -117,7 +114,6 @@
import android.text.InputType;
import android.text.TextUtils;
import android.util.AttributeSet;
-import android.util.DisplayMetrics;
import android.util.FloatProperty;
import android.util.LayoutDirection;
import android.util.Log;
@@ -5513,11 +5509,6 @@
private ViewTranslationResponse mViewTranslationResponse;
/**
- * A threshold value to determine the frame rate category of the View based on the size.
- */
- private static final float FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD = 0.07f;
-
- /**
* Simple constructor to use when creating a view from code.
*
* @param context The Context the view is running in, through which it can
@@ -20192,9 +20183,6 @@
return;
}
- // For VRR to vote the preferred frame rate
- votePreferredFrameRate();
-
// Reset content capture caches
mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK;
mContentCaptureSessionCached = false;
@@ -20297,8 +20285,6 @@
*/
protected void damageInParent() {
if (mParent != null && mAttachInfo != null) {
- // For VRR to vote the preferred frame rate
- votePreferredFrameRate();
mParent.onDescendantInvalidated(this, this);
}
}
@@ -32995,40 +32981,6 @@
return null;
}
- private float getSizePercentage() {
- if (mResources == null || getAlpha() == 0 || getVisibility() != VISIBLE) {
- return 0;
- }
-
- DisplayMetrics displayMetrics = mResources.getDisplayMetrics();
- int screenSize = displayMetrics.widthPixels
- * displayMetrics.heightPixels;
- int viewSize = getWidth() * getHeight();
-
- if (screenSize == 0 || viewSize == 0) {
- return 0f;
- }
- return (float) viewSize / screenSize;
- }
-
- private int calculateFrameRateCategory() {
- float sizePercentage = getSizePercentage();
-
- if (sizePercentage <= FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD) {
- return FRAME_RATE_CATEGORY_LOW;
- } else {
- return FRAME_RATE_CATEGORY_NORMAL;
- }
- }
-
- private void votePreferredFrameRate() {
- // use toolkitSetFrameRate flag to gate the change
- ViewRootImpl viewRootImpl = getViewRootImpl();
- if (toolkitSetFrameRate() && viewRootImpl != null && getSizePercentage() > 0) {
- viewRootImpl.votePreferredFrameRateCategory(calculateFrameRateCategory());
- }
- }
-
/**
* Set the current velocity of the View, we only track positive value.
* We will use the velocity information to adjust the frame rate when applicable.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 50eeed8..4a10cab 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -24,8 +24,6 @@
import static android.view.Display.INVALID_DISPLAY;
import static android.view.InputDevice.SOURCE_CLASS_NONE;
import static android.view.InsetsSource.ID_IME;
-import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
-import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
import static android.view.View.PFLAG_DRAW_ANIMATION;
import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
@@ -76,10 +74,7 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
@@ -92,7 +87,6 @@
import static android.view.accessibility.Flags.forceInvertColor;
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_FOCUS_CONTROLLER;
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INSETS_CONTROLLER;
-import static android.view.flags.Flags.toolkitSetFrameRate;
import android.Manifest;
import android.accessibilityservice.AccessibilityService;
@@ -661,6 +655,10 @@
*/
private boolean mCheckIfCanDraw = false;
+ private boolean mWasLastDrawCanceled;
+ private boolean mLastTraversalWasVisible = true;
+ private boolean mLastDrawScreenOff;
+
private boolean mDrewOnceForSync = false;
int mSyncSeqId = 0;
@@ -738,7 +736,6 @@
private SurfaceControl mBoundsLayer;
private final SurfaceSession mSurfaceSession = new SurfaceSession();
private final Transaction mTransaction = new Transaction();
- private final Transaction mFrameRateTransaction = new Transaction();
@UnsupportedAppUsage
boolean mAdded;
@@ -962,34 +959,6 @@
private AccessibilityWindowAttributes mAccessibilityWindowAttributes;
- /*
- * for Variable Refresh Rate project
- */
-
- // The preferred frame rate category of the view that
- // could be updated on a frame-by-frame basis.
- private int mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
- // The preferred frame rate category of the last frame that
- // could be used to lower frame rate after touch boost
- private int mLastPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
- // The preferred frame rate of the view that is mainly used for
- // touch boosting, view velocity handling, and TextureView.
- private float mPreferredFrameRate = 0;
- // Used to check if there were any view invalidations in
- // the previous time frame (FRAME_RATE_IDLENESS_REEVALUATE_TIME).
- private boolean mHasInvalidation = false;
- // Used to check if it is in the touch boosting period.
- private boolean mIsFrameRateBoosting = false;
- // Used to check if there is a message in the message queue
- // for idleness handling.
- private boolean mHasIdledMessage = false;
- // time for touch boost period.
- private static final int FRAME_RATE_TOUCH_BOOST_TIME = 1500;
- // time for checking idle status periodically.
- private static final int FRAME_RATE_IDLENESS_CHECK_TIME_MILLIS = 500;
- // time for revaluating the idle status before lowering the frame rate.
- private static final int FRAME_RATE_IDLENESS_REEVALUATE_TIME = 500;
-
/**
* A temporary object used so relayoutWindow can return the latest SyncSeqId
* system. The SyncSeqId system was designed to work without synchronous relayout
@@ -1926,12 +1895,19 @@
}
void handleAppVisibility(boolean visible) {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ Trace.instant(Trace.TRACE_TAG_VIEW, TextUtils.formatSimple(
+ "%s visibilityChanged oldVisibility=%b newVisibility=%b", mTag,
+ mAppVisible, visible));
+ }
if (mAppVisible != visible) {
final boolean previousVisible = getHostVisibility() == View.VISIBLE;
mAppVisible = visible;
final boolean currentVisible = getHostVisibility() == View.VISIBLE;
// Root view only cares about whether it is visible or not.
if (previousVisible != currentVisible) {
+ Log.d(mTag, "visibilityChanged oldVisibility=" + previousVisible + " newVisibility="
+ + currentVisible);
mAppVisibilityChanged = true;
scheduleTraversals();
}
@@ -3292,8 +3268,8 @@
|| mForceNextWindowRelayout) {
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW,
- TextUtils.formatSimple("relayoutWindow#"
- + "first=%b/resize=%b/vis=%b/params=%b/force=%b",
+ TextUtils.formatSimple("%s-relayoutWindow#"
+ + "first=%b/resize=%b/vis=%b/params=%b/force=%b", mTag,
mFirst, windowShouldResize, viewVisibilityChanged, params != null,
mForceNextWindowRelayout));
}
@@ -3882,11 +3858,7 @@
boolean cancelDueToPreDrawListener = mAttachInfo.mTreeObserver.dispatchOnPreDraw();
boolean cancelAndRedraw = cancelDueToPreDrawListener
|| (cancelDraw && mDrewOnceForSync);
- if (cancelAndRedraw) {
- Log.d(mTag, "Cancelling draw."
- + " cancelDueToPreDrawListener=" + cancelDueToPreDrawListener
- + " cancelDueToSync=" + (cancelDraw && mDrewOnceForSync));
- }
+
if (!cancelAndRedraw) {
// A sync was already requested before the WMS requested sync. This means we need to
// sync the buffer, regardless if WMS wants to sync the buffer.
@@ -3910,6 +3882,9 @@
}
if (!isViewVisible) {
+ if (mLastTraversalWasVisible) {
+ logAndTrace("Not drawing due to not visible");
+ }
mLastPerformTraversalsSkipDrawReason = "view_not_visible";
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
@@ -3921,12 +3896,23 @@
handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mHasPendingTransactions,
mPendingTransaction, "view not visible");
} else if (cancelAndRedraw) {
+ if (!mWasLastDrawCanceled) {
+ logAndTrace("Canceling draw."
+ + " cancelDueToPreDrawListener=" + cancelDueToPreDrawListener
+ + " cancelDueToSync=" + (cancelDraw && mDrewOnceForSync));
+ }
mLastPerformTraversalsSkipDrawReason = cancelDueToPreDrawListener
? "predraw_" + mAttachInfo.mTreeObserver.getLastDispatchOnPreDrawCanceledReason()
: "cancel_" + cancelReason;
// Try again
scheduleTraversals();
} else {
+ if (mWasLastDrawCanceled) {
+ logAndTrace("Draw frame after cancel");
+ }
+ if (!mLastTraversalWasVisible) {
+ logAndTrace("Start draw after previous draw not visible");
+ }
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
@@ -3938,6 +3924,8 @@
mPendingTransaction, mLastPerformDrawSkippedReason);
}
}
+ mWasLastDrawCanceled = cancelAndRedraw;
+ mLastTraversalWasVisible = isViewVisible;
if (mAttachInfo.mContentCaptureEvents != null) {
notifyContentCaptureEvents();
@@ -3958,12 +3946,6 @@
mWmsRequestSyncGroupState = WMS_SYNC_NONE;
}
}
-
- // For the variable refresh rate project.
- setPreferredFrameRate(mPreferredFrameRate);
- setPreferredFrameRateCategory(mPreferredFrameRateCategory);
- mLastPreferredFrameRateCategory = mPreferredFrameRateCategory;
- mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
}
private void createSyncIfNeeded() {
@@ -4733,10 +4715,7 @@
return didProduceBuffer -> {
if (!didProduceBuffer) {
- Trace.instant(Trace.TRACE_TAG_VIEW,
- "Transaction not synced due to no frame drawn-" + mTag);
- Log.d(mTag, "Pending transaction will not be applied in sync with a draw "
- + "because there was nothing new to draw");
+ logAndTrace("Transaction not synced due to no frame drawn");
mBlastBufferQueue.applyPendingTransactions(frame);
}
};
@@ -4753,17 +4732,26 @@
mLastPerformDrawSkippedReason = null;
if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
mLastPerformDrawSkippedReason = "screen_off";
+ if (!mLastDrawScreenOff) {
+ logAndTrace("Not drawing due to screen off");
+ }
+ mLastDrawScreenOff = true;
return false;
} else if (mView == null) {
mLastPerformDrawSkippedReason = "no_root_view";
return false;
}
+ if (mLastDrawScreenOff) {
+ logAndTrace("Resumed drawing after screen turned on");
+ mLastDrawScreenOff = false;
+ }
+
final boolean fullRedrawNeeded = mFullRedrawNeeded || surfaceSyncGroup != null;
mFullRedrawNeeded = false;
mIsDrawing = true;
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, mTag + "-draw");
addFrameCommitCallbackIfNeeded();
@@ -6016,8 +6004,6 @@
private static final int MSG_REPORT_KEEP_CLEAR_RECTS = 36;
private static final int MSG_PAUSED_FOR_SYNC_TIMEOUT = 37;
private static final int MSG_DECOR_VIEW_GESTURE_INTERCEPTION = 38;
- private static final int MSG_TOUCH_BOOST_TIMEOUT = 39;
- private static final int MSG_CHECK_INVALIDATION_IDLE = 40;
final class ViewRootHandler extends Handler {
@Override
@@ -6313,32 +6299,6 @@
mNumPausedForSync = 0;
scheduleTraversals();
break;
- case MSG_TOUCH_BOOST_TIMEOUT:
- /**
- * Lower the frame rate after the boosting period (FRAME_RATE_TOUCH_BOOST_TIME).
- */
- mIsFrameRateBoosting = false;
- setPreferredFrameRateCategory(Math.max(mPreferredFrameRateCategory,
- mLastPreferredFrameRateCategory));
- break;
- case MSG_CHECK_INVALIDATION_IDLE:
- if (!mHasInvalidation && !mIsFrameRateBoosting) {
- mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
- setPreferredFrameRateCategory(mPreferredFrameRateCategory);
- mHasIdledMessage = false;
- } else {
- /**
- * If there is no invalidation within a certain period,
- * we consider the display is idled.
- * We then set the frame rate catetogry to NO_PREFERENCE.
- * Note that SurfaceFlinger also has a mechanism to lower the refresh rate
- * if there is no updates of the buffer.
- */
- mHasInvalidation = false;
- mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE,
- FRAME_RATE_IDLENESS_REEVALUATE_TIME);
- }
- break;
}
}
}
@@ -7281,7 +7241,6 @@
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
- final int action = event.getAction();
boolean handled = mHandwritingInitiator.onTouchEvent(event);
if (handled) {
// If handwriting is started, toolkit doesn't receive ACTION_UP.
@@ -7302,22 +7261,6 @@
scheduleConsumeBatchedInputImmediately();
}
}
-
- // For the variable refresh rate project
- if (handled && shouldTouchBoost(action, mWindowAttributes.type)) {
- // set the frame rate to the maximum value.
- mIsFrameRateBoosting = true;
- setPreferredFrameRateCategory(mPreferredFrameRateCategory);
- }
- /**
- * We want to lower the refresh rate when MotionEvent.ACTION_UP,
- * MotionEvent.ACTION_CANCEL is detected.
- * Not using ACTION_MOVE to avoid checking and sending messages too frequently.
- */
- if (mIsFrameRateBoosting && (action == MotionEvent.ACTION_UP
- || action == MotionEvent.ACTION_CANCEL)) {
- sendDelayedEmptyMessage(MSG_TOUCH_BOOST_TIMEOUT, FRAME_RATE_TOUCH_BOOST_TIME);
- }
return handled ? FINISH_HANDLED : FORWARD;
}
@@ -11517,8 +11460,7 @@
@Override
public boolean applyTransactionOnDraw(@NonNull SurfaceControl.Transaction t) {
if (mRemoved || !isHardwareEnabled()) {
- Trace.instant(Trace.TRACE_TAG_VIEW, "applyTransactionOnDraw applyImmediately-" + mTag);
- Log.d(mTag, "applyTransactionOnDraw: Applying transaction immediately");
+ logAndTrace("applyTransactionOnDraw applyImmediately");
t.apply();
} else {
Trace.instant(Trace.TRACE_TAG_VIEW, "applyTransactionOnDraw-" + mTag);
@@ -11906,93 +11848,10 @@
t.clearTrustedPresentationCallback(getSurfaceControl());
}
- private void setPreferredFrameRateCategory(int preferredFrameRateCategory) {
- if (!shouldSetFrameRateCategory()) {
- return;
+ private void logAndTrace(String msg) {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ Trace.instant(Trace.TRACE_TAG_VIEW, mTag + "-" + msg);
}
-
- int frameRateCategory = mIsFrameRateBoosting
- ? FRAME_RATE_CATEGORY_HIGH : preferredFrameRateCategory;
-
- try {
- mFrameRateTransaction.setFrameRateCategory(mSurfaceControl,
- frameRateCategory, false).apply();
- } catch (Exception e) {
- Log.e(mTag, "Unable to set frame rate category", e);
- }
-
- if (mPreferredFrameRateCategory != FRAME_RATE_CATEGORY_NO_PREFERENCE && !mHasIdledMessage) {
- // Check where the display is idled periodically.
- // If so, set the frame rate category to NO_PREFERENCE
- mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE,
- FRAME_RATE_IDLENESS_CHECK_TIME_MILLIS);
- mHasIdledMessage = true;
- }
- }
-
- private void setPreferredFrameRate(float preferredFrameRate) {
- if (!shouldSetFrameRate()) {
- return;
- }
-
- try {
- mFrameRateTransaction.setFrameRate(mSurfaceControl,
- preferredFrameRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT).apply();
- } catch (Exception e) {
- Log.e(mTag, "Unable to set frame rate", e);
- }
- }
-
- private void sendDelayedEmptyMessage(int message, int delayedTime) {
- mHandler.removeMessages(message);
-
- mHandler.sendEmptyMessageDelayed(message, delayedTime);
- }
-
- private boolean shouldSetFrameRateCategory() {
- // use toolkitSetFrameRate flag to gate the change
- return mSurface.isValid() && toolkitSetFrameRate();
- }
-
- private boolean shouldSetFrameRate() {
- // use toolkitSetFrameRate flag to gate the change
- return mPreferredFrameRate > 0 && toolkitSetFrameRate();
- }
-
- private boolean shouldTouchBoost(int motionEventAction, int windowType) {
- boolean desiredAction = motionEventAction == MotionEvent.ACTION_DOWN
- || motionEventAction == MotionEvent.ACTION_MOVE
- || motionEventAction == MotionEvent.ACTION_UP;
- boolean desiredType = windowType == TYPE_BASE_APPLICATION || windowType == TYPE_APPLICATION
- || windowType == TYPE_APPLICATION_STARTING || windowType == TYPE_DRAWN_APPLICATION;
- // use toolkitSetFrameRate flag to gate the change
- return desiredAction && desiredType && toolkitSetFrameRate();
- }
-
- /**
- * Allow Views to vote for the preferred frame rate category
- *
- * @param frameRateCategory the preferred frame rate category of a View
- */
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
- public void votePreferredFrameRateCategory(int frameRateCategory) {
- mPreferredFrameRateCategory = Math.max(mPreferredFrameRateCategory, frameRateCategory);
- mHasInvalidation = true;
- }
-
- /**
- * Get the value of mPreferredFrameRateCategory
- */
- @VisibleForTesting
- public int getPreferredFrameRateCategory() {
- return mPreferredFrameRateCategory;
- }
-
- /**
- * Get the value of mPreferredFrameRate
- */
- @VisibleForTesting
- public float getPreferredFrameRate() {
- return mPreferredFrameRate;
+ Log.d(mTag, msg);
}
}
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index cc612ed..6888b50 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -1,10 +1,12 @@
package: "android.view.accessibility"
+# NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
+
flag {
+ name: "a11y_overlay_callbacks"
namespace: "accessibility"
- name: "force_invert_color"
- description: "Enable force force-dark for smart inversion and dark theme everywhere"
- bug: "282821643"
+ description: "Whether to allow the passing of result callbacks when attaching a11y overlays."
+ bug: "304478691"
}
flag {
@@ -15,8 +17,8 @@
}
flag {
- name: "a11y_overlay_callbacks"
namespace: "accessibility"
- description: "Whether to allow the passing of result callbacks when attaching a11y overlays."
- bug: "304478691"
+ name: "force_invert_color"
+ description: "Enable force force-dark for smart inversion and dark theme everywhere"
+ bug: "282821643"
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 17c82b6..a0628c4 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -9854,7 +9854,10 @@
outAttrs.initialCapsMode = ic.getCursorCapsMode(getInputType());
outAttrs.setInitialSurroundingText(mText);
outAttrs.contentMimeTypes = getReceiveContentMimeTypes();
-
+ if (android.view.inputmethod.Flags.editorinfoHandwritingEnabled()
+ && isAutoHandwritingEnabled()) {
+ outAttrs.setStylusHandwritingEnabled(true);
+ }
ArrayList<Class<? extends HandwritingGesture>> gestures = new ArrayList<>();
gestures.add(SelectGesture.class);
gestures.add(SelectRangeGesture.class);
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 7cfd35b..79b3b4f 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -8,6 +8,14 @@
}
flag {
+ name: "defer_display_updates"
+ namespace: "window_manager"
+ description: "Feature flag for deferring DisplayManager updates to WindowManager if Shell transition is running"
+ bug: "259220649"
+ is_fixed_read_only: true
+}
+
+flag {
name: "close_to_square_config_includes_status_bar"
namespace: "windowing_frontend"
description: "On close to square display, when necessary, configuration includes status bar"
diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
index b1d22e0..77e1502 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
@@ -81,11 +81,6 @@
public static final Flag PROPAGATE_CHANNEL_UPDATES_TO_CONVERSATIONS = releasedFlag(
"persist.sysui.notification.propagate_channel_updates_to_conversations");
- // TODO: b/291907312 - remove feature flags
- /** Gating the NMS->NotificationAttentionHelper buzzBeepBlink refactor */
- public static final Flag ENABLE_ATTENTION_HELPER_REFACTOR = devFlag(
- "persist.debug.sysui.notification.enable_attention_helper_refactor");
-
// TODO b/291899544: for released flags, use resource config values
/** Value used by polite notif. feature */
public static final Flag NOTIF_COOLDOWN_T1 = devFlag(
diff --git a/core/proto/android/nfc/Android.bp b/core/proto/android/nfc/Android.bp
deleted file mode 100644
index 6a62c91..0000000
--- a/core/proto/android/nfc/Android.bp
+++ /dev/null
@@ -1,43 +0,0 @@
-//
-// Copyright (C) 2023 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-filegroup {
- name: "srcs_nfc_proto",
- srcs: [
- "*.proto",
- ],
-}
-
-// Will be statically linked by `framework-nfc`.
-java_library {
- name: "nfc-proto-java-gen",
- installable: false,
- proto: {
- type: "stream",
- include_dirs: [
- "external/protobuf/src",
- ],
- },
- srcs: [
- ":srcs_nfc_proto",
- ],
- sdk_version: "current",
- min_sdk_version: "current",
-}
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 445ddf5..2993a0e 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -65,7 +65,6 @@
"device-time-shell-utils",
"testables",
"com.android.text.flags-aconfig-java",
- "flag-junit",
],
libs: [
@@ -76,7 +75,6 @@
"framework",
"ext",
"framework-res",
- "android.view.flags-aconfig-java",
],
jni_libs: [
"libpowermanagertest_jni",
diff --git a/core/tests/coretests/src/android/view/SurfaceControlRegistryTests.java b/core/tests/coretests/src/android/view/SurfaceControlRegistryTests.java
index e117051..71bdce4 100644
--- a/core/tests/coretests/src/android/view/SurfaceControlRegistryTests.java
+++ b/core/tests/coretests/src/android/view/SurfaceControlRegistryTests.java
@@ -23,6 +23,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.eq;
@@ -148,6 +149,28 @@
reporter.assertLastReportedSetEquals(sc5, sc6, sc7, sc8);
}
+ @Test
+ public void testCallStackDebugging_matchesFilters() {
+ SurfaceControlRegistry registry = SurfaceControlRegistry.getProcessInstance();
+
+ // Specific name, any call
+ registry.setCallStackDebuggingParams("com.android.app1", "");
+ assertFalse(registry.matchesForCallStackDebugging("com.android.noMatchApp", "setAlpha"));
+ assertTrue(registry.matchesForCallStackDebugging("com.android.app1", "setAlpha"));
+
+ // Any name, specific call
+ registry.setCallStackDebuggingParams("", "setAlpha");
+ assertFalse(registry.matchesForCallStackDebugging("com.android.app1", "setLayer"));
+ assertTrue(registry.matchesForCallStackDebugging("com.android.app1", "setAlpha"));
+ assertTrue(registry.matchesForCallStackDebugging("com.android.app2", "setAlpha"));
+
+ // Specific name, specific call
+ registry.setCallStackDebuggingParams("com.android.app1", "setAlpha");
+ assertFalse(registry.matchesForCallStackDebugging("com.android.app1", "setLayer"));
+ assertFalse(registry.matchesForCallStackDebugging("com.android.app2", "setAlpha"));
+ assertTrue(registry.matchesForCallStackDebugging("com.android.app1", "setAlpha"));
+ }
+
private SurfaceControl buildTestSurface() {
return new SurfaceControl.Builder()
.setContainerLayer()
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 1a38dec..6a9fc04 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -17,11 +17,6 @@
package android.view;
import static android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR;
-import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE;
-import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
-import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
-import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
-import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
@@ -53,12 +48,8 @@
import android.os.Binder;
import android.os.SystemProperties;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
-import android.util.DisplayMetrics;
import android.util.Log;
import android.view.WindowInsets.Side;
import android.view.WindowInsets.Type;
@@ -106,10 +97,6 @@
// state after the test completes.
private static boolean sOriginalTouchMode;
- @Rule
- public final CheckFlagsRule mCheckFlagsRule =
- DeviceFlagsValueProvider.createCheckFlagsRule();
-
@BeforeClass
public static void setUpClass() {
sContext = sInstrumentation.getTargetContext();
@@ -440,129 +427,6 @@
assertThat(result).isFalse();
}
- /**
- * Test the default values are properly set
- */
- @UiThreadTest
- @Test
- @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE)
- public void votePreferredFrameRate_getDefaultValues() {
- ViewRootImpl viewRootImpl = new ViewRootImpl(sContext,
- sContext.getDisplayNoVerify());
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
- FRAME_RATE_CATEGORY_NO_PREFERENCE);
- assertEquals(viewRootImpl.getPreferredFrameRate(), 0, 0.1);
- }
-
- /**
- * Test the value of the frame rate cateogry based on the visibility of a view
- * Invsible: FRAME_RATE_CATEGORY_NO_PREFERENCE
- * Visible: FRAME_RATE_CATEGORY_NORMAL
- */
- @Test
- @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE)
- public void votePreferredFrameRate_voteFrameRateCategory_visibility() {
- View view = new View(sContext);
- attachViewToWindow(view);
- ViewRootImpl viewRootImpl = view.getViewRootImpl();
- sInstrumentation.runOnMainSync(() -> {
- view.setVisibility(View.INVISIBLE);
- view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
- FRAME_RATE_CATEGORY_NO_PREFERENCE);
- });
-
- sInstrumentation.runOnMainSync(() -> {
- view.setVisibility(View.VISIBLE);
- view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
- FRAME_RATE_CATEGORY_NORMAL);
- });
- }
-
- /**
- * Test the value of the frame rate cateogry based on the size of a view.
- * The current threshold value is 7% of the screen size
- * <7%: FRAME_RATE_CATEGORY_LOW
- */
- @Test
- @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE)
- public void votePreferredFrameRate_voteFrameRateCategory_smallSize() {
- View view = new View(sContext);
- WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
- wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
- wmlp.width = 1;
- wmlp.height = 1;
-
- sInstrumentation.runOnMainSync(() -> {
- WindowManager wm = sContext.getSystemService(WindowManager.class);
- wm.addView(view, wmlp);
- });
- sInstrumentation.waitForIdleSync();
-
- ViewRootImpl viewRootImpl = view.getViewRootImpl();
- sInstrumentation.runOnMainSync(() -> {
- view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_LOW);
- });
- }
-
- /**
- * Test the value of the frame rate cateogry based on the size of a view.
- * The current threshold value is 7% of the screen size
- * >=7% : FRAME_RATE_CATEGORY_NORMAL
- */
- @Test
- @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE)
- public void votePreferredFrameRate_voteFrameRateCategory_normalSize() {
- View view = new View(sContext);
- WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
- wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
-
- sInstrumentation.runOnMainSync(() -> {
- WindowManager wm = sContext.getSystemService(WindowManager.class);
- Display display = wm.getDefaultDisplay();
- DisplayMetrics metrics = new DisplayMetrics();
- display.getMetrics(metrics);
- wmlp.width = (int) (metrics.widthPixels * 0.9);
- wmlp.height = (int) (metrics.heightPixels * 0.9);
- wm.addView(view, wmlp);
- });
- sInstrumentation.waitForIdleSync();
-
- ViewRootImpl viewRootImpl = view.getViewRootImpl();
- sInstrumentation.runOnMainSync(() -> {
- view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NORMAL);
- });
- }
-
- /**
- * Test how values of the frame rate cateogry are aggregated.
- * It should take the max value among all of the voted categories per frame.
- */
- @Test
- @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE)
- public void votePreferredFrameRate_voteFrameRateCategory_aggregate() {
- View view = new View(sContext);
- attachViewToWindow(view);
- sInstrumentation.runOnMainSync(() -> {
- ViewRootImpl viewRootImpl = view.getViewRootImpl();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
- FRAME_RATE_CATEGORY_NO_PREFERENCE);
- viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_LOW);
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_LOW);
- viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_NORMAL);
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NORMAL);
- viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_HIGH);
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
- viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_NORMAL);
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
- viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_LOW);
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
- });
- }
-
@Test
public void forceInvertOffDarkThemeOff_forceDarkModeDisabled() {
mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index b71aaf3..cc73ece 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -2887,6 +2887,12 @@
"group": "WM_DEBUG_RESIZE",
"at": "com\/android\/server\/wm\/WindowState.java"
},
+ "419378610": {
+ "message": "Content Recording: Apply transformations of shift %d x %d, scale %f x %f, crop (aka recorded content size) %d x %d for display %d; display has size %d x %d; surface has size %d x %d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_CONTENT_RECORDING",
+ "at": "com\/android\/server\/wm\/ContentRecorder.java"
+ },
"422634333": {
"message": "First draw done in potential wallpaper target %s",
"level": "VERBOSE",
@@ -4339,12 +4345,6 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
- "1936800105": {
- "message": "Content Recording: Apply transformations of shift %d x %d, scale %f, crop (aka recorded content size) %d x %d for display %d; display has size %d x %d; surface has size %d x %d",
- "level": "VERBOSE",
- "group": "WM_DEBUG_CONTENT_RECORDING",
- "at": "com\/android\/server\/wm\/ContentRecorder.java"
- },
"1945495497": {
"message": "Focused window didn't have a valid surface drawn.",
"level": "DEBUG",
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/OverlayCreateParams.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/OverlayCreateParams.java
new file mode 100644
index 0000000..ff49cdc
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/OverlayCreateParams.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.embedding;
+
+import static java.util.Objects.requireNonNull;
+
+import android.graphics.Rect;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * The parameter to create an overlay container that retrieved from
+ * {@link android.app.ActivityOptions} bundle.
+ */
+class OverlayCreateParams {
+
+ // TODO(b/295803704): Move them to WM Extensions so that we can reuse in WM Jetpack.
+ @VisibleForTesting
+ static final String KEY_OVERLAY_CREATE_PARAMS =
+ "androidx.window.extensions.OverlayCreateParams";
+
+ @VisibleForTesting
+ static final String KEY_OVERLAY_CREATE_PARAMS_TASK_ID =
+ "androidx.window.extensions.OverlayCreateParams.taskId";
+
+ @VisibleForTesting
+ static final String KEY_OVERLAY_CREATE_PARAMS_TAG =
+ "androidx.window.extensions.OverlayCreateParams.tag";
+
+ @VisibleForTesting
+ static final String KEY_OVERLAY_CREATE_PARAMS_BOUNDS =
+ "androidx.window.extensions.OverlayCreateParams.bounds";
+
+ private final int mTaskId;
+
+ @NonNull
+ private final String mTag;
+
+ @NonNull
+ private final Rect mBounds;
+
+ OverlayCreateParams(int taskId, @NonNull String tag, @NonNull Rect bounds) {
+ mTaskId = taskId;
+ mTag = requireNonNull(tag);
+ mBounds = requireNonNull(bounds);
+ }
+
+ int getTaskId() {
+ return mTaskId;
+ }
+
+ @NonNull
+ String getTag() {
+ return mTag;
+ }
+
+ @NonNull
+ Rect getBounds() {
+ return mBounds;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mTaskId;
+ result = 31 * result + mTag.hashCode();
+ result = 31 * result + mBounds.hashCode();
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) return true;
+ if (!(obj instanceof OverlayCreateParams thatParams)) return false;
+ return mTaskId == thatParams.mTaskId
+ && mTag.equals(thatParams.mTag)
+ && mBounds.equals(thatParams.mBounds);
+ }
+
+ @Override
+ public String toString() {
+ return OverlayCreateParams.class.getSimpleName() + ": {"
+ + "taskId=" + mTaskId
+ + ", tag=" + mTag
+ + ", bounds=" + mBounds
+ + "}";
+ }
+
+ /** Retrieves the {@link OverlayCreateParams} from {@link android.app.ActivityOptions} bundle */
+ @Nullable
+ static OverlayCreateParams fromBundle(@NonNull Bundle bundle) {
+ final Bundle paramsBundle = bundle.getBundle(KEY_OVERLAY_CREATE_PARAMS);
+ if (paramsBundle == null) {
+ return null;
+ }
+ final int taskId = paramsBundle.getInt(KEY_OVERLAY_CREATE_PARAMS_TASK_ID);
+ final String tag = requireNonNull(paramsBundle.getString(KEY_OVERLAY_CREATE_PARAMS_TAG));
+ final Rect bounds = requireNonNull(paramsBundle.getParcelable(
+ KEY_OVERLAY_CREATE_PARAMS_BOUNDS, Rect.class));
+
+ return new OverlayCreateParams(taskId, tag, bounds);
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index cdfc4c8..2f1eec1 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -40,9 +40,10 @@
import static androidx.window.extensions.embedding.SplitContainer.shouldFinishAssociatedContainerWhenAdjacent;
import static androidx.window.extensions.embedding.SplitContainer.shouldFinishAssociatedContainerWhenStacked;
import static androidx.window.extensions.embedding.SplitPresenter.RESULT_EXPAND_FAILED_NO_TF_INFO;
+import static androidx.window.extensions.embedding.SplitPresenter.boundsSmallerThanMinDimensions;
import static androidx.window.extensions.embedding.SplitPresenter.getActivitiesMinDimensionsPair;
import static androidx.window.extensions.embedding.SplitPresenter.getActivityIntentMinDimensionsPair;
-import static androidx.window.extensions.embedding.SplitPresenter.getTaskWindowMetrics;
+import static androidx.window.extensions.embedding.SplitPresenter.getMinDimensions;
import static androidx.window.extensions.embedding.SplitPresenter.shouldShowSplit;
import android.app.Activity;
@@ -87,6 +88,7 @@
import androidx.window.extensions.layout.WindowLayoutComponentImpl;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.window.flags.Flags;
import java.util.ArrayList;
import java.util.Collections;
@@ -123,8 +125,7 @@
* and unregistered via {@link #clearSplitAttributesCalculator()}.
* This is called when:
* <ul>
- * <li>{@link SplitPresenter#updateSplitContainer(SplitContainer, TaskFragmentContainer,
- * WindowContainerTransaction)}</li>
+ * <li>{@link SplitPresenter#updateSplitContainer}</li>
* <li>There's a started Activity which matches {@link SplitPairRule} </li>
* <li>Checking whether the place holder should be launched if there's a Activity matches
* {@link SplitPlaceholderRule} </li>
@@ -759,6 +760,8 @@
if (targetContainer == null) {
// When there is no embedding rule matched, try to place it in the top container
// like a normal launch.
+ // TODO(b/301034784): Check if it makes sense to place the activity in overlay
+ // container.
targetContainer = taskContainer.getTopNonFinishingTaskFragmentContainer();
}
if (targetContainer == null) {
@@ -1007,6 +1010,7 @@
if (taskContainer == null) {
return;
}
+ // TODO(b/301034784): Check if it makes sense to place the activity in overlay container.
final TaskFragmentContainer targetContainer =
taskContainer.getTopNonFinishingTaskFragmentContainer();
if (targetContainer == null) {
@@ -1166,7 +1170,7 @@
getActivitiesMinDimensionsPair(primaryActivity, secondaryActivity));
if (splitContainer != null && primaryContainer == splitContainer.getPrimaryContainer()
&& canReuseContainer(splitRule, splitContainer.getSplitRule(),
- getTaskWindowMetrics(taskProperties.getConfiguration()),
+ taskProperties.getTaskMetrics(),
calculatedSplitAttributes, splitContainer.getCurrentSplitAttributes())) {
// Can launch in the existing secondary container if the rules share the same
// presentation.
@@ -1408,6 +1412,22 @@
private TaskFragmentContainer createEmptyExpandedContainer(
@NonNull WindowContainerTransaction wct, @NonNull Intent intent, int taskId,
@Nullable Activity launchingActivity) {
+ return createEmptyContainer(wct, intent, taskId, new Rect(), launchingActivity,
+ null /* overlayTag */);
+ }
+
+ /**
+ * Returns an empty {@link TaskFragmentContainer} that we can launch an activity into.
+ * If {@code overlayTag} is set, it means the created {@link TaskFragmentContainer} is an
+ * overlay container.
+ */
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ @Nullable
+ TaskFragmentContainer createEmptyContainer(
+ @NonNull WindowContainerTransaction wct, @NonNull Intent intent, int taskId,
+ @NonNull Rect bounds, @Nullable Activity launchingActivity,
+ @Nullable String overlayTag) {
// We need an activity in the organizer process in the same Task to use as the owner
// activity, as well as to get the Task window info.
final Activity activityInTask;
@@ -1423,13 +1443,46 @@
// Can't find any activity in the Task that we can use as the owner activity.
return null;
}
- final TaskFragmentContainer expandedContainer = newContainer(intent, activityInTask,
- taskId);
- mPresenter.createTaskFragment(wct, expandedContainer.getTaskFragmentToken(),
- activityInTask.getActivityToken(), new Rect(), WINDOWING_MODE_UNDEFINED);
- mPresenter.updateAnimationParams(wct, expandedContainer.getTaskFragmentToken(),
+ final TaskFragmentContainer container = newContainer(null /* pendingAppearedActivity */,
+ intent, activityInTask, taskId, null /* pairedPrimaryContainer*/, overlayTag);
+ final IBinder taskFragmentToken = container.getTaskFragmentToken();
+ // Note that taskContainer will not exist before calling #newContainer if the container
+ // is the first embedded TF in the task.
+ final TaskContainer taskContainer = container.getTaskContainer();
+ final Rect taskBounds = taskContainer.getTaskProperties().getTaskMetrics().getBounds();
+ final Rect sanitizedBounds = sanitizeBounds(bounds, intent, taskBounds);
+ final int windowingMode = taskContainer
+ .getWindowingModeForSplitTaskFragment(sanitizedBounds);
+ mPresenter.createTaskFragment(wct, taskFragmentToken, activityInTask.getActivityToken(),
+ sanitizedBounds, windowingMode);
+ mPresenter.updateAnimationParams(wct, taskFragmentToken,
TaskFragmentAnimationParams.DEFAULT);
- return expandedContainer;
+ mPresenter.setTaskFragmentIsolatedNavigation(wct, taskFragmentToken,
+ overlayTag != null && !sanitizedBounds.isEmpty());
+
+ return container;
+ }
+
+ /**
+ * Returns the expanded bounds if the {@code bounds} violate minimum dimension or are not fully
+ * covered by the task bounds. Otherwise, returns {@code bounds}.
+ */
+ @NonNull
+ private static Rect sanitizeBounds(@NonNull Rect bounds, @NonNull Intent intent,
+ @NonNull Rect taskBounds) {
+ if (bounds.isEmpty()) {
+ // Don't need to check if the bounds follows the task bounds.
+ return bounds;
+ }
+ if (boundsSmallerThanMinDimensions(bounds, getMinDimensions(intent))) {
+ // Expand the bounds if the bounds are smaller than minimum dimensions.
+ return new Rect();
+ }
+ if (!taskBounds.contains(bounds)) {
+ // Expand the bounds if the bounds exceed the task bounds.
+ return new Rect();
+ }
+ return bounds;
}
/**
@@ -1449,8 +1502,7 @@
final SplitContainer splitContainer = getActiveSplitForContainer(existingContainer);
final TaskContainer.TaskProperties taskProperties = mPresenter
.getTaskProperties(primaryActivity);
- final WindowMetrics taskWindowMetrics = getTaskWindowMetrics(
- taskProperties.getConfiguration());
+ final WindowMetrics taskWindowMetrics = taskProperties.getTaskMetrics();
final SplitAttributes calculatedSplitAttributes = mPresenter.computeSplitAttributes(
taskProperties, splitRule, splitRule.getDefaultSplitAttributes(),
getActivityIntentMinDimensionsPair(primaryActivity, intent));
@@ -1519,14 +1571,22 @@
TaskFragmentContainer newContainer(@NonNull Activity pendingAppearedActivity,
@NonNull Activity activityInTask, int taskId) {
return newContainer(pendingAppearedActivity, null /* pendingAppearedIntent */,
- activityInTask, taskId, null /* pairedPrimaryContainer */);
+ activityInTask, taskId, null /* pairedPrimaryContainer */, null /* tag */);
}
@GuardedBy("mLock")
TaskFragmentContainer newContainer(@NonNull Intent pendingAppearedIntent,
@NonNull Activity activityInTask, int taskId) {
return newContainer(null /* pendingAppearedActivity */, pendingAppearedIntent,
- activityInTask, taskId, null /* pairedPrimaryContainer */);
+ activityInTask, taskId, null /* pairedPrimaryContainer */, null /* tag */);
+ }
+
+ @GuardedBy("mLock")
+ TaskFragmentContainer newContainer(@NonNull Intent pendingAppearedIntent,
+ @NonNull Activity activityInTask, int taskId,
+ @NonNull TaskFragmentContainer pairedPrimaryContainer) {
+ return newContainer(null /* pendingAppearedActivity */, pendingAppearedIntent,
+ activityInTask, taskId, pairedPrimaryContainer, null /* tag */);
}
/**
@@ -1540,11 +1600,14 @@
* @param taskId parent Task of the new TaskFragment.
* @param pairedPrimaryContainer the paired primary {@link TaskFragmentContainer}. When it is
* set, the new container will be added right above it.
+ * @param overlayTag The tag for the new created overlay container. It must be
+ * needed if {@code isOverlay} is {@code true}. Otherwise,
+ * it should be {@code null}.
*/
@GuardedBy("mLock")
TaskFragmentContainer newContainer(@Nullable Activity pendingAppearedActivity,
@Nullable Intent pendingAppearedIntent, @NonNull Activity activityInTask, int taskId,
- @Nullable TaskFragmentContainer pairedPrimaryContainer) {
+ @Nullable TaskFragmentContainer pairedPrimaryContainer, @Nullable String overlayTag) {
if (activityInTask == null) {
throw new IllegalArgumentException("activityInTask must not be null,");
}
@@ -1553,7 +1616,7 @@
}
final TaskContainer taskContainer = mTaskContainers.get(taskId);
final TaskFragmentContainer container = new TaskFragmentContainer(pendingAppearedActivity,
- pendingAppearedIntent, taskContainer, this, pairedPrimaryContainer);
+ pendingAppearedIntent, taskContainer, this, pairedPrimaryContainer, overlayTag);
return container;
}
@@ -1754,13 +1817,12 @@
* Updates {@link SplitContainer} with the given {@link SplitAttributes} if the
* {@link SplitContainer} is the top most and not finished. If passed {@link SplitAttributes}
* are {@code null}, the {@link SplitAttributes} will be calculated with
- * {@link SplitPresenter#computeSplitAttributes(TaskContainer.TaskProperties, SplitRule, Pair)}.
+ * {@link SplitPresenter#computeSplitAttributes}.
*
* @param splitContainer The {@link SplitContainer} to update
* @param splitAttributes Update with this {@code splitAttributes} if it is not {@code null}.
* Otherwise, use the value calculated by
- * {@link SplitPresenter#computeSplitAttributes(
- * TaskContainer.TaskProperties, SplitRule, Pair)}
+ * {@link SplitPresenter#computeSplitAttributes}
*
* @return {@code true} if the update succeed. Otherwise, returns {@code false}.
*/
@@ -2255,6 +2317,96 @@
return shouldRetainAssociatedContainer(finishingContainer, associatedContainer);
}
+ /**
+ * Gets all overlay containers from all tasks in this process, or an empty list if there's
+ * no overlay container.
+ * <p>
+ * Note that we only support one overlay container for each task, but an app could have multiple
+ * tasks.
+ */
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ @NonNull
+ List<TaskFragmentContainer> getAllOverlayTaskFragmentContainers() {
+ final List<TaskFragmentContainer> overlayContainers = new ArrayList<>();
+ for (int i = 0; i < mTaskContainers.size(); i++) {
+ final TaskContainer taskContainer = mTaskContainers.valueAt(i);
+ final TaskFragmentContainer overlayContainer = taskContainer.getOverlayContainer();
+ if (overlayContainer != null) {
+ overlayContainers.add(overlayContainer);
+ }
+ }
+ return overlayContainers;
+ }
+
+ @VisibleForTesting
+ // Suppress GuardedBy warning because lint ask to mark this method as
+ // @GuardedBy(container.mController.mLock), which is mLock itself
+ @SuppressWarnings("GuardedBy")
+ @GuardedBy("mLock")
+ @Nullable
+ TaskFragmentContainer createOrUpdateOverlayTaskFragmentIfNeeded(
+ @NonNull WindowContainerTransaction wct,
+ @NonNull OverlayCreateParams overlayCreateParams, int launchTaskId,
+ @NonNull Intent intent, @NonNull Activity launchActivity) {
+ final int taskId = overlayCreateParams.getTaskId();
+ if (taskId != launchTaskId) {
+ // The task ID doesn't match the launch activity's. Cannot determine the host task
+ // to launch the overlay.
+ throw new IllegalArgumentException("The task ID of "
+ + "OverlayCreateParams#launchingActivity must match the task ID of "
+ + "the activity to #startActivity with the activity options that takes "
+ + "OverlayCreateParams.");
+ }
+ final List<TaskFragmentContainer> overlayContainers =
+ getAllOverlayTaskFragmentContainers();
+ final String overlayTag = overlayCreateParams.getTag();
+
+ // If the requested bounds of OverlayCreateParams are smaller than minimum dimensions
+ // specified by Intent, expand the overlay container to fill the parent task instead.
+ final Rect bounds = overlayCreateParams.getBounds();
+ final Size minDimensions = getMinDimensions(intent);
+ final boolean shouldExpandContainer = boundsSmallerThanMinDimensions(bounds,
+ minDimensions);
+ if (!overlayContainers.isEmpty()) {
+ for (final TaskFragmentContainer overlayContainer : overlayContainers) {
+ if (!overlayTag.equals(overlayContainer.getOverlayTag())
+ && taskId == overlayContainer.getTaskId()) {
+ // If there's an overlay container with different tag shown in the same
+ // task, dismiss the existing overlay container.
+ overlayContainer.finish(false /* shouldFinishDependant */, mPresenter,
+ wct, SplitController.this);
+ }
+ if (overlayTag.equals(overlayContainer.getOverlayTag())
+ && taskId != overlayContainer.getTaskId()) {
+ // If there's an overlay container with same tag in a different task,
+ // dismiss the overlay container since the tag must be unique per process.
+ overlayContainer.finish(false /* shouldFinishDependant */, mPresenter,
+ wct, SplitController.this);
+ }
+ if (overlayTag.equals(overlayContainer.getOverlayTag())
+ && taskId == overlayContainer.getTaskId()) {
+ // If there's an overlay container with the same tag and task ID, we treat
+ // the OverlayCreateParams as the update to the container.
+ final Rect taskBounds = overlayContainer.getTaskContainer().getTaskProperties()
+ .getTaskMetrics().getBounds();
+ final IBinder overlayToken = overlayContainer.getTaskFragmentToken();
+ final Rect sanitizedBounds = sanitizeBounds(bounds, intent, taskBounds);
+ mPresenter.resizeTaskFragment(wct, overlayToken, sanitizedBounds);
+ mPresenter.setTaskFragmentIsolatedNavigation(wct, overlayToken,
+ !sanitizedBounds.isEmpty());
+ // We can just return the updated overlay container and don't need to
+ // check other condition since we only have one OverlayCreateParams, and
+ // if the tag and task are matched, it's impossible to match another task
+ // or tag since tags and tasks are all unique.
+ return overlayContainer;
+ }
+ }
+ }
+ return createEmptyContainer(wct, intent, taskId,
+ (shouldExpandContainer ? new Rect() : bounds), launchActivity, overlayTag);
+ }
+
private final class LifecycleCallbacks extends EmptyLifecycleCallbacksAdapter {
@Override
@@ -2417,8 +2569,16 @@
final TaskFragmentContainer launchedInTaskFragment;
if (launchingActivity != null) {
final int taskId = getTaskId(launchingActivity);
- launchedInTaskFragment = resolveStartActivityIntent(wct, taskId, intent,
- launchingActivity);
+ final OverlayCreateParams overlayCreateParams =
+ OverlayCreateParams.fromBundle(options);
+ if (Flags.activityEmbeddingOverlayPresentationFlag()
+ && overlayCreateParams != null) {
+ launchedInTaskFragment = createOrUpdateOverlayTaskFragmentIfNeeded(wct,
+ overlayCreateParams, taskId, intent, launchingActivity);
+ } else {
+ launchedInTaskFragment = resolveStartActivityIntent(wct, taskId, intent,
+ launchingActivity);
+ }
} else {
launchedInTaskFragment = resolveStartActivityIntentFromNonActivityContext(wct,
intent);
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index d894487..66e76c5 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -30,12 +30,10 @@
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
-import android.util.DisplayMetrics;
import android.util.LayoutDirection;
import android.util.Pair;
import android.util.Size;
import android.view.View;
-import android.view.WindowInsets;
import android.view.WindowMetrics;
import android.window.TaskFragmentAnimationParams;
import android.window.TaskFragmentCreationParams;
@@ -307,8 +305,8 @@
}
final int taskId = primaryContainer.getTaskId();
- final TaskFragmentContainer secondaryContainer = mController.newContainer(
- null /* pendingAppearedActivity */, activityIntent, launchingActivity, taskId,
+ final TaskFragmentContainer secondaryContainer = mController.newContainer(activityIntent,
+ launchingActivity, taskId,
// Pass in the primary container to make sure it is added right above the primary.
primaryContainer);
final TaskContainer taskContainer = mController.getTaskContainer(taskId);
@@ -618,7 +616,7 @@
@NonNull SplitRule rule, @NonNull SplitAttributes defaultSplitAttributes,
@Nullable Pair<Size, Size> minDimensionsPair) {
final Configuration taskConfiguration = taskProperties.getConfiguration();
- final WindowMetrics taskWindowMetrics = getTaskWindowMetrics(taskConfiguration);
+ final WindowMetrics taskWindowMetrics = taskProperties.getTaskMetrics();
final Function<SplitAttributesCalculatorParams, SplitAttributes> calculator =
mController.getSplitAttributesCalculator();
final boolean areDefaultConstraintsSatisfied = rule.checkParentMetrics(taskWindowMetrics);
@@ -713,11 +711,15 @@
return new Size(windowLayout.minWidth, windowLayout.minHeight);
}
- private static boolean boundsSmallerThanMinDimensions(@NonNull Rect bounds,
+ static boolean boundsSmallerThanMinDimensions(@NonNull Rect bounds,
@Nullable Size minDimensions) {
if (minDimensions == null) {
return false;
}
+ // Empty bounds mean the bounds follow the parent host task's bounds. Skip the check.
+ if (bounds.isEmpty()) {
+ return false;
+ }
return bounds.width() < minDimensions.getWidth()
|| bounds.height() < minDimensions.getHeight();
}
@@ -1066,14 +1068,6 @@
@NonNull
WindowMetrics getTaskWindowMetrics(@NonNull Activity activity) {
- return getTaskWindowMetrics(getTaskProperties(activity).getConfiguration());
- }
-
- @NonNull
- static WindowMetrics getTaskWindowMetrics(@NonNull Configuration taskConfiguration) {
- final Rect taskBounds = taskConfiguration.windowConfiguration.getBounds();
- // TODO(b/190433398): Supply correct insets.
- final float density = taskConfiguration.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
- return new WindowMetrics(taskBounds, WindowInsets.CONSUMED, density);
+ return getTaskProperties(activity).getTaskMetrics();
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index 9a0769a..f4427aa 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -30,7 +30,10 @@
import android.graphics.Rect;
import android.os.IBinder;
import android.util.ArraySet;
+import android.util.DisplayMetrics;
import android.util.Log;
+import android.view.WindowInsets;
+import android.view.WindowMetrics;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentParentInfo;
import android.window.WindowContainerTransaction;
@@ -61,6 +64,10 @@
@Nullable
private SplitPinContainer mSplitPinContainer;
+ /** The overlay container in this Task. */
+ @Nullable
+ private TaskFragmentContainer mOverlayContainer;
+
@NonNull
private final Configuration mConfiguration;
@@ -221,6 +228,12 @@
return null;
}
+ /** Returns the overlay container in the task, or {@code null} if it doesn't exist. */
+ @Nullable
+ TaskFragmentContainer getOverlayContainer() {
+ return mOverlayContainer;
+ }
+
int indexOf(@NonNull TaskFragmentContainer child) {
return mContainers.indexOf(child);
}
@@ -311,8 +324,8 @@
onTaskFragmentContainerUpdated();
}
- void removeTaskFragmentContainers(@NonNull List<TaskFragmentContainer> taskFragmentContainer) {
- mContainers.removeAll(taskFragmentContainer);
+ void removeTaskFragmentContainers(@NonNull List<TaskFragmentContainer> taskFragmentContainers) {
+ mContainers.removeAll(taskFragmentContainers);
onTaskFragmentContainerUpdated();
}
@@ -332,6 +345,15 @@
}
private void onTaskFragmentContainerUpdated() {
+ // TODO(b/300211704): Find a better mechanism to handle the z-order in case we introduce
+ // another special container that should also be on top in the future.
+ updateSplitPinContainerIfNecessary();
+ // Update overlay container after split pin container since the overlay should be on top of
+ // pin container.
+ updateOverlayContainerIfNecessary();
+ }
+
+ private void updateSplitPinContainerIfNecessary() {
if (mSplitPinContainer == null) {
return;
}
@@ -344,10 +366,7 @@
}
// Ensure the pinned container is top-most.
- if (pinnedContainerIndex != mContainers.size() - 1) {
- mContainers.remove(pinnedContainer);
- mContainers.add(pinnedContainer);
- }
+ moveContainerToLastIfNecessary(pinnedContainer);
// Update the primary container adjacent to the pinned container if needed.
final TaskFragmentContainer adjacentContainer =
@@ -359,6 +378,31 @@
}
}
+ private void updateOverlayContainerIfNecessary() {
+ final List<TaskFragmentContainer> overlayContainers = mContainers.stream()
+ .filter(TaskFragmentContainer::isOverlay).toList();
+ if (overlayContainers.size() > 1) {
+ throw new IllegalStateException("There must be at most one overlay container per Task");
+ }
+ mOverlayContainer = overlayContainers.isEmpty() ? null : overlayContainers.get(0);
+ if (mOverlayContainer != null) {
+ moveContainerToLastIfNecessary(mOverlayContainer);
+ }
+ }
+
+ /** Moves the {@code container} to the last to align taskFragments' z-order. */
+ private void moveContainerToLastIfNecessary(@NonNull TaskFragmentContainer container) {
+ final int index = mContainers.indexOf(container);
+ if (index < 0) {
+ Log.w(TAG, "The container:" + container + " is not in the container list!");
+ return;
+ }
+ if (index != mContainers.size() - 1) {
+ mContainers.remove(container);
+ mContainers.add(container);
+ }
+ }
+
/**
* Gets the descriptors of split states in this Task.
*
@@ -398,6 +442,15 @@
return mConfiguration;
}
+ /** A helper method to return task {@link WindowMetrics} from this {@link TaskProperties} */
+ @NonNull
+ WindowMetrics getTaskMetrics() {
+ final Rect taskBounds = mConfiguration.windowConfiguration.getBounds();
+ // TODO(b/190433398): Supply correct insets.
+ final float density = mConfiguration.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
+ return new WindowMetrics(taskBounds, WindowInsets.CONSUMED, density);
+ }
+
/** Translates the given absolute bounds to relative bounds in this Task coordinate. */
void translateAbsoluteBoundsToRelativeBounds(@NonNull Rect inOutBounds) {
if (inOutBounds.isEmpty()) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index 0a694b5..2ba5c9b 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -102,6 +102,9 @@
*/
private final List<IBinder> mActivitiesToFinishOnExit = new ArrayList<>();
+ @Nullable
+ private final String mOverlayTag;
+
/** Indicates whether the container was cleaned up after the last activity was removed. */
private boolean mIsFinished;
@@ -158,14 +161,28 @@
private boolean mHasCrossProcessActivities;
/**
+ * @see #TaskFragmentContainer(Activity, Intent, TaskContainer, SplitController,
+ * TaskFragmentContainer, String)
+ */
+ TaskFragmentContainer(@Nullable Activity pendingAppearedActivity,
+ @Nullable Intent pendingAppearedIntent,
+ @NonNull TaskContainer taskContainer,
+ @NonNull SplitController controller,
+ @Nullable TaskFragmentContainer pairedPrimaryContainer) {
+ this(pendingAppearedActivity, pendingAppearedIntent, taskContainer,
+ controller, pairedPrimaryContainer, null /* overlayTag */);
+ }
+
+ /**
* Creates a container with an existing activity that will be re-parented to it in a window
* container transaction.
* @param pairedPrimaryContainer when it is set, the new container will be add right above it
+ * @param overlayTag Sets to indicate this taskFragment is an overlay container
*/
TaskFragmentContainer(@Nullable Activity pendingAppearedActivity,
@Nullable Intent pendingAppearedIntent, @NonNull TaskContainer taskContainer,
@NonNull SplitController controller,
- @Nullable TaskFragmentContainer pairedPrimaryContainer) {
+ @Nullable TaskFragmentContainer pairedPrimaryContainer, @Nullable String overlayTag) {
if ((pendingAppearedActivity == null && pendingAppearedIntent == null)
|| (pendingAppearedActivity != null && pendingAppearedIntent != null)) {
throw new IllegalArgumentException(
@@ -174,6 +191,8 @@
mController = controller;
mToken = new Binder("TaskFragmentContainer");
mTaskContainer = taskContainer;
+ mOverlayTag = overlayTag;
+
if (pairedPrimaryContainer != null) {
// The TaskFragment will be positioned right above the paired container.
if (pairedPrimaryContainer.getTaskContainer() != taskContainer) {
@@ -863,6 +882,20 @@
return mTaskContainer.indexOf(this) > mTaskContainer.indexOf(other);
}
+ /** Returns whether this taskFragment container is an overlay container. */
+ boolean isOverlay() {
+ return mOverlayTag != null;
+ }
+
+ /**
+ * Returns the tag specified in {@link OverlayCreateParams#getTag()}. {@code null} if this
+ * taskFragment container is not an overlay container.
+ */
+ @Nullable
+ String getOverlayTag() {
+ return mOverlayTag;
+ }
+
@Override
public String toString() {
return toString(true /* includeContainersToFinishOnExit */);
@@ -881,6 +914,7 @@
+ " topNonFinishingActivity=" + getTopNonFinishingActivity()
+ " runningActivityCount=" + getRunningActivityCount()
+ " isFinished=" + mIsFinished
+ + " overlayTag=" + mOverlayTag
+ " lastRequestedBounds=" + mLastRequestedBounds
+ " pendingAppearedActivities=" + mPendingAppearedActivities
+ (includeContainersToFinishOnExit ? " containersToFinishOnExit="
diff --git a/libs/WindowManager/Jetpack/tests/unittest/Android.bp b/libs/WindowManager/Jetpack/tests/unittest/Android.bp
index ed2ff2d..4ddbd13 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/Android.bp
+++ b/libs/WindowManager/Jetpack/tests/unittest/Android.bp
@@ -36,6 +36,7 @@
"androidx.test.runner",
"androidx.test.rules",
"androidx.test.ext.junit",
+ "flag-junit",
"mockito-target-extended-minus-junit4",
"truth",
"testables",
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
new file mode 100644
index 0000000..af8017a
--- /dev/null
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -0,0 +1,393 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.embedding;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_BOUNDS;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMockTaskFragmentInfo;
+import static androidx.window.extensions.embedding.OverlayCreateParams.KEY_OVERLAY_CREATE_PARAMS;
+import static androidx.window.extensions.embedding.OverlayCreateParams.KEY_OVERLAY_CREATE_PARAMS_BOUNDS;
+import static androidx.window.extensions.embedding.OverlayCreateParams.KEY_OVERLAY_CREATE_PARAMS_TAG;
+import static androidx.window.extensions.embedding.OverlayCreateParams.KEY_OVERLAY_CREATE_PARAMS_TASK_ID;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.window.TaskFragmentInfo;
+import android.window.WindowContainerTransaction;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
+import androidx.window.extensions.layout.WindowLayoutComponentImpl;
+import androidx.window.extensions.layout.WindowLayoutInfo;
+
+import com.android.window.flags.Flags;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Test class for overlay presentation feature.
+ *
+ * Build/Install/Run:
+ * atest WMJetpackUnitTests:OverlayPresentationTest
+ */
+// Suppress GuardedBy warning on unit tests
+@SuppressWarnings("GuardedBy")
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class OverlayPresentationTest {
+
+ @Rule
+ public final SetFlagsRule mSetFlagRule = new SetFlagsRule();
+
+ private static final OverlayCreateParams TEST_OVERLAY_CREATE_PARAMS =
+ new OverlayCreateParams(TASK_ID, "test,", new Rect(0, 0, 200, 200));
+
+ private SplitController.ActivityStartMonitor mMonitor;
+
+ private Intent mIntent;
+
+ private TaskFragmentContainer mOverlayContainer1;
+
+ private TaskFragmentContainer mOverlayContainer2;
+
+ private Activity mActivity;
+ @Mock
+ private Resources mActivityResources;
+
+ @Mock
+ private WindowContainerTransaction mTransaction;
+ @Mock
+ private Handler mHandler;
+ @Mock
+ private WindowLayoutComponentImpl mWindowLayoutComponent;
+
+ private SplitController mSplitController;
+ private SplitPresenter mSplitPresenter;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ doReturn(new WindowLayoutInfo(new ArrayList<>())).when(mWindowLayoutComponent)
+ .getCurrentWindowLayoutInfo(anyInt(), any());
+ DeviceStateManagerFoldingFeatureProducer producer =
+ mock(DeviceStateManagerFoldingFeatureProducer.class);
+ mSplitController = new SplitController(mWindowLayoutComponent, producer);
+ mSplitPresenter = mSplitController.mPresenter;
+ mMonitor = mSplitController.getActivityStartMonitor();
+ mIntent = new Intent();
+
+ spyOn(mSplitController);
+ spyOn(mSplitPresenter);
+ spyOn(mMonitor);
+
+ doNothing().when(mSplitPresenter).applyTransaction(any(), anyInt(), anyBoolean());
+ final Configuration activityConfig = new Configuration();
+ activityConfig.windowConfiguration.setBounds(TASK_BOUNDS);
+ activityConfig.windowConfiguration.setMaxBounds(TASK_BOUNDS);
+ doReturn(activityConfig).when(mActivityResources).getConfiguration();
+ doReturn(mHandler).when(mSplitController).getHandler();
+ mActivity = createMockActivity();
+
+ mSetFlagRule.enableFlags(Flags.FLAG_ACTIVITY_EMBEDDING_OVERLAY_PRESENTATION_FLAG);
+ }
+
+ /** Creates a mock activity in the organizer process. */
+ @NonNull
+ private Activity createMockActivity() {
+ final Activity activity = mock(Activity.class);
+ doReturn(mActivityResources).when(activity).getResources();
+ final IBinder activityToken = new Binder();
+ doReturn(activityToken).when(activity).getActivityToken();
+ doReturn(activity).when(mSplitController).getActivity(activityToken);
+ doReturn(TASK_ID).when(activity).getTaskId();
+ doReturn(new ActivityInfo()).when(activity).getActivityInfo();
+ doReturn(DEFAULT_DISPLAY).when(activity).getDisplayId();
+ return activity;
+ }
+
+ @Test
+ public void testOverlayCreateParamsFromBundle() {
+ assertThat(OverlayCreateParams.fromBundle(new Bundle())).isNull();
+
+ assertThat(OverlayCreateParams.fromBundle(createOverlayCreateParamsTestBundle()))
+ .isEqualTo(TEST_OVERLAY_CREATE_PARAMS);
+ }
+
+ @Test
+ public void testStartActivity_overlayFeatureDisabled_notInvokeCreateOverlayContainer() {
+ mSetFlagRule.disableFlags(Flags.FLAG_ACTIVITY_EMBEDDING_OVERLAY_PRESENTATION_FLAG);
+
+ mMonitor.onStartActivity(mActivity, mIntent, createOverlayCreateParamsTestBundle());
+
+ verify(mSplitController, never()).createOrUpdateOverlayTaskFragmentIfNeeded(any(), any(),
+ anyInt(), any(), any());
+ }
+
+ @NonNull
+ private static Bundle createOverlayCreateParamsTestBundle() {
+ final Bundle bundle = new Bundle();
+
+ final Bundle paramsBundle = new Bundle();
+ paramsBundle.putInt(KEY_OVERLAY_CREATE_PARAMS_TASK_ID,
+ TEST_OVERLAY_CREATE_PARAMS.getTaskId());
+ paramsBundle.putString(KEY_OVERLAY_CREATE_PARAMS_TAG, TEST_OVERLAY_CREATE_PARAMS.getTag());
+ paramsBundle.putObject(KEY_OVERLAY_CREATE_PARAMS_BOUNDS,
+ TEST_OVERLAY_CREATE_PARAMS.getBounds());
+
+ bundle.putBundle(KEY_OVERLAY_CREATE_PARAMS, paramsBundle);
+
+ return bundle;
+ }
+
+ @Test
+ public void testGetOverlayContainers() {
+ assertThat(mSplitController.getAllOverlayTaskFragmentContainers()).isEmpty();
+
+ final TaskFragmentContainer overlayContainer1 =
+ createTestOverlayContainer(TASK_ID, "test1");
+
+ assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
+ .containsExactly(overlayContainer1);
+
+ assertThrows(
+ "The exception must throw if there are two overlay containers in the same task.",
+ IllegalStateException.class,
+ () -> createTestOverlayContainer(TASK_ID, "test2"));
+
+ final TaskFragmentContainer overlayContainer3 =
+ createTestOverlayContainer(TASK_ID + 1, "test3");
+
+ assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
+ .containsExactly(overlayContainer1, overlayContainer3);
+ }
+
+ @Test
+ public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_taskIdNotMatch_throwException() {
+ assertThrows("The method must return null due to task mismatch between"
+ + " launchingActivity and OverlayCreateParams", IllegalArgumentException.class,
+ () -> createOrUpdateOverlayTaskFragmentIfNeeded(
+ TEST_OVERLAY_CREATE_PARAMS, TASK_ID + 1));
+ }
+
+ @Test
+ public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_anotherTagInTask_dismissOverlay() {
+ createExistingOverlayContainers();
+
+ final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded(
+ new OverlayCreateParams(TASK_ID, "test3", new Rect(0, 0, 100, 100)), TASK_ID);
+
+ assertWithMessage("overlayContainer1 must be dismissed since the new overlay container"
+ + " is launched to the same task")
+ .that(mSplitController.getAllOverlayTaskFragmentContainers())
+ .containsExactly(mOverlayContainer2, overlayContainer);
+ }
+
+ @Test
+ public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_sameTagAnotherTask_dismissOverlay() {
+ createExistingOverlayContainers();
+
+ final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded(
+ new OverlayCreateParams(TASK_ID + 2, "test1", new Rect(0, 0, 100, 100)),
+ TASK_ID + 2);
+
+ assertWithMessage("overlayContainer1 must be dismissed since the new overlay container"
+ + " is launched with the same tag as an existing overlay container in a different "
+ + "task")
+ .that(mSplitController.getAllOverlayTaskFragmentContainers())
+ .containsExactly(mOverlayContainer2, overlayContainer);
+ }
+
+ @Test
+ public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_sameTagAndTask_updateOverlay() {
+ createExistingOverlayContainers();
+
+ final Rect bounds = new Rect(0, 0, 100, 100);
+ final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded(
+ new OverlayCreateParams(TASK_ID, "test1", bounds),
+ TASK_ID);
+
+ assertWithMessage("overlayContainer1 must be updated since the new overlay container"
+ + " is launched with the same tag and task")
+ .that(mSplitController.getAllOverlayTaskFragmentContainers())
+ .containsExactly(mOverlayContainer1, mOverlayContainer2);
+
+ assertThat(overlayContainer).isEqualTo(mOverlayContainer1);
+ verify(mSplitPresenter).resizeTaskFragment(eq(mTransaction),
+ eq(mOverlayContainer1.getTaskFragmentToken()), eq(bounds));
+ }
+
+ @Test
+ public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_dismissMultipleOverlays() {
+ createExistingOverlayContainers();
+
+ final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded(
+ new OverlayCreateParams(TASK_ID, "test2", new Rect(0, 0, 100, 100)),
+ TASK_ID);
+
+ // OverlayContainer1 is dismissed since new container is launched in the same task with
+ // different tag. OverlayContainer2 is dismissed since new container is launched with the
+ // same tag in different task.
+ assertWithMessage("overlayContainer1 and overlayContainer2 must be dismissed")
+ .that(mSplitController.getAllOverlayTaskFragmentContainers())
+ .containsExactly(overlayContainer);
+ }
+
+ private void createExistingOverlayContainers() {
+ mOverlayContainer1 = createTestOverlayContainer(TASK_ID, "test1");
+ mOverlayContainer2 = createTestOverlayContainer(TASK_ID + 1, "test2");
+ List<TaskFragmentContainer> overlayContainers = mSplitController
+ .getAllOverlayTaskFragmentContainers();
+ assertThat(overlayContainers).containsExactly(mOverlayContainer1, mOverlayContainer2);
+ }
+
+ @Test
+ public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_smallerThanMinDimens_expandOverlay() {
+ mIntent.setComponent(new ComponentName(ApplicationProvider.getApplicationContext(),
+ MinimumDimensionActivity.class));
+
+ final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded(
+ TEST_OVERLAY_CREATE_PARAMS, TASK_ID);
+ final IBinder overlayToken = overlayContainer.getTaskFragmentToken();
+
+ assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
+ .containsExactly(overlayContainer);
+ assertThat(overlayContainer.areLastRequestedBoundsEqual(new Rect())).isTrue();
+ verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, overlayToken,
+ false);
+
+ // Call createOrUpdateOverlayTaskFragmentIfNeeded again to check the update case.
+ clearInvocations(mSplitPresenter);
+ createOrUpdateOverlayTaskFragmentIfNeeded(TEST_OVERLAY_CREATE_PARAMS, TASK_ID);
+
+ verify(mSplitPresenter).resizeTaskFragment(mTransaction, overlayToken, new Rect());
+ verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, overlayToken,
+ false);
+ assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
+ .containsExactly(overlayContainer);
+ }
+
+ @Test
+ public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_notInTaskBounds_expandOverlay() {
+ final Rect bounds = new Rect(TASK_BOUNDS);
+ bounds.offset(10, 10);
+ final OverlayCreateParams paramsOutsideTaskBounds = new OverlayCreateParams(TASK_ID,
+ "test", bounds);
+
+ final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded(
+ paramsOutsideTaskBounds, TASK_ID);
+ final IBinder overlayToken = overlayContainer.getTaskFragmentToken();
+
+ assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
+ .containsExactly(overlayContainer);
+ assertThat(overlayContainer.areLastRequestedBoundsEqual(new Rect())).isTrue();
+ verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, overlayToken,
+ false);
+
+ // Call createOrUpdateOverlayTaskFragmentIfNeeded again to check the update case.
+ clearInvocations(mSplitPresenter);
+ createOrUpdateOverlayTaskFragmentIfNeeded(paramsOutsideTaskBounds, TASK_ID);
+
+ verify(mSplitPresenter).resizeTaskFragment(mTransaction, overlayToken, new Rect());
+ verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, overlayToken,
+ false);
+ assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
+ .containsExactly(overlayContainer);
+ }
+
+ @Test
+ public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_createOverlay() {
+ final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded(
+ TEST_OVERLAY_CREATE_PARAMS, TASK_ID);
+
+ assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
+ .containsExactly(overlayContainer);
+ assertThat(overlayContainer.getTaskId()).isEqualTo(TASK_ID);
+ assertThat(overlayContainer
+ .areLastRequestedBoundsEqual(TEST_OVERLAY_CREATE_PARAMS.getBounds())).isTrue();
+ assertThat(overlayContainer.getOverlayTag()).isEqualTo(TEST_OVERLAY_CREATE_PARAMS.getTag());
+ }
+
+ /**
+ * A simplified version of {@link SplitController.ActivityStartMonitor
+ * #createOrUpdateOverlayTaskFragmentIfNeeded}
+ */
+ @Nullable
+ private TaskFragmentContainer createOrUpdateOverlayTaskFragmentIfNeeded(
+ @NonNull OverlayCreateParams params, int taskId) {
+ return mSplitController.createOrUpdateOverlayTaskFragmentIfNeeded(mTransaction, params,
+ taskId, mIntent, mActivity);
+ }
+
+ @NonNull
+ private TaskFragmentContainer createTestOverlayContainer(int taskId, @NonNull String tag) {
+ TaskFragmentContainer overlayContainer = mSplitController.newContainer(
+ null /* pendingAppearedActivity */, mIntent, mActivity, taskId,
+ null /* pairedPrimaryContainer */, tag);
+ setupTaskFragmentInfo(overlayContainer, mActivity);
+ return overlayContainer;
+ }
+
+ private void setupTaskFragmentInfo(@NonNull TaskFragmentContainer container,
+ @NonNull Activity activity) {
+ final TaskFragmentInfo info = createMockTaskFragmentInfo(container, activity);
+ container.setInfo(mTransaction, info);
+ mSplitPresenter.mFragmentInfos.put(container.getTaskFragmentToken(), info);
+ }
+}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index d440a3e..6c0b3cf 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -60,6 +60,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.clearInvocations;
@@ -634,7 +635,8 @@
false /* isOnReparent */);
assertFalse(result);
- verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt(), any());
+ verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt(), any(),
+ anyString());
}
@Test
@@ -796,7 +798,8 @@
false /* isOnReparent */);
assertTrue(result);
- verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt(), any());
+ verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt(), any(),
+ anyString());
verify(mSplitController, never()).registerSplit(any(), any(), any(), any(), any(), any());
}
@@ -838,7 +841,8 @@
false /* isOnReparent */);
assertTrue(result);
- verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt(), any());
+ verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt(), any(),
+ anyString());
verify(mSplitController, never()).registerSplit(any(), any(), any(), any(), any(), any());
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
index 738c94e..79f306e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
@@ -19,6 +19,7 @@
import static android.view.View.LAYOUT_DIRECTION_RTL;
import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RESTING;
+import static com.android.wm.shell.bubbles.animation.FlingToDismissUtils.getFlingToDismissTargetWidth;
import android.content.res.Resources;
import android.graphics.Path;
@@ -375,6 +376,9 @@
mMagnetizedBubbleDraggingOut.setMagnetListener(listener);
mMagnetizedBubbleDraggingOut.setHapticsEnabled(true);
mMagnetizedBubbleDraggingOut.setFlingToTargetMinVelocity(FLING_TO_DISMISS_MIN_VELOCITY);
+ int screenWidthPx = mLayout.getContext().getResources().getDisplayMetrics().widthPixels;
+ mMagnetizedBubbleDraggingOut.setFlingToTargetWidthPercent(
+ getFlingToDismissTargetWidth(screenWidthPx));
}
private void springBubbleTo(View bubble, float x, float y) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/FlingToDismissUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/FlingToDismissUtils.kt
new file mode 100644
index 0000000..2a44f04
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/FlingToDismissUtils.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.bubbles.animation
+
+/** Utils related to the fling to dismiss animation. */
+object FlingToDismissUtils {
+
+ /** The target width surrounding the dismiss target on a small width screen, e.g. phone. */
+ private const val FLING_TO_DISMISS_TARGET_WIDTH_SMALL = 3f
+ /**
+ * The target width surrounding the dismiss target on a medium width screen, e.g. tablet in
+ * portrait.
+ */
+ private const val FLING_TO_DISMISS_TARGET_WIDTH_MEDIUM = 4.5f
+ /**
+ * The target width surrounding the dismiss target on a large width screen, e.g. tablet in
+ * landscape.
+ */
+ private const val FLING_TO_DISMISS_TARGET_WIDTH_LARGE = 6f
+
+ /** Returns the dismiss target width for the specified [screenWidthPx]. */
+ @JvmStatic
+ fun getFlingToDismissTargetWidth(screenWidthPx: Int) = when {
+ screenWidthPx >= 2000 -> FLING_TO_DISMISS_TARGET_WIDTH_LARGE
+ screenWidthPx >= 1500 -> FLING_TO_DISMISS_TARGET_WIDTH_MEDIUM
+ else -> FLING_TO_DISMISS_TARGET_WIDTH_SMALL
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
index aad2683..e487328 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.bubbles.animation;
import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RESTING;
+import static com.android.wm.shell.bubbles.animation.FlingToDismissUtils.getFlingToDismissTargetWidth;
import android.content.ContentResolver;
import android.content.res.Resources;
@@ -851,6 +852,15 @@
if (mLayout != null) {
Resources res = mLayout.getContext().getResources();
mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
+ updateFlingToDismissTargetWidth();
+ }
+ }
+
+ private void updateFlingToDismissTargetWidth() {
+ if (mLayout != null && mMagnetizedStack != null) {
+ int screenWidthPx = mLayout.getResources().getDisplayMetrics().widthPixels;
+ mMagnetizedStack.setFlingToTargetWidthPercent(
+ getFlingToDismissTargetWidth(screenWidthPx));
}
}
@@ -1022,23 +1032,8 @@
};
mMagnetizedStack.setHapticsEnabled(true);
mMagnetizedStack.setFlingToTargetMinVelocity(FLING_TO_DISMISS_MIN_VELOCITY);
+ updateFlingToDismissTargetWidth();
}
-
- final ContentResolver contentResolver = mLayout.getContext().getContentResolver();
- final float minVelocity = Settings.Secure.getFloat(contentResolver,
- "bubble_dismiss_fling_min_velocity",
- mMagnetizedStack.getFlingToTargetMinVelocity() /* default */);
- final float maxVelocity = Settings.Secure.getFloat(contentResolver,
- "bubble_dismiss_stick_max_velocity",
- mMagnetizedStack.getStickToTargetMaxXVelocity() /* default */);
- final float targetWidth = Settings.Secure.getFloat(contentResolver,
- "bubble_dismiss_target_width_percent",
- mMagnetizedStack.getFlingToTargetWidthPercent() /* default */);
-
- mMagnetizedStack.setFlingToTargetMinVelocity(minVelocity);
- mMagnetizedStack.setStickToTargetMaxXVelocity(maxVelocity);
- mMagnetizedStack.setFlingToTargetWidthPercent(targetWidth);
-
return mMagnetizedStack;
}
@@ -1053,7 +1048,7 @@
* property directly to move the first bubble and cause the stack to 'follow' to the new
* location.
*
- * This could also be achieved by simply animating the first bubble view and adding an update
+ * <p>This could also be achieved by simply animating the first bubble view and adding an update
* listener to dispatch movement to the rest of the stack. However, this would require
* duplication of logic in that update handler - it's simpler to keep all logic contained in the
* {@link #moveFirstBubbleWithStackFollowing} method.
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index e111edc..acfb259 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -220,19 +220,6 @@
}
android_test {
- name: "WMShellFlickerTestsPip",
- defaults: ["WMShellFlickerTestsDefault"],
- additional_manifests: ["manifests/AndroidManifestPip.xml"],
- package_name: "com.android.wm.shell.flicker.pip",
- instrumentation_target_package: "com.android.wm.shell.flicker.pip",
- srcs: [
- ":WMShellFlickerTestsBase-src",
- ":WMShellFlickerTestsPip3-src",
- ":WMShellFlickerTestsPipCommon-src",
- ],
-}
-
-android_test {
name: "WMShellFlickerTestsPip1",
defaults: ["WMShellFlickerTestsDefault"],
additional_manifests: ["manifests/AndroidManifestPip.xml"],
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index b00fc69..14602ef 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -139,6 +139,7 @@
mRenderNodes.clear();
mRenderThread.cacheManager().unregisterCanvasContext(this);
mRenderThread.renderState().removeContextCallback(this);
+ mHintSessionWrapper->destroy();
}
void CanvasContext::addRenderNode(RenderNode* node, bool placeFront) {
diff --git a/libs/hwui/renderthread/HintSessionWrapper.cpp b/libs/hwui/renderthread/HintSessionWrapper.cpp
index 1c3399a..2362331 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.cpp
+++ b/libs/hwui/renderthread/HintSessionWrapper.cpp
@@ -158,7 +158,6 @@
void HintSessionWrapper::sendLoadIncreaseHint() {
if (!init()) return;
mBinding->sendHint(mHintSession, static_cast<int32_t>(SessionHint::CPU_LOAD_UP));
- mLastFrameNotification = systemTime();
}
bool HintSessionWrapper::alive() {
diff --git a/libs/hwui/tests/unit/HintSessionWrapperTests.cpp b/libs/hwui/tests/unit/HintSessionWrapperTests.cpp
index a14ae1c..10a740a1 100644
--- a/libs/hwui/tests/unit/HintSessionWrapperTests.cpp
+++ b/libs/hwui/tests/unit/HintSessionWrapperTests.cpp
@@ -259,6 +259,31 @@
TEST_F(HintSessionWrapperTests, delayedDeletionDoesNotKillReusedSession) {
EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(0);
+ EXPECT_CALL(*sMockBinding, fakeReportActualWorkDuration(sessionPtr, 5_ms)).Times(1);
+
+ mWrapper->init();
+ waitForWrapperReady();
+ // Init a second time just to grab the wrapper from the promise
+ mWrapper->init();
+ EXPECT_EQ(mWrapper->alive(), true);
+
+ // First schedule the deletion
+ scheduleDelayedDestroyManaged();
+
+ // Then, report an actual duration
+ mWrapper->reportActualWorkDuration(5_ms);
+
+ // Then, run the delayed deletion after sending the update
+ allowDelayedDestructionToStart();
+ waitForDelayedDestructionToFinish();
+
+ // Ensure it didn't close within the timeframe of the test
+ Mock::VerifyAndClearExpectations(sMockBinding.get());
+ EXPECT_EQ(mWrapper->alive(), true);
+}
+
+TEST_F(HintSessionWrapperTests, loadUpDoesNotResetDeletionTimer) {
+ EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
EXPECT_CALL(*sMockBinding,
fakeSendHint(sessionPtr, static_cast<int32_t>(SessionHint::CPU_LOAD_UP)))
.Times(1);
@@ -272,16 +297,46 @@
// First schedule the deletion
scheduleDelayedDestroyManaged();
- // Then, send a hint to update the timestamp
+ // Then, send a load_up hint
mWrapper->sendLoadIncreaseHint();
// Then, run the delayed deletion after sending the update
allowDelayedDestructionToStart();
waitForDelayedDestructionToFinish();
- // Ensure it didn't close within the timeframe of the test
+ // Ensure it closed within the timeframe of the test
Mock::VerifyAndClearExpectations(sMockBinding.get());
+ EXPECT_EQ(mWrapper->alive(), false);
+}
+
+TEST_F(HintSessionWrapperTests, manualSessionDestroyPlaysNiceWithDelayedDestruct) {
+ EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
+
+ mWrapper->init();
+ waitForWrapperReady();
+ // Init a second time just to grab the wrapper from the promise
+ mWrapper->init();
EXPECT_EQ(mWrapper->alive(), true);
+
+ // First schedule the deletion
+ scheduleDelayedDestroyManaged();
+
+ // Then, kill the session
+ mWrapper->destroy();
+
+ // Verify it died
+ Mock::VerifyAndClearExpectations(sMockBinding.get());
+ EXPECT_EQ(mWrapper->alive(), false);
+
+ EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(0);
+
+ // Then, run the delayed deletion after manually killing the session
+ allowDelayedDestructionToStart();
+ waitForDelayedDestructionToFinish();
+
+ // Ensure it didn't close again and is still dead
+ Mock::VerifyAndClearExpectations(sMockBinding.get());
+ EXPECT_EQ(mWrapper->alive(), false);
}
} // namespace android::uirenderer::renderthread
\ No newline at end of file
diff --git a/media/Android.bp b/media/Android.bp
index f69dd3c..3493408 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -23,6 +23,10 @@
name: "soundtrigger_middleware-aidl",
unstable: true,
local_include_dir: "aidl",
+ defaults: [
+ "latest_android_media_audio_common_types_import_interface",
+ "latest_android_media_soundtrigger_types_import_interface",
+ ],
backend: {
java: {
sdk_version: "module_current",
@@ -32,8 +36,6 @@
"aidl/android/media/soundtrigger_middleware/*.aidl",
],
imports: [
- "android.media.audio.common.types-V2",
- "android.media.soundtrigger.types-V1",
"media_permission-aidl",
],
}
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 91fa873..cccf6f1 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -18,6 +18,9 @@
import static android.media.MediaRouter2Utils.toUniqueId;
+import static com.android.media.flags.Flags.FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER;
+
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -141,6 +144,8 @@
TYPE_WIRED_HEADPHONES,
TYPE_BLUETOOTH_A2DP,
TYPE_HDMI,
+ TYPE_HDMI_ARC,
+ TYPE_HDMI_EARC,
TYPE_USB_DEVICE,
TYPE_USB_ACCESSORY,
TYPE_DOCK,
@@ -206,6 +211,22 @@
public static final int TYPE_HDMI = AudioDeviceInfo.TYPE_HDMI;
/**
+ * Indicates the route is an Audio Return Channel of an HDMI connection.
+ *
+ * @see #getType
+ */
+ @FlaggedApi(FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER)
+ public static final int TYPE_HDMI_ARC = AudioDeviceInfo.TYPE_HDMI_ARC;
+
+ /**
+ * Indicates the route is an Enhanced Audio Return Channel of an HDMI connection.
+ *
+ * @see #getType
+ */
+ @FlaggedApi(FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER)
+ public static final int TYPE_HDMI_EARC = AudioDeviceInfo.TYPE_HDMI_EARC;
+
+ /**
* Indicates the route is a USB audio device.
*
* @see #getType
@@ -907,6 +928,10 @@
return "BLUETOOTH_A2DP";
case TYPE_HDMI:
return "HDMI";
+ case TYPE_HDMI_ARC:
+ return "HDMI_ARC";
+ case TYPE_HDMI_EARC:
+ return "HDMI_EARC";
case TYPE_DOCK:
return "DOCK";
case TYPE_USB_DEVICE:
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 159427b..76a00ac 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -310,8 +310,11 @@
IMediaRouterService.Stub.asInterface(
ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE));
+ mSystemController =
+ new SystemRoutingController(
+ ProxyMediaRouter2Impl.getSystemSessionInfoImpl(
+ mMediaRouterService, clientPackageName));
mImpl = new ProxyMediaRouter2Impl(context, clientPackageName);
- mSystemController = new SystemRoutingController(mImpl.getSystemSessionInfo());
}
/**
@@ -2057,13 +2060,7 @@
@Override
public RoutingSessionInfo getSystemSessionInfo() {
- RoutingSessionInfo result;
- try {
- result = mMediaRouterService.getSystemSessionInfoForPackage(mClientPackageName);
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
- return ensureClientPackageNameForSystemSession(result);
+ return getSystemSessionInfoImpl(mMediaRouterService, mClientPackageName);
}
/**
@@ -2428,6 +2425,23 @@
}
/**
+ * Retrieves the system session info for the given package.
+ *
+ * <p>The returned routing session is guaranteed to have a non-null {@link
+ * RoutingSessionInfo#getClientPackageName() client package name}.
+ *
+ * <p>Extracted into a static method to allow calling this from the constructor.
+ */
+ /* package */ static RoutingSessionInfo getSystemSessionInfoImpl(
+ @NonNull IMediaRouterService service, @NonNull String clientPackageName) {
+ try {
+ return service.getSystemSessionInfoForPackage(clientPackageName);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Sets the routing session's {@linkplain RoutingSessionInfo#getClientPackageName() client
* package name} to {@link #mClientPackageName} if empty and returns the session.
*
diff --git a/media/java/android/media/projection/IMediaProjectionManager.aidl b/media/java/android/media/projection/IMediaProjectionManager.aidl
index 31e65eb..d0e860c 100644
--- a/media/java/android/media/projection/IMediaProjectionManager.aidl
+++ b/media/java/android/media/projection/IMediaProjectionManager.aidl
@@ -176,4 +176,21 @@
@JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ ".permission.MANAGE_MEDIA_PROJECTION)")
oneway void notifyPermissionRequestStateChange(int hostUid, int state, int sessionCreationSource);
+
+ /**
+ * Notifies system server that the permission request was initiated.
+ *
+ * <p>Only used for emitting atoms.
+ *
+ * @param hostUid The uid of the process requesting consent to capture, may be an app or
+ * SystemUI.
+ * @param sessionCreationSource Only set if the state is MEDIA_PROJECTION_STATE_INITIATED.
+ * Indicates the entry point for requesting the permission. Must be
+ * a valid state defined
+ * in the SessionCreationSource enum.
+ */
+ @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION")
+ @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ + ".permission.MANAGE_MEDIA_PROJECTION)")
+ oneway void notifyPermissionRequestInitiated(int hostUid, int sessionCreationSource);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java b/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java
index e38e041..2a28417 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java
@@ -39,39 +39,50 @@
@DrawableRes private static final int DEFAULT_ICON = R.drawable.ic_smartphone;
public DeviceIconUtil() {
- List<Device> deviceList = Arrays.asList(
- new Device(
- AudioDeviceInfo.TYPE_USB_DEVICE,
- MediaRoute2Info.TYPE_USB_DEVICE,
- R.drawable.ic_headphone),
- new Device(
- AudioDeviceInfo.TYPE_USB_HEADSET,
- MediaRoute2Info.TYPE_USB_HEADSET,
- R.drawable.ic_headphone),
- new Device(
- AudioDeviceInfo.TYPE_USB_ACCESSORY,
- MediaRoute2Info.TYPE_USB_ACCESSORY,
- R.drawable.ic_headphone),
- new Device(
- AudioDeviceInfo.TYPE_DOCK,
- MediaRoute2Info.TYPE_DOCK,
- R.drawable.ic_dock_device),
- new Device(
- AudioDeviceInfo.TYPE_HDMI,
- MediaRoute2Info.TYPE_HDMI,
- R.drawable.ic_headphone),
- new Device(
- AudioDeviceInfo.TYPE_WIRED_HEADSET,
- MediaRoute2Info.TYPE_WIRED_HEADSET,
- R.drawable.ic_headphone),
- new Device(
- AudioDeviceInfo.TYPE_WIRED_HEADPHONES,
- MediaRoute2Info.TYPE_WIRED_HEADPHONES,
- R.drawable.ic_headphone),
- new Device(
- AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
- MediaRoute2Info.TYPE_BUILTIN_SPEAKER,
- R.drawable.ic_smartphone));
+ List<Device> deviceList =
+ Arrays.asList(
+ new Device(
+ AudioDeviceInfo.TYPE_USB_DEVICE,
+ MediaRoute2Info.TYPE_USB_DEVICE,
+ R.drawable.ic_headphone),
+ new Device(
+ AudioDeviceInfo.TYPE_USB_HEADSET,
+ MediaRoute2Info.TYPE_USB_HEADSET,
+ R.drawable.ic_headphone),
+ new Device(
+ AudioDeviceInfo.TYPE_USB_ACCESSORY,
+ MediaRoute2Info.TYPE_USB_ACCESSORY,
+ R.drawable.ic_headphone),
+ new Device(
+ AudioDeviceInfo.TYPE_DOCK,
+ MediaRoute2Info.TYPE_DOCK,
+ R.drawable.ic_dock_device),
+ new Device(
+ AudioDeviceInfo.TYPE_HDMI,
+ MediaRoute2Info.TYPE_HDMI,
+ R.drawable.ic_headphone),
+ // TODO: b/306359110 - Put proper iconography for HDMI_ARC type.
+ new Device(
+ AudioDeviceInfo.TYPE_HDMI_ARC,
+ MediaRoute2Info.TYPE_HDMI_ARC,
+ R.drawable.ic_headphone),
+ // TODO: b/306359110 - Put proper iconography for HDMI_EARC type.
+ new Device(
+ AudioDeviceInfo.TYPE_HDMI_EARC,
+ MediaRoute2Info.TYPE_HDMI_EARC,
+ R.drawable.ic_headphone),
+ new Device(
+ AudioDeviceInfo.TYPE_WIRED_HEADSET,
+ MediaRoute2Info.TYPE_WIRED_HEADSET,
+ R.drawable.ic_headphone),
+ new Device(
+ AudioDeviceInfo.TYPE_WIRED_HEADPHONES,
+ MediaRoute2Info.TYPE_WIRED_HEADPHONES,
+ R.drawable.ic_headphone),
+ new Device(
+ AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
+ MediaRoute2Info.TYPE_BUILTIN_SPEAKER,
+ R.drawable.ic_smartphone));
for (int i = 0; i < deviceList.size(); i++) {
Device device = deviceList.get(i);
mAudioDeviceTypeToIconMap.put(device.mAudioDeviceType, device);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index bf63f5d..5dacba5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -21,6 +21,8 @@
import static android.media.MediaRoute2Info.TYPE_DOCK;
import static android.media.MediaRoute2Info.TYPE_GROUP;
import static android.media.MediaRoute2Info.TYPE_HDMI;
+import static android.media.MediaRoute2Info.TYPE_HDMI_ARC;
+import static android.media.MediaRoute2Info.TYPE_HDMI_EARC;
import static android.media.MediaRoute2Info.TYPE_HEARING_AID;
import static android.media.MediaRoute2Info.TYPE_REMOTE_AUDIO_VIDEO_RECEIVER;
import static android.media.MediaRoute2Info.TYPE_REMOTE_CAR;
@@ -635,6 +637,8 @@
case TYPE_USB_ACCESSORY:
case TYPE_DOCK:
case TYPE_HDMI:
+ case TYPE_HDMI_ARC:
+ case TYPE_HDMI_EARC:
case TYPE_WIRED_HEADSET:
case TYPE_WIRED_HEADPHONES:
mediaDevice =
@@ -668,11 +672,12 @@
route,
mPackageName,
mPreferenceItemMap.get(route.getId()));
+ break;
default:
Log.w(TAG, "addMediaDevice() unknown device type : " + deviceType);
break;
-
}
+
if (mediaDevice != null && !TextUtils.isEmpty(mPackageName)
&& getRoutingSessionInfo().getSelectedRoutes().contains(route.getId())) {
mediaDevice.setState(STATE_SELECTED);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 147412d..8085c99 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -21,6 +21,8 @@
import static android.media.MediaRoute2Info.TYPE_DOCK;
import static android.media.MediaRoute2Info.TYPE_GROUP;
import static android.media.MediaRoute2Info.TYPE_HDMI;
+import static android.media.MediaRoute2Info.TYPE_HDMI_ARC;
+import static android.media.MediaRoute2Info.TYPE_HDMI_EARC;
import static android.media.MediaRoute2Info.TYPE_HEARING_AID;
import static android.media.MediaRoute2Info.TYPE_REMOTE_AUDIO_VIDEO_RECEIVER;
import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
@@ -140,7 +142,6 @@
mType = MediaDeviceType.TYPE_BLUETOOTH_DEVICE;
return;
}
-
switch (info.getType()) {
case TYPE_GROUP:
mType = MediaDeviceType.TYPE_CAST_GROUP_DEVICE;
@@ -157,6 +158,8 @@
case TYPE_USB_ACCESSORY:
case TYPE_DOCK:
case TYPE_HDMI:
+ case TYPE_HDMI_ARC:
+ case TYPE_HDMI_EARC:
mType = MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE;
break;
case TYPE_HEARING_AID:
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index a63bbdf..c44f66e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -18,6 +18,8 @@
import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
import static android.media.MediaRoute2Info.TYPE_DOCK;
import static android.media.MediaRoute2Info.TYPE_HDMI;
+import static android.media.MediaRoute2Info.TYPE_HDMI_ARC;
+import static android.media.MediaRoute2Info.TYPE_HDMI_EARC;
import static android.media.MediaRoute2Info.TYPE_USB_ACCESSORY;
import static android.media.MediaRoute2Info.TYPE_USB_DEVICE;
import static android.media.MediaRoute2Info.TYPE_USB_HEADSET;
@@ -71,6 +73,8 @@
name = context.getString(R.string.media_transfer_this_device_name);
break;
case TYPE_HDMI:
+ case TYPE_HDMI_ARC:
+ case TYPE_HDMI_EARC:
name = context.getString(R.string.media_transfer_external_device_name);
break;
default:
@@ -144,6 +148,8 @@
case TYPE_USB_ACCESSORY:
case TYPE_DOCK:
case TYPE_HDMI:
+ case TYPE_HDMI_ARC:
+ case TYPE_HDMI_EARC:
id = USB_HEADSET_ID;
break;
case TYPE_BUILTIN_SPEAKER:
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
index 0f55f35..eadcd7c 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
@@ -1,15 +1,17 @@
package: "com.android.systemui.accessibility.accessibilitymenu"
-flag {
- name: "a11y_menu_settings_back_button_fix_and_large_button_sizing"
- namespace: "accessibility"
- description: "Provides/restores back button functionality for the a11yMenu settings page. Also, fixes sizing problems with large shortcut buttons."
- bug: "298467628"
-}
+# NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
flag {
name: "a11y_menu_hide_before_taking_action"
namespace: "accessibility"
description: "Hides the AccessibilityMenuService UI before taking action instead of after."
bug: "292020123"
-}
\ No newline at end of file
+}
+
+flag {
+ name: "a11y_menu_settings_back_button_fix_and_large_button_sizing"
+ namespace: "accessibility"
+ description: "Provides/restores back button functionality for the a11yMenu settings page. Also, fixes sizing problems with large shortcut buttons."
+ bug: "298467628"
+}
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index 8841967..bcf1535 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -1,5 +1,7 @@
package: "com.android.systemui"
+# NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
+
flag {
name: "floating_menu_overlaps_nav_bars_flag"
namespace: "accessibility"
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 211af90..0567528 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -31,6 +31,13 @@
}
flag {
+ name: "notification_async_hybrid_view_inflation"
+ namespace: "systemui"
+ description: "Inflates the hybrid (single-line) notification views form the background thread."
+ bug: "217799515"
+}
+
+flag {
name: "scene_container"
namespace: "systemui"
description: "Enables the scene container framework go/flexiglass."
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index 6c4b695..af35ea4 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -68,6 +68,7 @@
override var launchContainer = ghostedView.rootView as ViewGroup
private val launchContainerOverlay: ViewGroupOverlay
get() = launchContainer.overlay
+
private val launchContainerLocation = IntArray(2)
/** The ghost view that is drawn and animated instead of the ghosted view. */
@@ -206,9 +207,8 @@
return
}
- backgroundView = FrameLayout(launchContainer.context).also {
- launchContainerOverlay.add(it)
- }
+ backgroundView =
+ FrameLayout(launchContainer.context).also { launchContainerOverlay.add(it) }
// We wrap the ghosted view background and use it to draw the expandable background. Its
// alpha will be set to 0 as soon as we start drawing the expanding background.
@@ -226,6 +226,17 @@
// the content before fading out the background.
ghostView = GhostView.addGhost(ghostedView, launchContainer)
+ // [GhostView.addGhost], the result of which is our [ghostView], creates a [GhostView], and
+ // adds it first to a [FrameLayout] container. It then adds _that_ container to an
+ // [OverlayViewGroup]. We need to turn off clipping for that container view. Currently,
+ // however, the only way to get a reference to that overlay is by going through our
+ // [ghostView]. The [OverlayViewGroup] will always be its grandparent view.
+ // TODO(b/306652954) reference the overlay view group directly if we can
+ (ghostView?.parent?.parent as? ViewGroup)?.let {
+ it.clipChildren = false
+ it.clipToPadding = false
+ }
+
val matrix = ghostView?.animationMatrix ?: Matrix.IDENTITY_MATRIX
matrix.getValues(initialGhostViewMatrixValues)
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 5a70b79..452bc31 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -66,7 +66,7 @@
<FrameLayout
android:id="@+id/status_bar_start_side_content"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:layout_gravity="center_vertical|start"
android:clipChildren="false">
@@ -77,7 +77,7 @@
and DISABLE_NOTIFICATION_ICONS, respectively -->
<LinearLayout
android:id="@+id/status_bar_start_side_except_heads_up"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:layout_width="match_parent"
android:layout_gravity="center_vertical|start"
android:clipChildren="false">
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index 8957903..7e03bd9 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -26,24 +26,6 @@
android:layout_height="match_parent"
android:fitsSystemWindows="true">
- <com.android.systemui.statusbar.BackDropView
- android:id="@+id/backdrop"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone"
- sysui:ignoreRightInset="true"
- >
- <ImageView android:id="@+id/backdrop_back"
- android:layout_width="match_parent"
- android:scaleType="centerCrop"
- android:layout_height="match_parent" />
- <ImageView android:id="@+id/backdrop_front"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerCrop"
- android:visibility="invisible" />
- </com.android.systemui.statusbar.BackDropView>
-
<com.android.systemui.scrim.ScrimView
android:id="@+id/scrim_behind"
android:layout_width="match_parent"
@@ -63,7 +45,8 @@
<com.android.systemui.statusbar.LightRevealScrim
android:id="@+id/light_reveal_scrim"
android:layout_width="match_parent"
- android:layout_height="match_parent" />
+ android:layout_height="match_parent"
+ sysui:ignoreRightInset="true" />
<include layout="@layout/status_bar_expanded"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values-land/config.xml b/packages/SystemUI/res/values-land/config.xml
index 587caaf..db526b1 100644
--- a/packages/SystemUI/res/values-land/config.xml
+++ b/packages/SystemUI/res/values-land/config.xml
@@ -46,4 +46,7 @@
For now, this value has effect only when flag lockscreen.enable_landscape is enabled.
TODO (b/293252410) - change this comment/resource when flag is enabled -->
<bool name="force_config_use_split_notification_shade">true</bool>
+
+ <!-- Whether to show bottom sheets edge to edge -->
+ <bool name="config_edgeToEdgeBottomSheetDialog">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 7a6d29a..9e2ebf6 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3224,6 +3224,9 @@
<!--- Label of the dismiss button of the dialog appearing when an external display is connected [CHAR LIMIT=NONE]-->
<string name="dismiss_dialog">Dismiss</string>
+ <!--- Content description of the connected display status bar icon that appears every time a display is connected [CHAR LIMIT=NONE]-->
+ <string name="connected_display_icon_desc">Display connected</string>
+
<!-- Title of the privacy dialog, shown for active / recent app usage of some phone sensors [CHAR LIMIT=30] -->
<string name="privacy_dialog_title">Microphone & Camera</string>
<!-- Subtitle of the privacy dialog, shown for active / recent app usage of some phone sensors [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/DeviceStateRepository.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/DeviceStateRepository.kt
new file mode 100644
index 0000000..f219cec
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/DeviceStateRepository.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.android.systemui.unfold.system
+
+import com.android.systemui.unfold.dagger.UnfoldMain
+import com.android.systemui.unfold.updates.FoldProvider
+import com.android.systemui.unfold.updates.FoldProvider.FoldCallback
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import javax.inject.Singleton
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.buffer
+import kotlinx.coroutines.flow.callbackFlow
+
+/** Provides whether the device is folded. */
+interface DeviceStateRepository {
+ val isFolded: Flow<Boolean>
+}
+
+@Singleton
+class DeviceStateRepositoryImpl
+@Inject
+constructor(
+ private val foldProvider: FoldProvider,
+ @UnfoldMain private val executor: Executor,
+) : DeviceStateRepository {
+
+ override val isFolded: Flow<Boolean>
+ get() =
+ callbackFlow {
+ val callback = FoldCallback { isFolded -> trySend(isFolded) }
+ foldProvider.registerCallback(callback, executor)
+ awaitClose { foldProvider.unregisterCallback(callback) }
+ }
+ .buffer(capacity = Channel.CONFLATED)
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt
index fe607e1..7b67e3f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt
@@ -48,6 +48,9 @@
abstract fun foldState(provider: DeviceStateManagerFoldProvider): FoldProvider
@Binds
+ abstract fun deviceStateRepository(provider: DeviceStateRepositoryImpl): DeviceStateRepository
+
+ @Binds
@UnfoldMain
abstract fun mainExecutor(@Main executor: Executor): Executor
diff --git a/packages/SystemUI/shared/src/com/android/systemui/util/TraceStateLogger.kt b/packages/SystemUI/shared/src/com/android/systemui/util/TraceStateLogger.kt
new file mode 100644
index 0000000..63ea116
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/util/TraceStateLogger.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.util
+
+import android.os.Trace
+
+/**
+ * Utility class used to log state changes easily in a track with a custom name.
+ *
+ * Example of usage:
+ * ```kotlin
+ * class MyClass {
+ * val screenStateLogger = TraceStateLogger("Screen state")
+ *
+ * fun onTurnedOn() { screenStateLogger.log("on") }
+ * fun onTurnedOff() { screenStateLogger.log("off") }
+ * }
+ * ```
+ *
+ * This creates a new slice in a perfetto trace only if the state is different than the previous
+ * one.
+ */
+class TraceStateLogger(
+ private val trackName: String,
+ private val logOnlyIfDifferent: Boolean = true,
+ private val instantEvent: Boolean = true
+) {
+
+ private var previousValue: String? = null
+
+ /** If needed, logs the value to a track with name [trackName]. */
+ fun log(newValue: String) {
+ if (instantEvent) {
+ Trace.instantForTrack(Trace.TRACE_TAG_APP, trackName, newValue)
+ }
+ if (logOnlyIfDifferent && previousValue == newValue) return
+ Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, trackName, 0)
+ Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, trackName, newValue, 0)
+ previousValue = newValue
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/GuestResetOrExitSessionReceiver.java b/packages/SystemUI/src/com/android/systemui/GuestResetOrExitSessionReceiver.java
index fd84543..494efb7 100644
--- a/packages/SystemUI/src/com/android/systemui/GuestResetOrExitSessionReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/GuestResetOrExitSessionReceiver.java
@@ -25,21 +25,24 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.UserInfo;
+import android.content.res.Resources;
import android.os.UserHandle;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.qs.QSUserSwitcherEvent;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.UserSwitcherController;
-import javax.inject.Inject;
-
+import dagger.Lazy;
import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedInject;
+import javax.inject.Inject;
+
/**
* Manages handling of guest session persistent notification
* and actions to reset guest or exit guest session
@@ -70,14 +73,14 @@
public AlertDialog mResetSessionDialog;
private final UserTracker mUserTracker;
private final BroadcastDispatcher mBroadcastDispatcher;
- private final ResetSessionDialog.Factory mResetSessionDialogFactory;
- private final ExitSessionDialog.Factory mExitSessionDialogFactory;
+ private final ResetSessionDialogFactory mResetSessionDialogFactory;
+ private final ExitSessionDialogFactory mExitSessionDialogFactory;
@Inject
public GuestResetOrExitSessionReceiver(UserTracker userTracker,
BroadcastDispatcher broadcastDispatcher,
- ResetSessionDialog.Factory resetSessionDialogFactory,
- ExitSessionDialog.Factory exitSessionDialogFactory) {
+ ResetSessionDialogFactory resetSessionDialogFactory,
+ ExitSessionDialogFactory exitSessionDialogFactory) {
mUserTracker = userTracker;
mBroadcastDispatcher = broadcastDispatcher;
mResetSessionDialogFactory = resetSessionDialogFactory;
@@ -111,8 +114,8 @@
mResetSessionDialog = mResetSessionDialogFactory.create(currentUser.id);
mResetSessionDialog.show();
} else if (ACTION_GUEST_EXIT.equals(action)) {
- mExitSessionDialog = mExitSessionDialogFactory.create(currentUser.id,
- currentUser.isEphemeral());
+ mExitSessionDialog = mExitSessionDialogFactory.create(
+ currentUser.isEphemeral(), currentUser.id);
mExitSessionDialog.show();
}
}
@@ -132,43 +135,69 @@
}
/**
+ * Factory class to create guest reset dialog instance
+ *
* Dialog shown when asking for confirmation before
* reset and restart of guest user.
*/
- public static final class ResetSessionDialog extends SystemUIDialog implements
- DialogInterface.OnClickListener {
+ public static final class ResetSessionDialogFactory {
+ private final Lazy<SystemUIDialog> mDialogLazy;
+ private final Resources mResources;
+ private final ResetSessionDialogClickListener.Factory mClickListenerFactory;
+ @Inject
+ public ResetSessionDialogFactory(
+ Lazy<SystemUIDialog> dialogLazy,
+ @Main Resources resources,
+ ResetSessionDialogClickListener.Factory clickListenerFactory) {
+ mDialogLazy = dialogLazy;
+ mResources = resources;
+ mClickListenerFactory = clickListenerFactory;
+ }
+
+ /** Create a guest reset dialog instance */
+ public AlertDialog create(int userId) {
+ SystemUIDialog dialog = mDialogLazy.get();
+ ResetSessionDialogClickListener listener = mClickListenerFactory.create(
+ userId, dialog);
+ dialog.setTitle(com.android.settingslib.R.string.guest_reset_and_restart_dialog_title);
+ dialog.setMessage(mResources.getString(
+ com.android.settingslib.R.string.guest_reset_and_restart_dialog_message));
+ dialog.setButton(
+ DialogInterface.BUTTON_NEUTRAL,
+ mResources.getString(android.R.string.cancel),
+ listener);
+ dialog.setButton(DialogInterface.BUTTON_POSITIVE,
+ mResources.getString(
+ com.android.settingslib.R.string.guest_reset_guest_confirm_button),
+ listener);
+ dialog.setCanceledOnTouchOutside(false);
+ return dialog;
+ }
+ }
+
+ public static class ResetSessionDialogClickListener implements DialogInterface.OnClickListener {
private final UserSwitcherController mUserSwitcherController;
private final UiEventLogger mUiEventLogger;
private final int mUserId;
+ private final DialogInterface mDialog;
- /** Factory class to create guest reset dialog instance */
@AssistedFactory
public interface Factory {
- /** Create a guest reset dialog instance */
- ResetSessionDialog create(int userId);
+ ResetSessionDialogClickListener create(int userId, DialogInterface dialog);
}
@AssistedInject
- ResetSessionDialog(Context context,
+ public ResetSessionDialogClickListener(
UserSwitcherController userSwitcherController,
UiEventLogger uiEventLogger,
- @Assisted int userId) {
- super(context);
-
- setTitle(com.android.settingslib.R.string.guest_reset_and_restart_dialog_title);
- setMessage(context.getString(
- com.android.settingslib.R.string.guest_reset_and_restart_dialog_message));
- setButton(DialogInterface.BUTTON_NEUTRAL,
- context.getString(android.R.string.cancel), this);
- setButton(DialogInterface.BUTTON_POSITIVE,
- context.getString(
- com.android.settingslib.R.string.guest_reset_guest_confirm_button), this);
- setCanceledOnTouchOutside(false);
-
+ @Assisted int userId,
+ @Assisted DialogInterface dialog
+ ) {
mUserSwitcherController = userSwitcherController;
mUiEventLogger = uiEventLogger;
mUserId = userId;
+ mDialog = dialog;
}
@Override
@@ -177,7 +206,7 @@
mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE);
mUserSwitcherController.removeGuestUser(mUserId, UserHandle.USER_NULL);
} else if (which == DialogInterface.BUTTON_NEUTRAL) {
- cancel();
+ mDialog.cancel();
}
}
}
@@ -186,58 +215,93 @@
* Dialog shown when asking for confirmation before
* exit of guest user.
*/
- public static final class ExitSessionDialog extends SystemUIDialog implements
- DialogInterface.OnClickListener {
+ public static final class ExitSessionDialogFactory {
+ private final Lazy<SystemUIDialog> mDialogLazy;
+ private final ExitSessionDialogClickListener.Factory mClickListenerFactory;
+ private final Resources mResources;
+ @Inject
+ public ExitSessionDialogFactory(
+ Lazy<SystemUIDialog> dialogLazy,
+ ExitSessionDialogClickListener.Factory clickListenerFactory,
+ @Main Resources resources) {
+ mDialogLazy = dialogLazy;
+ mClickListenerFactory = clickListenerFactory;
+ mResources = resources;
+ }
+
+ public AlertDialog create(boolean isEphemeral, int userId) {
+ SystemUIDialog dialog = mDialogLazy.get();
+ ExitSessionDialogClickListener clickListener = mClickListenerFactory.create(
+ isEphemeral, userId, dialog);
+ if (isEphemeral) {
+ dialog.setTitle(mResources.getString(
+ com.android.settingslib.R.string.guest_exit_dialog_title));
+ dialog.setMessage(mResources.getString(
+ com.android.settingslib.R.string.guest_exit_dialog_message));
+ dialog.setButton(
+ DialogInterface.BUTTON_NEUTRAL,
+ mResources.getString(android.R.string.cancel),
+ clickListener);
+ dialog.setButton(
+ DialogInterface.BUTTON_POSITIVE,
+ mResources.getString(
+ com.android.settingslib.R.string.guest_exit_dialog_button),
+ clickListener);
+ } else {
+ dialog.setTitle(mResources.getString(
+ com.android.settingslib
+ .R.string.guest_exit_dialog_title_non_ephemeral));
+ dialog.setMessage(mResources.getString(
+ com.android.settingslib
+ .R.string.guest_exit_dialog_message_non_ephemeral));
+ dialog.setButton(
+ DialogInterface.BUTTON_NEUTRAL,
+ mResources.getString(android.R.string.cancel),
+ clickListener);
+ dialog.setButton(
+ DialogInterface.BUTTON_NEGATIVE,
+ mResources.getString(
+ com.android.settingslib.R.string.guest_exit_clear_data_button),
+ clickListener);
+ dialog.setButton(
+ DialogInterface.BUTTON_POSITIVE,
+ mResources.getString(
+ com.android.settingslib.R.string.guest_exit_save_data_button),
+ clickListener);
+ }
+ dialog.setCanceledOnTouchOutside(false);
+
+ return dialog;
+ }
+
+ }
+
+ public static class ExitSessionDialogClickListener implements DialogInterface.OnClickListener {
private final UserSwitcherController mUserSwitcherController;
+ private final boolean mIsEphemeral;
private final int mUserId;
- private boolean mIsEphemeral;
+ private final DialogInterface mDialog;
- /** Factory class to create guest exit dialog instance */
@AssistedFactory
public interface Factory {
- /** Create a guest exit dialog instance */
- ExitSessionDialog create(int userId, boolean isEphemeral);
+ ExitSessionDialogClickListener create(
+ boolean isEphemeral,
+ int userId,
+ DialogInterface dialog);
}
@AssistedInject
- ExitSessionDialog(Context context,
+ public ExitSessionDialogClickListener(
UserSwitcherController userSwitcherController,
+ @Assisted boolean isEphemeral,
@Assisted int userId,
- @Assisted boolean isEphemeral) {
- super(context);
-
- if (isEphemeral) {
- setTitle(context.getString(
- com.android.settingslib.R.string.guest_exit_dialog_title));
- setMessage(context.getString(
- com.android.settingslib.R.string.guest_exit_dialog_message));
- setButton(DialogInterface.BUTTON_NEUTRAL,
- context.getString(android.R.string.cancel), this);
- setButton(DialogInterface.BUTTON_POSITIVE,
- context.getString(
- com.android.settingslib.R.string.guest_exit_dialog_button), this);
- } else {
- setTitle(context.getString(
- com.android.settingslib
- .R.string.guest_exit_dialog_title_non_ephemeral));
- setMessage(context.getString(
- com.android.settingslib
- .R.string.guest_exit_dialog_message_non_ephemeral));
- setButton(DialogInterface.BUTTON_NEUTRAL,
- context.getString(android.R.string.cancel), this);
- setButton(DialogInterface.BUTTON_NEGATIVE,
- context.getString(
- com.android.settingslib.R.string.guest_exit_clear_data_button), this);
- setButton(DialogInterface.BUTTON_POSITIVE,
- context.getString(
- com.android.settingslib.R.string.guest_exit_save_data_button), this);
- }
- setCanceledOnTouchOutside(false);
-
+ @Assisted DialogInterface dialog
+ ) {
mUserSwitcherController = userSwitcherController;
- mUserId = userId;
mIsEphemeral = isEphemeral;
+ mUserId = userId;
+ mDialog = dialog;
}
@Override
@@ -249,7 +313,7 @@
mUserSwitcherController.exitGuestUser(mUserId, UserHandle.USER_NULL, false);
} else if (which == DialogInterface.BUTTON_NEUTRAL) {
// Cancel clicked, do nothing
- cancel();
+ mDialog.cancel();
}
} else {
if (which == DialogInterface.BUTTON_POSITIVE) {
@@ -261,7 +325,7 @@
mUserSwitcherController.exitGuestUser(mUserId, UserHandle.USER_NULL, true);
} else if (which == DialogInterface.BUTTON_NEUTRAL) {
// Cancel clicked, do nothing
- cancel();
+ mDialog.cancel();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
index b573fad..0f5f869 100644
--- a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
@@ -27,6 +27,7 @@
import com.android.systemui.res.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.GuestResetOrExitSessionReceiver.ResetSessionDialogFactory;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
@@ -36,14 +37,14 @@
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.util.settings.SecureSettings;
-import java.util.concurrent.Executor;
-
-import javax.inject.Inject;
-
import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedInject;
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+
/**
* Manages notification when a guest session is resumed.
*/
@@ -58,7 +59,7 @@
private final Executor mMainExecutor;
private final UserTracker mUserTracker;
private final SecureSettings mSecureSettings;
- private final ResetSessionDialog.Factory mResetSessionDialogFactory;
+ private final ResetSessionDialogFactory mResetSessionDialogFactory;
private final GuestSessionNotification mGuestSessionNotification;
@VisibleForTesting
@@ -104,7 +105,7 @@
UserTracker userTracker,
SecureSettings secureSettings,
GuestSessionNotification guestSessionNotification,
- ResetSessionDialog.Factory resetSessionDialogFactory) {
+ ResetSessionDialogFactory resetSessionDialogFactory) {
mMainExecutor = mainExecutor;
mUserTracker = userTracker;
mSecureSettings = secureSettings;
diff --git a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
index 0bf5069..7d73896 100644
--- a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
+++ b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
@@ -19,7 +19,6 @@
import android.app.WallpaperColors;
import android.app.WallpaperManager;
import android.content.Context;
-import android.graphics.Color;
import android.os.UserHandle;
import com.android.internal.annotations.VisibleForTesting;
@@ -47,9 +46,7 @@
ConfigurationController.ConfigurationListener {
private static final String TAG = "SysuiColorExtractor";
private final Tonal mTonal;
- private boolean mHasMediaArtwork;
private final GradientColors mNeutralColorsLock;
- private final GradientColors mBackdropColors;
private Lazy<SelectedUserInteractor> mUserInteractor;
@Inject
@@ -82,9 +79,6 @@
mNeutralColorsLock = new GradientColors();
configurationController.addCallback(this);
dumpManager.registerDumpable(getClass().getSimpleName(), this);
-
- mBackdropColors = new GradientColors();
- mBackdropColors.setMainColor(Color.BLACK);
mUserInteractor = userInteractor;
// Listen to all users instead of only the current one.
@@ -123,14 +117,6 @@
triggerColorsChanged(WallpaperManager.FLAG_SYSTEM | WallpaperManager.FLAG_LOCK);
}
- @Override
- public GradientColors getColors(int which, int type) {
- if (mHasMediaArtwork && (which & WallpaperManager.FLAG_LOCK) != 0) {
- return mBackdropColors;
- }
- return super.getColors(which, type);
- }
-
/**
* Colors that should be using for scrims.
*
@@ -140,14 +126,7 @@
* - Black otherwise
*/
public GradientColors getNeutralColors() {
- return mHasMediaArtwork ? mBackdropColors : mNeutralColorsLock;
- }
-
- public void setHasMediaArtwork(boolean hasBackdrop) {
- if (mHasMediaArtwork != hasBackdrop) {
- mHasMediaArtwork = hasBackdrop;
- triggerColorsChanged(WallpaperManager.FLAG_LOCK);
- }
+ return mNeutralColorsLock;
}
@Override
@@ -164,7 +143,5 @@
pw.println(" system: " + Arrays.toString(system));
pw.println(" lock: " + Arrays.toString(lock));
pw.println(" Neutral colors: " + mNeutralColorsLock);
- pw.println(" Has media backdrop: " + mHasMediaArtwork);
-
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index 5d6949b..d8ff535 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -57,7 +57,6 @@
import com.android.systemui.statusbar.gesture.GesturePointerEventListener
import com.android.systemui.statusbar.notification.InstantAppNotifier
import com.android.systemui.statusbar.phone.KeyguardLiftController
-import com.android.systemui.statusbar.phone.LockscreenWallpaper
import com.android.systemui.statusbar.phone.ScrimController
import com.android.systemui.statusbar.phone.StatusBarHeadsUpChangeListener
import com.android.systemui.stylus.StylusUsiPowerStartable
@@ -344,11 +343,6 @@
@Binds
@IntoMap
- @ClassKey(LockscreenWallpaper::class)
- abstract fun bindLockscreenWallpaper(impl: LockscreenWallpaper): CoreStartable
-
- @Binds
- @IntoMap
@ClassKey(ScrimController::class)
abstract fun bindScrimController(impl: ScrimController): CoreStartable
diff --git a/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt b/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt
index d19efbd..3f21533 100644
--- a/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt
@@ -21,6 +21,7 @@
import android.widget.TextView
import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIBottomSheetDialog
+import com.android.systemui.statusbar.policy.ConfigurationController
/**
* Dialog used to decide what to do with a connected display.
@@ -32,8 +33,9 @@
context: Context,
private val onStartMirroringClickListener: View.OnClickListener,
private val onCancelMirroring: View.OnClickListener,
+ configurationController: ConfigurationController? = null,
theme: Int = R.style.Theme_SystemUI_Dialog,
-) : SystemUIBottomSheetDialog(context, theme) {
+) : SystemUIBottomSheetDialog(context, configurationController, theme) {
private lateinit var mirrorButton: TextView
private lateinit var dismissButton: TextView
diff --git a/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt b/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt
index 86ef439..91f535d 100644
--- a/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt
@@ -23,6 +23,7 @@
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.PendingDisplay
import com.android.systemui.display.ui.view.MirroringConfirmationDialog
+import com.android.systemui.statusbar.policy.ConfigurationController
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
@@ -41,7 +42,8 @@
private val context: Context,
private val connectedDisplayInteractor: ConnectedDisplayInteractor,
@Application private val scope: CoroutineScope,
- @Background private val bgDispatcher: CoroutineDispatcher
+ @Background private val bgDispatcher: CoroutineDispatcher,
+ private val configurationController: ConfigurationController
) {
private var dialog: Dialog? = null
@@ -71,7 +73,8 @@
onCancelMirroring = {
scope.launch(bgDispatcher) { pendingDisplay.ignore() }
hideDialog()
- }
+ },
+ configurationController
)
.apply { show() }
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index c91c9ac..10fac4d 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -549,12 +549,6 @@
val LOCKSCREEN_ENABLE_LANDSCAPE =
unreleasedFlag("lockscreen.enable_landscape")
- // TODO(b/273443374): Tracking Bug
- @Keep
- @JvmField
- val LOCKSCREEN_LIVE_WALLPAPER =
- sysPropBooleanFlag("persist.wm.debug.lockscreen_live_wallpaper", default = true)
-
// TODO(b/281648899): Tracking bug
@Keep
@JvmField
diff --git a/packages/SystemUI/src/com/android/systemui/flags/RefactorFlag.kt b/packages/SystemUI/src/com/android/systemui/flags/RefactorFlag.kt
index 7ccc26c..4a5cc64 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/RefactorFlag.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/RefactorFlag.kt
@@ -16,7 +16,6 @@
package com.android.systemui.flags
-import android.util.Log
import com.android.systemui.Dependency
/**
@@ -65,8 +64,7 @@
* }
* ````
*/
- fun assertInLegacyMode() =
- check(!isEnabled) { "Legacy code path not supported when $flagName is enabled." }
+ fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, flagName)
/**
* Called to ensure code is only run when the flag is enabled. This protects users from the
@@ -81,13 +79,8 @@
* }
* ```
*/
- fun isUnexpectedlyInLegacyMode(): Boolean {
- if (!isEnabled) {
- val message = "New code path expects $flagName to be enabled."
- Log.wtf(TAG, message, Exception(message))
- }
- return !isEnabled
- }
+ fun isUnexpectedlyInLegacyMode(): Boolean =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, flagName)
companion object {
private const val TAG = "RefactorFlag"
diff --git a/packages/SystemUI/src/com/android/systemui/flags/RefactorFlagUtils.kt b/packages/SystemUI/src/com/android/systemui/flags/RefactorFlagUtils.kt
new file mode 100644
index 0000000..2aa397f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/flags/RefactorFlagUtils.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.flags
+
+import android.util.Log
+
+/**
+ * Utilities for writing your own objects to uphold refactor flag conventions.
+ *
+ * Example usage:
+ * ```
+ * object SomeRefactor {
+ * const val FLAG_NAME = Flags.SOME_REFACTOR
+ * @JvmStatic inline val isEnabled get() = Flags.someRefactor()
+ * @JvmStatic inline fun isUnexpectedlyInLegacyMode() =
+ * RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+ * @JvmStatic inline fun assertInLegacyMode() =
+ * RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+ * }
+ * ```
+ */
+@Suppress("NOTHING_TO_INLINE")
+object RefactorFlagUtils {
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ *
+ * Example usage:
+ * ```
+ * public void setNewController(SomeController someController) {
+ * if (SomeRefactor.isUnexpectedlyInLegacyMode()) return;
+ * mSomeController = someController;
+ * }
+ * ```
+ */
+ inline fun isUnexpectedlyInLegacyMode(isEnabled: Boolean, flagName: Any): Boolean {
+ val inLegacyMode = !isEnabled
+ if (inLegacyMode) {
+ val message = "New code path expects $flagName to be enabled."
+ Log.wtf("RefactorFlag", message, IllegalStateException(message))
+ }
+ return inLegacyMode
+ }
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ *
+ * Example usage:
+ * ```
+ * public void setSomeLegacyController(SomeController someController) {
+ * SomeRefactor.assertInLegacyMode();
+ * mSomeController = someController;
+ * }
+ * ````
+ */
+ inline fun assertInLegacyMode(isEnabled: Boolean, flagName: Any) =
+ check(!isEnabled) { "Legacy code path not supported when $flagName is enabled." }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 4779895..2b1cdc2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -202,7 +202,7 @@
// Wrap Keyguard going away animation.
// Note: Also used for wrapping occlude by Dream animation. It works (with some redundancy).
public static IRemoteTransition wrap(final KeyguardViewMediator keyguardViewMediator,
- final IRemoteAnimationRunner runner, final boolean lockscreenLiveWallpaperEnabled) {
+ final IRemoteAnimationRunner runner) {
return new IRemoteTransition.Stub() {
@GuardedBy("mLeashMap")
@@ -236,9 +236,8 @@
}
}
initAlphaForAnimationTargets(t, apps);
- if (lockscreenLiveWallpaperEnabled) {
- initAlphaForAnimationTargets(t, wallpapers);
- }
+ initAlphaForAnimationTargets(t, wallpapers);
+
t.apply();
runner.onAnimationStart(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 0bac40b..c8c06ae 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -707,8 +707,7 @@
return@postDelayed
}
- if ((wallpaperTargets?.isNotEmpty() == true) &&
- wallpaperManager.isLockscreenLiveWallpaperEnabled()) {
+ if ((wallpaperTargets?.isNotEmpty() == true)) {
fadeInWallpaper()
hideKeyguardViewAfterRemoteAnimation()
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index e893c63..4e6a872 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -184,8 +184,6 @@
import java.util.concurrent.Executor;
import java.util.function.Consumer;
-
-
import kotlinx.coroutines.CoroutineDispatcher;
/**
@@ -1517,12 +1515,11 @@
setShowingLocked(false /* showing */, true /* forceCallbacks */);
}
- boolean isLLwpEnabled = getWallpaperManager().isLockscreenLiveWallpaperEnabled();
mKeyguardTransitions.register(
- KeyguardService.wrap(this, getExitAnimationRunner(), isLLwpEnabled),
- KeyguardService.wrap(this, getOccludeAnimationRunner(), isLLwpEnabled),
- KeyguardService.wrap(this, getOccludeByDreamAnimationRunner(), isLLwpEnabled),
- KeyguardService.wrap(this, getUnoccludeAnimationRunner(), isLLwpEnabled));
+ KeyguardService.wrap(this, getExitAnimationRunner()),
+ KeyguardService.wrap(this, getOccludeAnimationRunner()),
+ KeyguardService.wrap(this, getOccludeByDreamAnimationRunner()),
+ KeyguardService.wrap(this, getUnoccludeAnimationRunner()));
final ContentResolver cr = mContext.getContentResolver();
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
index d06f31f..7e360cf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -26,13 +26,13 @@
import com.android.systemui.util.kotlin.Utils.Companion.toQuint
import com.android.systemui.util.kotlin.sample
import com.android.wm.shell.animation.Interpolators
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
-import javax.inject.Inject
-import kotlin.time.Duration.Companion.milliseconds
@SysUISingleton
class FromAlternateBouncerTransitionInteractor
@@ -130,11 +130,16 @@
override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
return ValueAnimator().apply {
interpolator = Interpolators.LINEAR
- duration = TRANSITION_DURATION_MS.inWholeMilliseconds
+ duration =
+ when (toState) {
+ KeyguardState.GONE -> TO_GONE_DURATION
+ else -> TRANSITION_DURATION_MS
+ }.inWholeMilliseconds
}
}
companion object {
val TRANSITION_DURATION_MS = 300.milliseconds
+ val TO_GONE_DURATION = 500.milliseconds
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index fbe26de..b0b8577 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -143,6 +143,11 @@
val dozingToLockscreenTransition: Flow<TransitionStep> =
repository.transition(DOZING, LOCKSCREEN)
+ /** Receive all [TransitionStep] matching a filter of [from]->[to] */
+ fun transition(from: KeyguardState, to: KeyguardState): Flow<TransitionStep> {
+ return repository.transition(from, to)
+ }
+
/**
* AOD<->LOCKSCREEN transition information, mapped to dozeAmount range of AOD (1f) <->
* Lockscreen (0f).
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt
new file mode 100644
index 0000000..023d16ca
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor.Companion.TO_GONE_DURATION
+import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+import com.android.systemui.keyguard.shared.model.ScrimAlpha
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Breaks down ALTERNATE_BOUNCER->GONE transition into discrete steps for corresponding views to
+ * consume.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class AlternateBouncerToGoneTransitionViewModel
+@Inject
+constructor(
+ bouncerToGoneFlows: BouncerToGoneFlows,
+) {
+
+ /** Scrim alpha values */
+ val scrimAlpha: Flow<ScrimAlpha> =
+ bouncerToGoneFlows.scrimAlpha(TO_GONE_DURATION, ALTERNATE_BOUNCER)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
new file mode 100644
index 0000000..da74f2f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.ScrimAlpha
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.SysuiStatusBarStateController
+import dagger.Lazy
+import javax.inject.Inject
+import kotlin.time.Duration
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+
+/** ALTERNATE and PRIMARY bouncers common animations */
+@OptIn(ExperimentalCoroutinesApi::class)
+class BouncerToGoneFlows
+@Inject
+constructor(
+ private val interactor: KeyguardTransitionInteractor,
+ private val statusBarStateController: SysuiStatusBarStateController,
+ private val primaryBouncerInteractor: PrimaryBouncerInteractor,
+ private val keyguardDismissActionInteractor: Lazy<KeyguardDismissActionInteractor>,
+ private val featureFlags: FeatureFlagsClassic,
+ private val shadeInteractor: ShadeInteractor,
+) {
+ /** Common fade for scrim alpha values during *BOUNCER->GONE */
+ fun scrimAlpha(duration: Duration, fromState: KeyguardState): Flow<ScrimAlpha> {
+ return if (featureFlags.isEnabled(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT)) {
+ keyguardDismissActionInteractor
+ .get()
+ .willAnimateDismissActionOnLockscreen
+ .flatMapLatest { createScrimAlphaFlow(duration, fromState) { it } }
+ } else {
+ createScrimAlphaFlow(
+ duration,
+ fromState,
+ primaryBouncerInteractor::willRunDismissFromKeyguard
+ )
+ }
+ }
+
+ private fun createScrimAlphaFlow(
+ duration: Duration,
+ fromState: KeyguardState,
+ willRunAnimationOnKeyguard: () -> Boolean
+ ): Flow<ScrimAlpha> {
+ var isShadeExpanded = false
+ var leaveShadeOpen: Boolean = false
+ var willRunDismissFromKeyguard: Boolean = false
+ val transitionAnimation =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = duration,
+ transitionFlow = interactor.transition(fromState, GONE)
+ )
+
+ return shadeInteractor.shadeExpansion.flatMapLatest { shadeExpansion ->
+ transitionAnimation
+ .createFlow(
+ duration = duration,
+ interpolator = EMPHASIZED_ACCELERATE,
+ onStart = {
+ leaveShadeOpen = statusBarStateController.leaveOpenOnKeyguardHide()
+ willRunDismissFromKeyguard = willRunAnimationOnKeyguard()
+ isShadeExpanded = shadeExpansion > 0f
+ },
+ onStep = { 1f - it },
+ )
+ .map {
+ if (willRunDismissFromKeyguard) {
+ if (isShadeExpanded) {
+ ScrimAlpha(
+ behindAlpha = it,
+ notificationsAlpha = it,
+ )
+ } else {
+ ScrimAlpha()
+ }
+ } else if (leaveShadeOpen) {
+ ScrimAlpha(
+ behindAlpha = 1f,
+ notificationsAlpha = 1f,
+ )
+ } else {
+ ScrimAlpha(behindAlpha = it)
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
index 0783181..0e95be2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
@@ -16,7 +16,6 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlagsClassic
@@ -24,6 +23,8 @@
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.shared.model.ScrimAlpha
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -33,7 +34,6 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.map
/**
* Breaks down PRIMARY_BOUNCER->GONE transition into discrete steps for corresponding views to
@@ -49,11 +49,12 @@
private val primaryBouncerInteractor: PrimaryBouncerInteractor,
keyguardDismissActionInteractor: Lazy<KeyguardDismissActionInteractor>,
featureFlags: FeatureFlagsClassic,
+ bouncerToGoneFlows: BouncerToGoneFlows,
) {
private val transitionAnimation =
KeyguardTransitionAnimationFlow(
transitionDuration = TO_GONE_DURATION,
- transitionFlow = interactor.primaryBouncerToGoneTransition,
+ transitionFlow = interactor.transition(PRIMARY_BOUNCER, GONE)
)
private var leaveShadeOpen: Boolean = false
@@ -110,38 +111,6 @@
)
}
- /** Scrim alpha values */
val scrimAlpha: Flow<ScrimAlpha> =
- if (featureFlags.isEnabled(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT)) {
- keyguardDismissActionInteractor
- .get()
- .willAnimateDismissActionOnLockscreen
- .flatMapLatest { createScrimAlphaFlow { it } }
- } else {
- createScrimAlphaFlow(primaryBouncerInteractor::willRunDismissFromKeyguard)
- }
- private fun createScrimAlphaFlow(willRunAnimationOnKeyguard: () -> Boolean): Flow<ScrimAlpha> {
- return transitionAnimation
- .createFlow(
- duration = TO_GONE_DURATION,
- interpolator = EMPHASIZED_ACCELERATE,
- onStart = {
- leaveShadeOpen = statusBarStateController.leaveOpenOnKeyguardHide()
- willRunDismissFromKeyguard = willRunAnimationOnKeyguard()
- },
- onStep = { 1f - it },
- )
- .map {
- if (willRunDismissFromKeyguard) {
- ScrimAlpha()
- } else if (leaveShadeOpen) {
- ScrimAlpha(
- behindAlpha = 1f,
- notificationsAlpha = 1f,
- )
- } else {
- ScrimAlpha(behindAlpha = it)
- }
- }
- }
+ bouncerToGoneFlows.scrimAlpha(TO_GONE_DURATION, PRIMARY_BOUNCER)
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt
index ce8b79c..d8cab6a 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt
@@ -23,7 +23,6 @@
import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_CAST as METRICS_CREATION_SOURCE_CAST
import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_SYSTEM_UI_SCREEN_RECORDER as METRICS_CREATION_SOURCE_SYSTEM_UI_SCREEN_RECORDER
import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN as METRICS_CREATION_SOURCE_UNKNOWN
-import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED
import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_PERMISSION_REQUEST_DISPLAYED as METRICS_STATE_PERMISSION_REQUEST_DISPLAYED
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
@@ -36,16 +35,22 @@
class MediaProjectionMetricsLogger
@Inject
constructor(private val service: IMediaProjectionManager) {
+
/**
* Request to log that the permission was requested.
*
+ * @param hostUid The UID of the package that initiates MediaProjection.
* @param sessionCreationSource The entry point requesting permission to capture.
*/
- fun notifyProjectionInitiated(sessionCreationSource: SessionCreationSource) {
- notifyToServer(
- MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED,
- sessionCreationSource
- )
+ fun notifyProjectionInitiated(hostUid: Int, sessionCreationSource: SessionCreationSource) {
+ try {
+ service.notifyPermissionRequestInitiated(
+ hostUid,
+ sessionCreationSource.toMetricsConstant()
+ )
+ } catch (e: RemoteException) {
+ Log.e(TAG, "Error notifying server of projection initiated", e)
+ }
}
fun notifyPermissionRequestDisplayed() {
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index 2d830d3..922f7e0 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -140,7 +140,7 @@
if (MediaProjectionServiceHelper.hasProjectionPermission(mUid, mPackageName)) {
if (savedInstanceState == null) {
mMediaProjectionMetricsLogger.notifyProjectionInitiated(
- SessionCreationSource.APP);
+ mUid, SessionCreationSource.APP);
}
final IMediaProjection projection =
MediaProjectionServiceHelper.createOrReuseProjection(mUid, mPackageName,
@@ -242,6 +242,7 @@
if (savedInstanceState == null) {
mMediaProjectionMetricsLogger.notifyProjectionInitiated(
+ mUid,
appName == null
? SessionCreationSource.CAST
: SessionCreationSource.APP);
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index f469c6b..f602da4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -155,6 +155,7 @@
}
mMediaProjectionMetricsLogger.notifyProjectionInitiated(
+ mUserContextProvider.getUserContext().getUserId(),
SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER);
return flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
index 1ecb127..ead10d6 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
@@ -16,6 +16,7 @@
package com.android.systemui.settings.brightness;
+import static android.content.Intent.EXTRA_BRIGHTNESS_DIALOG_IS_FULL_WIDTH;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.WindowManagerPolicyConstants.EXTRA_FROM_BRIGHTNESS_KEY;
@@ -83,7 +84,7 @@
private void setWindowAttributes() {
final Window window = getWindow();
- window.setGravity(Gravity.TOP | Gravity.LEFT);
+ window.setGravity(Gravity.TOP | Gravity.START);
window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
window.requestFeature(Window.FEATURE_NO_TITLE);
@@ -130,13 +131,14 @@
Configuration configuration = getResources().getConfiguration();
int orientation = configuration.orientation;
+ int screenWidth = getWindowManager().getDefaultDisplay().getWidth();
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
- lp.width = getWindowManager().getDefaultDisplay().getWidth() / 2
- - lp.leftMargin * 2;
+ boolean shouldBeFullWidth = getIntent()
+ .getBooleanExtra(EXTRA_BRIGHTNESS_DIALOG_IS_FULL_WIDTH, false);
+ lp.width = (shouldBeFullWidth ? screenWidth : screenWidth / 2) - horizontalMargin * 2;
} else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
- lp.width = getWindowManager().getDefaultDisplay().getWidth()
- - lp.leftMargin * 2;
+ lp.width = screenWidth - horizontalMargin * 2;
}
frame.setLayoutParams(lp);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BackDropView.java b/packages/SystemUI/src/com/android/systemui/statusbar/BackDropView.java
deleted file mode 100644
index f1eb9fe..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BackDropView.java
+++ /dev/null
@@ -1,65 +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;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-
-/**
- * A view who contains media artwork.
- */
-public class BackDropView extends FrameLayout
-{
- private Runnable mOnVisibilityChangedRunnable;
-
- public BackDropView(Context context) {
- super(context);
- }
-
- public BackDropView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public BackDropView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- public BackDropView(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- @Override
- public boolean hasOverlappingRendering() {
- return false;
- }
-
- @Override
- protected void onVisibilityChanged(View changedView, int visibility) {
- super.onVisibilityChanged(changedView, visibility);
- if (changedView == this && mOnVisibilityChangedRunnable != null) {
- mOnVisibilityChangedRunnable.run();
- }
- }
-
- public void setOnVisibilityChangedRunnable(Runnable runnable) {
- mOnVisibilityChangedRunnable = runnable;
- }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index f32f1c2..710e59d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -21,6 +21,7 @@
import static android.os.UserHandle.USER_NULL;
import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS;
import static android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS;
+import static android.os.Flags.allowPrivateProfile;
import static com.android.systemui.DejankUtils.whitelistIpcs;
@@ -79,6 +80,7 @@
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executor;
+import java.util.Objects;
import javax.inject.Inject;
@@ -177,57 +179,50 @@
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- switch (action) {
- case Intent.ACTION_USER_REMOVED:
- int removedUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
- if (removedUserId != -1) {
- for (UserChangedListener listener : mListeners) {
- listener.onUserRemoved(removedUserId);
- }
+ if (Objects.equals(action, Intent.ACTION_USER_REMOVED)) {
+ int removedUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ if (removedUserId != -1) {
+ for (UserChangedListener listener : mListeners) {
+ listener.onUserRemoved(removedUserId);
}
- updateCurrentProfilesCache();
- break;
- case Intent.ACTION_USER_ADDED:
- updateCurrentProfilesCache();
- if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
- mBackgroundHandler.post(() -> {
- initValuesForUser(userId);
- });
+ }
+ updateCurrentProfilesCache();
+ } else if (Objects.equals(action, Intent.ACTION_USER_ADDED)){
+ updateCurrentProfilesCache();
+ if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
+ mBackgroundHandler.post(() -> {
+ initValuesForUser(userId);
+ });
+ }
+ } else if (profileAvailabilityActions(action)) {
+ updateCurrentProfilesCache();
+ } else if (Objects.equals(action, Intent.ACTION_USER_UNLOCKED)) {
+ // Start the overview connection to the launcher service
+ // Connect if user hasn't connected yet
+ if (mOverviewProxyServiceLazy.get().getProxy() == null) {
+ mOverviewProxyServiceLazy.get().startConnectionToCurrentUser();
+ }
+ } else if (Objects.equals(action, NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION)) {
+ final IntentSender intentSender = intent.getParcelableExtra(
+ Intent.EXTRA_INTENT);
+ final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX);
+ if (intentSender != null) {
+ try {
+ ActivityOptions options = ActivityOptions.makeBasic();
+ options.setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
+ mContext.startIntentSender(intentSender, null, 0, 0, 0,
+ options.toBundle());
+ } catch (IntentSender.SendIntentException e) {
+ /* ignore */
}
- break;
- case Intent.ACTION_MANAGED_PROFILE_AVAILABLE:
- case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE:
- updateCurrentProfilesCache();
- break;
- case Intent.ACTION_USER_UNLOCKED:
- // Start the overview connection to the launcher service
- // Connect if user hasn't connected yet
- if (mOverviewProxyServiceLazy.get().getProxy() == null) {
- mOverviewProxyServiceLazy.get().startConnectionToCurrentUser();
- }
- break;
- case NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION:
- final IntentSender intentSender = intent.getParcelableExtra(
- Intent.EXTRA_INTENT);
- final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX);
- if (intentSender != null) {
- try {
- ActivityOptions options = ActivityOptions.makeBasic();
- options.setPendingIntentBackgroundActivityStartMode(
- ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
- mContext.startIntentSender(intentSender, null, 0, 0, 0,
- options.toBundle());
- } catch (IntentSender.SendIntentException e) {
- /* ignore */
- }
- }
- if (notificationKey != null) {
- final NotificationVisibility nv = mVisibilityProviderLazy.get()
- .obtain(notificationKey, true);
- mClickNotifier.onNotificationClick(notificationKey, nv);
- }
- break;
+ }
+ if (notificationKey != null) {
+ final NotificationVisibility nv = mVisibilityProviderLazy.get()
+ .obtain(notificationKey, true);
+ mClickNotifier.onNotificationClick(notificationKey, nv);
+ }
}
}
};
@@ -403,6 +398,10 @@
filter.addAction(Intent.ACTION_USER_UNLOCKED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
+ if (allowPrivateProfile()){
+ filter.addAction(Intent.ACTION_PROFILE_AVAILABLE);
+ filter.addAction(Intent.ACTION_PROFILE_UNAVAILABLE);
+ }
mBroadcastDispatcher.registerReceiver(mBaseBroadcastReceiver, filter,
null /* executor */, UserHandle.ALL);
@@ -814,6 +813,14 @@
}
}
+ private boolean profileAvailabilityActions(String action){
+ return allowPrivateProfile()?
+ Objects.equals(action,Intent.ACTION_PROFILE_AVAILABLE)||
+ Objects.equals(action,Intent.ACTION_PROFILE_UNAVAILABLE):
+ Objects.equals(action,Intent.ACTION_MANAGED_PROFILE_AVAILABLE)||
+ Objects.equals(action,Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
+ }
+
@Override
public void dump(PrintWriter pw, String[] args) {
pw.println("NotificationLockscreenUserManager state:");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 389486f..9c4625e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -18,31 +18,21 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
-import android.app.WallpaperManager;
import android.content.Context;
-import android.graphics.Point;
import android.graphics.drawable.Icon;
-import android.hardware.display.DisplayManager;
import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
-import android.os.Trace;
import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.util.Log;
-import android.view.Display;
-import android.view.View;
-import android.widget.ImageView;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dumpable;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.controls.models.player.MediaData;
import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData;
import com.android.systemui.media.controls.pipeline.MediaDataManager;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.dagger.CentralSurfacesModule;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -50,21 +40,13 @@
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
-import com.android.systemui.statusbar.phone.BiometricUnlockController;
-import com.android.systemui.statusbar.phone.LockscreenWallpaper;
-import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
-import java.util.Comparator;
import java.util.HashSet;
-import java.util.List;
import java.util.Objects;
import java.util.Optional;
-import java.util.stream.Collectors;
/**
* Handles tasks and state related to media notifications. For example, there is a 'current' media
@@ -74,9 +56,6 @@
private static final String TAG = "NotificationMediaManager";
public static final boolean DEBUG_MEDIA = false;
- private final StatusBarStateController mStatusBarStateController;
- private final SysuiColorExtractor mColorExtractor;
- private final KeyguardStateController mKeyguardStateController;
private static final HashSet<Integer> PAUSED_MEDIA_STATES = new HashSet<>();
private static final HashSet<Integer> CONNECTING_MEDIA_STATES = new HashSet<>();
static {
@@ -93,15 +72,6 @@
private final NotifPipeline mNotifPipeline;
private final NotifCollection mNotifCollection;
- @Nullable
- private BiometricUnlockController mBiometricUnlockController;
- @Nullable
- private ScrimController mScrimController;
- @Nullable
- private LockscreenWallpaper mLockscreenWallpaper;
- @VisibleForTesting
- boolean mIsLockscreenLiveWallpaperEnabled;
-
private final Context mContext;
private final ArrayList<MediaListener> mMediaListeners;
@@ -110,16 +80,6 @@
private String mMediaNotificationKey;
private MediaMetadata mMediaMetadata;
- private BackDropView mBackdrop;
- private ImageView mBackdropFront;
- private ImageView mBackdropBack;
- private final Point mTmpDisplaySize = new Point();
-
- private final DisplayManager mDisplayManager;
- @Nullable
- private List<String> mSmallerInternalDisplayUids;
- private Display mCurrentDisplay;
-
private final MediaController.Callback mMediaListener = new MediaController.Callback() {
@Override
public void onPlaybackStateChanged(PlaybackState state) {
@@ -142,7 +102,7 @@
Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata);
}
mMediaMetadata = metadata;
- dispatchUpdateMediaMetaData(true /* changed */, true /* allowAnimation */);
+ dispatchUpdateMediaMetaData();
}
};
@@ -155,23 +115,13 @@
NotifPipeline notifPipeline,
NotifCollection notifCollection,
MediaDataManager mediaDataManager,
- StatusBarStateController statusBarStateController,
- SysuiColorExtractor colorExtractor,
- KeyguardStateController keyguardStateController,
- DumpManager dumpManager,
- WallpaperManager wallpaperManager,
- DisplayManager displayManager) {
+ DumpManager dumpManager) {
mContext = context;
mMediaListeners = new ArrayList<>();
mVisibilityProvider = visibilityProvider;
mMediaDataManager = mediaDataManager;
mNotifPipeline = notifPipeline;
mNotifCollection = notifCollection;
- mStatusBarStateController = statusBarStateController;
- mColorExtractor = colorExtractor;
- mKeyguardStateController = keyguardStateController;
- mDisplayManager = displayManager;
- mIsLockscreenLiveWallpaperEnabled = wallpaperManager.isLockscreenLiveWallpaperEnabled();
setupNotifPipeline();
@@ -275,7 +225,7 @@
public void onNotificationRemoved(String key) {
if (key.equals(mMediaNotificationKey)) {
clearCurrentMediaNotification();
- dispatchUpdateMediaMetaData(true /* changed */, true /* allowEnterAnimation */);
+ dispatchUpdateMediaMetaData();
}
}
@@ -309,21 +259,18 @@
}
public void findAndUpdateMediaNotifications() {
- boolean metaDataChanged;
// TODO(b/169655907): get the semi-filtered notifications for current user
Collection<NotificationEntry> allNotifications = mNotifPipeline.getAllNotifs();
- metaDataChanged = findPlayingMediaNotification(allNotifications);
- dispatchUpdateMediaMetaData(metaDataChanged, true /* allowEnterAnimation */);
+ findPlayingMediaNotification(allNotifications);
+ dispatchUpdateMediaMetaData();
}
/**
* Find a notification and media controller associated with the playing media session, and
* update this manager's internal state.
- * @return whether the current MediaMetadata changed (and needs to be announced to listeners).
+ * TODO(b/273443374) check this method
*/
- boolean findPlayingMediaNotification(
- @NonNull Collection<NotificationEntry> allNotifications) {
- boolean metaDataChanged = false;
+ void findPlayingMediaNotification(@NonNull Collection<NotificationEntry> allNotifications) {
// Promote the media notification with a controller in 'playing' state, if any.
NotificationEntry mediaNotification = null;
MediaController controller = null;
@@ -359,8 +306,6 @@
Log.v(TAG, "DEBUG_MEDIA: insert listener, found new controller: "
+ mMediaController + ", receive metadata: " + mMediaMetadata);
}
-
- metaDataChanged = true;
}
if (mediaNotification != null
@@ -371,8 +316,6 @@
+ mMediaNotificationKey);
}
}
-
- return metaDataChanged;
}
public void clearCurrentMediaNotification() {
@@ -380,10 +323,7 @@
clearCurrentMediaNotificationSession();
}
- private void dispatchUpdateMediaMetaData(boolean changed, boolean allowEnterAnimation) {
- if (mPresenter != null) {
- mPresenter.updateMediaMetaData(changed, allowEnterAnimation);
- }
+ private void dispatchUpdateMediaMetaData() {
@PlaybackState.State int state = getMediaControllerPlaybackState(mMediaController);
ArrayList<MediaListener> callbacks = new ArrayList<>(mMediaListeners);
for (int i = 0; i < callbacks.size(); i++) {
@@ -446,125 +386,6 @@
mMediaController = null;
}
- /**
- * Notify lockscreen wallpaper drawable the current internal display.
- */
- public void onDisplayUpdated(Display display) {
- Trace.beginSection("NotificationMediaManager#onDisplayUpdated");
- mCurrentDisplay = display;
- Trace.endSection();
- }
-
- private boolean isOnSmallerInternalDisplays() {
- if (mSmallerInternalDisplayUids == null) {
- mSmallerInternalDisplayUids = findSmallerInternalDisplayUids();
- }
- return mSmallerInternalDisplayUids.contains(mCurrentDisplay.getUniqueId());
- }
-
- private List<String> findSmallerInternalDisplayUids() {
- if (mSmallerInternalDisplayUids != null) {
- return mSmallerInternalDisplayUids;
- }
- List<Display> internalDisplays = Arrays.stream(mDisplayManager.getDisplays(
- DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED))
- .filter(display -> display.getType() == Display.TYPE_INTERNAL)
- .collect(Collectors.toList());
- if (internalDisplays.isEmpty()) {
- return List.of();
- }
- Display largestDisplay = internalDisplays.stream()
- .max(Comparator.comparingInt(this::getRealDisplayArea))
- .orElse(internalDisplays.get(0));
- internalDisplays.remove(largestDisplay);
- return internalDisplays.stream().map(Display::getUniqueId).collect(Collectors.toList());
- }
-
- private int getRealDisplayArea(Display display) {
- display.getRealSize(mTmpDisplaySize);
- return mTmpDisplaySize.x * mTmpDisplaySize.y;
- }
-
- /**
- * Update media state of lockscreen media views and controllers .
- */
- public void updateMediaMetaData(boolean metaDataChanged) {
-
- if (mIsLockscreenLiveWallpaperEnabled) return;
-
- Trace.beginSection("CentralSurfaces#updateMediaMetaData");
- if (getBackDropView() == null) {
- Trace.endSection();
- return; // called too early
- }
-
- boolean wakeAndUnlock = mBiometricUnlockController != null
- && mBiometricUnlockController.isWakeAndUnlock();
- if (mKeyguardStateController.isLaunchTransitionFadingAway() || wakeAndUnlock) {
- mBackdrop.setVisibility(View.INVISIBLE);
- Trace.endSection();
- return;
- }
-
- MediaMetadata mediaMetadata = getMediaMetadata();
-
- if (DEBUG_MEDIA) {
- Log.v(TAG, "DEBUG_MEDIA: updating album art for notification "
- + getMediaNotificationKey()
- + " metadata=" + mediaMetadata
- + " metaDataChanged=" + metaDataChanged
- + " state=" + mStatusBarStateController.getState());
- }
-
- mColorExtractor.setHasMediaArtwork(false);
- if (mScrimController != null) {
- mScrimController.setHasBackdrop(false);
- }
-
- Trace.endSection();
- }
-
- public void setup(BackDropView backdrop, ImageView backdropFront, ImageView backdropBack,
- ScrimController scrimController, LockscreenWallpaper lockscreenWallpaper) {
- mBackdrop = backdrop;
- mBackdropFront = backdropFront;
- mBackdropBack = backdropBack;
- mScrimController = scrimController;
- mLockscreenWallpaper = lockscreenWallpaper;
- }
-
- public void setBiometricUnlockController(BiometricUnlockController biometricUnlockController) {
- mBiometricUnlockController = biometricUnlockController;
- }
-
- /**
- * Hide the album artwork that is fading out and release its bitmap.
- */
- protected final Runnable mHideBackdropFront = new Runnable() {
- @Override
- public void run() {
- if (DEBUG_MEDIA) {
- Log.v(TAG, "DEBUG_MEDIA: removing fade layer");
- }
- mBackdropFront.setVisibility(View.INVISIBLE);
- mBackdropFront.animate().cancel();
- mBackdropFront.setImageDrawable(null);
- }
- };
-
- // TODO(b/273443374): remove
- public boolean isLockscreenWallpaperOnNotificationShade() {
- return mBackdrop != null && mLockscreenWallpaper != null
- && !mLockscreenWallpaper.isLockscreenLiveWallpaperEnabled()
- && (mBackdropFront.isVisibleToUser() || mBackdropBack.isVisibleToUser());
- }
-
- // TODO(b/273443374) temporary test helper; remove
- @VisibleForTesting
- BackDropView getBackDropView() {
- return mBackdrop;
- }
-
public interface MediaListener {
/**
* Called whenever there's new metadata or playback state.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
index 5dcf6d1..f3b5ab6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
@@ -32,11 +32,6 @@
boolean isPresenterFullyCollapsed();
/**
- * Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper.
- */
- void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation);
-
- /**
* Called when the current user changes.
* @param newUserId new user id
*/
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 125c8efe..1fe6b83 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -16,9 +16,7 @@
package com.android.systemui.statusbar.dagger;
-import android.app.WallpaperManager;
import android.content.Context;
-import android.hardware.display.DisplayManager;
import android.os.RemoteException;
import android.service.dreams.IDreamManager;
import android.util.Log;
@@ -29,7 +27,6 @@
import com.android.systemui.animation.AnimationFeatureFlags;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpHandler;
import com.android.systemui.dump.DumpManager;
@@ -121,24 +118,14 @@
NotifPipeline notifPipeline,
NotifCollection notifCollection,
MediaDataManager mediaDataManager,
- StatusBarStateController statusBarStateController,
- SysuiColorExtractor colorExtractor,
- KeyguardStateController keyguardStateController,
- DumpManager dumpManager,
- WallpaperManager wallpaperManager,
- DisplayManager displayManager) {
+ DumpManager dumpManager) {
return new NotificationMediaManager(
context,
visibilityProvider,
notifPipeline,
notifCollection,
mediaDataManager,
- statusBarStateController,
- colorExtractor,
- keyguardStateController,
- dumpManager,
- wallpaperManager,
- displayManager);
+ dumpManager);
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt
index bde298d..ea1d782 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt
@@ -36,6 +36,11 @@
val showAnimation: Boolean
val viewCreator: ViewCreator
var contentDescription: String?
+ /**
+ * When true, an accessibility event with [contentDescription] is announced when the view
+ * becomes visible.
+ */
+ val shouldAnnounceAccessibilityEvent: Boolean
// Update this event with values from another event.
fun updateFromEvent(other: StatusEvent?) {
@@ -76,6 +81,7 @@
override var forceVisible = false
override val showAnimation = true
override var contentDescription: String? = ""
+ override val shouldAnnounceAccessibilityEvent: Boolean = false
override val viewCreator: ViewCreator = { context ->
BatteryStatusChip(context).apply {
@@ -95,6 +101,7 @@
override var forceVisible = false
override val showAnimation = true
override var contentDescription: String? = ""
+ override val shouldAnnounceAccessibilityEvent: Boolean = true
override val viewCreator: ViewCreator = { context ->
ConnectedDisplayChip(context)
@@ -110,6 +117,7 @@
override var contentDescription: String? = null
override val priority = 100
override var forceVisible = true
+ override val shouldAnnounceAccessibilityEvent: Boolean = false
var privacyItems: List<PrivacyItem> = listOf()
private var privacyChip: OngoingPrivacyChip? = null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
index dccc23f..73c0bfe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
@@ -273,6 +273,11 @@
})
}
+ /** Announces [contentDescriptions] for accessibility. */
+ fun announceForAccessibility(contentDescriptions: String) {
+ currentAnimatedView?.view?.announceForAccessibility(contentDescriptions)
+ }
+
private fun updateDimens(contentArea: Rect) {
val lp = animationWindowView.layoutParams as FrameLayout.LayoutParams
lp.height = contentArea.height()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
index 7d866df..8ee1ade 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
@@ -100,9 +100,12 @@
}
private fun startConnectedDisplayCollection() {
+ val connectedDisplayEvent = ConnectedDisplayEvent().apply {
+ contentDescription = context.getString(R.string.connected_display_icon_desc)
+ }
connectedDisplayCollectionJob =
onDisplayConnectedFlow
- .onEach { scheduler.onStatusEvent(ConnectedDisplayEvent()) }
+ .onEach { scheduler.onStatusEvent(connectedDisplayEvent) }
.launchIn(appScope)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt
index a3bc002..f0e60dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt
@@ -254,11 +254,18 @@
currentlyRunningAnimationJob =
coroutineScope.launch {
runChipAppearAnimation()
+ announceForAccessibilityIfNeeded(event)
delay(APPEAR_ANIMATION_DURATION + DISPLAY_LENGTH)
runChipDisappearAnimation()
}
}
+ private fun announceForAccessibilityIfNeeded(event: StatusEvent) {
+ val description = event.contentDescription ?: return
+ if (!event.shouldAnnounceAccessibilityEvent) return
+ chipAnimationController.announceForAccessibility(description)
+ }
+
/**
* 1. Define a total budget for the chip animation (1500ms)
* 2. Send out callbacks to listeners so that they can generate animations locally
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
index 8957f29..f98f39e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
@@ -20,6 +20,7 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
+import android.util.Log;
import android.util.Property;
import android.view.View;
import android.view.animation.Interpolator;
@@ -33,6 +34,7 @@
* An animator to animate properties
*/
public class PropertyAnimator {
+ private static final String TAG = "PropertyAnimator";
/**
* Set a property on a view, updating its value, even if it's already animating.
@@ -123,6 +125,8 @@
view.setTag(animatorTag, null);
view.setTag(animationStartTag, null);
view.setTag(animationEndTag, null);
+ } else {
+ Log.e(TAG, "Unexpected Animator set during onAnimationEnd. Not cleaning up.");
}
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/shared/FooterViewRefactor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/shared/FooterViewRefactor.kt
new file mode 100644
index 0000000..94e70e5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/shared/FooterViewRefactor.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.footer.shared
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the FooterView refactor flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object FooterViewRefactor {
+ const val FLAG_NAME = Flags.FLAG_NOTIFICATIONS_FOOTER_VIEW_REFACTOR
+
+ /** Is the refactor enabled */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.notificationsFooterViewRefactor()
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun isUnexpectedlyInLegacyMode() =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 59f10ae..daa4f18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -511,7 +511,6 @@
case MODE_WAKE_AND_UNLOCK:
if (mMode == MODE_WAKE_AND_UNLOCK_PULSING) {
Trace.beginSection("MODE_WAKE_AND_UNLOCK_PULSING");
- mMediaManager.updateMediaMetaData(false /* metaDataChanged */);
} else if (mMode == MODE_WAKE_AND_UNLOCK){
Trace.beginSection("MODE_WAKE_AND_UNLOCK");
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 8d9fd12..7cd32f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -76,7 +76,6 @@
import android.util.EventLog;
import android.util.IndentingPrintWriter;
import android.util.Log;
-import android.util.MathUtils;
import android.view.Display;
import android.view.IRemoteAnimationRunner;
import android.view.IWindowManager;
@@ -176,7 +175,6 @@
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.statusbar.AutoHideUiElement;
-import com.android.systemui.statusbar.BackDropView;
import com.android.systemui.statusbar.CircleReveal;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.GestureRecorder;
@@ -237,10 +235,10 @@
import com.android.wm.shell.startingsurface.SplashscreenContentDrawer;
import com.android.wm.shell.startingsurface.StartingSurface;
-import dagger.Lazy;
-
import dalvik.annotation.optimization.NeverCompile;
+import dagger.Lazy;
+
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
@@ -376,9 +374,6 @@
private boolean mBrightnessMirrorVisible;
private BiometricUnlockController mBiometricUnlockController;
private final LightBarController mLightBarController;
- private final Lazy<LockscreenWallpaper> mLockscreenWallpaperLazy;
- @Nullable
- protected LockscreenWallpaper mLockscreenWallpaper;
private final AutoHideController mAutoHideController;
private final Point mCurrentDisplaySize = new Point();
@@ -658,7 +653,6 @@
NotificationExpansionRepository notificationExpansionRepository,
DozeParameters dozeParameters,
ScrimController scrimController,
- Lazy<LockscreenWallpaper> lockscreenWallpaperLazy,
Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
AuthRippleController authRippleController,
DozeServiceHost dozeServiceHost,
@@ -770,7 +764,6 @@
mPowerManager = powerManager;
mDozeParameters = dozeParameters;
mScrimController = scrimController;
- mLockscreenWallpaperLazy = lockscreenWallpaperLazy;
mDozeScrimController = dozeScrimController;
mBiometricUnlockControllerLazy = biometricUnlockControllerLazy;
mAuthRippleController = authRippleController;
@@ -1198,10 +1191,6 @@
createNavigationBar(result);
- if (ENABLE_LOCKSCREEN_WALLPAPER && mWallpaperSupported) {
- mLockscreenWallpaper = mLockscreenWallpaperLazy.get();
- }
-
mAmbientIndicationContainer = getNotificationShadeWindowView().findViewById(
R.id.ambient_indication_container);
@@ -1268,24 +1257,6 @@
mNotificationShelfController,
mHeadsUpManager);
- BackDropView backdrop = getNotificationShadeWindowView().findViewById(R.id.backdrop);
- if (mWallpaperManager.isLockscreenLiveWallpaperEnabled()) {
- mMediaManager.setup(null, null, null, mScrimController, null);
- } else {
- mMediaManager.setup(backdrop, backdrop.findViewById(R.id.backdrop_front),
- backdrop.findViewById(R.id.backdrop_back), mScrimController,
- mLockscreenWallpaper);
- }
- float maxWallpaperZoom = mContext.getResources().getFloat(
- com.android.internal.R.dimen.config_wallpaperMaxScale);
- mNotificationShadeDepthControllerLazy.get().addListener(depth -> {
- float scale = MathUtils.lerp(maxWallpaperZoom, 1f, depth);
- backdrop.setPivotX(backdrop.getWidth() / 2f);
- backdrop.setPivotY(backdrop.getHeight() / 2f);
- backdrop.setScaleX(scale);
- backdrop.setScaleY(scale);
- });
-
// Set up the quick settings tile panel
final View container = getNotificationShadeWindowView().findViewById(R.id.qs_frame);
if (container != null) {
@@ -1357,14 +1328,6 @@
// receive broadcasts
registerBroadcastReceiver();
- IntentFilter demoFilter = new IntentFilter();
- if (DEBUG_MEDIA_FAKE_ARTWORK) {
- demoFilter.addAction(ACTION_FAKE_ARTWORK);
- }
- mContext.registerReceiverAsUser(mDemoReceiver, UserHandle.ALL, demoFilter,
- android.Manifest.permission.DUMP, null,
- Context.RECEIVER_EXPORTED_UNAUDITED);
-
// listen for USER_SETUP_COMPLETE setting (per-user)
mDeviceProvisionedController.addCallback(mUserSetupObserver);
mUserSetupObserver.onUserSetupChanged();
@@ -1583,7 +1546,6 @@
mRemoteInputManager.addControllerCallback(mStatusBarKeyguardViewManager);
mLightBarController.setBiometricUnlockController(mBiometricUnlockController);
- mMediaManager.setBiometricUnlockController(mBiometricUnlockController);
Trace.endSection();
}
@@ -1870,7 +1832,6 @@
void updateDisplaySize() {
mDisplay.getMetrics(mDisplayMetrics);
mDisplay.getSize(mCurrentDisplaySize);
- mMediaManager.onDisplayUpdated(mDisplay);
if (DEBUG_GESTURES) {
mGestureRec.tag("display",
String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
@@ -1944,19 +1905,6 @@
}
};
- private final BroadcastReceiver mDemoReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (DEBUG) Log.v(TAG, "onReceive: " + intent);
- String action = intent.getAction();
- if (ACTION_FAKE_ARTWORK.equals(action)) {
- if (DEBUG_MEDIA_FAKE_ARTWORK) {
- mPresenterLazy.get().updateMediaMetaData(true, true);
- }
- }
- }
- };
-
/**
* Reload some of our resources when the configuration changes.
*
@@ -2139,7 +2087,6 @@
releaseGestureWakeLock();
runLaunchTransitionEndRunnable();
mKeyguardStateController.setLaunchTransitionFadingAway(false);
- mPresenterLazy.get().updateMediaMetaData(true /* metaDataChanged */, true);
}
/**
@@ -2163,7 +2110,6 @@
beforeFading.run();
}
updateScrimController();
- mPresenterLazy.get().updateMediaMetaData(false, true);
mShadeSurface.resetAlpha();
mShadeSurface.fadeOut(
FADE_KEYGUARD_START_DELAY, FADE_KEYGUARD_DURATION,
@@ -3222,8 +3168,6 @@
updateDozingState();
checkBarModes();
updateScrimController();
- mPresenterLazy.get()
- .updateMediaMetaData(false, mState != StatusBarState.KEYGUARD);
Trace.endSection();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index 2960520..2206be5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -546,8 +546,7 @@
* (1.0f - mKeyguardHeadsUpShowingAmount);
}
- if (mSystemEventAnimator.isAnimationRunning()
- && !mNotificationMediaManager.isLockscreenWallpaperOnNotificationShade()) {
+ if (mSystemEventAnimator.isAnimationRunning()) {
newAlpha = Math.min(newAlpha, mSystemEventAnimatorAlpha);
} else {
mView.setTranslationX(0);
@@ -704,21 +703,11 @@
private StatusBarSystemEventDefaultAnimator getSystemEventAnimator(boolean isAnimationRunning) {
return new StatusBarSystemEventDefaultAnimator(getResources(), (alpha) -> {
- // TODO(b/273443374): remove if-else condition
- if (!mNotificationMediaManager.isLockscreenWallpaperOnNotificationShade()) {
- mSystemEventAnimatorAlpha = alpha;
- } else {
- mSystemEventAnimatorAlpha = 1f;
- }
+ mSystemEventAnimatorAlpha = alpha;
updateViewState();
return Unit.INSTANCE;
}, (translationX) -> {
- // TODO(b/273443374): remove if-else condition
- if (!mNotificationMediaManager.isLockscreenWallpaperOnNotificationShade()) {
- mView.setTranslationX(translationX);
- } else {
- mView.setTranslationX(0);
- }
+ mView.setTranslationX(translationX);
return Unit.INSTANCE;
}, isAnimationRunning);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
deleted file mode 100644
index 00fd9fb..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ /dev/null
@@ -1,434 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.annotation.Nullable;
-import android.app.IWallpaperManager;
-import android.app.IWallpaperManagerCallback;
-import android.app.WallpaperColors;
-import android.app.WallpaperManager;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Rect;
-import android.graphics.Xfermode;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.DrawableWrapper;
-import android.os.AsyncTask;
-import android.os.Handler;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.CoreStartable;
-import com.android.systemui.Dumpable;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.user.data.model.SelectedUserModel;
-import com.android.systemui.user.data.model.SelectionStatus;
-import com.android.systemui.user.data.repository.UserRepository;
-import com.android.systemui.util.kotlin.JavaAdapter;
-
-import libcore.io.IoUtils;
-
-import java.io.PrintWriter;
-import java.util.Objects;
-
-import javax.inject.Inject;
-
-/**
- * Manages the lockscreen wallpaper.
- */
-@SysUISingleton
-public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implements Runnable,
- Dumpable, CoreStartable {
-
- private static final String TAG = "LockscreenWallpaper";
-
- // TODO(b/253507223): temporary; remove this
- private static final String DISABLED_ERROR_MESSAGE = "Methods from LockscreenWallpaper.java "
- + "should not be called in this version. The lock screen wallpaper should be "
- + "managed by the WallpaperManagerService and not by this class.";
-
- private final NotificationMediaManager mMediaManager;
- private final WallpaperManager mWallpaperManager;
- private final KeyguardUpdateMonitor mUpdateMonitor;
- private final Handler mH;
- private final JavaAdapter mJavaAdapter;
- private final UserRepository mUserRepository;
-
- private boolean mCached;
- private Bitmap mCache;
- private int mCurrentUserId;
- // The user selected in the UI, or null if no user is selected or UI doesn't support selecting
- // users.
- private UserHandle mSelectedUser;
- private AsyncTask<Void, Void, LoaderResult> mLoader;
-
- @Inject
- public LockscreenWallpaper(WallpaperManager wallpaperManager,
- @Nullable IWallpaperManager iWallpaperManager,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- DumpManager dumpManager,
- NotificationMediaManager mediaManager,
- @Main Handler mainHandler,
- JavaAdapter javaAdapter,
- UserRepository userRepository,
- UserTracker userTracker) {
- dumpManager.registerDumpable(getClass().getSimpleName(), this);
- mWallpaperManager = wallpaperManager;
- mCurrentUserId = userTracker.getUserId();
- mUpdateMonitor = keyguardUpdateMonitor;
- mMediaManager = mediaManager;
- mH = mainHandler;
- mJavaAdapter = javaAdapter;
- mUserRepository = userRepository;
-
- if (iWallpaperManager != null && !mWallpaperManager.isLockscreenLiveWallpaperEnabled()) {
- // Service is disabled on some devices like Automotive
- try {
- iWallpaperManager.setLockWallpaperCallback(this);
- } catch (RemoteException e) {
- Log.e(TAG, "System dead?" + e);
- }
- }
- }
-
- @Override
- public void start() {
- if (!isLockscreenLiveWallpaperEnabled()) {
- mJavaAdapter.alwaysCollectFlow(
- mUserRepository.getSelectedUser(), this::setSelectedUser);
- }
- }
-
- public Bitmap getBitmap() {
- assertLockscreenLiveWallpaperNotEnabled();
-
- if (mCached) {
- return mCache;
- }
- if (!mWallpaperManager.isWallpaperSupported()) {
- mCached = true;
- mCache = null;
- return null;
- }
-
- LoaderResult result = loadBitmap(mCurrentUserId, mSelectedUser);
- if (result.success) {
- mCached = true;
- mCache = result.bitmap;
- }
- return mCache;
- }
-
- public LoaderResult loadBitmap(int currentUserId, UserHandle selectedUser) {
- // May be called on any thread - only use thread safe operations.
-
- assertLockscreenLiveWallpaperNotEnabled();
-
-
- if (!mWallpaperManager.isWallpaperSupported()) {
- // When wallpaper is not supported, show the system wallpaper
- return LoaderResult.success(null);
- }
-
- // Prefer the selected user (when specified) over the current user for the FLAG_SET_LOCK
- // wallpaper.
- final int lockWallpaperUserId =
- selectedUser != null ? selectedUser.getIdentifier() : currentUserId;
- ParcelFileDescriptor fd = mWallpaperManager.getWallpaperFile(
- WallpaperManager.FLAG_LOCK, lockWallpaperUserId);
-
- if (fd != null) {
- try {
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inPreferredConfig = Bitmap.Config.HARDWARE;
- return LoaderResult.success(BitmapFactory.decodeFileDescriptor(
- fd.getFileDescriptor(), null, options));
- } catch (OutOfMemoryError e) {
- Log.w(TAG, "Can't decode file", e);
- return LoaderResult.fail();
- } finally {
- IoUtils.closeQuietly(fd);
- }
- } else {
- if (selectedUser != null) {
- // Show the selected user's static wallpaper.
- return LoaderResult.success(mWallpaperManager.getBitmapAsUser(
- selectedUser.getIdentifier(), true /* hardware */));
-
- } else {
- // When there is no selected user, show the system wallpaper
- return LoaderResult.success(null);
- }
- }
- }
-
- private void setSelectedUser(SelectedUserModel selectedUserModel) {
- assertLockscreenLiveWallpaperNotEnabled();
-
- if (selectedUserModel.getSelectionStatus().equals(SelectionStatus.SELECTION_IN_PROGRESS)) {
- // Wait until the selection has finished before updating.
- return;
- }
-
- int user = selectedUserModel.getUserInfo().id;
- if (user != mCurrentUserId) {
- if (mSelectedUser == null || user != mSelectedUser.getIdentifier()) {
- mCached = false;
- }
- mCurrentUserId = user;
- }
- }
-
- public void setSelectedUser(UserHandle selectedUser) {
- assertLockscreenLiveWallpaperNotEnabled();
-
- if (Objects.equals(selectedUser, mSelectedUser)) {
- return;
- }
- mSelectedUser = selectedUser;
- postUpdateWallpaper();
- }
-
- @Override
- public void onWallpaperChanged() {
- assertLockscreenLiveWallpaperNotEnabled();
- // Called on Binder thread.
- postUpdateWallpaper();
- }
-
- @Override
- public void onWallpaperColorsChanged(WallpaperColors colors, int which, int userId) {
- assertLockscreenLiveWallpaperNotEnabled();
- }
-
- private void postUpdateWallpaper() {
- assertLockscreenLiveWallpaperNotEnabled();
- if (mH == null) {
- Log.wtfStack(TAG, "Trying to use LockscreenWallpaper before initialization.");
- return;
- }
- mH.removeCallbacks(this);
- mH.post(this);
- }
- @Override
- public void run() {
- // Called in response to onWallpaperChanged on the main thread.
-
- assertLockscreenLiveWallpaperNotEnabled();
-
- if (mLoader != null) {
- mLoader.cancel(false /* interrupt */);
- }
-
- final int currentUser = mCurrentUserId;
- final UserHandle selectedUser = mSelectedUser;
- mLoader = new AsyncTask<Void, Void, LoaderResult>() {
- @Override
- protected LoaderResult doInBackground(Void... params) {
- return loadBitmap(currentUser, selectedUser);
- }
-
- @Override
- protected void onPostExecute(LoaderResult result) {
- super.onPostExecute(result);
- if (isCancelled()) {
- return;
- }
- if (result.success) {
- mCached = true;
- mCache = result.bitmap;
- mMediaManager.updateMediaMetaData(true /* metaDataChanged */);
- }
- mLoader = null;
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }
-
- // TODO(b/273443374): remove
- public boolean isLockscreenLiveWallpaperEnabled() {
- return mWallpaperManager.isLockscreenLiveWallpaperEnabled();
- }
-
- @Override
- public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
- pw.println(getClass().getSimpleName() + ":");
- IndentingPrintWriter iPw = new IndentingPrintWriter(pw, " ").increaseIndent();
- iPw.println("mCached=" + mCached);
- iPw.println("mCache=" + mCache);
- iPw.println("mCurrentUserId=" + mCurrentUserId);
- iPw.println("mSelectedUser=" + mSelectedUser);
- }
-
- private static class LoaderResult {
- public final boolean success;
- public final Bitmap bitmap;
-
- LoaderResult(boolean success, Bitmap bitmap) {
- this.success = success;
- this.bitmap = bitmap;
- }
-
- static LoaderResult success(Bitmap b) {
- return new LoaderResult(true, b);
- }
-
- static LoaderResult fail() {
- return new LoaderResult(false, null);
- }
- }
-
- /**
- * Drawable that aligns left horizontally and center vertically (like ImageWallpaper).
- *
- * <p>Aligns to the center when showing on the smaller internal display of a multi display
- * device.
- */
- public static class WallpaperDrawable extends DrawableWrapper {
-
- private final ConstantState mState;
- private final Rect mTmpRect = new Rect();
- private boolean mIsOnSmallerInternalDisplays;
-
- public WallpaperDrawable(Resources r, Bitmap b, boolean isOnSmallerInternalDisplays) {
- this(r, new ConstantState(b), isOnSmallerInternalDisplays);
- }
-
- private WallpaperDrawable(Resources r, ConstantState state,
- boolean isOnSmallerInternalDisplays) {
- super(new BitmapDrawable(r, state.mBackground));
- mState = state;
- mIsOnSmallerInternalDisplays = isOnSmallerInternalDisplays;
- }
-
- @Override
- public void setXfermode(@Nullable Xfermode mode) {
- // DrawableWrapper does not call this for us.
- getDrawable().setXfermode(mode);
- }
-
- @Override
- public int getIntrinsicWidth() {
- return -1;
- }
-
- @Override
- public int getIntrinsicHeight() {
- return -1;
- }
-
- @Override
- protected void onBoundsChange(Rect bounds) {
- int vwidth = getBounds().width();
- int vheight = getBounds().height();
- int dwidth = mState.mBackground.getWidth();
- int dheight = mState.mBackground.getHeight();
- float scale;
- float dx = 0, dy = 0;
-
- if (dwidth * vheight > vwidth * dheight) {
- scale = (float) vheight / (float) dheight;
- } else {
- scale = (float) vwidth / (float) dwidth;
- }
-
- if (scale <= 1f) {
- scale = 1f;
- }
- dy = (vheight - dheight * scale) * 0.5f;
-
- int offsetX = 0;
- // Offset to show the center area of the wallpaper on a smaller display for multi
- // display device
- if (mIsOnSmallerInternalDisplays) {
- offsetX = bounds.centerX() - (Math.round(dwidth * scale) / 2);
- }
-
- mTmpRect.set(
- bounds.left + offsetX,
- bounds.top + Math.round(dy),
- bounds.left + Math.round(dwidth * scale) + offsetX,
- bounds.top + Math.round(dheight * scale + dy));
-
- super.onBoundsChange(mTmpRect);
- }
-
- @Override
- public ConstantState getConstantState() {
- return mState;
- }
-
- /**
- * Update bounds when the hosting display or the display size has changed.
- *
- * @param isOnSmallerInternalDisplays true if the drawable is on one of the internal
- * displays with the smaller area.
- */
- public void onDisplayUpdated(boolean isOnSmallerInternalDisplays) {
- mIsOnSmallerInternalDisplays = isOnSmallerInternalDisplays;
- onBoundsChange(getBounds());
- }
-
- static class ConstantState extends Drawable.ConstantState {
-
- private final Bitmap mBackground;
-
- ConstantState(Bitmap background) {
- mBackground = background;
- }
-
- @Override
- public Drawable newDrawable() {
- return newDrawable(null);
- }
-
- @Override
- public Drawable newDrawable(@Nullable Resources res) {
- return new WallpaperDrawable(res, this, /* isOnSmallerInternalDisplays= */ false);
- }
-
- @Override
- public int getChangingConfigurations() {
- // DrawableWrapper already handles this for us.
- return 0;
- }
- }
- }
-
- /**
- * Feature b/253507223 will adapt the logic to always use the
- * WallpaperManagerService to render the lock screen wallpaper.
- * Methods of this class should not be called at all if the project flag is enabled.
- * TODO(b/253507223) temporary assertion; remove this
- */
- private void assertLockscreenLiveWallpaperNotEnabled() {
- if (mWallpaperManager.isLockscreenLiveWallpaperEnabled()) {
- throw new IllegalStateException(DISABLED_ERROR_MESSAGE);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 54d81b8..5a8b636 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -300,7 +300,8 @@
mIconController.setIconVisibility(mSlotCast, false);
// connected display
- mIconController.setIcon(mSlotConnectedDisplay, R.drawable.stat_sys_connected_display, null);
+ mIconController.setIcon(mSlotConnectedDisplay, R.drawable.stat_sys_connected_display,
+ mResources.getString(R.string.connected_display_icon_desc));
mIconController.setIconVisibility(mSlotConnectedDisplay, false);
// hotspot
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 5b55264..744d70e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -16,6 +16,9 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER;
+import static com.android.systemui.keyguard.shared.model.KeyguardState.GONE;
+import static com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
import static java.lang.Float.isNaN;
@@ -51,7 +54,6 @@
import com.android.systemui.CoreStartable;
import com.android.systemui.DejankUtils;
import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants;
import com.android.systemui.dagger.SysUISingleton;
@@ -62,7 +64,9 @@
import com.android.systemui.keyguard.shared.model.ScrimAlpha;
import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
+import com.android.systemui.res.R;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
@@ -273,6 +277,7 @@
private CoroutineDispatcher mMainDispatcher;
private boolean mIsBouncerToGoneTransitionRunning = false;
private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
+ private AlternateBouncerToGoneTransitionViewModel mAlternateBouncerToGoneTransitionViewModel;
private final Consumer<ScrimAlpha> mScrimAlphaConsumer =
(ScrimAlpha alphas) -> {
mInFrontAlpha = alphas.getFrontAlpha();
@@ -285,7 +290,7 @@
mScrimBehind.setViewAlpha(mBehindAlpha);
};
- Consumer<TransitionStep> mPrimaryBouncerToGoneTransition;
+ Consumer<TransitionStep> mBouncerToGoneTransition;
@Inject
public ScrimController(
@@ -304,6 +309,7 @@
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel,
+ AlternateBouncerToGoneTransitionViewModel alternateBouncerToGoneTransitionViewModel,
KeyguardTransitionInteractor keyguardTransitionInteractor,
WallpaperRepository wallpaperRepository,
@Main CoroutineDispatcher mainDispatcher,
@@ -349,6 +355,7 @@
});
mColors = new GradientColors();
mPrimaryBouncerToGoneTransitionViewModel = primaryBouncerToGoneTransitionViewModel;
+ mAlternateBouncerToGoneTransitionViewModel = alternateBouncerToGoneTransitionViewModel;
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
mWallpaperRepository = wallpaperRepository;
mMainDispatcher = mainDispatcher;
@@ -405,7 +412,7 @@
// Directly control transition to UNLOCKED scrim state from PRIMARY_BOUNCER, and make sure
// to report back that keyguard has faded away. This fixes cases where the scrim state was
// rapidly switching on unlock, due to shifts in state in CentralSurfacesImpl
- mPrimaryBouncerToGoneTransition =
+ mBouncerToGoneTransition =
(TransitionStep step) -> {
TransitionState state = step.getTransitionState();
@@ -425,10 +432,17 @@
}
};
- collectFlow(behindScrim, mKeyguardTransitionInteractor.getPrimaryBouncerToGoneTransition(),
- mPrimaryBouncerToGoneTransition, mMainDispatcher);
+ // PRIMARY_BOUNCER->GONE
+ collectFlow(behindScrim, mKeyguardTransitionInteractor.transition(PRIMARY_BOUNCER, GONE),
+ mBouncerToGoneTransition, mMainDispatcher);
collectFlow(behindScrim, mPrimaryBouncerToGoneTransitionViewModel.getScrimAlpha(),
mScrimAlphaConsumer, mMainDispatcher);
+
+ // ALTERNATE_BOUNCER->GONE
+ collectFlow(behindScrim, mKeyguardTransitionInteractor.transition(ALTERNATE_BOUNCER, GONE),
+ mBouncerToGoneTransition, mMainDispatcher);
+ collectFlow(behindScrim, mAlternateBouncerToGoneTransitionViewModel.getScrimAlpha(),
+ mScrimAlphaConsumer, mMainDispatcher);
}
// TODO(b/270984686) recompute scrim height accurately, based on shade contents.
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 90fddd9..267b563 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -964,9 +964,6 @@
SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED,
SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN);
}
- if (isShowing) {
- mMediaManager.updateMediaMetaData(false);
- }
mNotificationShadeWindowController.setKeyguardOccluded(isOccluded);
// setDozing(false) will call reset once we stop dozing. Also, if we're going away, there's
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 57a8e6f..a5cd062 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -205,7 +205,6 @@
// End old BaseStatusBar.userSwitched
mCommandQueue.animateCollapsePanels();
mMediaManager.clearCurrentMediaNotification();
- updateMediaMetaData(true, false);
}
@Override
@@ -220,11 +219,6 @@
}
@Override
- public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
- mMediaManager.updateMediaMetaData(metaDataChanged);
- }
-
- @Override
public void onExpandClicked(NotificationEntry clickedEntry, View clickedView,
boolean nowExpanded) {
mHeadsUpManager.setExpanded(clickedEntry, nowExpanded);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialog.kt
index 85fd2af..d7f3b07 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialog.kt
@@ -17,42 +17,71 @@
import android.app.Dialog
import android.content.Context
+import android.content.res.Configuration
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.view.Gravity
-import android.view.WindowManager
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import android.view.WindowManager.LayoutParams.MATCH_PARENT
+import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
+import android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
+import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL
import com.android.systemui.res.R
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
/** A dialog shown as a bottom sheet. */
open class SystemUIBottomSheetDialog(
context: Context,
- theme: Int = R.style.Theme_SystemUI_Dialog,
+ private val configurationController: ConfigurationController? = null,
+ theme: Int = R.style.Theme_SystemUI_Dialog
) : Dialog(context, theme) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
-
- window?.apply {
- setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL)
- addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS)
-
- setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
- setGravity(Gravity.BOTTOM)
- val edgeToEdgeHorizontally =
- context.resources.getBoolean(R.bool.config_edgeToEdgeBottomSheetDialog)
- if (edgeToEdgeHorizontally) {
- decorView.setPadding(0, 0, 0, 0)
- setLayout(
- WindowManager.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.WRAP_CONTENT
- )
-
- val lp = attributes
- lp.fitInsetsSides = 0
- lp.horizontalMargin = 0f
- attributes = lp
- }
- }
+ setupWindow()
+ setupEdgeToEdge()
setCanceledOnTouchOutside(true)
}
+
+ private fun setupWindow() {
+ window?.apply {
+ setType(TYPE_STATUS_BAR_SUB_PANEL)
+ addPrivateFlags(SYSTEM_FLAG_SHOW_FOR_ALL_USERS or PRIVATE_FLAG_NO_MOVE_ANIMATION)
+ setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+ setGravity(Gravity.BOTTOM)
+ decorView.setPadding(0, 0, 0, 0)
+ attributes =
+ attributes.apply {
+ fitInsetsSides = 0
+ horizontalMargin = 0f
+ }
+ }
+ }
+
+ private fun setupEdgeToEdge() {
+ val edgeToEdgeHorizontally =
+ context.resources.getBoolean(R.bool.config_edgeToEdgeBottomSheetDialog)
+ val width = if (edgeToEdgeHorizontally) MATCH_PARENT else WRAP_CONTENT
+ val height = WRAP_CONTENT
+ window?.setLayout(width, height)
+ }
+
+ override fun onStart() {
+ super.onStart()
+ configurationController?.addCallback(onConfigChanged)
+ }
+
+ override fun onStop() {
+ super.onStop()
+ configurationController?.removeCallback(onConfigChanged)
+ }
+
+ private val onConfigChanged =
+ object : ConfigurationListener {
+ override fun onConfigChanged(newConfig: Configuration?) {
+ super.onConfigChanged(newConfig)
+ setupEdgeToEdge()
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 9d627af..2558645 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -45,6 +45,7 @@
import com.android.systemui.res.R;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.model.SysUiState;
@@ -54,8 +55,14 @@
import java.util.ArrayList;
import java.util.List;
+import javax.inject.Inject;
+
/**
- * Base class for dialogs that should appear over panels and keyguard.
+ * Class for dialogs that should appear over panels and keyguard.
+ *
+ * DO NOT SUBCLASS THIS. See {@link SystemUIDialog.Delegate} for an interface that enables
+ * customizing behavior via composition instead of inheritance. Clients should implement the
+ * Delegate class and then pass their implementation into the SystemUIDialog constructor.
*
* Optionally provide a {@link SystemUIDialogManager} to its constructor to send signals to
* listeners on whether this dialog is showing.
@@ -72,6 +79,7 @@
private final Context mContext;
private final FeatureFlags mFeatureFlags;
+ private final Delegate mDelegate;
@Nullable private final DismissReceiver mDismissReceiver;
private final Handler mHandler = new Handler();
private final SystemUIDialogManager mDialogManager;
@@ -101,18 +109,102 @@
Dependency.get(SystemUIDialogManager.class),
Dependency.get(SysUiState.class),
Dependency.get(BroadcastDispatcher.class),
- Dependency.get(DialogLaunchAnimator.class));
+ Dependency.get(DialogLaunchAnimator.class),
+ new Delegate() {});
}
- public SystemUIDialog(Context context, int theme, boolean dismissOnDeviceLock,
+ @Inject
+ public SystemUIDialog(
+ @Application Context context,
+ FeatureFlags featureFlags,
+ SystemUIDialogManager systemUIDialogManager,
+ SysUiState sysUiState,
+ BroadcastDispatcher broadcastDispatcher,
+ DialogLaunchAnimator dialogLaunchAnimator) {
+ this(context,
+ DEFAULT_THEME,
+ DEFAULT_DISMISS_ON_DEVICE_LOCK,
+ featureFlags,
+ systemUIDialogManager,
+ sysUiState,
+ broadcastDispatcher,
+ dialogLaunchAnimator,
+ new Delegate(){});
+ }
+
+ public static class Factory {
+ private final Context mContext;
+ private final FeatureFlags mFeatureFlags;
+ private final SystemUIDialogManager mSystemUIDialogManager;
+ private final SysUiState mSysUiState;
+ private final BroadcastDispatcher mBroadcastDispatcher;
+ private final DialogLaunchAnimator mDialogLaunchAnimator;
+
+ @Inject
+ public Factory(
+ @Application Context context,
+ FeatureFlags featureFlags,
+ SystemUIDialogManager systemUIDialogManager,
+ SysUiState sysUiState,
+ BroadcastDispatcher broadcastDispatcher,
+ DialogLaunchAnimator dialogLaunchAnimator) {
+ mContext = context;
+ mFeatureFlags = featureFlags;
+ mSystemUIDialogManager = systemUIDialogManager;
+ mSysUiState = sysUiState;
+ mBroadcastDispatcher = broadcastDispatcher;
+ mDialogLaunchAnimator = dialogLaunchAnimator;
+ }
+
+ public SystemUIDialog create(Delegate delegate) {
+ return new SystemUIDialog(
+ mContext,
+ DEFAULT_THEME,
+ DEFAULT_DISMISS_ON_DEVICE_LOCK,
+ mFeatureFlags,
+ mSystemUIDialogManager,
+ mSysUiState,
+ mBroadcastDispatcher,
+ mDialogLaunchAnimator,
+ delegate);
+ }
+ }
+
+ public SystemUIDialog(
+ Context context,
+ int theme,
+ boolean dismissOnDeviceLock,
FeatureFlags featureFlags,
SystemUIDialogManager dialogManager,
SysUiState sysUiState,
BroadcastDispatcher broadcastDispatcher,
DialogLaunchAnimator dialogLaunchAnimator) {
+ this(
+ context,
+ theme,
+ dismissOnDeviceLock,
+ featureFlags,
+ dialogManager,
+ sysUiState,
+ broadcastDispatcher,
+ dialogLaunchAnimator,
+ new Delegate(){});
+ }
+
+ public SystemUIDialog(
+ Context context,
+ int theme,
+ boolean dismissOnDeviceLock,
+ FeatureFlags featureFlags,
+ SystemUIDialogManager dialogManager,
+ SysUiState sysUiState,
+ BroadcastDispatcher broadcastDispatcher,
+ DialogLaunchAnimator dialogLaunchAnimator,
+ Delegate delegate) {
super(context, theme);
mContext = context;
mFeatureFlags = featureFlags;
+ mDelegate = delegate;
applyFlags(this);
WindowManager.LayoutParams attrs = getWindow().getAttributes();
@@ -127,7 +219,9 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
+ mDelegate.beforeCreate(this, savedInstanceState);
super.onCreate(savedInstanceState);
+ mDelegate.onCreate(this, savedInstanceState);
Configuration config = getContext().getResources().getConfiguration();
mLastConfigurationWidthDp = config.screenWidthDp;
@@ -172,6 +266,8 @@
updateWindowSize();
}
+
+ mDelegate.onConfigurationChanged(this, configuration);
}
/**
@@ -212,7 +308,9 @@
* Called when {@link #onStart} is called. Subclasses wishing to override {@link #onStart()}
* should override this method instead.
*/
- protected void start() {}
+ protected void start() {
+ mDelegate.start(this);
+ }
@Override
protected final void onStop() {
@@ -234,7 +332,15 @@
* Called when {@link #onStop} is called. Subclasses wishing to override {@link #onStop()}
* should override this method instead.
*/
- protected void stop() {}
+ protected void stop() {
+ mDelegate.stop(this);
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ super.onWindowFocusChanged(hasFocus);
+ mDelegate.onWindowFocusChanged(this, hasFocus);
+ }
public void setShowForAllUsers(boolean show) {
setShowForAllUsers(this, show);
@@ -353,7 +459,6 @@
registerDismissListener(dialog, null);
}
-
/**
* Registers a listener that dismisses the given dialog when it receives
* the screen off / close system dialogs broadcast.
@@ -480,4 +585,42 @@
}
}
+ /**
+ * A delegate class that should be implemented in place of sublcassing {@link SystemUIDialog}.
+ *
+ * Implement this interface and then pass an instance of your implementation to
+ * {@link SystemUIDialog.Factory#create(Delegate)}.
+ */
+ public interface Delegate {
+ /**
+ * Called before {@link AlertDialog#onCreate} is called.
+ */
+ default void beforeCreate(SystemUIDialog dialog, Bundle savedInstanceState) {}
+
+ /**
+ * Called after {@link AlertDialog#onCreate} is called.
+ */
+ default void onCreate(SystemUIDialog dialog, Bundle savedInstanceState) {}
+
+ /**
+ * Called after {@link AlertDialog#onStart} is called.
+ */
+ default void start(SystemUIDialog dialog) {}
+
+ /**
+ * Called after {@link AlertDialog#onStop} is called.
+ */
+ default void stop(SystemUIDialog dialog) {}
+
+ /**
+ * Called after {@link AlertDialog#onWindowFocusChanged(boolean)} is called.
+ */
+ default void onWindowFocusChanged(SystemUIDialog dialog, boolean hasFocus) {}
+
+ /**
+ * Called as part of
+ * {@link ViewRootImpl.ConfigChangedCallback#onConfigurationChanged(Configuration)}.
+ */
+ default void onConfigurationChanged(SystemUIDialog dialog, Configuration configuration) {}
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt
index 9269df3..8c66c2f 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt
@@ -19,6 +19,7 @@
import android.content.ContentResolver
import android.content.Context
import android.hardware.devicestate.DeviceStateManager
+import android.os.Trace
import android.util.Log
import com.android.internal.util.LatencyTracker
import com.android.systemui.dagger.SysUISingleton
@@ -57,6 +58,7 @@
private var folded: Boolean? = null
private var isTransitionEnabled: Boolean? = null
private val foldStateListener = FoldStateListener(context)
+ private var unfoldInProgress = false
private val isFoldable: Boolean
get() =
context.resources
@@ -95,7 +97,7 @@
// the unfold animation (e.g. it could be disabled because of battery saver).
// When animation is enabled finishing of the tracking will be done in onTransitionStarted.
if (folded == false && isTransitionEnabled == false) {
- latencyTracker.onActionEnd(LatencyTracker.ACTION_SWITCH_DISPLAY_UNFOLD)
+ onUnfoldEnded()
if (DEBUG) {
Log.d(TAG, "onScreenTurnedOn: ending ACTION_SWITCH_DISPLAY_UNFOLD")
@@ -116,7 +118,7 @@
}
if (folded == false && isTransitionEnabled == true) {
- latencyTracker.onActionEnd(LatencyTracker.ACTION_SWITCH_DISPLAY_UNFOLD)
+ onUnfoldEnded()
if (DEBUG) {
Log.d(TAG, "onTransitionStarted: ending ACTION_SWITCH_DISPLAY_UNFOLD")
@@ -124,6 +126,22 @@
}
}
+ private fun onUnfoldStarted() {
+ if (unfoldInProgress) return
+ unfoldInProgress = true
+ // As LatencyTracker might be disabled, let's also log a parallel slice to the trace to be
+ // able to debug all cases.
+ latencyTracker.onActionStart(LatencyTracker.ACTION_SWITCH_DISPLAY_UNFOLD)
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_APP, UNFOLD_IN_PROGRESS_TRACE_NAME, /* cookie= */ 0)
+ }
+
+ private fun onUnfoldEnded() {
+ if (!unfoldInProgress) return
+ unfoldInProgress = false
+ latencyTracker.onActionEnd(LatencyTracker.ACTION_SWITCH_DISPLAY_UNFOLD)
+ Trace.endAsyncSection(UNFOLD_IN_PROGRESS_TRACE_NAME, 0)
+ }
+
private fun onFoldEvent(folded: Boolean) {
val oldFolded = this.folded
@@ -139,7 +157,7 @@
// unfolding the device.
if (oldFolded != null && !folded) {
// Unfolding started
- latencyTracker.onActionStart(LatencyTracker.ACTION_SWITCH_DISPLAY_UNFOLD)
+ onUnfoldStarted()
isTransitionEnabled =
transitionProgressProvider.isPresent && contentResolver.areAnimationsEnabled()
@@ -159,4 +177,5 @@
}
private const val TAG = "UnfoldLatencyTracker"
+private const val UNFOLD_IN_PROGRESS_TRACE_NAME = "Switch displays during unfold"
private val DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE)
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt
new file mode 100644
index 0000000..ed960f3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.unfold
+
+import android.content.Context
+import android.os.Trace
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.unfold.system.DeviceStateRepository
+import com.android.systemui.unfold.updates.FoldStateRepository
+import com.android.systemui.util.TraceStateLogger
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * Logs several unfold related details in a trace. Mainly used for debugging and investigate
+ * droidfooders traces.
+ */
+@SysUISingleton
+class UnfoldTraceLogger
+@Inject
+constructor(
+ private val context: Context,
+ private val foldStateRepository: FoldStateRepository,
+ @Application private val applicationScope: CoroutineScope,
+ private val deviceStateRepository: DeviceStateRepository
+) : CoreStartable {
+ private val isFoldable: Boolean
+ get() =
+ context.resources
+ .getIntArray(com.android.internal.R.array.config_foldedDeviceStates)
+ .isNotEmpty()
+
+ override fun start() {
+ if (!isFoldable) return
+
+ applicationScope.launch {
+ val foldUpdateLogger = TraceStateLogger("FoldUpdate")
+ foldStateRepository.foldUpdate.collect { foldUpdateLogger.log(it.name) }
+ }
+
+ applicationScope.launch {
+ foldStateRepository.hingeAngle.collect {
+ Trace.traceCounter(Trace.TRACE_TAG_APP, "hingeAngle", it.toInt())
+ }
+ }
+ applicationScope.launch {
+ val foldedStateLogger = TraceStateLogger("FoldedState")
+ deviceStateRepository.isFolded.collect { isFolded ->
+ foldedStateLogger.log(
+ if (isFolded) {
+ "folded"
+ } else {
+ "unfolded"
+ }
+ )
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
index ed3eacd..71314f1 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.hardware.devicestate.DeviceStateManager
import android.os.SystemProperties
+import com.android.systemui.CoreStartable
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.LifecycleScreenStatusProvider
@@ -34,16 +35,26 @@
import com.android.systemui.unfold.util.UnfoldTransitionATracePrefix
import com.android.systemui.util.time.SystemClockImpl
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider
+import dagger.Binds
import dagger.Lazy
import dagger.Module
import dagger.Provides
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
import java.util.Optional
import java.util.concurrent.Executor
import javax.inject.Named
import javax.inject.Provider
import javax.inject.Singleton
-@Module(includes = [UnfoldSharedModule::class, SystemUnfoldSharedModule::class])
+@Module(
+ includes =
+ [
+ UnfoldSharedModule::class,
+ SystemUnfoldSharedModule::class,
+ UnfoldTransitionModule.Bindings::class
+ ]
+)
class UnfoldTransitionModule {
@Provides @UnfoldTransitionATracePrefix fun tracingTagPrefix() = "systemui"
@@ -136,13 +147,22 @@
null
}
- return resultingProvider?.get()?.orElse(null)?.let {
- unfoldProgressProvider -> UnfoldProgressProvider(unfoldProgressProvider, foldProvider)
- } ?: ShellUnfoldProgressProvider.NO_PROVIDER
+ return resultingProvider?.get()?.orElse(null)?.let { unfoldProgressProvider ->
+ UnfoldProgressProvider(unfoldProgressProvider, foldProvider)
+ }
+ ?: ShellUnfoldProgressProvider.NO_PROVIDER
}
@Provides
fun screenStatusProvider(impl: LifecycleScreenStatusProvider): ScreenStatusProvider = impl
+
+ @Module
+ interface Bindings {
+ @Binds
+ @IntoMap
+ @ClassKey(UnfoldTraceLogger::class)
+ fun bindUnfoldTraceLogger(impl: UnfoldTraceLogger): CoreStartable
+ }
}
const val UNFOLD_STATUS_BAR = "unfold_status_bar"
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
index aea3030..fdf59664 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
@@ -126,8 +126,6 @@
private int mBitmapUsages = 0;
private final Object mLock = new Object();
- private boolean mIsLockscreenLiveWallpaperEnabled;
-
CanvasEngine() {
super();
setFixedSizeAllowed(true);
@@ -171,12 +169,8 @@
Log.d(TAG, "onCreate");
}
mWallpaperManager = getDisplayContext().getSystemService(WallpaperManager.class);
- mIsLockscreenLiveWallpaperEnabled = mWallpaperManager
- .isLockscreenLiveWallpaperEnabled();
mSurfaceHolder = surfaceHolder;
- Rect dimensions = mIsLockscreenLiveWallpaperEnabled
- ? mWallpaperManager.peekBitmapDimensions(getSourceFlag(), true)
- : mWallpaperManager.peekBitmapDimensions();
+ Rect dimensions = mWallpaperManager.peekBitmapDimensions(getSourceFlag(), true);
int width = Math.max(MIN_SURFACE_WIDTH, dimensions.width());
int height = Math.max(MIN_SURFACE_HEIGHT, dimensions.height());
mSurfaceHolder.setFixedSize(width, height);
@@ -327,10 +321,8 @@
boolean loadSuccess = false;
Bitmap bitmap;
try {
- bitmap = mIsLockscreenLiveWallpaperEnabled
- ? mWallpaperManager.getBitmapAsUser(
- mUserTracker.getUserId(), false, getSourceFlag(), true)
- : mWallpaperManager.getBitmapAsUser(mUserTracker.getUserId(), false);
+ bitmap = mWallpaperManager.getBitmapAsUser(
+ mUserTracker.getUserId(), false, getSourceFlag(), true);
if (bitmap != null
&& bitmap.getByteCount() > RecordingCanvas.MAX_BITMAP_SIZE) {
throw new RuntimeException("Wallpaper is too large to draw!");
@@ -341,18 +333,11 @@
// be loaded, we will go into a cycle. Don't do a build where the
// default wallpaper can't be loaded.
Log.w(TAG, "Unable to load wallpaper!", exception);
- if (mIsLockscreenLiveWallpaperEnabled) {
- mWallpaperManager.clearWallpaper(getWallpaperFlags(), mUserTracker.getUserId());
- } else {
- mWallpaperManager.clearWallpaper(
- WallpaperManager.FLAG_SYSTEM, mUserTracker.getUserId());
- }
+ mWallpaperManager.clearWallpaper(getWallpaperFlags(), mUserTracker.getUserId());
try {
- bitmap = mIsLockscreenLiveWallpaperEnabled
- ? mWallpaperManager.getBitmapAsUser(
- mUserTracker.getUserId(), false, getSourceFlag(), true)
- : mWallpaperManager.getBitmapAsUser(mUserTracker.getUserId(), false);
+ bitmap = mWallpaperManager.getBitmapAsUser(
+ mUserTracker.getUserId(), false, getSourceFlag(), true);
} catch (RuntimeException | OutOfMemoryError e) {
Log.w(TAG, "Unable to load default wallpaper!", e);
bitmap = null;
@@ -373,9 +358,7 @@
mBitmap.recycle();
}
mBitmap = bitmap;
- mWideColorGamut = mIsLockscreenLiveWallpaperEnabled
- ? mWallpaperManager.wallpaperSupportsWcg(getSourceFlag())
- : mWallpaperManager.wallpaperSupportsWcg(WallpaperManager.FLAG_SYSTEM);
+ mWideColorGamut = mWallpaperManager.wallpaperSupportsWcg(getSourceFlag());
// +2 usages for the color extraction and the delayed unload.
mBitmapUsages += 2;
diff --git a/packages/SystemUI/tests/src/com/android/SysUITestModule.kt b/packages/SystemUI/tests/src/com/android/SysUITestModule.kt
index ea74510..0b3c3b4 100644
--- a/packages/SystemUI/tests/src/com/android/SysUITestModule.kt
+++ b/packages/SystemUI/tests/src/com/android/SysUITestModule.kt
@@ -16,10 +16,12 @@
package com.android
import android.content.Context
+import android.content.res.Resources
import com.android.systemui.FakeSystemUiModule
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
import dagger.Module
import dagger.Provides
@@ -36,6 +38,9 @@
@Provides @Application fun provideAppContext(test: SysuiTestCase): Context = test.context
+ @Provides @Main
+ fun provideResources(@Application context: Context): Resources = context.resources
+
@Provides
fun provideBroadcastDispatcher(test: SysuiTestCase): BroadcastDispatcher =
test.fakeBroadcastDispatcher
diff --git a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt b/packages/SystemUI/tests/src/com/android/TestMocksModule.kt
index 167e341..ff1d5b2 100644
--- a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt
+++ b/packages/SystemUI/tests/src/com/android/TestMocksModule.kt
@@ -22,7 +22,9 @@
import com.android.internal.logging.MetricsLogger
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardViewController
import com.android.systemui.GuestResumeSessionReceiver
+import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.demomode.DemoModeController
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.ScreenLifecycle
@@ -31,6 +33,7 @@
import com.android.systemui.log.dagger.BroadcastDispatcherLog
import com.android.systemui.log.dagger.SceneFrameworkLog
import com.android.systemui.media.controls.ui.MediaHierarchyManager
+import com.android.systemui.model.SysUiState
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.DarkIconDispatcher
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -86,6 +89,9 @@
@get:Provides val statusBarStateController: SysuiStatusBarStateController = mock(),
@get:Provides val statusBarWindowController: StatusBarWindowController = mock(),
@get:Provides val wakefulnessLifecycle: WakefulnessLifecycle = mock(),
+ @get:Provides val keyguardViewController: KeyguardViewController = mock(),
+ @get:Provides val dialogLaunchAnimator: DialogLaunchAnimator = mock(),
+ @get:Provides val sysuiState: SysUiState = mock(),
// log buffers
@get:[Provides BroadcastDispatcherLog]
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
index df7d1b7..aed795a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
@@ -222,14 +222,14 @@
@Test
public void showingImeInsetsChange_overlapOnIme_menuShownAboveIme() {
- final float menuTop = IME_TOP + 100;
- mMenuAnimationController.moveAndPersistPosition(new PointF(0, menuTop));
+ mMenuAnimationController.moveAndPersistPosition(new PointF(0, IME_TOP + 100));
+ final PointF beforePosition = mMenuView.getMenuPosition();
dispatchShowingImeInsets();
final float menuBottom = mMenuView.getTranslationY() + mMenuView.getMenuHeight();
- assertThat(mMenuView.getTranslationX()).isEqualTo(0);
- assertThat(menuBottom).isLessThan(IME_TOP);
+ assertThat(mMenuView.getTranslationX()).isEqualTo(beforePosition.x);
+ assertThat(menuBottom).isLessThan(beforePosition.y);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
index 80b281e..882bcab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
@@ -17,7 +17,6 @@
package com.android.systemui.colorextraction;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -105,21 +104,6 @@
}
@Test
- public void getColors_fallbackWhenMediaIsVisible() {
- simulateEvent(mColorExtractor);
- mColorExtractor.setHasMediaArtwork(true);
-
- ColorExtractor.GradientColors fallbackColors = mColorExtractor.getNeutralColors();
-
- for (int type : sTypes) {
- assertEquals("Not using fallback!",
- mColorExtractor.getColors(WallpaperManager.FLAG_LOCK, type), fallbackColors);
- assertNotEquals("Media visibility should not affect system wallpaper.",
- mColorExtractor.getColors(WallpaperManager.FLAG_SYSTEM, type), fallbackColors);
- }
- }
-
- @Test
public void onUiModeChanged_reloadsColors() {
Tonal tonal = mock(Tonal.class);
ConfigurationController configurationController = mock(ConfigurationController.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
index 7a13a0a..489665c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
@@ -106,7 +106,6 @@
whenever(keyguardViewController.viewRootImpl).thenReturn(mock(ViewRootImpl::class.java))
whenever(powerManager.isInteractive).thenReturn(true)
- whenever(wallpaperManager.isLockscreenLiveWallpaperEnabled).thenReturn(false)
// All of these fields are final, so we can't mock them, but are needed so that the surface
// appear amount setter doesn't short circuit.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
new file mode 100644
index 0000000..1ff46db
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
+import com.android.systemui.keyguard.shared.model.ScrimAlpha
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.util.mockito.whenever
+import com.google.common.collect.Range
+import com.google.common.truth.Truth.assertThat
+import dagger.Lazy
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class BouncerToGoneFlowsTest : SysuiTestCase() {
+ private lateinit var underTest: BouncerToGoneFlows
+ private lateinit var repository: FakeKeyguardTransitionRepository
+ private lateinit var featureFlags: FakeFeatureFlags
+ @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
+ @Mock private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
+ @Mock
+ private lateinit var keyguardDismissActionInteractor: Lazy<KeyguardDismissActionInteractor>
+ @Mock private lateinit var shadeInteractor: ShadeInteractor
+
+ private val shadeExpansionStateFlow = MutableStateFlow(0.1f)
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ whenever(shadeInteractor.shadeExpansion).thenReturn(shadeExpansionStateFlow)
+
+ repository = FakeKeyguardTransitionRepository()
+ val featureFlags =
+ FakeFeatureFlags().apply { set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false) }
+ val interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor
+ underTest =
+ BouncerToGoneFlows(
+ interactor,
+ statusBarStateController,
+ primaryBouncerInteractor,
+ keyguardDismissActionInteractor,
+ featureFlags,
+ shadeInteractor,
+ )
+
+ whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(false)
+ whenever(statusBarStateController.leaveOpenOnKeyguardHide()).thenReturn(false)
+ }
+
+ @Test
+ fun scrimAlpha_runDimissFromKeyguard_shadeExpanded() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER))
+ shadeExpansionStateFlow.value = 1f
+ whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(true)
+
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(0.6f))
+ repository.sendTransitionStep(step(1f))
+
+ assertThat(values.size).isEqualTo(4)
+ values.forEach { assertThat(it.frontAlpha).isEqualTo(0f) }
+ values.forEach { assertThat(it.behindAlpha).isIn(Range.closed(0f, 1f)) }
+ values.forEach { assertThat(it.notificationsAlpha).isIn(Range.closed(0f, 1f)) }
+ }
+
+ @Test
+ fun scrimAlpha_runDimissFromKeyguard_shadeNotExpanded() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER))
+ shadeExpansionStateFlow.value = 0f
+
+ whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(true)
+
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(0.6f))
+ repository.sendTransitionStep(step(1f))
+
+ assertThat(values.size).isEqualTo(4)
+ values.forEach { assertThat(it).isEqualTo(ScrimAlpha()) }
+ }
+
+ @Test
+ fun scrimBehindAlpha_leaveShadeOpen() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER))
+
+ whenever(statusBarStateController.leaveOpenOnKeyguardHide()).thenReturn(true)
+
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(0.6f))
+ repository.sendTransitionStep(step(1f))
+
+ assertThat(values.size).isEqualTo(4)
+ values.forEach {
+ assertThat(it).isEqualTo(ScrimAlpha(notificationsAlpha = 1f, behindAlpha = 1f))
+ }
+ }
+
+ @Test
+ fun scrimBehindAlpha_doNotLeaveShadeOpen() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER))
+
+ whenever(statusBarStateController.leaveOpenOnKeyguardHide()).thenReturn(false)
+
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(0.6f))
+ repository.sendTransitionStep(step(1f))
+
+ assertThat(values.size).isEqualTo(4)
+ values.forEach { assertThat(it.notificationsAlpha).isEqualTo(0f) }
+ values.forEach { assertThat(it.frontAlpha).isEqualTo(0f) }
+ values.forEach { assertThat(it.behindAlpha).isIn(Range.closed(0f, 1f)) }
+ assertThat(values[3].behindAlpha).isEqualTo(0f)
+ }
+
+ private fun step(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = KeyguardState.GONE,
+ value = value,
+ transitionState = state,
+ ownerName = "PrimaryBouncerToGoneTransitionViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
index d7802aa..6cab023 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
@@ -27,7 +27,6 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.ScrimAlpha
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -35,6 +34,7 @@
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
import dagger.Lazy
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
@@ -52,12 +52,16 @@
private lateinit var featureFlags: FakeFeatureFlags
@Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
@Mock private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
+ @Mock private lateinit var bouncerToGoneFlows: BouncerToGoneFlows
@Mock
private lateinit var keyguardDismissActionInteractor: Lazy<KeyguardDismissActionInteractor>
+ private val shadeExpansionStateFlow = MutableStateFlow(0.1f)
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+
repository = FakeKeyguardTransitionRepository()
val featureFlags =
FakeFeatureFlags().apply { set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false) }
@@ -74,6 +78,7 @@
primaryBouncerInteractor,
keyguardDismissActionInteractor,
featureFlags,
+ bouncerToGoneFlows,
)
whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(false)
@@ -148,59 +153,6 @@
values.forEach { assertThat(it).isEqualTo(1f) }
}
- @Test
- fun scrimAlpha_runDimissFromKeyguard() =
- runTest(UnconfinedTestDispatcher()) {
- val values by collectValues(underTest.scrimAlpha)
-
- whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(true)
-
- repository.sendTransitionStep(step(0f, TransitionState.STARTED))
- repository.sendTransitionStep(step(0.3f))
- repository.sendTransitionStep(step(0.6f))
- repository.sendTransitionStep(step(1f))
-
- assertThat(values.size).isEqualTo(4)
- values.forEach { assertThat(it).isEqualTo(ScrimAlpha()) }
- }
-
- @Test
- fun scrimBehindAlpha_leaveShadeOpen() =
- runTest(UnconfinedTestDispatcher()) {
- val values by collectValues(underTest.scrimAlpha)
-
- whenever(statusBarStateController.leaveOpenOnKeyguardHide()).thenReturn(true)
-
- repository.sendTransitionStep(step(0f, TransitionState.STARTED))
- repository.sendTransitionStep(step(0.3f))
- repository.sendTransitionStep(step(0.6f))
- repository.sendTransitionStep(step(1f))
-
- assertThat(values.size).isEqualTo(4)
- values.forEach {
- assertThat(it).isEqualTo(ScrimAlpha(notificationsAlpha = 1f, behindAlpha = 1f))
- }
- }
-
- @Test
- fun scrimBehindAlpha_doNotLeaveShadeOpen() =
- runTest(UnconfinedTestDispatcher()) {
- val values by collectValues(underTest.scrimAlpha)
-
- whenever(statusBarStateController.leaveOpenOnKeyguardHide()).thenReturn(false)
-
- repository.sendTransitionStep(step(0f, TransitionState.STARTED))
- repository.sendTransitionStep(step(0.3f))
- repository.sendTransitionStep(step(0.6f))
- repository.sendTransitionStep(step(1f))
-
- assertThat(values.size).isEqualTo(4)
- values.forEach { assertThat(it.notificationsAlpha).isEqualTo(0f) }
- values.forEach { assertThat(it.frontAlpha).isEqualTo(0f) }
- values.forEach { assertThat(it.behindAlpha).isIn(Range.closed(0f, 1f)) }
- assertThat(values[3].behindAlpha).isEqualTo(0f)
- }
-
private fun step(
value: Float,
state: TransitionState = TransitionState.RUNNING
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLoggerTest.kt
new file mode 100644
index 0000000..429aad1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLoggerTest.kt
@@ -0,0 +1,66 @@
+package com.android.systemui.mediaprojection
+
+import android.media.projection.IMediaProjectionManager
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_APP as METRICS_CREATION_SOURCE_APP
+import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_CAST as METRICS_CREATION_SOURCE_CAST
+import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_SYSTEM_UI_SCREEN_RECORDER as METRICS_CREATION_SOURCE_SYSTEM_UI_SCREEN_RECORDER
+import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN as METRICS_CREATION_SOURCE_UNKNOWN
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.verify
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class MediaProjectionMetricsLoggerTest : SysuiTestCase() {
+
+ private val service = mock<IMediaProjectionManager>()
+ private val logger = MediaProjectionMetricsLogger(service)
+
+ @Test
+ fun notifyProjectionInitiated_sourceApp_forwardsToServiceWithMetricsValue() {
+ val hostUid = 123
+ val sessionCreationSource = SessionCreationSource.APP
+
+ logger.notifyProjectionInitiated(hostUid, sessionCreationSource)
+
+ verify(service).notifyPermissionRequestInitiated(hostUid, METRICS_CREATION_SOURCE_APP)
+ }
+
+ @Test
+ fun notifyProjectionInitiated_sourceCast_forwardsToServiceWithMetricsValue() {
+ val hostUid = 123
+ val sessionCreationSource = SessionCreationSource.CAST
+
+ logger.notifyProjectionInitiated(hostUid, sessionCreationSource)
+
+ verify(service).notifyPermissionRequestInitiated(hostUid, METRICS_CREATION_SOURCE_CAST)
+ }
+
+ @Test
+ fun notifyProjectionInitiated_sourceSysUI_forwardsToServiceWithMetricsValue() {
+ val hostUid = 123
+ val sessionCreationSource = SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER
+
+ logger.notifyProjectionInitiated(hostUid, sessionCreationSource)
+
+ verify(service)
+ .notifyPermissionRequestInitiated(
+ hostUid,
+ METRICS_CREATION_SOURCE_SYSTEM_UI_SCREEN_RECORDER
+ )
+ }
+
+ @Test
+ fun notifyProjectionInitiated_sourceUnknown_forwardsToServiceWithMetricsValue() {
+ val hostUid = 123
+ val sessionCreationSource = SessionCreationSource.UNKNOWN
+
+ logger.notifyProjectionInitiated(hostUid, sessionCreationSource)
+
+ verify(service).notifyPermissionRequestInitiated(hostUid, METRICS_CREATION_SOURCE_UNKNOWN)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
index 90d2e78..c439cfe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
@@ -23,11 +23,13 @@
import static org.mockito.ArgumentMatchers.any;
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 android.app.Dialog;
import android.app.PendingIntent;
+import android.content.Context;
import android.content.Intent;
import android.os.Looper;
import android.testing.AndroidTestingRunner;
@@ -64,6 +66,8 @@
*/
public class RecordingControllerTest extends SysuiTestCase {
+ private static final int TEST_USER_ID = 12345;
+
private FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
@Mock
@@ -91,6 +95,11 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ Context spiedContext = spy(mContext);
+ when(spiedContext.getUserId()).thenReturn(TEST_USER_ID);
+
+ when(mUserContextProvider.getUserContext()).thenReturn(spiedContext);
+
mFeatureFlags = new FakeFeatureFlags();
mController = new RecordingController(
mMainExecutor,
@@ -288,7 +297,6 @@
if (Looper.myLooper() == null) {
Looper.prepare();
}
-
mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING, true);
mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, true);
when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(false);
@@ -297,6 +305,8 @@
mDialogLaunchAnimator, mActivityStarter, /* onStartRecordingClicked= */ null);
verify(mMediaProjectionMetricsLogger)
- .notifyProjectionInitiated(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER);
+ .notifyProjectionInitiated(
+ TEST_USER_ID,
+ SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index a5f5fc7..43adc69 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -22,14 +22,18 @@
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
+import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE;
import static android.os.UserHandle.USER_ALL;
import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS;
import static android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS;
+import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -99,6 +103,7 @@
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
+ private static final int TEST_PROFILE_USERHANDLE = 12;
@Mock
private NotificationPresenter mPresenter;
@Mock
@@ -701,6 +706,60 @@
assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(newUserId));
}
+ @Test
+ public void testProfileAvailabilityIntent() {
+ mSetFlagsRule.enableFlags(FLAG_ALLOW_PRIVATE_PROFILE);
+ mLockscreenUserManager.mCurrentProfiles.clear();
+ assertEquals(0, mLockscreenUserManager.mCurrentProfiles.size());
+ mLockscreenUserManager.mCurrentProfiles.append(0, mock(UserInfo.class));
+ simulateProfileAvailabilityActions(Intent.ACTION_PROFILE_AVAILABLE);
+ assertEquals(2, mLockscreenUserManager.mCurrentProfiles.size());
+ }
+
+ @Test
+ public void testProfileUnAvailabilityIntent() {
+ mSetFlagsRule.enableFlags(FLAG_ALLOW_PRIVATE_PROFILE);
+ mLockscreenUserManager.mCurrentProfiles.clear();
+ assertEquals(0, mLockscreenUserManager.mCurrentProfiles.size());
+ mLockscreenUserManager.mCurrentProfiles.append(0, mock(UserInfo.class));
+ simulateProfileAvailabilityActions(Intent.ACTION_PROFILE_UNAVAILABLE);
+ assertEquals(2, mLockscreenUserManager.mCurrentProfiles.size());
+ }
+
+ @Test
+ public void testManagedProfileAvailabilityIntent() {
+ mSetFlagsRule.disableFlags(FLAG_ALLOW_PRIVATE_PROFILE);
+ mLockscreenUserManager.mCurrentProfiles.clear();
+ mLockscreenUserManager.mCurrentManagedProfiles.clear();
+ assertEquals(0, mLockscreenUserManager.mCurrentProfiles.size());
+ assertEquals(0, mLockscreenUserManager.mCurrentManagedProfiles.size());
+ mLockscreenUserManager.mCurrentProfiles.append(0, mock(UserInfo.class));
+ simulateProfileAvailabilityActions(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
+ assertEquals(2, mLockscreenUserManager.mCurrentProfiles.size());
+ assertEquals(1, mLockscreenUserManager.mCurrentManagedProfiles.size());
+ }
+
+ @Test
+ public void testManagedProfileUnAvailabilityIntent() {
+ mSetFlagsRule.disableFlags(FLAG_ALLOW_PRIVATE_PROFILE);
+ mLockscreenUserManager.mCurrentProfiles.clear();
+ mLockscreenUserManager.mCurrentManagedProfiles.clear();
+ assertEquals(0, mLockscreenUserManager.mCurrentProfiles.size());
+ assertEquals(0, mLockscreenUserManager.mCurrentManagedProfiles.size());
+ mLockscreenUserManager.mCurrentProfiles.append(0, mock(UserInfo.class));
+ simulateProfileAvailabilityActions(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
+ assertEquals(2, mLockscreenUserManager.mCurrentProfiles.size());
+ assertEquals(1, mLockscreenUserManager.mCurrentManagedProfiles.size());
+ }
+
+ private void simulateProfileAvailabilityActions(String intentAction) {
+ BroadcastReceiver broadcastReceiver =
+ mLockscreenUserManager.getBaseBroadcastReceiverForTest();
+ final Intent intent = new Intent(intentAction);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, TEST_PROFILE_USERHANDLE);
+ broadcastReceiver.onReceive(mContext, intent);
+ }
+
private class TestNotificationLockscreenUserManager
extends NotificationLockscreenUserManagerImpl {
public TestNotificationLockscreenUserManager(Context context) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt
deleted file mode 100644
index cfcf425..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar
-
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.mockito.whenever
-import org.junit.After
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.anyBoolean
-import org.mockito.Mockito.doCallRealMethod
-import org.mockito.Mockito.doReturn
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-/**
- * Temporary test for the lock screen live wallpaper project.
- *
- * TODO(b/273443374): remove this test
- */
-@RunWith(AndroidTestingRunner::class)
-@SmallTest
-class NotificationMediaManagerTest : SysuiTestCase() {
-
- @Mock private lateinit var notificationMediaManager: NotificationMediaManager
-
- @Mock private lateinit var mockBackDropView: BackDropView
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
- doCallRealMethod().whenever(notificationMediaManager).updateMediaMetaData(anyBoolean())
- doReturn(mockBackDropView).whenever(notificationMediaManager).backDropView
- }
-
- @After fun tearDown() {}
-
- /** Check that updateMediaMetaData is a no-op with mIsLockscreenLiveWallpaperEnabled = true */
- @Test
- fun testUpdateMediaMetaDataDisabled() {
- notificationMediaManager.mIsLockscreenLiveWallpaperEnabled = true
- for (metaDataChanged in listOf(true, false)) {
- for (allowEnterAnimation in listOf(true, false)) {
- notificationMediaManager.updateMediaMetaData(metaDataChanged)
- verify(notificationMediaManager, never()).mediaMetadata
- }
- }
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/FakeStatusEvent.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/FakeStatusEvent.kt
index 8397702..df8afde 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/FakeStatusEvent.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/FakeStatusEvent.kt
@@ -26,6 +26,7 @@
override var forceVisible: Boolean = false,
override val showAnimation: Boolean = true,
override var contentDescription: String? = "",
+ override val shouldAnnounceAccessibilityEvent: Boolean = false
) : StatusEvent
class FakePrivacyStatusEvent(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
index 4fcccf8..fee8b82 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
@@ -33,6 +33,8 @@
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import junit.framework.Assert.assertEquals
@@ -370,6 +372,63 @@
}
@Test
+ fun testAccessibilityAnnouncement_announced() = runTest {
+ // Instantiate class under test with TestScope from runTest
+ initializeSystemStatusAnimationScheduler(testScope = this)
+ val accessibilityDesc = "Some desc"
+ val mockView = mock<View>()
+ val mockAnimatableView =
+ mock<BackgroundAnimatableView> { whenever(view).thenReturn(mockView) }
+
+ scheduleFakeEventWithView(
+ accessibilityDesc,
+ mockAnimatableView,
+ shouldAnnounceAccessibilityEvent = true
+ )
+ fastForwardAnimationToState(ANIMATING_OUT)
+
+ verify(mockView).announceForAccessibility(eq(accessibilityDesc))
+ }
+
+ @Test
+ fun testAccessibilityAnnouncement_nullDesc_noAnnouncement() = runTest {
+ // Instantiate class under test with TestScope from runTest
+ initializeSystemStatusAnimationScheduler(testScope = this)
+ val accessibilityDesc = null
+ val mockView = mock<View>()
+ val mockAnimatableView =
+ mock<BackgroundAnimatableView> { whenever(view).thenReturn(mockView) }
+
+ scheduleFakeEventWithView(
+ accessibilityDesc,
+ mockAnimatableView,
+ shouldAnnounceAccessibilityEvent = true
+ )
+ fastForwardAnimationToState(ANIMATING_OUT)
+
+ verify(mockView, never()).announceForAccessibility(any())
+ }
+
+ @Test
+ fun testAccessibilityAnnouncement_notNeeded_noAnnouncement() = runTest {
+ // Instantiate class under test with TestScope from runTest
+ initializeSystemStatusAnimationScheduler(testScope = this)
+ val accessibilityDesc = "something"
+ val mockView = mock<View>()
+ val mockAnimatableView =
+ mock<BackgroundAnimatableView> { whenever(view).thenReturn(mockView) }
+
+ scheduleFakeEventWithView(
+ accessibilityDesc,
+ mockAnimatableView,
+ shouldAnnounceAccessibilityEvent = false
+ )
+ fastForwardAnimationToState(ANIMATING_OUT)
+
+ verify(mockView, never()).announceForAccessibility(any())
+ }
+
+ @Test
fun testPrivacyDot_isRemovedDuringChipAnimation() = runTest {
// Instantiate class under test with TestScope from runTest
initializeSystemStatusAnimationScheduler(testScope = this)
@@ -572,6 +631,20 @@
return privacyChip
}
+ private fun scheduleFakeEventWithView(
+ desc: String?,
+ view: BackgroundAnimatableView,
+ shouldAnnounceAccessibilityEvent: Boolean
+ ) {
+ val fakeEvent =
+ FakeStatusEvent(
+ viewCreator = { view },
+ contentDescription = desc,
+ shouldAnnounceAccessibilityEvent = shouldAnnounceAccessibilityEvent
+ )
+ systemStatusAnimationScheduler.onStatusEvent(fakeEvent)
+ }
+
private fun createAndScheduleFakeBatteryEvent(): BatteryStatusChip {
val batteryChip = BatteryStatusChip(mContext)
val fakeBatteryEvent =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index c8cbe42..a59cd87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -277,8 +277,6 @@
mNotificationShadeWindowViewControllerLazy;
@Mock private NotificationShelfController mNotificationShelfController;
@Mock private DozeParameters mDozeParameters;
- @Mock private Lazy<LockscreenWallpaper> mLockscreenWallpaperLazy;
- @Mock private LockscreenWallpaper mLockscreenWallpaper;
@Mock private DozeServiceHost mDozeServiceHost;
@Mock private BackActionInteractor mBackActionInteractor;
@Mock private ViewMediatorCallback mKeyguardVieMediatorCallback;
@@ -404,7 +402,6 @@
when(mGradientColors.supportsDarkText()).thenReturn(true);
when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
- when(mLockscreenWallpaperLazy.get()).thenReturn(mLockscreenWallpaper);
when(mBiometricUnlockControllerLazy.get()).thenReturn(mBiometricUnlockController);
when(mCameraLauncherLazy.get()).thenReturn(mCameraLauncher);
when(mNotificationShadeWindowViewControllerLazy.get())
@@ -508,7 +505,6 @@
new NotificationExpansionRepository(),
mDozeParameters,
mScrimController,
- mLockscreenWallpaperLazy,
mBiometricUnlockControllerLazy,
mAuthRippleController,
mDozeServiceHost,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenWallpaperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenWallpaperTest.kt
deleted file mode 100644
index 47671fb..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenWallpaperTest.kt
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.phone
-
-import android.app.WallpaperManager
-import android.content.pm.UserInfo
-import android.os.Looper
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.user.data.model.SelectionStatus
-import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.util.kotlin.JavaAdapter
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
-import com.android.systemui.utils.os.FakeHandler
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Test
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mockito.verify
-
-@SmallTest
-@OptIn(ExperimentalCoroutinesApi::class)
-class LockscreenWallpaperTest : SysuiTestCase() {
-
- private lateinit var underTest: LockscreenWallpaper
-
- private val testScope = TestScope(StandardTestDispatcher())
- private val userRepository = FakeUserRepository()
-
- private val wallpaperManager: WallpaperManager = mock()
-
- @Before
- fun setUp() {
- whenever(wallpaperManager.isLockscreenLiveWallpaperEnabled).thenReturn(false)
- whenever(wallpaperManager.isWallpaperSupported).thenReturn(true)
- underTest =
- LockscreenWallpaper(
- /* wallpaperManager= */ wallpaperManager,
- /* iWallpaperManager= */ mock(),
- /* keyguardUpdateMonitor= */ mock(),
- /* dumpManager= */ mock(),
- /* mediaManager= */ mock(),
- /* mainHandler= */ FakeHandler(Looper.getMainLooper()),
- /* javaAdapter= */ JavaAdapter(testScope.backgroundScope),
- /* userRepository= */ userRepository,
- /* userTracker= */ mock(),
- )
- underTest.start()
- }
-
- @Test
- fun getBitmap_matchesUserIdFromUserRepo() =
- testScope.runTest {
- val info = UserInfo(/* id= */ 5, /* name= */ "id5", /* flags= */ 0)
- userRepository.setUserInfos(listOf(info))
- userRepository.setSelectedUserInfo(info)
-
- underTest.bitmap
-
- verify(wallpaperManager).getWallpaperFile(any(), eq(5))
- }
-
- @Test
- fun getBitmap_usesOldUserIfNewUserInProgress() =
- testScope.runTest {
- val info5 = UserInfo(/* id= */ 5, /* name= */ "id5", /* flags= */ 0)
- val info6 = UserInfo(/* id= */ 6, /* name= */ "id6", /* flags= */ 0)
- userRepository.setUserInfos(listOf(info5, info6))
- userRepository.setSelectedUserInfo(info5)
-
- // WHEN the selection of user 6 is only in progress
- userRepository.setSelectedUserInfo(
- info6,
- selectionStatus = SelectionStatus.SELECTION_IN_PROGRESS
- )
-
- underTest.bitmap
-
- // THEN we still use user 5 for wallpaper selection
- verify(wallpaperManager).getWallpaperFile(any(), eq(5))
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 6b3bd22..15c09b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -70,6 +70,7 @@
import com.android.systemui.keyguard.shared.model.KeyguardState;
import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
@@ -141,6 +142,8 @@
@Mock private ScreenOffAnimationController mScreenOffAnimationController;
@Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
@Mock private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
+ @Mock private AlternateBouncerToGoneTransitionViewModel
+ mAlternateBouncerToGoneTransitionViewModel;
@Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
private final FakeWallpaperRepository mWallpaperRepository = new FakeWallpaperRepository();
@Mock private CoroutineDispatcher mMainDispatcher;
@@ -264,10 +267,12 @@
when(mDelayedWakeLockBuilder.build()).thenReturn(mWakeLock);
when(mDockManager.isDocked()).thenReturn(false);
- when(mKeyguardTransitionInteractor.getPrimaryBouncerToGoneTransition())
+ when(mKeyguardTransitionInteractor.transition(any(), any()))
.thenReturn(emptyFlow());
when(mPrimaryBouncerToGoneTransitionViewModel.getScrimAlpha())
.thenReturn(emptyFlow());
+ when(mAlternateBouncerToGoneTransitionViewModel.getScrimAlpha())
+ .thenReturn(emptyFlow());
mScrimController = new ScrimController(
mLightBarController,
@@ -285,6 +290,7 @@
mKeyguardUnlockAnimationController,
mStatusBarKeyguardViewManager,
mPrimaryBouncerToGoneTransitionViewModel,
+ mAlternateBouncerToGoneTransitionViewModel,
mKeyguardTransitionInteractor,
mWallpaperRepository,
mMainDispatcher,
@@ -992,6 +998,7 @@
mKeyguardUnlockAnimationController,
mStatusBarKeyguardViewManager,
mPrimaryBouncerToGoneTransitionViewModel,
+ mAlternateBouncerToGoneTransitionViewModel,
mKeyguardTransitionInteractor,
mWallpaperRepository,
mMainDispatcher,
@@ -1775,7 +1782,7 @@
@Test
public void ignoreTransitionRequestWhileKeyguardTransitionRunning() {
mScrimController.transitionTo(ScrimState.UNLOCKED);
- mScrimController.mPrimaryBouncerToGoneTransition.accept(
+ mScrimController.mBouncerToGoneTransition.accept(
new TransitionStep(KeyguardState.PRIMARY_BOUNCER, KeyguardState.GONE, 0f,
TransitionState.RUNNING, "ScrimControllerTest"));
@@ -1787,7 +1794,7 @@
@Test
public void primaryBouncerToGoneOnFinishCallsKeyguardFadedAway() {
when(mKeyguardStateController.isKeyguardFadingAway()).thenReturn(true);
- mScrimController.mPrimaryBouncerToGoneTransition.accept(
+ mScrimController.mBouncerToGoneTransition.accept(
new TransitionStep(KeyguardState.PRIMARY_BOUNCER, KeyguardState.GONE, 0f,
TransitionState.FINISHED, "ScrimControllerTest"));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialogTest.kt
new file mode 100644
index 0000000..21d22bc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialogTest.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.android.systemui.statusbar.phone
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import kotlin.test.Test
+import org.junit.Before
+import org.junit.runner.RunWith
+import org.mockito.Mockito.verify
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper(setAsMainLooper = true)
+class SystemUIBottomSheetDialogTest : SysuiTestCase() {
+
+ private val configurationController = mock<ConfigurationController>()
+
+ private lateinit var dialog: SystemUIBottomSheetDialog
+
+ @Before
+ fun setup() {
+ dialog = SystemUIBottomSheetDialog(mContext, configurationController)
+ }
+
+ @Test
+ fun onStart_registersConfigCallback() {
+ dialog.show()
+
+ verify(configurationController).addCallback(any())
+ }
+
+ @Test
+ fun onStop_unregisterConfigCallback() {
+ dialog.show()
+ dialog.dismiss()
+
+ verify(configurationController).removeCallback(any())
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceStateRepositoryTest.kt
new file mode 100644
index 0000000..4eb1591
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceStateRepositoryTest.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.unfold.updates
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.unfold.system.DeviceStateRepositoryImpl
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.verify
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class DeviceStateRepositoryTest : SysuiTestCase() {
+
+ private val foldProvider = mock<FoldProvider>()
+ private val testScope = TestScope(UnconfinedTestDispatcher())
+
+ private val foldStateRepository = DeviceStateRepositoryImpl(foldProvider) { r -> r.run() }
+
+ @Test
+ fun onHingeAngleUpdate_received() =
+ testScope.runTest {
+ val flowValue = collectLastValue(foldStateRepository.isFolded)
+ val foldCallback = argumentCaptor<FoldProvider.FoldCallback>()
+
+ verify(foldProvider).registerCallback(capture(foldCallback), any())
+
+ foldCallback.value.onFoldUpdated(true)
+ assertThat(flowValue()).isEqualTo(true)
+
+ foldCallback.value.onFoldUpdated(false)
+ assertThat(flowValue()).isEqualTo(false)
+ }
+
+ @Test
+ fun onHingeAngleUpdate_unregisters() {
+ testScope.runTest {
+ val flowValue = collectLastValue(foldStateRepository.isFolded)
+
+ verify(foldProvider).registerCallback(any(), any())
+ }
+ verify(foldProvider).unregisterCallback(any())
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/FoldStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/FoldStateRepositoryTest.kt
new file mode 100644
index 0000000..0651323
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/FoldStateRepositoryTest.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.unfold.updates
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.unfold.updates.FoldStateRepository.FoldUpdate
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.verify
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class FoldStateRepositoryTest : SysuiTestCase() {
+
+ private val foldStateProvider = mock<FoldStateProvider>()
+ private val foldUpdatesListener = argumentCaptor<FoldStateProvider.FoldUpdatesListener>()
+ private val testScope = TestScope(UnconfinedTestDispatcher())
+
+ private val foldStateRepository = FoldStateRepositoryImpl(foldStateProvider)
+ @Test
+ fun onHingeAngleUpdate_received() =
+ testScope.runTest {
+ val flowValue = collectLastValue(foldStateRepository.hingeAngle)
+
+ verify(foldStateProvider).addCallback(capture(foldUpdatesListener))
+ foldUpdatesListener.value.onHingeAngleUpdate(42f)
+
+ assertThat(flowValue()).isEqualTo(42f)
+ }
+
+ @Test
+ fun onFoldUpdate_received() =
+ testScope.runTest {
+ val flowValue = collectLastValue(foldStateRepository.foldUpdate)
+
+ verify(foldStateProvider).addCallback(capture(foldUpdatesListener))
+ foldUpdatesListener.value.onFoldUpdate(FOLD_UPDATE_START_OPENING)
+
+ assertThat(flowValue()).isEqualTo(FoldUpdate.START_OPENING)
+ }
+
+ @Test
+ fun foldUpdates_mappedCorrectly() {
+ mapOf(
+ FOLD_UPDATE_START_OPENING to FoldUpdate.START_OPENING,
+ FOLD_UPDATE_START_CLOSING to FoldUpdate.START_CLOSING,
+ FOLD_UPDATE_FINISH_HALF_OPEN to FoldUpdate.FINISH_HALF_OPEN,
+ FOLD_UPDATE_FINISH_FULL_OPEN to FoldUpdate.FINISH_FULL_OPEN,
+ FOLD_UPDATE_FINISH_CLOSED to FoldUpdate.FINISH_CLOSED
+ )
+ .forEach { (id, expected) ->
+ assertThat(FoldUpdate.fromFoldUpdateId(id)).isEqualTo(expected)
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
index 468c5a7..fc2030f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
@@ -16,15 +16,20 @@
package com.android.systemui.wallpapers;
+import static android.app.WallpaperManager.FLAG_LOCK;
+import static android.app.WallpaperManager.FLAG_SYSTEM;
+
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -111,7 +116,8 @@
when(mWallpaperBitmap.getConfig()).thenReturn(Bitmap.Config.ARGB_8888);
// set up wallpaper manager
- when(mWallpaperManager.getBitmapAsUser(eq(ActivityManager.getCurrentUser()), anyBoolean()))
+ when(mWallpaperManager.getBitmapAsUser(
+ eq(ActivityManager.getCurrentUser()), anyBoolean(), anyInt(), anyBoolean()))
.thenReturn(mWallpaperBitmap);
when(mMockContext.getSystemService(WallpaperManager.class)).thenReturn(mWallpaperManager);
@@ -208,6 +214,7 @@
ImageWallpaper.CanvasEngine spyEngine = spy(engine);
doNothing().when(spyEngine).drawFrameOnCanvas(any(Bitmap.class));
doNothing().when(spyEngine).reportEngineShown(anyBoolean());
+ doReturn(FLAG_SYSTEM | FLAG_LOCK).when(spyEngine).getWallpaperFlags();
doAnswer(invocation -> {
((ImageWallpaper.CanvasEngine) invocation.getMock()).onMiniBitmapUpdated();
return null;
@@ -216,7 +223,7 @@
}
private void setBitmapDimensions(int bitmapWidth, int bitmapHeight) {
- when(mWallpaperManager.peekBitmapDimensions())
+ when(mWallpaperManager.peekBitmapDimensions(anyInt(), anyBoolean()))
.thenReturn(new Rect(0, 0, bitmapWidth, bitmapHeight));
when(mWallpaperBitmap.getWidth()).thenReturn(bitmapWidth);
when(mWallpaperBitmap.getHeight()).thenReturn(bitmapHeight);
@@ -234,9 +241,7 @@
clearInvocations(mSurfaceHolder);
setBitmapDimensions(bitmapWidth, bitmapHeight);
- ImageWallpaper imageWallpaper = createImageWallpaper();
- ImageWallpaper.CanvasEngine engine =
- (ImageWallpaper.CanvasEngine) imageWallpaper.onCreateEngine();
+ ImageWallpaper.CanvasEngine engine = getSpyEngine();
engine.onCreate(mSurfaceHolder);
verify(mSurfaceHolder, times(1)).setFixedSize(
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
index 5ffc094..7473ca6 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
@@ -22,6 +22,8 @@
import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder
import com.android.systemui.unfold.updates.DeviceFoldStateProvider
import com.android.systemui.unfold.updates.FoldStateProvider
+import com.android.systemui.unfold.updates.FoldStateRepository
+import com.android.systemui.unfold.updates.FoldStateRepositoryImpl
import com.android.systemui.unfold.updates.hinge.EmptyHingeAngleProvider
import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
import com.android.systemui.unfold.updates.hinge.HingeSensorAngleProvider
@@ -55,6 +57,12 @@
fun unfoldKeyguardVisibilityManager(
impl: UnfoldKeyguardVisibilityManagerImpl
): UnfoldKeyguardVisibilityManager = impl
+
+ @Provides
+ @Singleton
+ fun foldStateRepository(
+ impl: FoldStateRepositoryImpl
+ ): FoldStateRepository = impl
}
/**
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
index 6743515..003013e 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -17,7 +17,6 @@
import android.content.Context
import android.os.Handler
-import android.os.Trace
import android.util.Log
import androidx.annotation.FloatRange
import androidx.annotation.VisibleForTesting
@@ -130,7 +129,6 @@
"lastHingeAngleBeforeTransition: $lastHingeAngleBeforeTransition"
)
}
- Trace.setCounter("DeviceFoldStateProvider#onHingeAngle", angle.toLong())
val currentDirection =
if (angle < lastHingeAngle) FOLD_UPDATE_START_CLOSING else FOLD_UPDATE_START_OPENING
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldProvider.kt
index 6e87bee..ea6786e 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldProvider.kt
@@ -20,7 +20,7 @@
fun registerCallback(callback: FoldCallback, executor: Executor)
fun unregisterCallback(callback: FoldCallback)
- interface FoldCallback {
+ fun interface FoldCallback {
fun onFoldUpdated(isFolded: Boolean)
}
}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateRepository.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateRepository.kt
new file mode 100644
index 0000000..61b0b40
--- /dev/null
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateRepository.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.unfold.updates
+
+import com.android.systemui.unfold.updates.FoldStateRepository.FoldUpdate
+import javax.inject.Inject
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.buffer
+import kotlinx.coroutines.flow.callbackFlow
+
+/**
+ * Allows to subscribe to main events related to fold/unfold process such as hinge angle update,
+ * start folding/unfolding, screen availability
+ */
+interface FoldStateRepository {
+ /** Latest fold update, as described by [FoldStateProvider.FoldUpdate]. */
+ val foldUpdate: Flow<FoldUpdate>
+
+ /** Provides the hinge angle while the fold/unfold is in progress. */
+ val hingeAngle: Flow<Float>
+
+ enum class FoldUpdate {
+ START_OPENING,
+ START_CLOSING,
+ FINISH_HALF_OPEN,
+ FINISH_FULL_OPEN,
+ FINISH_CLOSED;
+
+ companion object {
+ /** Maps the old [FoldStateProvider.FoldUpdate] to [FoldStateRepository.FoldUpdate]. */
+ fun fromFoldUpdateId(@FoldStateProvider.FoldUpdate oldId: Int): FoldUpdate {
+ return when (oldId) {
+ FOLD_UPDATE_START_OPENING -> START_OPENING
+ FOLD_UPDATE_START_CLOSING -> START_CLOSING
+ FOLD_UPDATE_FINISH_HALF_OPEN -> FINISH_HALF_OPEN
+ FOLD_UPDATE_FINISH_FULL_OPEN -> FINISH_FULL_OPEN
+ FOLD_UPDATE_FINISH_CLOSED -> FINISH_CLOSED
+ else -> error("FoldUpdateNotFound")
+ }
+ }
+ }
+ }
+}
+
+class FoldStateRepositoryImpl
+@Inject
+constructor(
+ private val foldStateProvider: FoldStateProvider,
+) : FoldStateRepository {
+
+ override val hingeAngle: Flow<Float>
+ get() =
+ callbackFlow {
+ val callback =
+ object : FoldStateProvider.FoldUpdatesListener {
+ override fun onHingeAngleUpdate(angle: Float) {
+ trySend(angle)
+ }
+ }
+ foldStateProvider.addCallback(callback)
+ awaitClose { foldStateProvider.removeCallback(callback) }
+ }
+ .buffer(capacity = Channel.CONFLATED)
+
+ override val foldUpdate: Flow<FoldUpdate>
+ get() =
+ callbackFlow {
+ val callback =
+ object : FoldStateProvider.FoldUpdatesListener {
+ override fun onFoldUpdate(update: Int) {
+ trySend(FoldUpdate.fromFoldUpdateId(update))
+ }
+ }
+ foldStateProvider.addCallback(callback)
+ awaitClose { foldStateProvider.removeCallback(callback) }
+ }
+ .buffer(capacity = Channel.CONFLATED)
+}
diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
index 3406102..98421a9 100644
--- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
+++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
@@ -367,11 +367,7 @@
ComponentName wpService = parseWallpaperComponent(infoStage, "wp");
mSystemHasLiveComponent = wpService != null;
- ComponentName kwpService = null;
- boolean lockscreenLiveWallpaper = mWallpaperManager.isLockscreenLiveWallpaperEnabled();
- if (lockscreenLiveWallpaper) {
- kwpService = parseWallpaperComponent(infoStage, "kwp");
- }
+ ComponentName kwpService = parseWallpaperComponent(infoStage, "kwp");
mLockHasLiveComponent = kwpService != null;
boolean separateLockWallpaper = mLockHasLiveComponent || lockImageStage.exists();
@@ -381,17 +377,16 @@
// It is valid for the imagery to be absent; it means that we were not permitted
// to back up the original image on the source device, or there was no user-supplied
// wallpaper image present.
- if (!lockscreenLiveWallpaper) restoreFromStage(imageStage, infoStage, "wp", sysWhich);
if (lockImageStageExists) {
restoreFromStage(lockImageStage, infoStage, "kwp", FLAG_LOCK);
}
- if (lockscreenLiveWallpaper) restoreFromStage(imageStage, infoStage, "wp", sysWhich);
+ restoreFromStage(imageStage, infoStage, "wp", sysWhich);
// And reset to the wallpaper service we should be using
- if (lockscreenLiveWallpaper && mLockHasLiveComponent) {
- updateWallpaperComponent(kwpService, false, FLAG_LOCK);
+ if (mLockHasLiveComponent) {
+ updateWallpaperComponent(kwpService, FLAG_LOCK);
}
- updateWallpaperComponent(wpService, !lockImageStageExists, sysWhich);
+ updateWallpaperComponent(wpService, sysWhich);
} catch (Exception e) {
Slog.e(TAG, "Unable to restore wallpaper: " + e.getMessage());
mEventLogger.onRestoreException(e);
@@ -410,36 +405,24 @@
}
@VisibleForTesting
- void updateWallpaperComponent(ComponentName wpService, boolean applyToLock, int which)
+ void updateWallpaperComponent(ComponentName wpService, int which)
throws IOException {
- boolean lockscreenLiveWallpaper = mWallpaperManager.isLockscreenLiveWallpaperEnabled();
if (servicePackageExists(wpService)) {
Slog.i(TAG, "Using wallpaper service " + wpService);
- if (lockscreenLiveWallpaper) {
- mWallpaperManager.setWallpaperComponentWithFlags(wpService, which);
- if ((which & FLAG_LOCK) != 0) {
- mEventLogger.onLockLiveWallpaperRestored(wpService);
- }
- if ((which & FLAG_SYSTEM) != 0) {
- mEventLogger.onSystemLiveWallpaperRestored(wpService);
- }
- return;
- }
- mWallpaperManager.setWallpaperComponent(wpService);
- if (applyToLock) {
- // We have a live wallpaper and no static lock image,
- // allow live wallpaper to show "through" on lock screen.
- mWallpaperManager.clear(FLAG_LOCK);
+ mWallpaperManager.setWallpaperComponentWithFlags(wpService, which);
+ if ((which & FLAG_LOCK) != 0) {
mEventLogger.onLockLiveWallpaperRestored(wpService);
}
- mEventLogger.onSystemLiveWallpaperRestored(wpService);
+ if ((which & FLAG_SYSTEM) != 0) {
+ mEventLogger.onSystemLiveWallpaperRestored(wpService);
+ }
} else {
// If we've restored a live wallpaper, but the component doesn't exist,
// we should log it as an error so we can easily identify the problem
// in reports from users
if (wpService != null) {
// TODO(b/268471749): Handle delayed case
- applyComponentAtInstall(wpService, applyToLock, which);
+ applyComponentAtInstall(wpService, which);
Slog.w(TAG, "Wallpaper service " + wpService + " isn't available. "
+ " Will try to apply later");
}
@@ -579,21 +562,17 @@
// Intentionally blank
}
- private void applyComponentAtInstall(ComponentName componentName, boolean applyToLock,
- int which) {
+ private void applyComponentAtInstall(ComponentName componentName, int which) {
PackageMonitor packageMonitor = getWallpaperPackageMonitor(
- componentName, applyToLock, which);
+ componentName, which);
packageMonitor.register(getBaseContext(), null, UserHandle.ALL, true);
}
@VisibleForTesting
- PackageMonitor getWallpaperPackageMonitor(ComponentName componentName, boolean applyToLock,
- int which) {
+ PackageMonitor getWallpaperPackageMonitor(ComponentName componentName, int which) {
return new PackageMonitor() {
@Override
public void onPackageAdded(String packageName, int uid) {
- boolean lockscreenLiveWallpaper =
- mWallpaperManager.isLockscreenLiveWallpaperEnabled();
if (!isDeviceInRestore()) {
// We don't want to reapply the wallpaper outside a restore.
unregister();
@@ -601,9 +580,11 @@
// We have finished restore and not succeeded, so let's log that as an error.
WallpaperEventLogger logger = new WallpaperEventLogger(
mBackupManager.getDelayedRestoreLogger());
- logger.onSystemLiveWallpaperRestoreFailed(
- WallpaperEventLogger.ERROR_LIVE_PACKAGE_NOT_INSTALLED);
- if (applyToLock) {
+ if ((which & FLAG_SYSTEM) != 0) {
+ logger.onSystemLiveWallpaperRestoreFailed(
+ WallpaperEventLogger.ERROR_LIVE_PACKAGE_NOT_INSTALLED);
+ }
+ if ((which & FLAG_LOCK) != 0) {
logger.onLockLiveWallpaperRestoreFailed(
WallpaperEventLogger.ERROR_LIVE_PACKAGE_NOT_INSTALLED);
}
@@ -614,37 +595,27 @@
if (componentName.getPackageName().equals(packageName)) {
Slog.d(TAG, "Applying component " + componentName);
- boolean success = lockscreenLiveWallpaper
- ? mWallpaperManager.setWallpaperComponentWithFlags(componentName, which)
- : mWallpaperManager.setWallpaperComponent(componentName);
+ boolean success = mWallpaperManager.setWallpaperComponentWithFlags(
+ componentName, which);
WallpaperEventLogger logger = new WallpaperEventLogger(
mBackupManager.getDelayedRestoreLogger());
if (success) {
- if (!lockscreenLiveWallpaper || (which & FLAG_SYSTEM) != 0) {
+ if ((which & FLAG_SYSTEM) != 0) {
logger.onSystemLiveWallpaperRestored(componentName);
}
- if (lockscreenLiveWallpaper && (which & FLAG_LOCK) != 0) {
+ if ((which & FLAG_LOCK) != 0) {
logger.onLockLiveWallpaperRestored(componentName);
}
} else {
- if (!lockscreenLiveWallpaper || (which & FLAG_SYSTEM) != 0) {
+ if ((which & FLAG_SYSTEM) != 0) {
logger.onSystemLiveWallpaperRestoreFailed(
WallpaperEventLogger.ERROR_SET_COMPONENT_EXCEPTION);
}
- if (lockscreenLiveWallpaper && (which & FLAG_LOCK) != 0) {
+ if ((which & FLAG_LOCK) != 0) {
logger.onLockLiveWallpaperRestoreFailed(
WallpaperEventLogger.ERROR_SET_COMPONENT_EXCEPTION);
}
}
- if (applyToLock && !lockscreenLiveWallpaper) {
- try {
- mWallpaperManager.clear(FLAG_LOCK);
- logger.onLockLiveWallpaperRestored(componentName);
- } catch (IOException e) {
- Slog.w(TAG, "Failed to apply live wallpaper to lock screen: " + e);
- logger.onLockLiveWallpaperRestoreFailed(e.getClass().getName());
- }
- }
// We're only expecting to restore the wallpaper component once.
unregister();
mBackupManager.reportDelayedRestoreResult(logger.getBackupRestoreLogger());
diff --git a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
index dc1126e..4c224fb 100644
--- a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
+++ b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
@@ -116,8 +116,6 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
-
- when(mWallpaperManager.isLockscreenLiveWallpaperEnabled()).thenReturn(true);
when(mWallpaperManager.isWallpaperBackupEligible(eq(FLAG_SYSTEM))).thenReturn(true);
when(mWallpaperManager.isWallpaperBackupEligible(eq(FLAG_LOCK))).thenReturn(true);
@@ -363,25 +361,19 @@
@Test
public void testUpdateWallpaperComponent_doesApplyLater() throws IOException {
mWallpaperBackupAgent.mIsDeviceInRestore = true;
-
mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent,
- /* applyToLock */ true, FLAG_LOCK | FLAG_SYSTEM);
+ /* which */ FLAG_LOCK | FLAG_SYSTEM);
// Imitate wallpaper component installation.
mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE,
/* uid */0);
- if (mWallpaperManager.isLockscreenLiveWallpaperEnabled()) {
- verify(mWallpaperManager, times(1))
- .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_LOCK | FLAG_SYSTEM);
- verify(mWallpaperManager, never())
- .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_SYSTEM);
- verify(mWallpaperManager, never())
- .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_LOCK);
- verify(mWallpaperManager, never()).clear(anyInt());
- } else {
- verify(mWallpaperManager, times(1)).setWallpaperComponent(mWallpaperComponent);
- verify(mWallpaperManager, times(1)).clear(eq(FLAG_LOCK));
- }
+ verify(mWallpaperManager, times(1))
+ .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_LOCK | FLAG_SYSTEM);
+ verify(mWallpaperManager, never())
+ .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_SYSTEM);
+ verify(mWallpaperManager, never())
+ .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_LOCK);
+ verify(mWallpaperManager, never()).clear(anyInt());
}
@Test
@@ -390,24 +382,19 @@
mWallpaperBackupAgent.mIsDeviceInRestore = true;
mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent,
- /* applyToLock */ false, FLAG_SYSTEM);
+ /* which */ FLAG_SYSTEM);
// Imitate wallpaper component installation.
mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE,
/* uid */0);
- if (mWallpaperManager.isLockscreenLiveWallpaperEnabled()) {
- verify(mWallpaperManager, times(1))
- .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_SYSTEM);
- verify(mWallpaperManager, never())
- .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_LOCK);
- verify(mWallpaperManager, never())
- .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_LOCK | FLAG_SYSTEM);
- verify(mWallpaperManager, never()).clear(anyInt());
- } else {
- verify(mWallpaperManager, times(1)).setWallpaperComponent(mWallpaperComponent);
- verify(mWallpaperManager, never()).clear(eq(FLAG_LOCK));
- }
+ verify(mWallpaperManager, times(1))
+ .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_SYSTEM);
+ verify(mWallpaperManager, never())
+ .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_LOCK);
+ verify(mWallpaperManager, never())
+ .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_LOCK | FLAG_SYSTEM);
+ verify(mWallpaperManager, never()).clear(anyInt());
}
@Test
@@ -416,7 +403,7 @@
mWallpaperBackupAgent.mIsDeviceInRestore = false;
mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent,
- /* applyToLock */ true, FLAG_LOCK | FLAG_SYSTEM);
+ /* which */ FLAG_LOCK | FLAG_SYSTEM);
// Imitate wallpaper component installation.
mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE,
@@ -432,7 +419,7 @@
mWallpaperBackupAgent.mIsDeviceInRestore = false;
mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent,
- /* applyToLock */ true, FLAG_LOCK | FLAG_SYSTEM);
+ /* which */ FLAG_LOCK | FLAG_SYSTEM);
// Imitate "wrong" wallpaper component installation.
mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(/* packageName */"",
@@ -622,7 +609,7 @@
}
@Test
- public void testOnRestore_systemWallpaperImgSuccess_logsSuccess() throws Exception {
+ public void testOnRestore_wallpaperImgSuccess_logsSuccess() throws Exception {
mockStagedWallpaperFile(WALLPAPER_INFO_STAGE);
mockStagedWallpaperFile(SYSTEM_WALLPAPER_STAGE);
mWallpaperBackupAgent.onCreate(USER_HANDLE, BackupAnnotations.BackupDestination.CLOUD,
@@ -630,17 +617,16 @@
mWallpaperBackupAgent.onRestoreFinished();
+ // wallpaper will be applied to home & lock screen, a success for both screens in expected
DataTypeResult result = getLoggingResult(WALLPAPER_IMG_SYSTEM,
mWallpaperBackupAgent.getBackupRestoreEventLogger().getLoggingResults());
assertThat(result).isNotNull();
assertThat(result.getSuccessCount()).isEqualTo(1);
- if (mWallpaperManager.isLockscreenLiveWallpaperEnabled()) {
- result = getLoggingResult(WALLPAPER_IMG_LOCK,
- mWallpaperBackupAgent.getBackupRestoreEventLogger().getLoggingResults());
- assertThat(result).isNotNull();
- assertThat(result.getSuccessCount()).isEqualTo(1);
- }
+ result = getLoggingResult(WALLPAPER_IMG_LOCK,
+ mWallpaperBackupAgent.getBackupRestoreEventLogger().getLoggingResults());
+ assertThat(result).isNotNull();
+ assertThat(result.getSuccessCount()).isEqualTo(1);
}
@Test
@@ -758,7 +744,7 @@
mWallpaperBackupAgent.setBackupManagerForTesting(mBackupManager);
mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent,
- /* applyToLock */ true, FLAG_LOCK | FLAG_SYSTEM);
+ /* which */ FLAG_LOCK | FLAG_SYSTEM);
// Imitate wallpaper component installation.
mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE,
/* uid */0);
@@ -782,7 +768,7 @@
mWallpaperBackupAgent.setBackupManagerForTesting(mBackupManager);
mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent,
- /* applyToLock */ true, FLAG_LOCK | FLAG_SYSTEM);
+ /* which */ FLAG_LOCK | FLAG_SYSTEM);
// Imitate wallpaper component installation.
mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE,
/* uid */0);
@@ -804,7 +790,7 @@
mWallpaperBackupAgent.setBackupManagerForTesting(mBackupManager);
mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent,
- /* applyToLock */ true, FLAG_LOCK | FLAG_SYSTEM);
+ /* which */ FLAG_LOCK | FLAG_SYSTEM);
// Imitate wallpaper component installation.
mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE,
@@ -938,10 +924,8 @@
}
@Override
- PackageMonitor getWallpaperPackageMonitor(ComponentName componentName,
- boolean applyToLock, int which) {
- mWallpaperPackageMonitor = super.getWallpaperPackageMonitor(
- componentName, applyToLock, which);
+ PackageMonitor getWallpaperPackageMonitor(ComponentName componentName, int which) {
+ mWallpaperPackageMonitor = super.getWallpaperPackageMonitor(componentName, which);
return mWallpaperPackageMonitor;
}
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 1a735f8..75ecdb7 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -1,32 +1,6 @@
package: "com.android.server.accessibility"
-flag {
- name: "proxy_use_apps_on_virtual_device_listener"
- namespace: "accessibility"
- description: "Fixes race condition described in b/286587811"
- bug: "286587811"
-}
-
-flag {
- name: "enable_magnification_multiple_finger_multiple_tap_gesture"
- namespace: "accessibility"
- description: "Whether to enable multi-finger-multi-tap gesture for magnification"
- bug: "257274411"
-}
-
-flag {
- name: "enable_magnification_joystick"
- namespace: "accessibility"
- description: "Whether to enable joystick controls for magnification"
- bug: "297211257"
-}
-
-flag {
- name: "send_a11y_events_based_on_state"
- namespace: "accessibility"
- description: "Sends accessibility events in TouchExplorer#onAccessibilityEvent based on internal state to keep it consistent. This reduces test flakiness."
- bug: "295575684"
-}
+# NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
flag {
name: "add_window_token_without_lock"
@@ -36,20 +10,6 @@
}
flag {
- name: "pinch_zoom_zero_min_span"
- namespace: "accessibility"
- description: "Whether to set min span of ScaleGestureDetector to zero."
- bug: "295327792"
-}
-
-flag {
- name: "disable_continuous_shortcut_on_force_stop"
- namespace: "accessibility"
- description: "When a package is force stopped, remove the button shortcuts of any continuously-running shortcuts."
- bug: "198018180"
-}
-
-flag {
name: "deprecate_package_list_observer"
namespace: "accessibility"
description: "Stops using the deprecated PackageListObserver."
@@ -57,10 +17,38 @@
}
flag {
- name: "scan_packages_without_lock"
+ name: "disable_continuous_shortcut_on_force_stop"
namespace: "accessibility"
- description: "Scans packages for accessibility service/activity info without holding the A11yMS lock"
- bug: "295969873"
+ description: "When a package is force stopped, remove the button shortcuts of any continuously-running shortcuts."
+ bug: "198018180"
+}
+
+flag {
+ name: "enable_magnification_joystick"
+ namespace: "accessibility"
+ description: "Whether to enable joystick controls for magnification"
+ bug: "297211257"
+}
+
+flag {
+ name: "enable_magnification_multiple_finger_multiple_tap_gesture"
+ namespace: "accessibility"
+ description: "Whether to enable multi-finger-multi-tap gesture for magnification"
+ bug: "257274411"
+}
+
+flag {
+ name: "pinch_zoom_zero_min_span"
+ namespace: "accessibility"
+ description: "Whether to set min span of ScaleGestureDetector to zero."
+ bug: "295327792"
+}
+
+flag {
+ name: "proxy_use_apps_on_virtual_device_listener"
+ namespace: "accessibility"
+ description: "Fixes race condition described in b/286587811"
+ bug: "286587811"
}
flag {
@@ -69,3 +57,17 @@
description: "Reduces touch exploration sensitivity by only sending a hover event when the ifnger has moved the amount of pixels defined by the system's touch slop."
bug: "303677860"
}
+
+flag {
+ name: "scan_packages_without_lock"
+ namespace: "accessibility"
+ description: "Scans packages for accessibility service/activity info without holding the A11yMS lock"
+ bug: "295969873"
+}
+
+flag {
+ name: "send_a11y_events_based_on_state"
+ namespace: "accessibility"
+ description: "Sends accessibility events in TouchExplorer#onAccessibilityEvent based on internal state to keep it consistent. This reduces test flakiness."
+ bug: "295575684"
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index e65a185..87f9cf1 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -4929,8 +4929,8 @@
private final Uri mTouchExplorationEnabledUri = Settings.Secure.getUriFor(
Settings.Secure.TOUCH_EXPLORATION_ENABLED);
- private final Uri mDisplayMagnificationEnabledUri = Settings.Secure.getUriFor(
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED);
+ private final Uri mMagnificationmSingleFingerTripleTapEnabledUri = Settings.Secure
+ .getUriFor(Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED);
private final Uri mAutoclickEnabledUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED);
@@ -4987,7 +4987,7 @@
public void register(ContentResolver contentResolver) {
contentResolver.registerContentObserver(mTouchExplorationEnabledUri,
false, this, UserHandle.USER_ALL);
- contentResolver.registerContentObserver(mDisplayMagnificationEnabledUri,
+ contentResolver.registerContentObserver(mMagnificationmSingleFingerTripleTapEnabledUri,
false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(mAutoclickEnabledUri,
false, this, UserHandle.USER_ALL);
@@ -5035,7 +5035,7 @@
if (readTouchExplorationEnabledSettingLocked(userState)) {
onUserStateChangedLocked(userState);
}
- } else if (mDisplayMagnificationEnabledUri.equals(uri)) {
+ } else if (mMagnificationmSingleFingerTripleTapEnabledUri.equals(uri)) {
if (readMagnificationEnabledSettingsLocked(userState)) {
onUserStateChangedLocked(userState);
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 898cdcc..b696998 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -136,6 +136,7 @@
":display-device-config",
":display-layout-config",
":device-state-config",
+ ":framework-core-nfc-infcadapter-sources",
"java/com/android/server/EventLogTags.logtags",
"java/com/android/server/am/EventLogTags.logtags",
"java/com/android/server/wm/EventLogTags.logtags",
@@ -195,6 +196,7 @@
"android.hardware.rebootescrow-V1-java",
"android.hardware.power.stats-V2-java",
"android.hidl.manager-V1.2-java",
+ "android.nfc.flags-aconfig-java",
"cbor-java",
"icu4j_calendar_astronomer",
"android.security.aaid_aidl-java",
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index beea063..8624dd5 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1236,7 +1236,7 @@
private void onUserStopped(int userId) {
Slog.d(TAG, "onUserStopped " + userId);
- Watchdog.getInstance().setOneOffTimeoutForMonitors(
+ Watchdog.getInstance().pauseWatchingMonitorsFor(
SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#onUserStopped might be slow");
try {
mVold.onUserStopped(userId);
@@ -1320,7 +1320,7 @@
unlockedUsers.add(userId);
}
}
- Watchdog.getInstance().setOneOffTimeoutForMonitors(
+ Watchdog.getInstance().pauseWatchingMonitorsFor(
SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#onUserStopped might be slow");
for (Integer userId : unlockedUsers) {
try {
@@ -2341,7 +2341,7 @@
try {
// TODO(b/135341433): Remove cautious logging when FUSE is stable
Slog.i(TAG, "Mounting volume " + vol);
- Watchdog.getInstance().setOneOffTimeoutForMonitors(
+ Watchdog.getInstance().pauseWatchingMonitorsFor(
SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#mount might be slow");
mVold.mount(vol.id, vol.mountFlags, vol.mountUserId, new IVoldMountCallback.Stub() {
@Override
@@ -2472,7 +2472,7 @@
final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
- Watchdog.getInstance().setOneOffTimeoutForMonitors(
+ Watchdog.getInstance().pauseWatchingMonitorsFor(
PARTITION_OPERATION_WATCHDOG_TIMEOUT_MS, "#partition might be very slow");
try {
mVold.partition(diskId, IVold.PARTITION_TYPE_PUBLIC, -1);
@@ -2491,7 +2491,7 @@
final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
- Watchdog.getInstance().setOneOffTimeoutForMonitors(
+ Watchdog.getInstance().pauseWatchingMonitorsFor(
PARTITION_OPERATION_WATCHDOG_TIMEOUT_MS, "#partition might be very slow");
try {
mVold.partition(diskId, IVold.PARTITION_TYPE_PRIVATE, -1);
@@ -2510,7 +2510,7 @@
final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
- Watchdog.getInstance().setOneOffTimeoutForMonitors(
+ Watchdog.getInstance().pauseWatchingMonitorsFor(
PARTITION_OPERATION_WATCHDOG_TIMEOUT_MS, "#partition might be very slow");
try {
mVold.partition(diskId, IVold.PARTITION_TYPE_MIXED, ratio);
@@ -3620,7 +3620,7 @@
@Override
public ParcelFileDescriptor open() throws AppFuseMountException {
- Watchdog.getInstance().setOneOffTimeoutForMonitors(
+ Watchdog.getInstance().pauseWatchingMonitorsFor(
SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#open might be slow");
try {
final FileDescriptor fd = mVold.mountAppFuse(uid, mountId);
@@ -3634,7 +3634,7 @@
@Override
public ParcelFileDescriptor openFile(int mountId, int fileId, int flags)
throws AppFuseMountException {
- Watchdog.getInstance().setOneOffTimeoutForMonitors(
+ Watchdog.getInstance().pauseWatchingMonitorsFor(
SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#openFile might be slow");
try {
return new ParcelFileDescriptor(
@@ -3646,7 +3646,7 @@
@Override
public void close() throws Exception {
- Watchdog.getInstance().setOneOffTimeoutForMonitors(
+ Watchdog.getInstance().pauseWatchingMonitorsFor(
SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#close might be slow");
if (mMounted) {
mVold.unmountAppFuse(uid, mountId);
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 55aa716..003046a 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -250,7 +250,7 @@
private Monitor mCurrentMonitor;
private long mStartTimeMillis;
private int mPauseCount;
- private long mOneOffTimeoutMillis;
+ private long mPauseEndTimeMillis;
HandlerChecker(Handler handler, String name) {
mHandler = handler;
@@ -270,20 +270,19 @@
* @param handlerCheckerTimeoutMillis the timeout to use for this run
*/
public void scheduleCheckLocked(long handlerCheckerTimeoutMillis) {
- if (mOneOffTimeoutMillis > 0) {
- mWaitMaxMillis = mOneOffTimeoutMillis;
- mOneOffTimeoutMillis = 0;
- } else {
- mWaitMaxMillis = handlerCheckerTimeoutMillis;
- }
+ mWaitMaxMillis = handlerCheckerTimeoutMillis;
if (mCompleted) {
// Safe to update monitors in queue, Handler is not in the middle of work
mMonitors.addAll(mMonitorQueue);
mMonitorQueue.clear();
}
+
+ long nowMillis = SystemClock.uptimeMillis();
+ boolean isPaused = mPauseCount > 0
+ || (mPauseEndTimeMillis > 0 && mPauseEndTimeMillis < nowMillis);
if ((mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling())
- || (mPauseCount > 0)) {
+ || isPaused) {
// Don't schedule until after resume OR
// If the target looper has recently been polling, then
// there is no reason to enqueue our checker on it since that
@@ -301,7 +300,8 @@
mCompleted = false;
mCurrentMonitor = null;
- mStartTimeMillis = SystemClock.uptimeMillis();
+ mStartTimeMillis = nowMillis;
+ mPauseEndTimeMillis = 0;
mHandler.postAtFrontOfQueue(this);
}
@@ -360,20 +360,19 @@
}
/**
- * Sets the timeout of the HandlerChecker for one run.
+ * Pauses the checks for the given time.
*
- * <p>The current run will be ignored and the next run will be set to this timeout.
- *
- * <p>If a one off timeout is already set, the maximum timeout will be used.
+ * <p>The current run will be ignored and another run will be scheduled after
+ * the given time.
*/
- public void setOneOffTimeoutLocked(int temporaryTimeoutMillis, String reason) {
- mOneOffTimeoutMillis = Math.max(temporaryTimeoutMillis, mOneOffTimeoutMillis);
+ public void pauseForLocked(int pauseMillis, String reason) {
+ mPauseEndTimeMillis = SystemClock.uptimeMillis() + pauseMillis;
// Mark as completed, because there's a chance we called this after the watchog
// thread loop called Object#wait after 'WAITED_HALF'. In that case we want to ensure
// the next call to #getCompletionStateLocked for this checker returns 'COMPLETED'
mCompleted = true;
- Slog.i(TAG, "Extending timeout of HandlerChecker: " + mName + " for reason: "
- + reason + ". New timeout: " + mOneOffTimeoutMillis);
+ Slog.i(TAG, "Pausing of HandlerChecker: " + mName + " for reason: "
+ + reason + ". Pause end time: " + mPauseEndTimeMillis);
}
/** Pause the HandlerChecker. */
@@ -623,34 +622,32 @@
}
/**
- * Sets a one-off timeout for the next run of the watchdog for this thread. This is useful
+ * Pauses the checks of the watchdog for this thread. This is useful
* to run a slow operation on one of the monitored thread.
*
- * <p>After the next run, the timeout will go back to the default value.
- *
- * <p>If the current thread has not been added to the Watchdog, this call is a no-op.
- *
- * <p>If a one-off timeout for the current thread is already, the max value will be used.
+ * <p>After the given time, the timeout will go back to the default value.
+ * <p>This method does not require resume to be called.
*/
- public void setOneOffTimeoutForCurrentThread(int oneOffTimeoutMillis, String reason) {
+ public void pauseWatchingCurrentThreadFor(int pauseMillis, String reason) {
synchronized (mLock) {
for (HandlerCheckerAndTimeout hc : mHandlerCheckers) {
HandlerChecker checker = hc.checker();
if (Thread.currentThread().equals(checker.getThread())) {
- checker.setOneOffTimeoutLocked(oneOffTimeoutMillis, reason);
+ checker.pauseForLocked(pauseMillis, reason);
}
}
}
}
/**
- * Sets a one-off timeout for the next run of the watchdog for the monitor thread.
+ * Pauses the checks of the watchdog for the monitor thread for the given time
*
- * <p>Simiar to {@link setOneOffTimeoutForCurrentThread} but used for monitors added through
- * {@link #addMonitor}
+ * <p>Similar to {@link pauseWatchingCurrentThreadFor} but used for monitors added
+ * through {@link #addMonitor}
+ * <p>This method does not require resume to be called.
*/
- public void setOneOffTimeoutForMonitors(int oneOffTimeoutMillis, String reason) {
- mMonitorChecker.setOneOffTimeoutLocked(oneOffTimeoutMillis, reason);
+ public void pauseWatchingMonitorsFor(int pauseMillis, String reason) {
+ mMonitorChecker.pauseForLocked(pauseMillis, reason);
}
/**
@@ -664,7 +661,7 @@
* adds another pause and will require an additional {@link #resumeCurrentThread} to resume.
*
* <p>Note: Use with care, as any deadlocks on the current thread will be undetected until all
- * pauses have been resumed. Prefer to use #setOneOffTimeoutForCurrentThread.
+ * pauses have been resumed. Prefer to use #pauseWatchingCurrentThreadFor.
*/
public void pauseWatchingCurrentThread(String reason) {
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/am/AppWaitingForDebuggerDialog.java b/services/core/java/com/android/server/am/AppWaitingForDebuggerDialog.java
index 9b5f18c..710278d 100644
--- a/services/core/java/com/android/server/am/AppWaitingForDebuggerDialog.java
+++ b/services/core/java/com/android/server/am/AppWaitingForDebuggerDialog.java
@@ -16,6 +16,8 @@
package com.android.server.am;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+
import android.content.Context;
import android.content.DialogInterface;
import android.os.Handler;
@@ -54,6 +56,7 @@
setButton(DialogInterface.BUTTON_POSITIVE, "Force Close", mHandler.obtainMessage(1, app));
setTitle("Waiting For Debugger");
WindowManager.LayoutParams attrs = getWindow().getAttributes();
+ attrs.privateFlags |= SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
attrs.setTitle("Waiting For Debugger: " + app.info.processName);
getWindow().setAttributes(attrs);
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index e0e6cad..59d8e7e 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -869,6 +869,8 @@
ApplicationExitInfo.REASON_LOW_MEMORY,
ApplicationExitInfo.SUBREASON_OOM_KILL,
"oom");
+
+ oomKill.logKillOccurred();
}
}
}
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index bb9ea28..cbaf05b 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -15,3 +15,10 @@
description: "Feature flag for the ANR timer service"
bug: "282428924"
}
+
+flag {
+ name: "fgs_abuse_detection"
+ namespace: "backstage_power"
+ description: "Detect abusive FGS behavior for certain types (camera, mic, media, location)."
+ bug: "295545575"
+}
diff --git a/services/core/java/com/android/server/audio/AdiDeviceState.java b/services/core/java/com/android/server/audio/AdiDeviceState.java
index ba43c8d..292fc14 100644
--- a/services/core/java/com/android/server/audio/AdiDeviceState.java
+++ b/services/core/java/com/android/server/audio/AdiDeviceState.java
@@ -188,7 +188,7 @@
* {@link AdiDeviceState#toPersistableString()}.
*/
public static int getPeristedMaxSize() {
- return 36; /* (mDeviceType)2 + (mDeviceAddresss)17 + (mInternalDeviceType)9 + (mSAEnabled)1
+ return 36; /* (mDeviceType)2 + (mDeviceAddress)17 + (mInternalDeviceType)9 + (mSAEnabled)1
+ (mHasHeadTracker)1 + (mHasHeadTrackerEnabled)1
+ (SETTINGS_FIELD_SEPARATOR)5 */
}
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 35260ed..7abd9c7 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -39,10 +39,9 @@
import android.media.ISpatializerHeadTrackingModeCallback;
import android.media.ISpatializerOutputCallback;
import android.media.MediaMetrics;
-import android.media.SpatializationLevel;
-import android.media.SpatializationMode;
import android.media.Spatializer;
-import android.media.SpatializerHeadTrackingMode;
+import android.media.audio.common.HeadTracking;
+import android.media.audio.common.Spatialization;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.text.TextUtils;
@@ -84,22 +83,22 @@
/*package*/ static final SparseIntArray SPAT_MODE_FOR_DEVICE_TYPE = new SparseIntArray(14) {
{
- append(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, SpatializationMode.SPATIALIZER_TRANSAURAL);
- append(AudioDeviceInfo.TYPE_WIRED_HEADSET, SpatializationMode.SPATIALIZER_BINAURAL);
- append(AudioDeviceInfo.TYPE_WIRED_HEADPHONES, SpatializationMode.SPATIALIZER_BINAURAL);
+ append(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, Spatialization.Mode.TRANSAURAL);
+ append(AudioDeviceInfo.TYPE_WIRED_HEADSET, Spatialization.Mode.BINAURAL);
+ append(AudioDeviceInfo.TYPE_WIRED_HEADPHONES, Spatialization.Mode.BINAURAL);
// assumption for A2DP: mostly headsets
- append(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, SpatializationMode.SPATIALIZER_BINAURAL);
- append(AudioDeviceInfo.TYPE_DOCK, SpatializationMode.SPATIALIZER_TRANSAURAL);
- append(AudioDeviceInfo.TYPE_USB_ACCESSORY, SpatializationMode.SPATIALIZER_TRANSAURAL);
- append(AudioDeviceInfo.TYPE_USB_DEVICE, SpatializationMode.SPATIALIZER_TRANSAURAL);
- append(AudioDeviceInfo.TYPE_USB_HEADSET, SpatializationMode.SPATIALIZER_BINAURAL);
- append(AudioDeviceInfo.TYPE_LINE_ANALOG, SpatializationMode.SPATIALIZER_TRANSAURAL);
- append(AudioDeviceInfo.TYPE_LINE_DIGITAL, SpatializationMode.SPATIALIZER_TRANSAURAL);
- append(AudioDeviceInfo.TYPE_AUX_LINE, SpatializationMode.SPATIALIZER_TRANSAURAL);
- append(AudioDeviceInfo.TYPE_BLE_HEADSET, SpatializationMode.SPATIALIZER_BINAURAL);
- append(AudioDeviceInfo.TYPE_BLE_SPEAKER, SpatializationMode.SPATIALIZER_TRANSAURAL);
+ append(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, Spatialization.Mode.BINAURAL);
+ append(AudioDeviceInfo.TYPE_DOCK, Spatialization.Mode.TRANSAURAL);
+ append(AudioDeviceInfo.TYPE_USB_ACCESSORY, Spatialization.Mode.TRANSAURAL);
+ append(AudioDeviceInfo.TYPE_USB_DEVICE, Spatialization.Mode.TRANSAURAL);
+ append(AudioDeviceInfo.TYPE_USB_HEADSET, Spatialization.Mode.BINAURAL);
+ append(AudioDeviceInfo.TYPE_LINE_ANALOG, Spatialization.Mode.TRANSAURAL);
+ append(AudioDeviceInfo.TYPE_LINE_DIGITAL, Spatialization.Mode.TRANSAURAL);
+ append(AudioDeviceInfo.TYPE_AUX_LINE, Spatialization.Mode.TRANSAURAL);
+ append(AudioDeviceInfo.TYPE_BLE_HEADSET, Spatialization.Mode.BINAURAL);
+ append(AudioDeviceInfo.TYPE_BLE_SPEAKER, Spatialization.Mode.TRANSAURAL);
// assumption that BLE broadcast would be mostly consumed on headsets
- append(AudioDeviceInfo.TYPE_BLE_BROADCAST, SpatializationMode.SPATIALIZER_BINAURAL);
+ append(AudioDeviceInfo.TYPE_BLE_BROADCAST, Spatialization.Mode.BINAURAL);
}
};
@@ -226,12 +225,12 @@
ArrayList<Integer> list = new ArrayList<>(0);
for (byte value : values) {
switch (value) {
- case SpatializerHeadTrackingMode.OTHER:
- case SpatializerHeadTrackingMode.DISABLED:
+ case HeadTracking.Mode.OTHER:
+ case HeadTracking.Mode.DISABLED:
// not expected here, skip
break;
- case SpatializerHeadTrackingMode.RELATIVE_WORLD:
- case SpatializerHeadTrackingMode.RELATIVE_SCREEN:
+ case HeadTracking.Mode.RELATIVE_WORLD:
+ case HeadTracking.Mode.RELATIVE_SCREEN:
list.add(headTrackingModeTypeToSpatializerInt(value));
break;
default:
@@ -254,10 +253,10 @@
byte[] spatModes = spat.getSupportedModes();
for (byte mode : spatModes) {
switch (mode) {
- case SpatializationMode.SPATIALIZER_BINAURAL:
+ case Spatialization.Mode.BINAURAL:
mBinauralSupported = true;
break;
- case SpatializationMode.SPATIALIZER_TRANSAURAL:
+ case Spatialization.Mode.TRANSAURAL:
mTransauralSupported = true;
break;
default:
@@ -274,8 +273,8 @@
// initialize list of compatible devices
for (int i = 0; i < SPAT_MODE_FOR_DEVICE_TYPE.size(); i++) {
int mode = SPAT_MODE_FOR_DEVICE_TYPE.valueAt(i);
- if ((mode == (int) SpatializationMode.SPATIALIZER_BINAURAL && mBinauralSupported)
- || (mode == (int) SpatializationMode.SPATIALIZER_TRANSAURAL
+ if ((mode == (int) Spatialization.Mode.BINAURAL && mBinauralSupported)
+ || (mode == (int) Spatialization.Mode.TRANSAURAL
&& mTransauralSupported)) {
mSACapableDeviceTypes.add(SPAT_MODE_FOR_DEVICE_TYPE.keyAt(i));
}
@@ -577,9 +576,9 @@
int spatMode = SPAT_MODE_FOR_DEVICE_TYPE.get(device.getDeviceType(),
Integer.MIN_VALUE);
- device.setSAEnabled(spatMode == SpatializationMode.SPATIALIZER_BINAURAL
+ device.setSAEnabled(spatMode == Spatialization.Mode.BINAURAL
? mBinauralEnabledDefault
- : spatMode == SpatializationMode.SPATIALIZER_TRANSAURAL
+ : spatMode == Spatialization.Mode.TRANSAURAL
? mTransauralEnabledDefault
: false);
device.setHeadTrackerEnabled(mHeadTrackingEnabledDefault);
@@ -629,9 +628,9 @@
if (isBluetoothDevice(internalDeviceType)) return deviceType;
final int spatMode = SPAT_MODE_FOR_DEVICE_TYPE.get(deviceType, Integer.MIN_VALUE);
- if (spatMode == SpatializationMode.SPATIALIZER_TRANSAURAL) {
+ if (spatMode == Spatialization.Mode.TRANSAURAL) {
return AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
- } else if (spatMode == SpatializationMode.SPATIALIZER_BINAURAL) {
+ } else if (spatMode == Spatialization.Mode.BINAURAL) {
return AudioDeviceInfo.TYPE_WIRED_HEADPHONES;
}
return AudioDeviceInfo.TYPE_UNKNOWN;
@@ -690,8 +689,7 @@
// since their physical characteristics are unknown
if (deviceState.getAudioDeviceCategory() == AUDIO_DEVICE_CATEGORY_UNKNOWN
|| deviceState.getAudioDeviceCategory() == AUDIO_DEVICE_CATEGORY_HEADPHONES) {
- available = (spatMode == SpatializationMode.SPATIALIZER_BINAURAL)
- && mBinauralSupported;
+ available = (spatMode == Spatialization.Mode.BINAURAL) && mBinauralSupported;
} else {
available = false;
}
@@ -804,8 +802,8 @@
// not be included.
final byte modeForDevice = (byte) SPAT_MODE_FOR_DEVICE_TYPE.get(ada.getType(),
/*default when type not found*/ -1);
- if ((modeForDevice == SpatializationMode.SPATIALIZER_BINAURAL && mBinauralSupported)
- || (modeForDevice == SpatializationMode.SPATIALIZER_TRANSAURAL
+ if ((modeForDevice == Spatialization.Mode.BINAURAL && mBinauralSupported)
+ || (modeForDevice == Spatialization.Mode.TRANSAURAL
&& mTransauralSupported)) {
return true;
}
@@ -1479,7 +1477,7 @@
}
synchronized void onInitSensors() {
- final boolean init = mFeatureEnabled && (mSpatLevel != SpatializationLevel.NONE);
+ final boolean init = mFeatureEnabled && (mSpatLevel != Spatialization.Level.NONE);
final String action = init ? "initializing" : "releasing";
if (mSpat == null) {
logloge("not " + action + " sensors, null spatializer");
@@ -1545,13 +1543,13 @@
// SDK <-> AIDL converters
private static int headTrackingModeTypeToSpatializerInt(byte mode) {
switch (mode) {
- case SpatializerHeadTrackingMode.OTHER:
+ case HeadTracking.Mode.OTHER:
return Spatializer.HEAD_TRACKING_MODE_OTHER;
- case SpatializerHeadTrackingMode.DISABLED:
+ case HeadTracking.Mode.DISABLED:
return Spatializer.HEAD_TRACKING_MODE_DISABLED;
- case SpatializerHeadTrackingMode.RELATIVE_WORLD:
+ case HeadTracking.Mode.RELATIVE_WORLD:
return Spatializer.HEAD_TRACKING_MODE_RELATIVE_WORLD;
- case SpatializerHeadTrackingMode.RELATIVE_SCREEN:
+ case HeadTracking.Mode.RELATIVE_SCREEN:
return Spatializer.HEAD_TRACKING_MODE_RELATIVE_DEVICE;
default:
throw (new IllegalArgumentException("Unexpected head tracking mode:" + mode));
@@ -1561,13 +1559,13 @@
private static byte spatializerIntToHeadTrackingModeType(int sdkMode) {
switch (sdkMode) {
case Spatializer.HEAD_TRACKING_MODE_OTHER:
- return SpatializerHeadTrackingMode.OTHER;
+ return HeadTracking.Mode.OTHER;
case Spatializer.HEAD_TRACKING_MODE_DISABLED:
- return SpatializerHeadTrackingMode.DISABLED;
+ return HeadTracking.Mode.DISABLED;
case Spatializer.HEAD_TRACKING_MODE_RELATIVE_WORLD:
- return SpatializerHeadTrackingMode.RELATIVE_WORLD;
+ return HeadTracking.Mode.RELATIVE_WORLD;
case Spatializer.HEAD_TRACKING_MODE_RELATIVE_DEVICE:
- return SpatializerHeadTrackingMode.RELATIVE_SCREEN;
+ return HeadTracking.Mode.RELATIVE_SCREEN;
default:
throw (new IllegalArgumentException("Unexpected head tracking mode:" + sdkMode));
}
@@ -1575,11 +1573,11 @@
private static int spatializationLevelToSpatializerInt(byte level) {
switch (level) {
- case SpatializationLevel.NONE:
+ case Spatialization.Level.NONE:
return Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
- case SpatializationLevel.SPATIALIZER_MULTICHANNEL:
+ case Spatialization.Level.MULTICHANNEL:
return Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL;
- case SpatializationLevel.SPATIALIZER_MCHAN_BED_PLUS_OBJECTS:
+ case Spatialization.Level.BED_PLUS_OBJECTS:
return Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_MCHAN_BED_PLUS_OBJECTS;
default:
throw (new IllegalArgumentException("Unexpected spatializer level:" + level));
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 3d347be..f9bc8dc 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -53,6 +53,8 @@
import android.hardware.usb.UsbManager;
import android.media.AudioManager;
import android.nfc.INfcAdapter;
+import android.nfc.NfcAdapter;
+import android.nfc.NfcManager;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerExecutor;
@@ -163,10 +165,6 @@
* SCALER_ROTATE_AND_CROP_NONE -> Always return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE
*/
- // Flags arguments to NFC adapter to enable/disable NFC
- public static final int DISABLE_POLLING_FLAGS = 0x1000;
- public static final int ENABLE_POLLING_FLAGS = 0x0000;
-
// Handler message codes
private static final int MSG_SWITCH_USER = 1;
private static final int MSG_NOTIFY_DEVICE_STATE = 2;
@@ -216,7 +214,6 @@
private final List<CameraUsageEvent> mCameraUsageHistory = new ArrayList<>();
private static final String NFC_NOTIFICATION_PROP = "ro.camera.notify_nfc";
- private static final String NFC_SERVICE_BINDER_NAME = "nfc";
private static final IBinder nfcInterfaceToken = new Binder();
private final boolean mNotifyNfc;
@@ -1274,8 +1271,13 @@
}
}
- private void notifyNfcService(boolean enablePolling) {
-
+ // TODO(b/303286040): Remove the raw INfcAdapter usage once |ENABLE_NFC_MAINLINE_FLAG| is
+ // rolled out.
+ private static final String NFC_SERVICE_BINDER_NAME = "nfc";
+ // Flags arguments to NFC adapter to enable/disable NFC
+ public static final int DISABLE_POLLING_FLAGS = 0x1000;
+ public static final int ENABLE_POLLING_FLAGS = 0x0000;
+ private void setNfcReaderModeUsingINfcAdapter(boolean enablePolling) {
IBinder nfcServiceBinder = getBinderService(NFC_SERVICE_BINDER_NAME);
if (nfcServiceBinder == null) {
Slog.w(TAG, "Could not connect to NFC service to notify it of camera state");
@@ -1291,6 +1293,25 @@
}
}
+ private void notifyNfcService(boolean enablePolling) {
+ if (android.nfc.Flags.enableNfcMainline()) {
+ NfcManager nfcManager = mContext.getSystemService(NfcManager.class);
+ if (nfcManager == null) {
+ Slog.w(TAG, "Could not connect to NFC service to notify it of camera state");
+ return;
+ }
+ NfcAdapter nfcAdapter = nfcManager.getDefaultAdapter();
+ if (nfcAdapter == null) {
+ Slog.w(TAG, "Could not connect to NFC service to notify it of camera state");
+ return;
+ }
+ if (DEBUG) Slog.v(TAG, "Setting NFC reader mode. enablePolling: " + enablePolling);
+ nfcAdapter.setReaderMode(enablePolling);
+ } else {
+ setNfcReaderModeUsingINfcAdapter(enablePolling);
+ }
+ }
+
private static int[] toArray(Collection<Integer> c) {
int len = c.size();
int[] ret = new int[len];
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index a788968..e5f01df 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2307,8 +2307,10 @@
@GuardedBy("mSyncRoot")
private boolean hdrConversionIntroducesLatencyLocked() {
+ HdrConversionMode mode = getHdrConversionModeSettingInternal();
final int preferredHdrOutputType =
- getHdrConversionModeSettingInternal().getPreferredHdrOutputType();
+ mode.getConversionMode() == HdrConversionMode.HDR_CONVERSION_SYSTEM
+ ? mSystemPreferredHdrOutputType : mode.getPreferredHdrOutputType();
if (preferredHdrOutputType != Display.HdrCapabilities.HDR_TYPE_INVALID) {
int[] hdrTypesWithLatency = mInjector.getHdrOutputTypesWithLatency();
return ArrayUtils.contains(hdrTypesWithLatency, preferredHdrOutputType);
@@ -2589,16 +2591,14 @@
// TODO(b/202378408) set minimal post-processing only if it's supported once we have a
// separate API for disabling on-device processing.
boolean mppRequest = isMinimalPostProcessingAllowed() && preferMinimalPostProcessing;
- boolean disableHdrConversionForLatency = false;
+ // If HDR conversion introduces latency, disable that in case minimal
+ // post-processing is requested
+ boolean disableHdrConversionForLatency =
+ mppRequest ? hdrConversionIntroducesLatencyLocked() : false;
if (display.getRequestedMinimalPostProcessingLocked() != mppRequest) {
display.setRequestedMinimalPostProcessingLocked(mppRequest);
shouldScheduleTraversal = true;
- // If HDR conversion introduces latency, disable that in case minimal
- // post-processing is requested
- if (mppRequest) {
- disableHdrConversionForLatency = hdrConversionIntroducesLatencyLocked();
- }
}
if (shouldScheduleTraversal) {
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index d97c8e7..8c39d7d 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -16,6 +16,13 @@
package com.android.server.display;
+import static android.view.Display.TYPE_EXTERNAL;
+import static android.view.Display.TYPE_INTERNAL;
+import static android.view.Display.TYPE_OVERLAY;
+import static android.view.Display.TYPE_UNKNOWN;
+import static android.view.Display.TYPE_VIRTUAL;
+import static android.view.Display.TYPE_WIFI;
+
import android.content.Context;
import android.content.Intent;
import android.hardware.display.DisplayManager;
@@ -26,7 +33,10 @@
import com.android.server.display.feature.DisplayManagerFlags;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
class DisplayManagerShellCommand extends ShellCommand {
private static final String TAG = "DisplayManagerShellCommand";
@@ -153,9 +163,12 @@
pw.println(" Sets the user disabled HDR types as TYPES");
pw.println(" get-user-disabled-hdr-types");
pw.println(" Returns the user disabled HDR types");
- pw.println(" get-displays [CATEGORY]");
+ pw.println(" get-displays [-c|--category CATEGORY] [-i|--ids-only] [-t|--type TYPE]");
+ pw.println(" [CATEGORY]");
pw.println(" Returns the current displays. Can specify string category among");
pw.println(" DisplayManager.DISPLAY_CATEGORY_*; must use the actual string value.");
+ pw.println(" Can choose to print only the ids of the displays. " + "Can filter by");
+ pw.println(" display types. For example, '--type external'");
pw.println(" dock");
pw.println(" Sets brightness to docked + idle screen brightness mode");
pw.println(" undock");
@@ -171,17 +184,94 @@
}
private int getDisplays() {
- String category = getNextArg();
+ String opt = "", requestedType, category = null;
+ PrintWriter out = getOutPrintWriter();
+
+ List<Integer> displayTypeList = new ArrayList<>();
+ boolean showIdsOnly = false, filterByType = false;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-i":
+ case "--ids-only":
+ showIdsOnly = true;
+ break;
+ case "-t":
+ case "--type":
+ requestedType = getNextArgRequired();
+ int displayType = getType(requestedType, out);
+ if (displayType == -1) {
+ return 1;
+ }
+ displayTypeList.add(displayType);
+ filterByType = true;
+ break;
+ case "-c":
+ case "--category":
+ if (category != null) {
+ out.println("Error: the category has been specified more than one time. "
+ + "Please select only one category.");
+ return 1;
+ }
+ category = getNextArgRequired();
+ break;
+ case "":
+ break;
+ default:
+ out.println("Error: unknown option '" + opt + "'");
+ return 1;
+ }
+ }
+
+ String lastCategoryArgument = getNextArg();
+ if (lastCategoryArgument != null) {
+ if (category != null) {
+ out.println("Error: the category has been specified both with the -c option and "
+ + "the positional argument. Please select only one category.");
+ return 1;
+ }
+ category = lastCategoryArgument;
+ }
+
DisplayManager dm = mService.getContext().getSystemService(DisplayManager.class);
Display[] displays = dm.getDisplays(category);
- PrintWriter out = getOutPrintWriter();
- out.println("Displays:");
+
+ if (filterByType) {
+ displays = Arrays.stream(displays).filter(d -> displayTypeList.contains(d.getType()))
+ .toArray(Display[]::new);
+ }
+
+ if (!showIdsOnly) {
+ out.println("Displays:");
+ }
for (int i = 0; i < displays.length; i++) {
- out.println(" " + displays[i]);
+ out.println((showIdsOnly ? displays[i].getDisplayId() : displays[i]));
}
return 0;
}
+ private int getType(String type, PrintWriter out) {
+ type = type.toUpperCase(Locale.ENGLISH);
+ switch (type) {
+ case "UNKNOWN":
+ return TYPE_UNKNOWN;
+ case "INTERNAL":
+ return TYPE_INTERNAL;
+ case "EXTERNAL":
+ return TYPE_EXTERNAL;
+ case "WIFI":
+ return TYPE_WIFI;
+ case "OVERLAY":
+ return TYPE_OVERLAY;
+ case "VIRTUAL":
+ return TYPE_VIRTUAL;
+ default:
+ out.println("Error: argument for display type should be "
+ + "one of 'UNKNOWN', 'INTERNAL', 'EXTERNAL', 'WIFI', 'OVERLAY', 'VIRTUAL', "
+ + "but got '" + type + "' instead.");
+ return -1;
+ }
+ }
+
private int showNotification() {
final String notificationType = getNextArg();
if (notificationType == null) {
diff --git a/services/core/java/com/android/server/media/AudioAttributesUtils.java b/services/core/java/com/android/server/media/AudioAttributesUtils.java
index 5d5d59b..8cb334d 100644
--- a/services/core/java/com/android/server/media/AudioAttributesUtils.java
+++ b/services/core/java/com/android/server/media/AudioAttributesUtils.java
@@ -23,6 +23,8 @@
import android.media.AudioDeviceInfo;
import android.media.MediaRoute2Info;
+import com.android.media.flags.Flags;
+
/* package */ final class AudioAttributesUtils {
/* package */ static final AudioAttributes ATTRIBUTES_MEDIA = new AudioAttributes.Builder()
@@ -36,6 +38,14 @@
@MediaRoute2Info.Type
/* package */ static int mapToMediaRouteType(
@NonNull AudioDeviceAttributes audioDeviceAttributes) {
+ if (Flags.enableAudioPoliciesDeviceAndBluetoothController()) {
+ switch (audioDeviceAttributes.getType()) {
+ case AudioDeviceInfo.TYPE_HDMI_ARC:
+ return MediaRoute2Info.TYPE_HDMI_ARC;
+ case AudioDeviceInfo.TYPE_HDMI_EARC:
+ return MediaRoute2Info.TYPE_HDMI_EARC;
+ }
+ }
switch (audioDeviceAttributes.getType()) {
case AudioDeviceInfo.TYPE_BUILTIN_EARPIECE:
case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER:
@@ -64,7 +74,6 @@
}
}
-
/* package */ static boolean isDeviceOutputAttributes(
@Nullable AudioDeviceAttributes audioDeviceAttributes) {
if (audioDeviceAttributes == null) {
diff --git a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
index 33190ad..360a6a7 100644
--- a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
+++ b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
@@ -22,6 +22,8 @@
import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
import static android.media.MediaRoute2Info.TYPE_DOCK;
import static android.media.MediaRoute2Info.TYPE_HDMI;
+import static android.media.MediaRoute2Info.TYPE_HDMI_ARC;
+import static android.media.MediaRoute2Info.TYPE_HDMI_EARC;
import static android.media.MediaRoute2Info.TYPE_USB_DEVICE;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
@@ -160,7 +162,6 @@
@NonNull
private MediaRoute2Info createRouteFromAudioInfo(@MediaRoute2Info.Type int type) {
int name = R.string.default_audio_route_name;
-
switch (type) {
case TYPE_WIRED_HEADPHONES:
case TYPE_WIRED_HEADSET:
@@ -170,6 +171,8 @@
name = R.string.default_audio_route_name_dock_speakers;
break;
case TYPE_HDMI:
+ case TYPE_HDMI_ARC:
+ case TYPE_HDMI_EARC:
name = R.string.default_audio_route_name_external_device;
break;
case TYPE_USB_DEVICE:
@@ -211,6 +214,8 @@
case TYPE_WIRED_HEADSET:
case TYPE_DOCK:
case TYPE_HDMI:
+ case TYPE_HDMI_ARC:
+ case TYPE_HDMI_EARC:
case TYPE_USB_DEVICE:
return true;
default:
diff --git a/services/core/java/com/android/server/media/projection/FrameworkStatsLogWrapper.java b/services/core/java/com/android/server/media/projection/FrameworkStatsLogWrapper.java
new file mode 100644
index 0000000..5bad067
--- /dev/null
+++ b/services/core/java/com/android/server/media/projection/FrameworkStatsLogWrapper.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media.projection;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+/** Wrapper around {@link FrameworkStatsLog} */
+public class FrameworkStatsLogWrapper {
+
+ /** Wrapper around {@link FrameworkStatsLog#write}. */
+ public void write(
+ int code,
+ int sessionId,
+ int state,
+ int previousState,
+ int hostUid,
+ int targetUid,
+ int timeSinceLastActive,
+ int creationSource) {
+ FrameworkStatsLog.write(
+ code,
+ sessionId,
+ state,
+ previousState,
+ hostUid,
+ targetUid,
+ timeSinceLastActive,
+ creationSource);
+ }
+}
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 8cbc368..ce35a61 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -162,7 +162,7 @@
mWmInternal = LocalServices.getService(WindowManagerInternal.class);
mMediaRouter = (MediaRouter) mContext.getSystemService(Context.MEDIA_ROUTER_SERVICE);
mMediaRouterCallback = new MediaRouterCallback();
- mMediaProjectionMetricsLogger = injector.mediaProjectionMetricsLogger();
+ mMediaProjectionMetricsLogger = injector.mediaProjectionMetricsLogger(context);
Watchdog.getInstance().addMonitor(this);
}
@@ -197,8 +197,8 @@
return Looper.getMainLooper();
}
- MediaProjectionMetricsLogger mediaProjectionMetricsLogger() {
- return MediaProjectionMetricsLogger.getInstance();
+ MediaProjectionMetricsLogger mediaProjectionMetricsLogger(Context context) {
+ return MediaProjectionMetricsLogger.getInstance(context);
}
}
@@ -293,6 +293,12 @@
private void stopProjectionLocked(final MediaProjection projection) {
Slog.d(TAG, "Content Recording: Stopped active MediaProjection and "
+ "dispatching stop to callbacks");
+ ContentRecordingSession session = projection.mSession;
+ int targetUid =
+ session != null
+ ? session.getTargetUid()
+ : ContentRecordingSession.TARGET_UID_UNKNOWN;
+ mMediaProjectionMetricsLogger.logStopped(projection.uid, targetUid);
mProjectionToken = null;
mProjectionGrant = null;
dispatchStop(projection);
@@ -452,6 +458,11 @@
.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
}
+ @VisibleForTesting
+ void notifyPermissionRequestInitiated(int hostUid, int sessionCreationSource) {
+ mMediaProjectionMetricsLogger.logInitiated(hostUid, sessionCreationSource);
+ }
+
/**
* Handles result of dialog shown from
* {@link BinderService#buildReviewGrantedConsentIntentLocked()}.
@@ -842,6 +853,19 @@
}
@Override // Binder call
+ @EnforcePermission(MANAGE_MEDIA_PROJECTION)
+ public void notifyPermissionRequestInitiated(int hostUid, int sessionCreationSource) {
+ notifyPermissionRequestInitiated_enforcePermission();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ MediaProjectionManagerService.this.notifyPermissionRequestInitiated(
+ hostUid, sessionCreationSource);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
final long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java b/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java
index f18ecad..c8b932a 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java
@@ -16,35 +16,127 @@
package com.android.server.media.projection;
+import android.content.Context;
import com.android.internal.util.FrameworkStatsLog;
-/**
- * Class for emitting logs describing a MediaProjection session.
- */
+import java.time.Duration;
+
+/** Class for emitting logs describing a MediaProjection session. */
public class MediaProjectionMetricsLogger {
+ private static final int TARGET_UID_UNKNOWN = -2;
+ private static final int TIME_SINCE_LAST_ACTIVE_UNKNOWN = -1;
+
private static MediaProjectionMetricsLogger sSingleton = null;
- public static MediaProjectionMetricsLogger getInstance() {
+ private final FrameworkStatsLogWrapper mFrameworkStatsLogWrapper;
+ private final MediaProjectionSessionIdGenerator mSessionIdGenerator;
+ private final MediaProjectionTimestampStore mTimestampStore;
+
+ private int mPreviousState =
+ FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN;
+
+ MediaProjectionMetricsLogger(
+ FrameworkStatsLogWrapper frameworkStatsLogWrapper,
+ MediaProjectionSessionIdGenerator sessionIdGenerator,
+ MediaProjectionTimestampStore timestampStore) {
+ mFrameworkStatsLogWrapper = frameworkStatsLogWrapper;
+ mSessionIdGenerator = sessionIdGenerator;
+ mTimestampStore = timestampStore;
+ }
+
+ /** Returns a singleton instance of {@link MediaProjectionMetricsLogger}. */
+ public static MediaProjectionMetricsLogger getInstance(Context context) {
if (sSingleton == null) {
- sSingleton = new MediaProjectionMetricsLogger();
+ sSingleton =
+ new MediaProjectionMetricsLogger(
+ new FrameworkStatsLogWrapper(),
+ MediaProjectionSessionIdGenerator.getInstance(context),
+ MediaProjectionTimestampStore.getInstance(context));
}
return sSingleton;
}
- void notifyProjectionStateChange(int hostUid, int state, int sessionCreationSource) {
+ /**
+ * Logs that the media projection session was initiated by the app requesting the user's consent
+ * to capture. Should be sent even if the permission dialog is not shown.
+ *
+ * @param hostUid UID of the package that initiates MediaProjection.
+ * @param sessionCreationSource Where this session started. One of:
+ * <ul>
+ * <li>{@link
+ * FrameworkStatsLog#MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_APP}
+ * <li>{@link
+ * FrameworkStatsLog#MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_CAST}
+ * <li>{@link
+ * FrameworkStatsLog#MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_SYSTEM_UI_SCREEN_RECORDER}
+ * <li>{@link
+ * FrameworkStatsLog#MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN}
+ * </ul>
+ */
+ public void logInitiated(int hostUid, int sessionCreationSource) {
+ Duration durationSinceLastActiveSession = mTimestampStore.timeSinceLastActiveSession();
+ int timeSinceLastActiveInSeconds =
+ durationSinceLastActiveSession == null
+ ? TIME_SINCE_LAST_ACTIVE_UNKNOWN
+ : (int) durationSinceLastActiveSession.toSeconds();
+ write(
+ mSessionIdGenerator.createAndGetNewSessionId(),
+ FrameworkStatsLog
+ .MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED,
+ hostUid,
+ TARGET_UID_UNKNOWN,
+ timeSinceLastActiveInSeconds,
+ sessionCreationSource);
+ }
+
+ /** Logs that the capturing stopped, either normally or because of error. */
+ public void logStopped(int hostUid, int targetUid) {
+ write(
+ mSessionIdGenerator.getCurrentSessionId(),
+ FrameworkStatsLog
+ .MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_STOPPED,
+ hostUid,
+ targetUid,
+ TIME_SINCE_LAST_ACTIVE_UNKNOWN,
+ FrameworkStatsLog
+ .MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN);
+ mTimestampStore.registerActiveSessionEnded();
+ }
+
+ public void notifyProjectionStateChange(int hostUid, int state, int sessionCreationSource) {
write(hostUid, state, sessionCreationSource);
}
private void write(int hostUid, int state, int sessionCreationSource) {
- FrameworkStatsLog.write(
+ mFrameworkStatsLogWrapper.write(
/* code */ FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED,
/* session_id */ 123,
/* state */ state,
- /* previous_state */ FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN,
+ /* previous_state */ FrameworkStatsLog
+ .MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN,
/* host_uid */ hostUid,
/* target_uid */ -1,
/* time_since_last_active */ 0,
/* creation_source */ sessionCreationSource);
}
+
+ private void write(
+ int sessionId,
+ int state,
+ int hostUid,
+ int targetUid,
+ int timeSinceLastActive,
+ int creationSource) {
+ mFrameworkStatsLogWrapper.write(
+ /* code */ FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED,
+ sessionId,
+ state,
+ mPreviousState,
+ hostUid,
+ targetUid,
+ timeSinceLastActive,
+ creationSource);
+ mPreviousState = state;
+ }
}
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionSessionIdGenerator.java b/services/core/java/com/android/server/media/projection/MediaProjectionSessionIdGenerator.java
index ff70cb3..244de0b 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionSessionIdGenerator.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionSessionIdGenerator.java
@@ -47,8 +47,11 @@
if (sInstance == null) {
File preferencesFile =
new File(Environment.getDataSystemDirectory(), PREFERENCES_FILE_NAME);
+ // Needed as this class is instantiated before the device is unlocked.
+ Context directBootContext = context.createDeviceProtectedStorageContext();
SharedPreferences preferences =
- context.getSharedPreferences(preferencesFile, Context.MODE_PRIVATE);
+ directBootContext.getSharedPreferences(
+ preferencesFile, Context.MODE_PRIVATE);
sInstance = new MediaProjectionSessionIdGenerator(preferences);
}
return sInstance;
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionTimestampStore.java b/services/core/java/com/android/server/media/projection/MediaProjectionTimestampStore.java
index 4026d0c..bfec58c 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionTimestampStore.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionTimestampStore.java
@@ -59,8 +59,11 @@
if (sInstance == null) {
File preferencesFile =
new File(Environment.getDataSystemDirectory(), PREFERENCES_FILE_NAME);
+ // Needed as this class is instantiated before the device is unlocked.
+ Context directBootContext = context.createDeviceProtectedStorageContext();
SharedPreferences preferences =
- context.getSharedPreferences(preferencesFile, Context.MODE_PRIVATE);
+ directBootContext.getSharedPreferences(
+ preferencesFile, Context.MODE_PRIVATE);
sInstance = new MediaProjectionTimestampStore(preferences, InstantSource.system());
}
return sInstance;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index b4d36db..7ca5699 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -71,6 +71,7 @@
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
+import static android.os.Flags.allowPrivateProfile;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
import static android.os.PowerWhitelistManager.REASON_NOTIFICATION_SERVICE;
@@ -289,7 +290,6 @@
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
-import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.logging.MetricsLogger;
@@ -1179,7 +1179,7 @@
@Override
public void onSetDisabled(int status) {
synchronized (mNotificationLock) {
- if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ if (Flags.refactorAttentionHelper()) {
mAttentionHelper.updateDisableNotificationEffectsLocked(status);
} else {
mDisableNotificationEffects =
@@ -1325,7 +1325,7 @@
public void clearEffects() {
synchronized (mNotificationLock) {
if (DBG) Slog.d(TAG, "clearEffects");
- if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ if (Flags.refactorAttentionHelper()) {
mAttentionHelper.clearAttentionEffects();
} else {
clearSoundLocked();
@@ -1554,8 +1554,7 @@
int changedFlags = data.getFlags() ^ flags;
if ((changedFlags & FLAG_SUPPRESS_NOTIFICATION) != 0) {
// Suppress notification flag changed, clear any effects
- if (mFlagResolver.isEnabled(
- NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ if (Flags.refactorAttentionHelper()) {
mAttentionHelper.clearEffectsLocked(key);
} else {
clearEffectsLocked(key);
@@ -1904,7 +1903,7 @@
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- if (!mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ if (!Flags.refactorAttentionHelper()) {
if (action.equals(Intent.ACTION_SCREEN_ON)) {
// Keep track of screen on/off state, but do not turn off the notification light
// until user passes through the lock screen or views the notification.
@@ -1931,7 +1930,8 @@
cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, userHandle,
REASON_USER_STOPPED);
}
- } else if (action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)) {
+ } else if (
+ isProfileUnavailable(action)) {
int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
if (userHandle >= 0 && !mDpm.isKeepProfilesRunningEnabled()) {
cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, userHandle,
@@ -1982,6 +1982,12 @@
}
}
}
+
+ private boolean isProfileUnavailable(String action) {
+ return allowPrivateProfile() ?
+ action.equals(Intent.ACTION_PROFILE_UNAVAILABLE) :
+ action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
+ }
};
private final class SettingsObserver extends ContentObserver {
@@ -2011,7 +2017,7 @@
ContentResolver resolver = getContext().getContentResolver();
resolver.registerContentObserver(NOTIFICATION_BADGING_URI,
false, this, UserHandle.USER_ALL);
- if (!mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ if (!Flags.refactorAttentionHelper()) {
resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
false, this, UserHandle.USER_ALL);
}
@@ -2037,7 +2043,7 @@
public void update(Uri uri) {
ContentResolver resolver = getContext().getContentResolver();
- if (!mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ if (!Flags.refactorAttentionHelper()) {
if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
boolean pulseEnabled = Settings.System.getIntForUser(resolver,
Settings.System.NOTIFICATION_LIGHT_PULSE, 0, UserHandle.USER_CURRENT)
@@ -2530,7 +2536,7 @@
mToastRateLimiter = toastRateLimiter;
- if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ if (Flags.refactorAttentionHelper()) {
mAttentionHelper = new NotificationAttentionHelper(getContext(), lightsManager,
mAccessibilityManager, mPackageManagerClient, userManager, usageStats,
mNotificationManagerPrivate, mZenModeHelper, flagResolver);
@@ -2540,7 +2546,7 @@
// If this is called within a test, make sure to unregister the intent receivers by
// calling onDestroy()
IntentFilter filter = new IntentFilter();
- if (!mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ if (!Flags.refactorAttentionHelper()) {
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
@@ -2552,6 +2558,9 @@
filter.addAction(Intent.ACTION_USER_REMOVED);
filter.addAction(Intent.ACTION_USER_UNLOCKED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
+ if (allowPrivateProfile()){
+ filter.addAction(Intent.ACTION_PROFILE_UNAVAILABLE);
+ }
getContext().registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter, null, null);
IntentFilter pkgFilter = new IntentFilter();
@@ -2865,7 +2874,7 @@
}
registerNotificationPreferencesPullers();
new LockPatternUtils(getContext()).registerStrongAuthTracker(mStrongAuthTracker);
- if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ if (Flags.refactorAttentionHelper()) {
mAttentionHelper.onSystemReady();
}
} else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
@@ -6490,7 +6499,7 @@
pw.println(" mMaxPackageEnqueueRate=" + mMaxPackageEnqueueRate);
pw.println(" hideSilentStatusBar="
+ mPreferencesHelper.shouldHideSilentStatusIcons());
- if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ if (Flags.refactorAttentionHelper()) {
mAttentionHelper.dump(pw, " ", filter);
}
}
@@ -7756,7 +7765,7 @@
boolean wasPosted = removeFromNotificationListsLocked(r);
cancelNotificationLocked(r, false, REASON_SNOOZED, wasPosted, null,
SystemClock.elapsedRealtime());
- if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ if (Flags.refactorAttentionHelper()) {
mAttentionHelper.updateLightsLocked();
} else {
updateLightsLocked();
@@ -7889,7 +7898,7 @@
cancelGroupChildrenLocked(r, mCallingUid, mCallingPid, listenerName,
mSendDelete, childrenFlagChecker, mReason,
mCancellationElapsedTimeMs);
- if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ if (Flags.refactorAttentionHelper()) {
mAttentionHelper.updateLightsLocked();
} else {
updateLightsLocked();
@@ -8186,7 +8195,7 @@
int buzzBeepBlinkLoggingCode = 0;
if (!r.isHidden()) {
- if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ if (Flags.refactorAttentionHelper()) {
buzzBeepBlinkLoggingCode = mAttentionHelper.buzzBeepBlinkLocked(r,
new NotificationAttentionHelper.Signals(
mUserProfiles.isCurrentProfile(r.getUserId()),
@@ -9173,7 +9182,7 @@
|| interruptiveChanged;
if (interceptBefore && !record.isIntercepted()
&& record.isNewEnoughForAlerting(System.currentTimeMillis())) {
- if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ if (Flags.refactorAttentionHelper()) {
mAttentionHelper.buzzBeepBlinkLocked(record,
new NotificationAttentionHelper.Signals(
mUserProfiles.isCurrentProfile(record.getUserId()), mListenerHints));
@@ -9553,7 +9562,7 @@
});
}
- if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ if (Flags.refactorAttentionHelper()) {
mAttentionHelper.clearEffectsLocked(canceledKey);
} else {
// sound
@@ -9917,7 +9926,7 @@
cancellationElapsedTimeMs);
}
}
- if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ if (Flags.refactorAttentionHelper()) {
mAttentionHelper.updateLightsLocked();
} else {
updateLightsLocked();
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 7556f27..2967818 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -113,7 +113,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.ArchivedPackageParcel;
import android.content.pm.DataLoaderType;
-import android.content.pm.Flags;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageInstaller;
@@ -162,7 +161,6 @@
import com.android.server.EventLogTags;
import com.android.server.LocalManagerRegistry;
import com.android.server.SystemConfig;
-import com.android.server.art.model.ArtFlags;
import com.android.server.art.model.DexoptParams;
import com.android.server.art.model.DexoptResult;
import com.android.server.pm.Installer.LegacyDexoptDisabledException;
@@ -2564,15 +2562,8 @@
LocalManagerRegistry.getManager(PackageManagerLocal.class);
try (PackageManagerLocal.FilteredSnapshot snapshot =
packageManagerLocal.withFilteredSnapshot()) {
- boolean ignoreDexoptProfile =
- (installRequest.getInstallFlags()
- & PackageManager.INSTALL_IGNORE_DEXOPT_PROFILE)
- != 0;
- /*@DexoptFlags*/ int extraFlags =
- ignoreDexoptProfile && Flags.useArtServiceV2()
- ? ArtFlags.FLAG_IGNORE_PROFILE
- : 0;
- DexoptParams params = dexoptOptions.convertToDexoptParams(extraFlags);
+ DexoptParams params =
+ dexoptOptions.convertToDexoptParams(0 /* extraFlags */);
DexoptResult dexOptResult = DexOptHelper.getArtManagerLocal().dexoptPackage(
snapshot, packageName, params);
installRequest.onDexoptFinished(dexOptResult);
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index d4abad8..7264e2e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -3703,9 +3703,6 @@
sessionParams.installFlags |=
PackageManager.INSTALL_BYPASS_LOW_TARGET_SDK_BLOCK;
break;
- case "--ignore-dexopt-profile":
- sessionParams.installFlags |= PackageManager.INSTALL_IGNORE_DEXOPT_PROFILE;
- break;
default:
throw new IllegalArgumentException("Unknown option " + opt);
}
@@ -4802,7 +4799,7 @@
pw.println(" [--enable-rollback]");
pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]");
pw.println(" [--apex] [--non-staged] [--force-non-staged]");
- pw.println(" [--staged-ready-timeout TIMEOUT] [--ignore-dexopt-profile]");
+ pw.println(" [--staged-ready-timeout TIMEOUT]");
pw.println(" [PATH [SPLIT...]|-]");
pw.println(" Install an application. Must provide the apk data to install, either as");
pw.println(" file path(s) or '-' to read from stdin. Options are:");
@@ -4842,13 +4839,6 @@
pw.println(" milliseconds for pre-reboot verification to complete when");
pw.println(" performing staged install. This flag is used to alter the waiting");
pw.println(" time. You can skip the waiting time by specifying a TIMEOUT of '0'");
- pw.println(" --ignore-dexopt-profile: If set, all profiles are ignored by dexopt");
- pw.println(" during the installation, including the profile in the DM file and");
- pw.println(" the profile embedded in the APK file. If an invalid profile is");
- pw.println(" provided during installation, no warning will be reported by `adb");
- pw.println(" install`.");
- pw.println(" This option does not affect later dexopt operations (e.g.,");
- pw.println(" background dexopt and manual `pm compile` invocations).");
pw.println("");
pw.println(" install-existing [--user USER_ID|all|current]");
pw.println(" [--instant] [--full] [--wait] [--restrict-permissions] PACKAGE");
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 3a6664a..7c0fc99 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -139,6 +139,7 @@
import android.os.FactoryTest;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.PowerManager.WakeReason;
@@ -715,6 +716,11 @@
private static final int MSG_LOG_KEYBOARD_SYSTEM_EVENT = 26;
private class PolicyHandler extends Handler {
+
+ private PolicyHandler(Looper looper) {
+ super(looper);
+ }
+
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -2166,10 +2172,12 @@
static class Injector {
private final Context mContext;
private final WindowManagerFuncs mWindowManagerFuncs;
+ private final Looper mLooper;
- Injector(Context context, WindowManagerFuncs funcs) {
+ Injector(Context context, WindowManagerFuncs funcs, Looper looper) {
mContext = context;
mWindowManagerFuncs = funcs;
+ mLooper = looper;
}
Context getContext() {
@@ -2180,6 +2188,10 @@
return mWindowManagerFuncs;
}
+ Looper getLooper() {
+ return mLooper;
+ }
+
AccessibilityShortcutController getAccessibilityShortcutController(
Context context, Handler handler, int initialUserId) {
return new AccessibilityShortcutController(context, handler, initialUserId);
@@ -2208,7 +2220,7 @@
/** {@inheritDoc} */
@Override
public void init(Context context, WindowManagerFuncs funcs) {
- init(new Injector(context, funcs));
+ init(new Injector(context, funcs, Looper.myLooper()));
}
@VisibleForTesting
@@ -2284,7 +2296,7 @@
mContext, minHorizontal, maxHorizontal, minVertical, maxVertical, maxRadius);
}
- mHandler = new PolicyHandler();
+ mHandler = new PolicyHandler(injector.getLooper());
mWakeGestureListener = new MyWakeGestureListener(mContext, mHandler);
mSettingsObserver = new SettingsObserver(mHandler);
mSettingsObserver.observe();
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java b/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java
index c54e3bd..5f8bbe5 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java
@@ -29,7 +29,6 @@
import android.annotation.Nullable;
import android.app.WallpaperColors;
-import android.app.WallpaperManager;
import android.app.WallpaperManager.SetWallpaperFlags;
import android.app.backup.WallpaperBackupHelper;
import android.content.ComponentName;
@@ -38,7 +37,6 @@
import android.content.res.Resources;
import android.graphics.Color;
import android.os.FileUtils;
-import android.os.SystemProperties;
import android.util.Slog;
import android.util.SparseArray;
import android.util.Xml;
@@ -77,8 +75,6 @@
private final WallpaperCropper mWallpaperCropper;
private final Context mContext;
- private final boolean mIsLockscreenLiveWallpaperEnabled;
-
WallpaperDataParser(Context context, WallpaperDisplayHelper wallpaperDisplayHelper,
WallpaperCropper wallpaperCropper) {
mContext = context;
@@ -86,8 +82,6 @@
mWallpaperCropper = wallpaperCropper;
mImageWallpaper = ComponentName.unflattenFromString(
context.getResources().getString(R.string.image_wallpaper_component));
- mIsLockscreenLiveWallpaperEnabled =
- SystemProperties.getBoolean("persist.wm.debug.lockscreen_live_wallpaper", true);
}
private JournaledFile makeJournaledFile(int userId) {
@@ -127,42 +121,26 @@
}
/**
- * TODO(b/197814683) adapt comment once flag is removed
- *
* Load the system wallpaper (and the lock wallpaper, if it exists) from disk
* @param userId the id of the user for which the wallpaper should be loaded
* @param keepDimensionHints if false, parse and set the
* {@link DisplayData} width and height for the specified userId
- * @param wallpaper the wallpaper object to reuse to do the modifications.
- * If null, a new object will be created.
- * @param lockWallpaper the lock wallpaper object to reuse to do the modifications.
- * If null, a new object will be created.
- * @param which The wallpaper(s) to load. Only has effect if
- * {@link WallpaperManager#isLockscreenLiveWallpaperEnabled} is true,
- * otherwise both wallpaper will always be loaded.
+ * @param migrateFromOld whether the current wallpaper is pre-N and needs migration
+ * @param which The wallpaper(s) to load.
* @return a {@link WallpaperLoadingResult} object containing the wallpaper data.
- * This object will contain the {@code wallpaper} and
- * {@code lockWallpaper} provided as parameters, if they are not null.
*/
public WallpaperLoadingResult loadSettingsLocked(int userId, boolean keepDimensionHints,
- WallpaperData wallpaper, WallpaperData lockWallpaper, @SetWallpaperFlags int which) {
+ boolean migrateFromOld, @SetWallpaperFlags int which) {
JournaledFile journal = makeJournaledFile(userId);
FileInputStream stream = null;
File file = journal.chooseForRead();
- boolean migrateFromOld = wallpaper == null;
+ boolean loadSystem = (which & FLAG_SYSTEM) != 0;
+ boolean loadLock = (which & FLAG_LOCK) != 0;
+ WallpaperData wallpaper = null;
+ WallpaperData lockWallpaper = null;
- boolean separateLockscreenEngine = mIsLockscreenLiveWallpaperEnabled;
- boolean loadSystem = !separateLockscreenEngine || (which & FLAG_SYSTEM) != 0;
- boolean loadLock = !separateLockscreenEngine || (which & FLAG_LOCK) != 0;
-
- // don't reuse the wallpaper objects in the new version
- if (separateLockscreenEngine) {
- wallpaper = null;
- lockWallpaper = null;
- }
-
- if (wallpaper == null && loadSystem) {
+ if (loadSystem) {
// Do this once per boot
if (migrateFromOld) migrateFromOld();
wallpaper = new WallpaperData(userId, FLAG_SYSTEM);
@@ -188,11 +166,8 @@
type = parser.next();
if (type == XmlPullParser.START_TAG) {
String tag = parser.getName();
- if (("wp".equals(tag) && loadSystem)
- || ("kwp".equals(tag) && mIsLockscreenLiveWallpaperEnabled
- && loadLock)) {
-
- if ("kwp".equals(tag) && lockWallpaper == null) {
+ if (("wp".equals(tag) && loadSystem) || ("kwp".equals(tag) && loadLock)) {
+ if ("kwp".equals(tag)) {
lockWallpaper = new WallpaperData(userId, FLAG_LOCK);
}
WallpaperData wallpaperToParse =
@@ -219,12 +194,6 @@
Slog.v(TAG, "mNextWallpaperComponent:"
+ wallpaper.nextWallpaperComponent);
}
- } else if ("kwp".equals(tag) && !mIsLockscreenLiveWallpaperEnabled) {
- // keyguard-specific wallpaper for this user (legacy code)
- if (lockWallpaper == null) {
- lockWallpaper = new WallpaperData(userId, FLAG_LOCK);
- }
- parseWallpaperAttributes(parser, lockWallpaper, false);
}
}
} while (type != XmlPullParser.END_DOCUMENT);
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index c7a3c43..bdcde66 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -188,8 +188,6 @@
}
private final Object mLock = new Object();
- /** True to enable a second engine for lock screen wallpaper when different from system wp. */
- private final boolean mIsLockscreenLiveWallpaperEnabled;
/** True to support different crops for different display dimensions */
private final boolean mIsMultiCropEnabled;
/** Tracks wallpaper being migrated from system+lock to lock when setting static wp. */
@@ -230,7 +228,7 @@
mWallpaperLockFile = new File(mWallpaperDir, WALLPAPER_LOCK_ORIG);
}
- WallpaperData dataForEvent(boolean sysChanged, boolean lockChanged) {
+ WallpaperData dataForEvent(boolean lockChanged) {
WallpaperData wallpaper = null;
synchronized (mLock) {
if (lockChanged) {
@@ -252,7 +250,7 @@
final File changedFile = new File(mWallpaperDir, path);
final boolean sysWallpaperChanged = (mWallpaperFile.equals(changedFile));
final boolean lockWallpaperChanged = (mWallpaperLockFile.equals(changedFile));
- final WallpaperData wallpaper = dataForEvent(sysWallpaperChanged, lockWallpaperChanged);
+ final WallpaperData wallpaper = dataForEvent(lockWallpaperChanged);
final boolean moved = (event == MOVED_TO);
final boolean written = (event == CLOSE_WRITE || moved);
@@ -378,7 +376,7 @@
}
saveSettingsLocked(wallpaper.userId);
- if ((sysWallpaperChanged || lockWallpaperChanged) && localSync != null) {
+ if (localSync != null) {
localSync.complete();
}
}
@@ -389,129 +387,9 @@
}
}
- // Handles static wallpaper changes generated by WallpaperObserver events when
- // enableSeparateLockScreenEngine() is false.
- // TODO(b/266818039) Remove this method
- private void updateWallpapersLegacy(int event, String path) {
- final boolean moved = (event == MOVED_TO);
- final boolean written = (event == CLOSE_WRITE || moved);
- final File changedFile = new File(mWallpaperDir, path);
-
- // System and system+lock changes happen on the system wallpaper input file;
- // lock-only changes happen on the dedicated lock wallpaper input file
- final boolean sysWallpaperChanged = (mWallpaperFile.equals(changedFile));
- final boolean lockWallpaperChanged = (mWallpaperLockFile.equals(changedFile));
- int notifyColorsWhich = 0;
- WallpaperData wallpaper = dataForEvent(sysWallpaperChanged, lockWallpaperChanged);
-
- if (DEBUG) {
- Slog.v(TAG, "Wallpaper file change: evt=" + event
- + " path=" + path
- + " sys=" + sysWallpaperChanged
- + " lock=" + lockWallpaperChanged
- + " imagePending=" + wallpaper.imageWallpaperPending
- + " mWhich=0x" + Integer.toHexString(wallpaper.mWhich)
- + " written=" + written);
- }
-
- if (moved && lockWallpaperChanged) {
- // We just migrated sys -> lock to preserve imagery for an impending
- // new system-only wallpaper. Tell keyguard about it and make sure it
- // has the right SELinux label.
- if (DEBUG) {
- Slog.i(TAG, "Sys -> lock MOVED_TO");
- }
- SELinux.restorecon(changedFile);
- notifyLockWallpaperChanged();
- notifyWallpaperColorsChanged(wallpaper, FLAG_LOCK);
- return;
- }
-
- synchronized (mLock) {
- if (sysWallpaperChanged || lockWallpaperChanged) {
- notifyCallbacksLocked(wallpaper);
- if (wallpaper.wallpaperComponent == null
- || event != CLOSE_WRITE // includes the MOVED_TO case
- || wallpaper.imageWallpaperPending) {
- if (written) {
- // The image source has finished writing the source image,
- // so we now produce the crop rect (in the background), and
- // only publish the new displayable (sub)image as a result
- // of that work.
- if (DEBUG) {
- Slog.v(TAG, "Wallpaper written; generating crop");
- }
- SELinux.restorecon(changedFile);
- if (moved) {
- // This is a restore, so generate the crop using any just-restored new
- // crop guidelines, making sure to preserve our local dimension hints.
- // We also make sure to reapply the correct SELinux label.
- if (DEBUG) {
- Slog.v(TAG, "moved-to, therefore restore; reloading metadata");
- }
- loadSettingsLocked(wallpaper.userId, true, FLAG_SYSTEM | FLAG_LOCK);
- }
- mWallpaperCropper.generateCrop(wallpaper);
- if (DEBUG) {
- Slog.v(TAG, "Crop done; invoking completion callback");
- }
- wallpaper.imageWallpaperPending = false;
- if (sysWallpaperChanged) {
- IRemoteCallback.Stub callback = new IRemoteCallback.Stub() {
- @Override
- public void sendResult(Bundle data) throws RemoteException {
- Slog.d(TAG, "publish system wallpaper changed!");
- notifyWallpaperChanged(wallpaper);
- }
- };
- // If this was the system wallpaper, rebind...
- bindWallpaperComponentLocked(mImageWallpaper, true,
- false, wallpaper, callback);
- notifyColorsWhich |= FLAG_SYSTEM;
- }
- if (lockWallpaperChanged
- || (wallpaper.mWhich & FLAG_LOCK) != 0) {
- if (DEBUG) {
- Slog.i(TAG, "Lock-relevant wallpaper changed");
- }
- // either a lock-only wallpaper commit or a system+lock event.
- // if it's system-plus-lock we need to wipe the lock bookkeeping;
- // we're falling back to displaying the system wallpaper there.
- if (!lockWallpaperChanged) {
- mLockWallpaperMap.remove(wallpaper.userId);
- }
- // and in any case, tell keyguard about it
- notifyLockWallpaperChanged();
- notifyColorsWhich |= FLAG_LOCK;
- }
-
- saveSettingsLocked(wallpaper.userId);
- // Notify the client immediately if only lockscreen wallpaper changed.
- if (lockWallpaperChanged && !sysWallpaperChanged) {
- notifyWallpaperChanged(wallpaper);
- }
- }
- }
- }
- }
-
- // Outside of the lock since it will synchronize itself
- if (notifyColorsWhich != 0) {
- notifyWallpaperColorsChanged(wallpaper, notifyColorsWhich);
- }
- }
-
@Override
public void onEvent(int event, String path) {
- if (path == null) {
- return;
- }
-
- if (mIsLockscreenLiveWallpaperEnabled) {
- updateWallpapers(event, path);
- } else {
- updateWallpapersLegacy(event, path);
- }
+ if (path != null) updateWallpapers(event, path);
}
}
@@ -528,17 +406,6 @@
}
}
- private void notifyLockWallpaperChanged() {
- final IWallpaperManagerCallback cb = mKeyguardListener;
- if (cb != null) {
- try {
- cb.onWallpaperChanged();
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to notify keyguard callback about wallpaper changes", e);
- }
- }
- }
-
void notifyWallpaperColorsChanged(@NonNull WallpaperData wallpaper, int which) {
if (DEBUG) {
Slog.i(TAG, "Notifying wallpaper colors changed");
@@ -597,14 +464,12 @@
private void notifyColorListeners(@NonNull WallpaperColors wallpaperColors, int which,
int userId, int displayId) {
- final IWallpaperManagerCallback keyguardListener;
final ArrayList<IWallpaperManagerCallback> colorListeners = new ArrayList<>();
synchronized (mLock) {
final RemoteCallbackList<IWallpaperManagerCallback> currentUserColorListeners =
getWallpaperCallbacks(userId, displayId);
final RemoteCallbackList<IWallpaperManagerCallback> userAllColorListeners =
getWallpaperCallbacks(UserHandle.USER_ALL, displayId);
- keyguardListener = mKeyguardListener;
if (currentUserColorListeners != null) {
final int count = currentUserColorListeners.beginBroadcast();
@@ -633,15 +498,6 @@
Slog.w(TAG, "onWallpaperColorsChanged() threw an exception", e);
}
}
-
- // Only shows Keyguard on default display
- if (keyguardListener != null && displayId == DEFAULT_DISPLAY) {
- try {
- keyguardListener.onWallpaperColorsChanged(wallpaperColors, which, userId);
- } catch (RemoteException e) {
- Slog.w(TAG, "keyguardListener.onWallpaperColorsChanged threw an exception", e);
- }
- }
}
/**
@@ -762,8 +618,6 @@
private final MyPackageMonitor mMonitor;
private final AppOpsManager mAppOpsManager;
- // TODO("b/264637309") probably move this in WallpaperDisplayUtils,
- // after logic is changed for the lockscreen lwp project
private final DisplayManager.DisplayListener mDisplayListener =
new DisplayManager.DisplayListener() {
@@ -814,8 +668,6 @@
protected WallpaperData mLastWallpaper;
// The currently bound lock screen only wallpaper, or null if none
protected WallpaperData mLastLockWallpaper;
- private IWallpaperManagerCallback mKeyguardListener;
- private boolean mWaitingForUnlock;
/**
* Flag set to true after reboot if the home wallpaper is waiting for the device to be unlocked.
@@ -1017,8 +869,8 @@
if (!mWallpaper.wallpaperUpdating && mWallpaper.userId == mCurrentUserId) {
Slog.w(TAG, "Wallpaper reconnect timed out for " + mWallpaper.wallpaperComponent
+ ", reverting to built-in wallpaper!");
- int which = mIsLockscreenLiveWallpaperEnabled ? mWallpaper.mWhich : FLAG_SYSTEM;
- clearWallpaperLocked(which, mWallpaper.userId, null);
+ int which = mWallpaper.mWhich;
+ clearWallpaperLocked(which, mWallpaper.userId, false, null);
}
}
};
@@ -1198,7 +1050,7 @@
} else {
// Timeout
Slog.w(TAG, "Reverting to built-in wallpaper!");
- clearWallpaperLocked(mWallpaper.mWhich, mWallpaper.userId, null);
+ clearWallpaperLocked(mWallpaper.mWhich, mWallpaper.userId, false, null);
final String flattened = wpService.flattenToString();
EventLog.writeEvent(EventLogTags.WP_WALLPAPER_CRASHED,
flattened.substring(0, Math.min(flattened.length(),
@@ -1238,7 +1090,7 @@
if (mLmkLimitRebindRetries <= 0) {
Slog.w(TAG, "Reverting to built-in wallpaper due to lmk!");
clearWallpaperLocked(
- mWallpaper.mWhich, mWallpaper.userId, null);
+ mWallpaper.mWhich, mWallpaper.userId, false, null);
mLmkLimitRebindRetries = LMK_RECONNECT_REBIND_RETRIES;
return;
}
@@ -1257,7 +1109,7 @@
&& mWallpaper.lastDiedTime + MIN_WALLPAPER_CRASH_TIME
> SystemClock.uptimeMillis()) {
Slog.w(TAG, "Reverting to built-in wallpaper!");
- clearWallpaperLocked(FLAG_SYSTEM, mWallpaper.userId, null);
+ clearWallpaperLocked(FLAG_SYSTEM, mWallpaper.userId, false, null);
} else {
mWallpaper.lastDiedTime = SystemClock.uptimeMillis();
tryToRebind();
@@ -1294,19 +1146,8 @@
if (mImageWallpaper.equals(mWallpaper.wallpaperComponent)) {
return;
}
-
- // Live wallpapers always are system wallpapers unless lock screen live wp is
- // enabled.
- which = mIsLockscreenLiveWallpaperEnabled ? mWallpaper.mWhich : FLAG_SYSTEM;
+ which = mWallpaper.mWhich;
mWallpaper.primaryColors = primaryColors;
-
- // It's also the lock screen wallpaper when we don't have a bitmap in there.
- if (displayId == DEFAULT_DISPLAY) {
- final WallpaperData lockedWallpaper = mLockWallpaperMap.get(mWallpaper.userId);
- if (lockedWallpaper == null) {
- which |= FLAG_LOCK;
- }
- }
}
if (which != 0) {
notifyWallpaperColorsChangedOnDisplay(mWallpaper, which, displayId);
@@ -1492,9 +1333,7 @@
wallpaper, null)) {
Slog.w(TAG, "Wallpaper " + wpService
+ " no longer available; reverting to default");
- int which = mIsLockscreenLiveWallpaperEnabled
- ? wallpaper.mWhich : FLAG_SYSTEM;
- clearWallpaperLocked(which, wallpaper.userId, null);
+ clearWallpaperLocked(wallpaper.mWhich, wallpaper.userId, false, null);
}
}
}
@@ -1568,7 +1407,6 @@
boolean doPackagesChangedLocked(boolean doit, WallpaperData wallpaper) {
boolean changed = false;
- int which = mIsLockscreenLiveWallpaperEnabled ? wallpaper.mWhich : FLAG_SYSTEM;
if (wallpaper.wallpaperComponent != null) {
int change = isPackageDisappearing(wallpaper.wallpaperComponent
.getPackageName());
@@ -1578,7 +1416,7 @@
if (doit) {
Slog.w(TAG, "Wallpaper uninstalled, removing: "
+ wallpaper.wallpaperComponent);
- clearWallpaperLocked(which, wallpaper.userId, null);
+ clearWallpaperLocked(wallpaper.mWhich, wallpaper.userId, false, null);
}
}
}
@@ -1599,7 +1437,7 @@
} catch (NameNotFoundException e) {
Slog.w(TAG, "Wallpaper component gone, removing: "
+ wallpaper.wallpaperComponent);
- clearWallpaperLocked(which, wallpaper.userId, null);
+ clearWallpaperLocked(wallpaper.mWhich, wallpaper.userId, false, null);
}
}
if (wallpaper.nextWallpaperComponent != null
@@ -1686,9 +1524,6 @@
mColorsChangedListeners = new SparseArray<>();
mWallpaperDataParser = new WallpaperDataParser(mContext, mWallpaperDisplayHelper,
mWallpaperCropper);
-
- mIsLockscreenLiveWallpaperEnabled =
- SystemProperties.getBoolean("persist.wm.debug.lockscreen_live_wallpaper", true);
mIsMultiCropEnabled =
SystemProperties.getBoolean("persist.wm.debug.wallpaper_multi_crop", false);
LocalServices.addService(WallpaperManagerInternal.class, new LocalService());
@@ -1755,8 +1590,7 @@
if (DEBUG) {
Slog.i(TAG, "Unable to regenerate crop; resetting");
}
- int which = isLockscreenLiveWallpaperEnabled() ? wallpaper.mWhich : FLAG_SYSTEM;
- clearWallpaperLocked(which, UserHandle.USER_SYSTEM, null);
+ clearWallpaperLocked(wallpaper.mWhich, UserHandle.USER_SYSTEM, false, null);
}
} else {
if (DEBUG) {
@@ -1883,29 +1717,19 @@
public void onUnlockUser(final int userId) {
synchronized (mLock) {
if (mCurrentUserId == userId) {
- if (mIsLockscreenLiveWallpaperEnabled) {
- if (mHomeWallpaperWaitingForUnlock) {
- final WallpaperData systemWallpaper =
- getWallpaperSafeLocked(userId, FLAG_SYSTEM);
- switchWallpaper(systemWallpaper, null);
- // TODO(b/278261563): call notifyCallbacksLocked inside switchWallpaper
- notifyCallbacksLocked(systemWallpaper);
- }
- if (mLockWallpaperWaitingForUnlock) {
- final WallpaperData lockWallpaper =
- getWallpaperSafeLocked(userId, FLAG_LOCK);
- switchWallpaper(lockWallpaper, null);
- notifyCallbacksLocked(lockWallpaper);
- }
- }
-
- if (mWaitingForUnlock && !mIsLockscreenLiveWallpaperEnabled) {
- // the desired wallpaper is not direct-boot aware, load it now
+ if (mHomeWallpaperWaitingForUnlock) {
final WallpaperData systemWallpaper =
getWallpaperSafeLocked(userId, FLAG_SYSTEM);
switchWallpaper(systemWallpaper, null);
+ // TODO(b/278261563): call notifyCallbacksLocked inside switchWallpaper
notifyCallbacksLocked(systemWallpaper);
}
+ if (mLockWallpaperWaitingForUnlock) {
+ final WallpaperData lockWallpaper =
+ getWallpaperSafeLocked(userId, FLAG_LOCK);
+ switchWallpaper(lockWallpaper, null);
+ notifyCallbacksLocked(lockWallpaper);
+ }
// Make sure that the SELinux labeling of all the relevant files is correct.
// This corrects for mislabeling bugs that might have arisen from move-to
@@ -1954,21 +1778,15 @@
}
mCurrentUserId = userId;
systemWallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
-
- if (mIsLockscreenLiveWallpaperEnabled) {
- lockWallpaper = systemWallpaper.mWhich == (FLAG_LOCK | FLAG_SYSTEM)
- ? systemWallpaper : getWallpaperSafeLocked(userId, FLAG_LOCK);
- } else {
- final WallpaperData tmpLockWallpaper = mLockWallpaperMap.get(userId);
- lockWallpaper = tmpLockWallpaper == null ? systemWallpaper : tmpLockWallpaper;
- }
+ lockWallpaper = systemWallpaper.mWhich == (FLAG_LOCK | FLAG_SYSTEM)
+ ? systemWallpaper : getWallpaperSafeLocked(userId, FLAG_LOCK);
// Not started watching yet, in case wallpaper data was loaded for other reasons.
if (systemWallpaper.wallpaperObserver == null) {
systemWallpaper.wallpaperObserver = new WallpaperObserver(systemWallpaper);
systemWallpaper.wallpaperObserver.startWatching();
}
- if (mIsLockscreenLiveWallpaperEnabled && lockWallpaper != systemWallpaper) {
+ if (lockWallpaper != systemWallpaper) {
switchWallpaper(lockWallpaper, null);
}
switchWallpaper(systemWallpaper, reply);
@@ -1988,11 +1806,8 @@
void switchWallpaper(WallpaperData wallpaper, IRemoteCallback reply) {
synchronized (mLock) {
- mWaitingForUnlock = false;
- if (mIsLockscreenLiveWallpaperEnabled) {
- if ((wallpaper.mWhich & FLAG_SYSTEM) != 0) mHomeWallpaperWaitingForUnlock = false;
- if ((wallpaper.mWhich & FLAG_LOCK) != 0) mLockWallpaperWaitingForUnlock = false;
- }
+ if ((wallpaper.mWhich & FLAG_SYSTEM) != 0) mHomeWallpaperWaitingForUnlock = false;
+ if ((wallpaper.mWhich & FLAG_LOCK) != 0) mLockWallpaperWaitingForUnlock = false;
final ComponentName cname = wallpaper.wallpaperComponent != null ?
wallpaper.wallpaperComponent : wallpaper.nextWallpaperComponent;
@@ -2006,37 +1821,19 @@
} catch (RemoteException e) {
Slog.w(TAG, "Failure starting previous wallpaper; clearing", e);
}
-
- if (mIsLockscreenLiveWallpaperEnabled) {
- onSwitchWallpaperFailLocked(wallpaper, reply, si);
- return;
- }
-
- if (si == null) {
- clearWallpaperLocked(FLAG_SYSTEM, wallpaper.userId, reply);
- } else {
- Slog.w(TAG, "Wallpaper isn't direct boot aware; using fallback until unlocked");
- // We might end up persisting the current wallpaper data
- // while locked, so pretend like the component was actually
- // bound into place
- wallpaper.wallpaperComponent = wallpaper.nextWallpaperComponent;
- final WallpaperData fallback = new WallpaperData(wallpaper.userId, FLAG_LOCK);
- bindWallpaperComponentLocked(mImageWallpaper, true, false, fallback, reply);
- mWaitingForUnlock = true;
- }
+ onSwitchWallpaperFailLocked(wallpaper, reply, si);
}
}
}
/**
* Fallback method if a wallpaper fails to load on boot or after a user switch.
- * Only called if mIsLockscreenLiveWallpaperEnabled is true.
*/
private void onSwitchWallpaperFailLocked(
WallpaperData wallpaper, IRemoteCallback reply, ServiceInfo serviceInfo) {
if (serviceInfo == null) {
- clearWallpaperLocked(wallpaper.mWhich, wallpaper.userId, reply);
+ clearWallpaperLocked(wallpaper.mWhich, wallpaper.userId, false, reply);
return;
}
Slog.w(TAG, "Wallpaper isn't direct boot aware; using fallback until unlocked");
@@ -2067,12 +1864,8 @@
WallpaperData data = null;
synchronized (mLock) {
- if (mIsLockscreenLiveWallpaperEnabled) {
- boolean fromForeground = isFromForegroundApp(callingPackage);
- clearWallpaperLocked(which, userId, fromForeground, null);
- } else {
- clearWallpaperLocked(which, userId, null);
- }
+ boolean fromForeground = isFromForegroundApp(callingPackage);
+ clearWallpaperLocked(which, userId, fromForeground, null);
if (which == FLAG_LOCK) {
data = mLockWallpaperMap.get(userId);
@@ -2153,91 +1946,6 @@
}
}
- // TODO(b/266818039) remove
- private void clearWallpaperLocked(int which, int userId, IRemoteCallback reply) {
-
- if (mIsLockscreenLiveWallpaperEnabled) {
- clearWallpaperLocked(which, userId, false, reply);
- return;
- }
-
- if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
- throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to clear");
- }
-
- WallpaperData wallpaper = null;
- if (which == FLAG_LOCK) {
- wallpaper = mLockWallpaperMap.get(userId);
- if (wallpaper == null) {
- // It's already gone; we're done.
- if (DEBUG) {
- Slog.i(TAG, "Lock wallpaper already cleared");
- }
- return;
- }
- } else {
- wallpaper = mWallpaperMap.get(userId);
- if (wallpaper == null) {
- // Might need to bring it in the first time to establish our rewrite
- loadSettingsLocked(userId, false, FLAG_SYSTEM);
- wallpaper = mWallpaperMap.get(userId);
- }
- }
- if (wallpaper == null) {
- return;
- }
-
- final long ident = Binder.clearCallingIdentity();
- try {
- if (clearWallpaperBitmaps(wallpaper)) {
- if (which == FLAG_LOCK) {
- mLockWallpaperMap.remove(userId);
- final IWallpaperManagerCallback cb = mKeyguardListener;
- if (cb != null) {
- if (DEBUG) {
- Slog.i(TAG, "Notifying keyguard of lock wallpaper clear");
- }
- try {
- cb.onWallpaperChanged();
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to notify keyguard after wallpaper clear", e);
- }
- }
- saveSettingsLocked(userId);
- return;
- }
- }
-
- RuntimeException e = null;
- try {
- wallpaper.primaryColors = null;
- wallpaper.imageWallpaperPending = false;
- if (userId != mCurrentUserId) return;
- if (bindWallpaperComponentLocked(null, true, false, wallpaper, reply)) {
- return;
- }
- } catch (IllegalArgumentException e1) {
- e = e1;
- }
-
- // This can happen if the default wallpaper component doesn't
- // exist. This should be a system configuration problem, but
- // let's not let it crash the system and just live with no
- // wallpaper.
- Slog.e(TAG, "Default wallpaper component not found!", e);
- clearWallpaperComponentLocked(wallpaper);
- if (reply != null) {
- try {
- reply.sendResult(null);
- } catch (RemoteException e1) {
- Slog.w(TAG, "Failed to notify callback after wallpaper clear", e1);
- }
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
private boolean hasCrossUserPermission() {
final int interactPermission =
mContext.checkCallingPermission(INTERACT_ACROSS_USERS_FULL);
@@ -2615,45 +2323,20 @@
* @param animationDuration Duration of the animation, or 0 when immediate.
*/
public void setInAmbientMode(boolean inAmbientMode, long animationDuration) {
- if (mIsLockscreenLiveWallpaperEnabled) {
- List<IWallpaperEngine> engines = new ArrayList<>();
- synchronized (mLock) {
- mInAmbientMode = inAmbientMode;
- for (WallpaperData data : getActiveWallpapers()) {
- if (data.connection.mInfo == null
- || data.connection.mInfo.supportsAmbientMode()) {
- // TODO(multi-display) Extends this method with specific display.
- IWallpaperEngine engine = data.connection
- .getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
- if (engine != null) engines.add(engine);
- }
- }
- }
- for (IWallpaperEngine engine : engines) {
- try {
- engine.setInAmbientMode(inAmbientMode, animationDuration);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to set ambient mode", e);
- }
- }
- return;
- }
-
- final IWallpaperEngine engine;
+ List<IWallpaperEngine> engines = new ArrayList<>();
synchronized (mLock) {
mInAmbientMode = inAmbientMode;
- final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
- // The wallpaper info is null for image wallpaper, also use the engine in this case.
- if (data != null && data.connection != null && (data.connection.mInfo == null
- || data.connection.mInfo.supportsAmbientMode())) {
- // TODO(multi-display) Extends this method with specific display.
- engine = data.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
- } else {
- engine = null;
+ for (WallpaperData data : getActiveWallpapers()) {
+ if (data.connection.mInfo == null
+ || data.connection.mInfo.supportsAmbientMode()) {
+ // TODO(multi-display) Extends this method with specific display.
+ IWallpaperEngine engine = data.connection
+ .getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
+ if (engine != null) engines.add(engine);
+ }
}
}
-
- if (engine != null) {
+ for (IWallpaperEngine engine : engines) {
try {
engine.setInAmbientMode(inAmbientMode, animationDuration);
} catch (RemoteException e) {
@@ -2664,11 +2347,8 @@
private void pauseOrResumeRenderingImmediately(boolean pause) {
synchronized (mLock) {
- final WallpaperData[] wallpapers = mIsLockscreenLiveWallpaperEnabled
- ? getActiveWallpapers() : new WallpaperData[] {
- mWallpaperMap.get(mCurrentUserId) };
- for (WallpaperData data : wallpapers) {
- if (data.connection == null || data.connection.mInfo == null) {
+ for (WallpaperData data : getActiveWallpapers()) {
+ if (data.connection.mInfo == null) {
continue;
}
if (pause || LocalServices.getService(ActivityTaskManagerInternal.class)
@@ -2697,34 +2377,17 @@
public void notifyWakingUp(int x, int y, @NonNull Bundle extras) {
checkCallerIsSystemOrSystemUi();
synchronized (mLock) {
- if (mIsLockscreenLiveWallpaperEnabled) {
- for (WallpaperData data : getActiveWallpapers()) {
- data.connection.forEachDisplayConnector(displayConnector -> {
- if (displayConnector.mEngine != null) {
- try {
- displayConnector.mEngine.dispatchWallpaperCommand(
- WallpaperManager.COMMAND_WAKING_UP, x, y, -1, extras);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to dispatch COMMAND_WAKING_UP", e);
- }
+ for (WallpaperData data : getActiveWallpapers()) {
+ data.connection.forEachDisplayConnector(displayConnector -> {
+ if (displayConnector.mEngine != null) {
+ try {
+ displayConnector.mEngine.dispatchWallpaperCommand(
+ WallpaperManager.COMMAND_WAKING_UP, x, y, -1, extras);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to dispatch COMMAND_WAKING_UP", e);
}
- });
- }
- return;
- }
- final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
- if (data != null && data.connection != null) {
- data.connection.forEachDisplayConnector(
- displayConnector -> {
- if (displayConnector.mEngine != null) {
- try {
- displayConnector.mEngine.dispatchWallpaperCommand(
- WallpaperManager.COMMAND_WAKING_UP, x, y, -1, extras);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to dispatch COMMAND_WAKING_UP", e);
- }
- }
- });
+ }
+ });
}
}
}
@@ -2735,36 +2398,18 @@
public void notifyGoingToSleep(int x, int y, @NonNull Bundle extras) {
checkCallerIsSystemOrSystemUi();
synchronized (mLock) {
- if (mIsLockscreenLiveWallpaperEnabled) {
- for (WallpaperData data : getActiveWallpapers()) {
- data.connection.forEachDisplayConnector(displayConnector -> {
- if (displayConnector.mEngine != null) {
- try {
- displayConnector.mEngine.dispatchWallpaperCommand(
- WallpaperManager.COMMAND_GOING_TO_SLEEP, x, y, -1,
- extras);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to dispatch COMMAND_GOING_TO_SLEEP", e);
- }
+ for (WallpaperData data : getActiveWallpapers()) {
+ data.connection.forEachDisplayConnector(displayConnector -> {
+ if (displayConnector.mEngine != null) {
+ try {
+ displayConnector.mEngine.dispatchWallpaperCommand(
+ WallpaperManager.COMMAND_GOING_TO_SLEEP, x, y, -1,
+ extras);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to dispatch COMMAND_GOING_TO_SLEEP", e);
}
- });
- }
- return;
- }
- final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
- if (data != null && data.connection != null) {
- data.connection.forEachDisplayConnector(
- displayConnector -> {
- if (displayConnector.mEngine != null) {
- try {
- displayConnector.mEngine.dispatchWallpaperCommand(
- WallpaperManager.COMMAND_GOING_TO_SLEEP, x, y, -1,
- extras);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to dispatch COMMAND_GOING_TO_SLEEP", e);
- }
- }
- });
+ }
+ });
}
}
}
@@ -2774,35 +2419,18 @@
*/
private void notifyScreenTurnedOn(int displayId) {
synchronized (mLock) {
- if (mIsLockscreenLiveWallpaperEnabled) {
- for (WallpaperData data : getActiveWallpapers()) {
- if (data.connection.containsDisplay(displayId)) {
- final IWallpaperEngine engine = data.connection
- .getDisplayConnectorOrCreate(displayId).mEngine;
- if (engine != null) {
- try {
- engine.onScreenTurnedOn();
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to notify that the screen turned on", e);
- }
+ for (WallpaperData data : getActiveWallpapers()) {
+ if (data.connection.containsDisplay(displayId)) {
+ final IWallpaperEngine engine = data.connection
+ .getDisplayConnectorOrCreate(displayId).mEngine;
+ if (engine != null) {
+ try {
+ engine.onScreenTurnedOn();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to notify that the screen turned on", e);
}
}
}
- return;
- }
- final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
- if (data != null
- && data.connection != null
- && data.connection.containsDisplay(displayId)) {
- final IWallpaperEngine engine = data.connection
- .getDisplayConnectorOrCreate(displayId).mEngine;
- if (engine != null) {
- try {
- engine.onScreenTurnedOn();
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to notify that the screen turned on", e);
- }
- }
}
}
}
@@ -2812,35 +2440,18 @@
*/
private void notifyScreenTurningOn(int displayId) {
synchronized (mLock) {
- if (mIsLockscreenLiveWallpaperEnabled) {
- for (WallpaperData data : getActiveWallpapers()) {
- if (data.connection.containsDisplay(displayId)) {
- final IWallpaperEngine engine = data.connection
- .getDisplayConnectorOrCreate(displayId).mEngine;
- if (engine != null) {
- try {
- engine.onScreenTurningOn();
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to notify that the screen is turning on", e);
- }
+ for (WallpaperData data : getActiveWallpapers()) {
+ if (data.connection.containsDisplay(displayId)) {
+ final IWallpaperEngine engine = data.connection
+ .getDisplayConnectorOrCreate(displayId).mEngine;
+ if (engine != null) {
+ try {
+ engine.onScreenTurningOn();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to notify that the screen is turning on", e);
}
}
}
- return;
- }
- final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
- if (data != null
- && data.connection != null
- && data.connection.containsDisplay(displayId)) {
- final IWallpaperEngine engine = data.connection
- .getDisplayConnectorOrCreate(displayId).mEngine;
- if (engine != null) {
- try {
- engine.onScreenTurningOn();
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to notify that the screen is turning on", e);
- }
- }
}
}
}
@@ -2850,25 +2461,7 @@
*/
private void notifyKeyguardGoingAway() {
synchronized (mLock) {
- if (mIsLockscreenLiveWallpaperEnabled) {
- for (WallpaperData data : getActiveWallpapers()) {
- data.connection.forEachDisplayConnector(displayConnector -> {
- if (displayConnector.mEngine != null) {
- try {
- displayConnector.mEngine.dispatchWallpaperCommand(
- WallpaperManager.COMMAND_KEYGUARD_GOING_AWAY,
- -1, -1, -1, new Bundle());
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to notify that the keyguard is going away", e);
- }
- }
- });
- }
- return;
- }
-
- final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
- if (data != null && data.connection != null) {
+ for (WallpaperData data : getActiveWallpapers()) {
data.connection.forEachDisplayConnector(displayConnector -> {
if (displayConnector.mEngine != null) {
try {
@@ -2884,15 +2477,6 @@
}
}
- @Override
- public boolean setLockWallpaperCallback(IWallpaperManagerCallback cb) {
- checkPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW);
- synchronized (mLock) {
- mKeyguardListener = cb;
- }
- return true;
- }
-
private WallpaperData[] getActiveWallpapers() {
WallpaperData systemWallpaper = mWallpaperMap.get(mCurrentUserId);
WallpaperData lockWallpaper = mLockWallpaperMap.get(mCurrentUserId);
@@ -2904,12 +2488,11 @@
: new WallpaperData[0];
}
- // TODO(b/266818039) remove
private WallpaperData[] getWallpapers() {
WallpaperData systemWallpaper = mWallpaperMap.get(mCurrentUserId);
WallpaperData lockWallpaper = mLockWallpaperMap.get(mCurrentUserId);
boolean systemValid = systemWallpaper != null;
- boolean lockValid = lockWallpaper != null && isLockscreenLiveWallpaperEnabled();
+ boolean lockValid = lockWallpaper != null;
return systemValid && lockValid ? new WallpaperData[]{systemWallpaper, lockWallpaper}
: systemValid ? new WallpaperData[]{systemWallpaper}
: lockValid ? new WallpaperData[]{lockWallpaper}
@@ -3043,54 +2626,29 @@
lockWallpaper.mWallpaperDimAmount = maxDimAmount;
}
- if (mIsLockscreenLiveWallpaperEnabled) {
- boolean changed = false;
- for (WallpaperData wp : getActiveWallpapers()) {
- if (wp != null && wp.connection != null) {
- wp.connection.forEachDisplayConnector(connector -> {
- if (connector.mEngine != null) {
- try {
- connector.mEngine.applyDimming(maxDimAmount);
- } catch (RemoteException e) {
- Slog.w(TAG, "Can't apply dimming on wallpaper display "
- + "connector", e);
- }
- }
- });
- // Need to extract colors again to re-calculate dark hints after
- // applying dimming.
- wp.mIsColorExtractedFromDim = true;
- pendingColorExtraction.add(wp);
- changed = true;
- }
- }
- if (changed) {
- saveSettingsLocked(wallpaper.userId);
- }
- } else {
- if (wallpaper.connection != null) {
- wallpaper.connection.forEachDisplayConnector(connector -> {
+ boolean changed = false;
+ for (WallpaperData wp : getActiveWallpapers()) {
+ if (wp != null && wp.connection != null) {
+ wp.connection.forEachDisplayConnector(connector -> {
if (connector.mEngine != null) {
try {
connector.mEngine.applyDimming(maxDimAmount);
} catch (RemoteException e) {
- Slog.w(TAG,
- "Can't apply dimming on wallpaper display connector",
- e);
+ Slog.w(TAG, "Can't apply dimming on wallpaper display "
+ + "connector", e);
}
}
});
// Need to extract colors again to re-calculate dark hints after
// applying dimming.
- wallpaper.mIsColorExtractedFromDim = true;
- notifyWallpaperColorsChanged(wallpaper, FLAG_SYSTEM);
- if (lockWallpaper != null) {
- lockWallpaper.mIsColorExtractedFromDim = true;
- notifyWallpaperColorsChanged(lockWallpaper, FLAG_LOCK);
- }
- saveSettingsLocked(wallpaper.userId);
+ wp.mIsColorExtractedFromDim = true;
+ pendingColorExtraction.add(wp);
+ changed = true;
}
}
+ if (changed) {
+ saveSettingsLocked(wallpaper.userId);
+ }
}
for (WallpaperData wp: pendingColorExtraction) {
notifyWallpaperColorsChanged(wp, wp.mWhich);
@@ -3246,10 +2804,7 @@
if (which == FLAG_SYSTEM && systemIsStatic && systemIsBoth) {
Slog.i(TAG, "Migrating current wallpaper to be lock-only before"
+ " updating system wallpaper");
- if (!migrateStaticSystemToLockWallpaperLocked(userId)
- && !isLockscreenLiveWallpaperEnabled()) {
- which |= FLAG_LOCK;
- }
+ migrateStaticSystemToLockWallpaperLocked(userId);
}
wallpaper = getWallpaperSafeLocked(userId, which);
@@ -3277,13 +2832,13 @@
}
}
- private boolean migrateStaticSystemToLockWallpaperLocked(int userId) {
+ private void migrateStaticSystemToLockWallpaperLocked(int userId) {
WallpaperData sysWP = mWallpaperMap.get(userId);
if (sysWP == null) {
if (DEBUG) {
Slog.i(TAG, "No system wallpaper? Not tracking for lock-only");
}
- return true;
+ return;
}
// We know a-priori that there is no lock-only wallpaper currently
@@ -3297,25 +2852,21 @@
// Migrate the bitmap files outright; no need to copy
try {
- if (!mIsLockscreenLiveWallpaperEnabled || sysWP.getWallpaperFile().exists()) {
+ if (sysWP.getWallpaperFile().exists()) {
Os.rename(sysWP.getWallpaperFile().getAbsolutePath(),
lockWP.getWallpaperFile().getAbsolutePath());
}
- if (!mIsLockscreenLiveWallpaperEnabled || sysWP.getCropFile().exists()) {
+ if (sysWP.getCropFile().exists()) {
Os.rename(sysWP.getCropFile().getAbsolutePath(),
lockWP.getCropFile().getAbsolutePath());
}
mLockWallpaperMap.put(userId, lockWP);
- if (mIsLockscreenLiveWallpaperEnabled) {
- SELinux.restorecon(lockWP.getWallpaperFile());
- mLastLockWallpaper = lockWP;
- }
- return true;
+ SELinux.restorecon(lockWP.getWallpaperFile());
+ mLastLockWallpaper = lockWP;
} catch (ErrnoException e) {
// can happen when migrating default wallpaper (which is not stored in wallpaperFile)
Slog.w(TAG, "Couldn't migrate system wallpaper: " + e.getMessage());
clearWallpaperBitmaps(lockWP);
- return false;
}
}
@@ -3372,13 +2923,8 @@
@VisibleForTesting
boolean setWallpaperComponent(ComponentName name, String callingPackage,
@SetWallpaperFlags int which, int userId) {
- if (mIsLockscreenLiveWallpaperEnabled) {
- boolean fromForeground = isFromForegroundApp(callingPackage);
- return setWallpaperComponentInternal(name, which, userId, false, fromForeground, null);
- } else {
- setWallpaperComponentInternalLegacy(name, callingPackage, which, userId);
- return true;
- }
+ boolean fromForeground = isFromForegroundApp(callingPackage);
+ return setWallpaperComponentInternal(name, which, userId, false, fromForeground, null);
}
private boolean setWallpaperComponentInternal(ComponentName name, @SetWallpaperFlags int which,
@@ -3491,87 +3037,6 @@
return bindSuccess;
}
- // TODO(b/266818039) Remove this method
- private void setWallpaperComponentInternalLegacy(ComponentName name, String callingPackage,
- @SetWallpaperFlags int which, int userId) {
- userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
- false /* all */, true /* full */, "changing live wallpaper", null /* pkg */);
- checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
-
- int legacyWhich = FLAG_SYSTEM;
- boolean shouldNotifyColors = false;
- WallpaperData wallpaper;
-
- synchronized (mLock) {
- Slog.v(TAG, "setWallpaperComponentLegacy name=" + name + ", which=" + which);
- wallpaper = mWallpaperMap.get(userId);
- if (wallpaper == null) {
- throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
- }
- final long ident = Binder.clearCallingIdentity();
-
- // Live wallpapers can't be specified for keyguard. If we're using a static
- // system+lock image currently, migrate the system wallpaper to be a lock-only
- // image as part of making a different live component active as the system
- // wallpaper.
- if (mImageWallpaper.equals(wallpaper.wallpaperComponent)) {
- if (mLockWallpaperMap.get(userId) == null) {
- // We're using the static imagery and there is no lock-specific image in place,
- // therefore it's a shared system+lock image that we need to migrate.
- Slog.i(TAG, "Migrating current wallpaper to be lock-only before"
- + "updating system wallpaper");
- if (!migrateStaticSystemToLockWallpaperLocked(userId)) {
- which |= FLAG_LOCK;
- }
- }
- }
-
- // New live wallpaper is also a lock wallpaper if nothing is set
- if (mLockWallpaperMap.get(userId) == null) {
- legacyWhich |= FLAG_LOCK;
- }
-
- try {
- wallpaper.imageWallpaperPending = false;
- wallpaper.mWhich = which;
- wallpaper.fromForegroundApp = isFromForegroundApp(callingPackage);
- boolean same = changingToSame(name, wallpaper);
-
- // force rebind when reapplying a system-only wallpaper to system+lock
- boolean forceRebind = same && mLockWallpaperMap.get(userId) != null
- && which == (FLAG_SYSTEM | FLAG_LOCK);
- if (bindWallpaperComponentLocked(name, forceRebind, true, wallpaper, null)) {
- if (!same) {
- wallpaper.primaryColors = null;
- } else {
- if (wallpaper.connection != null) {
- wallpaper.connection.forEachDisplayConnector(displayConnector -> {
- try {
- if (displayConnector.mEngine != null) {
- displayConnector.mEngine.dispatchWallpaperCommand(
- COMMAND_REAPPLY, 0, 0, 0, null);
- }
- } catch (RemoteException e) {
- Slog.w(TAG, "Error sending apply message to wallpaper", e);
- }
- });
- }
- }
- wallpaper.wallpaperId = makeWallpaperIdLocked();
- notifyCallbacksLocked(wallpaper);
- shouldNotifyColors = true;
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- if (shouldNotifyColors) {
- notifyWallpaperColorsChanged(wallpaper, legacyWhich);
- notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
- }
- }
-
/**
* Determines if the given component name is the default component. Note: a null name can be
* used to represent the default component.
@@ -3743,21 +3208,11 @@
Slog.w(TAG, msg);
return false;
}
- if (mIsLockscreenLiveWallpaperEnabled) {
- maybeDetachLastWallpapers(wallpaper);
- } else if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null
- && !wallpaper.equals(mFallbackWallpaper)) {
- detachWallpaperLocked(mLastWallpaper);
- }
+ maybeDetachLastWallpapers(wallpaper);
wallpaper.wallpaperComponent = componentName;
wallpaper.connection = newConn;
newConn.mReply = reply;
- if (mIsLockscreenLiveWallpaperEnabled) {
- updateCurrentWallpapers(wallpaper);
- } else if (wallpaper.userId == mCurrentUserId && !wallpaper.equals(
- mFallbackWallpaper)) {
- mLastWallpaper = wallpaper;
- }
+ updateCurrentWallpapers(wallpaper);
updateFallbackConnection();
} catch (RemoteException e) {
String msg = "Remote exception for " + componentName + "\n" + e;
@@ -3773,7 +3228,6 @@
}
// Updates tracking of the currently bound wallpapers.
- // Assumes isLockscreenLiveWallpaperEnabled is true.
private void updateCurrentWallpapers(WallpaperData newWallpaper) {
if (newWallpaper.userId != mCurrentUserId || newWallpaper.equals(mFallbackWallpaper)) {
return;
@@ -3787,8 +3241,7 @@
}
}
- // Detaches previously bound wallpapers if no longer in use. Assumes
- // isLockscreenLiveWallpaperEnabled is true.
+ // Detaches previously bound wallpapers if no longer in use.
private void maybeDetachLastWallpapers(WallpaperData newWallpaper) {
if (newWallpaper.userId != mCurrentUserId || newWallpaper.equals(mFallbackWallpaper)) {
return;
@@ -3981,11 +3434,6 @@
}
@Override
- public boolean isLockscreenLiveWallpaperEnabled() {
- return mIsLockscreenLiveWallpaperEnabled;
- }
-
- @Override
public boolean isMultiCropEnabled() {
return mIsMultiCropEnabled;
}
@@ -4074,13 +3522,12 @@
private void loadSettingsLocked(int userId, boolean keepDimensionHints, int which) {
initializeFallbackWallpaper();
- WallpaperData wallpaperData = mWallpaperMap.get(userId);
- WallpaperData lockWallpaperData = mLockWallpaperMap.get(userId);
+ boolean restoreFromOld = !mWallpaperMap.contains(userId);
WallpaperDataParser.WallpaperLoadingResult result = mWallpaperDataParser.loadSettingsLocked(
- userId, keepDimensionHints, wallpaperData, lockWallpaperData, which);
+ userId, keepDimensionHints, restoreFromOld, which);
- boolean updateSystem = !mIsLockscreenLiveWallpaperEnabled || (which & FLAG_SYSTEM) != 0;
- boolean updateLock = !mIsLockscreenLiveWallpaperEnabled || (which & FLAG_LOCK) != 0;
+ boolean updateSystem = (which & FLAG_SYSTEM) != 0;
+ boolean updateLock = (which & FLAG_LOCK) != 0;
if (updateSystem) mWallpaperMap.put(userId, result.getSystemWallpaperData());
if (updateLock) {
@@ -4243,8 +3690,6 @@
if (mFallbackWallpaper != null) {
dumpWallpaper(mFallbackWallpaper, pw);
}
- pw.print("mIsLockscreenLiveWallpaperEnabled=");
- pw.println(mIsLockscreenLiveWallpaperEnabled);
}
}
}
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index 06448d0..022ef61 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -28,6 +28,7 @@
import android.annotation.Nullable;
import android.content.res.Configuration;
import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.media.projection.IMediaProjectionManager;
import android.os.IBinder;
@@ -36,10 +37,12 @@
import android.view.ContentRecordingSession;
import android.view.ContentRecordingSession.RecordContent;
import android.view.Display;
+import android.view.DisplayInfo;
import android.view.SurfaceControl;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.server.display.feature.DisplayManagerFlags;
/**
* Manages content recording for a particular {@link DisplayContent}.
@@ -47,6 +50,16 @@
final class ContentRecorder implements WindowContainerListener {
/**
+ * Maximum acceptable anisotropy for the output image.
+ *
+ * Necessary to avoid unnecessary scaling when the anisotropy is almost the same, as it is not
+ * exact anyway. For external displays, we expect an anisoptry of about 2% even if the pixels
+ * are, in fact, square due to the imprecision of the display's actual size (rounded to the
+ * nearest cm).
+ */
+ private static final float MAX_ANISOTROPY = 0.025f;
+
+ /**
* The display content this class is handling recording for.
*/
@NonNull
@@ -87,15 +100,20 @@
@Configuration.Orientation
private int mLastOrientation = ORIENTATION_UNDEFINED;
+ private final boolean mCorrectForAnisotropicPixels;
+
ContentRecorder(@NonNull DisplayContent displayContent) {
- this(displayContent, new RemoteMediaProjectionManagerWrapper(displayContent.mDisplayId));
+ this(displayContent, new RemoteMediaProjectionManagerWrapper(displayContent.mDisplayId),
+ new DisplayManagerFlags().isConnectedDisplayManagementEnabled());
}
@VisibleForTesting
ContentRecorder(@NonNull DisplayContent displayContent,
- @NonNull MediaProjectionManagerWrapper mediaProjectionManager) {
+ @NonNull MediaProjectionManagerWrapper mediaProjectionManager,
+ boolean correctForAnisotropicPixels) {
mDisplayContent = displayContent;
mMediaProjectionManager = mediaProjectionManager;
+ mCorrectForAnisotropicPixels = correctForAnisotropicPixels;
}
/**
@@ -460,6 +478,33 @@
}
}
+ private void computeScaling(int inputSizeX, int inputSizeY,
+ float inputDpiX, float inputDpiY,
+ int outputSizeX, int outputSizeY,
+ float outputDpiX, float outputDpiY,
+ PointF scaleOut) {
+ float relAnisotropy = (inputDpiY / inputDpiX) / (outputDpiY / outputDpiX);
+ if (!mCorrectForAnisotropicPixels
+ || (relAnisotropy > (1 - MAX_ANISOTROPY) && relAnisotropy < (1 + MAX_ANISOTROPY))) {
+ // Calculate the scale to apply to the root mirror SurfaceControl to fit the size of the
+ // output surface.
+ float scaleX = outputSizeX / (float) inputSizeX;
+ float scaleY = outputSizeY / (float) inputSizeY;
+ float scale = Math.min(scaleX, scaleY);
+ scaleOut.x = scale;
+ scaleOut.y = scale;
+ return;
+ }
+
+ float relDpiX = outputDpiX / inputDpiX;
+ float relDpiY = outputDpiY / inputDpiY;
+
+ float scale = Math.min(outputSizeX / relDpiX / inputSizeX,
+ outputSizeY / relDpiY / inputSizeY);
+ scaleOut.x = scale * relDpiX;
+ scaleOut.y = scale * relDpiY;
+ }
+
/**
* Apply transformations to the mirrored surface to ensure the captured contents are scaled to
* fit and centred in the output surface.
@@ -473,13 +518,19 @@
*/
@VisibleForTesting void updateMirroredSurface(SurfaceControl.Transaction transaction,
Rect recordedContentBounds, Point surfaceSize) {
- // Calculate the scale to apply to the root mirror SurfaceControl to fit the size of the
- // output surface.
- float scaleX = surfaceSize.x / (float) recordedContentBounds.width();
- float scaleY = surfaceSize.y / (float) recordedContentBounds.height();
- float scale = Math.min(scaleX, scaleY);
- int scaledWidth = Math.round(scale * (float) recordedContentBounds.width());
- int scaledHeight = Math.round(scale * (float) recordedContentBounds.height());
+
+ DisplayInfo inputDisplayInfo = mRecordedWindowContainer.mDisplayContent.getDisplayInfo();
+ DisplayInfo outputDisplayInfo = mDisplayContent.getDisplayInfo();
+
+ PointF scale = new PointF();
+ computeScaling(recordedContentBounds.width(), recordedContentBounds.height(),
+ inputDisplayInfo.physicalXDpi, inputDisplayInfo.physicalYDpi,
+ surfaceSize.x, surfaceSize.y,
+ outputDisplayInfo.physicalXDpi, outputDisplayInfo.physicalYDpi,
+ scale);
+
+ int scaledWidth = Math.round(scale.x * (float) recordedContentBounds.width());
+ int scaledHeight = Math.round(scale.y * (float) recordedContentBounds.height());
// Calculate the shift to apply to the root mirror SurfaceControl to centre the mirrored
// contents in the output surface.
@@ -493,10 +544,10 @@
}
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
- "Content Recording: Apply transformations of shift %d x %d, scale %f, crop (aka "
- + "recorded content size) %d x %d for display %d; display has size %d x "
- + "%d; surface has size %d x %d",
- shiftedX, shiftedY, scale, recordedContentBounds.width(),
+ "Content Recording: Apply transformations of shift %d x %d, scale %f x %f, crop "
+ + "(aka recorded content size) %d x %d for display %d; display has size "
+ + "%d x %d; surface has size %d x %d",
+ shiftedX, shiftedY, scale.x, scale.y, recordedContentBounds.width(),
recordedContentBounds.height(), mDisplayContent.getDisplayId(),
mDisplayContent.getConfiguration().screenWidthDp,
mDisplayContent.getConfiguration().screenHeightDp, surfaceSize.x, surfaceSize.y);
@@ -508,7 +559,7 @@
recordedContentBounds.height())
// Scale the root mirror SurfaceControl, based upon the size difference between the
// source (DisplayArea to capture) and output (surface the app reads images from).
- .setMatrix(mRecordedSurface, scale, 0 /* dtdx */, 0 /* dtdy */, scale)
+ .setMatrix(mRecordedSurface, scale.x, 0 /* dtdx */, 0 /* dtdy */, scale.y)
// Position needs to be updated when the mirrored DisplayArea has changed, since
// the content will no longer be centered in the output surface.
.setPosition(mRecordedSurface, shiftedX /* x */, shiftedY /* y */);
diff --git a/services/core/java/com/android/server/wm/SnapshotController.java b/services/core/java/com/android/server/wm/SnapshotController.java
index 2e7ff7a..f68e392 100644
--- a/services/core/java/com/android/server/wm/SnapshotController.java
+++ b/services/core/java/com/android/server/wm/SnapshotController.java
@@ -85,7 +85,7 @@
if (info.mWindowingMode == WINDOWING_MODE_PINNED) continue;
if (info.mContainer.isActivityTypeHome()) continue;
final Task task = info.mContainer.asTask();
- if (task != null && !task.isVisibleRequested()) {
+ if (task != null && !task.mCreatedByOrganizer && !task.isVisibleRequested()) {
mTaskSnapshotController.recordSnapshot(task, info);
}
// Won't need to capture activity snapshot in close transition.
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 94e66ff..33ef3c5 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -45,7 +45,6 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.util.ArraySet;
import android.util.MathUtils;
import android.util.Slog;
@@ -117,8 +116,6 @@
private boolean mShouldOffsetWallpaperCenter;
- final boolean mIsLockscreenLiveWallpaperEnabled;
-
private final Consumer<WindowState> mFindWallpapers = w -> {
if (w.mAttrs.type == TYPE_WALLPAPER) {
WallpaperWindowToken token = w.mToken.asWallpaperToken();
@@ -236,9 +233,6 @@
WallpaperController(WindowManagerService service, DisplayContent displayContent) {
mService = service;
mDisplayContent = displayContent;
- mIsLockscreenLiveWallpaperEnabled =
- SystemProperties.getBoolean("persist.wm.debug.lockscreen_live_wallpaper", true);
-
Resources resources = service.mContext.getResources();
mMinWallpaperScale =
resources.getFloat(com.android.internal.R.dimen.config_wallpaperMinScale);
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 1ed1431..15bd607 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -82,18 +82,16 @@
return;
}
mShowWhenLocked = showWhenLocked;
- if (mDisplayContent.mWallpaperController.mIsLockscreenLiveWallpaperEnabled) {
- // Move the window token to the front (private) or back (showWhenLocked). This is
- // possible
- // because the DisplayArea underneath TaskDisplayArea only contains TYPE_WALLPAPER
- // windows.
- final int position = showWhenLocked ? POSITION_BOTTOM : POSITION_TOP;
+ // Move the window token to the front (private) or back (showWhenLocked). This is
+ // possible
+ // because the DisplayArea underneath TaskDisplayArea only contains TYPE_WALLPAPER
+ // windows.
+ final int position = showWhenLocked ? POSITION_BOTTOM : POSITION_TOP;
- // Note: Moving all the way to the front or back breaks ordering based on addition
- // times.
- // We should never have more than one non-animating token of each type.
- getParent().positionChildAt(position, this /* child */, false /*includingParents */);
- }
+ // Note: Moving all the way to the front or back breaks ordering based on addition
+ // times.
+ // We should never have more than one non-animating token of each type.
+ getParent().positionChildAt(position, this /* child */, false /*includingParents */);
}
boolean canShowWhenLocked() {
diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp
index 6eacef7..c617ec4 100644
--- a/services/tests/PackageManagerServiceTests/host/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/Android.bp
@@ -58,6 +58,7 @@
":PackageManagerTestOverlayTarget",
":PackageManagerTestOverlayTargetNoOverlayable",
":PackageManagerTestAppDeclaresStaticLibrary",
+ ":PackageManagerTestAppDifferentPkgName",
":PackageManagerTestAppStub",
":PackageManagerTestAppUsesStaticLibrary",
":PackageManagerTestAppVersion1",
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/TamperedUpdatedSystemPackageTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/TamperedUpdatedSystemPackageTest.kt
index c490604..304f605 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/TamperedUpdatedSystemPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/TamperedUpdatedSystemPackageTest.kt
@@ -44,6 +44,10 @@
private const val VERSION_TWO_ALT_KEY = "PackageManagerTestAppVersion2AltKey.apk"
private const val VERSION_TWO_ALT_KEY_IDSIG =
"PackageManagerTestAppVersion2AltKey.apk.idsig"
+
+ private const val ANOTHER_PKG_NAME = "com.android.server.pm.test.test_app2"
+ private const val ANOTHER_PKG = "PackageManagerTestAppDifferentPkgName.apk"
+
private const val STRICT_SIGNATURE_CONFIG_PATH =
"/system/etc/sysconfig/preinstalled-packages-strict-signature.xml"
private const val TIMESTAMP_REFERENCE_FILE_PATH = "/data/local/tmp/timestamp.ref"
@@ -74,6 +78,7 @@
@After
fun removeApk() {
device.uninstallPackage(TEST_PKG_NAME)
+ device.uninstallPackage(ANOTHER_PKG_NAME)
}
@Before
@@ -90,7 +95,9 @@
.readText()
.replace(
"</config>",
- "<require-strict-signature package=\"${TEST_PKG_NAME}\"/></config>"
+ "<require-strict-signature package=\"${TEST_PKG_NAME}\"/>" +
+ "<require-strict-signature package=\"${ANOTHER_PKG_NAME}\"/>" +
+ "</config>"
)
writeText(newConfigText)
}
@@ -146,10 +153,7 @@
tempFolder.newFile()
)
assertThat(device.installPackage(versionTwoFile, true)).isNull()
- val baseApkPath = device.executeShellCommand("pm path ${TEST_PKG_NAME}")
- .lineSequence()
- .first()
- .replace("package:", "")
+ val baseApkPath = getBaseApkPath(TEST_PKG_NAME)
assertThat(baseApkPath).doesNotContain(productPath.toString())
preparer.pushResourceFile(VERSION_TWO_ALT_KEY_IDSIG, baseApkPath.toString() + ".idsig")
@@ -175,4 +179,23 @@
assertThat(device.executeShellCommand("pm path ${TEST_PKG_NAME}"))
.contains(productPath.toString())
}
+
+ @Test
+ fun allowlistedPackageIsNotASystemApp() {
+ // If an allowlisted package isn't a system app, make sure install and boot still works
+ // normally.
+ assertThat(device.installJavaResourceApk(tempFolder, ANOTHER_PKG, /* reinstall */ false))
+ .isNull()
+ assertThat(getBaseApkPath(ANOTHER_PKG_NAME)).startsWith("/data/app/")
+
+ preparer.reboot()
+ assertThat(getBaseApkPath(ANOTHER_PKG_NAME)).startsWith("/data/app/")
+ }
+
+ private fun getBaseApkPath(pkgName: String): String {
+ return device.executeShellCommand("pm path $pkgName")
+ .lineSequence()
+ .first()
+ .replace("package:", "")
+ }
}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/Generic/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/Generic/Android.bp
index bee7c40..b826590 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/Generic/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/Generic/Android.bp
@@ -76,3 +76,11 @@
certificate: ":FrameworksServicesTests_keyset_A_cert",
v4_signature: true,
}
+
+android_test_helper_app {
+ name: "PackageManagerTestAppDifferentPkgName",
+ manifest: "AndroidManifestDifferentPkgName.xml",
+ srcs: [
+ "src/**/*.kt",
+ ],
+}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/Generic/AndroidManifestDifferentPkgName.xml b/services/tests/PackageManagerServiceTests/host/test-apps/Generic/AndroidManifestDifferentPkgName.xml
new file mode 100644
index 0000000..0c5d36e
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/Generic/AndroidManifestDifferentPkgName.xml
@@ -0,0 +1,33 @@
+<?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.
+ -->
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.pm.test.test_app2"
+ android:versionCode="1"
+ >
+
+ <permission
+ android:name="com.android.server.pm.test.test_app.TEST_PERMISSION"
+ android:protectionLevel="normal"
+ />
+
+ <application>
+ <activity android:name="com.android.server.pm.test.test_app.TestActivity"
+ android:label="PackageManagerTestApp" />
+ </application>
+
+</manifest>
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
index eefe5af..3dbab13 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -462,7 +462,7 @@
wallpaper.wallpaperObserver.stopWatching();
spyOn(wallpaper.wallpaperObserver);
- doReturn(wallpaper).when(wallpaper.wallpaperObserver).dataForEvent(true, false);
+ doReturn(wallpaper).when(wallpaper.wallpaperObserver).dataForEvent(false);
wallpaper.wallpaperObserver.onEvent(CLOSE_WRITE, WALLPAPER);
// ACTION_WALLPAPER_CHANGED should be invoked before onWallpaperColorsChanged.
diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
index 6bfd93b..4bb7d63 100644
--- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
@@ -4,6 +4,8 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static com.android.server.job.JobStore.JOB_FILE_SPLIT_PREFIX;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -46,6 +48,7 @@
import org.junit.runner.RunWith;
import java.io.File;
+import java.nio.file.Files;
import java.time.Clock;
import java.time.ZoneOffset;
import java.util.ArrayList;
@@ -209,6 +212,43 @@
assertEquals("Incorrect # of persisted tasks.", 0, jobStatusSet.size());
}
+ @Test
+ public void testSkipExtraFiles() throws Exception {
+ setUseSplitFiles(true);
+ final JobInfo task1 = new Builder(8, mComponent)
+ .setRequiresDeviceIdle(true)
+ .setPeriodic(10000L)
+ .setRequiresCharging(true)
+ .setPersisted(true)
+ .build();
+ final JobInfo task2 = new Builder(12, mComponent)
+ .setMinimumLatency(5000L)
+ .setBackoffCriteria(15000L, JobInfo.BACKOFF_POLICY_LINEAR)
+ .setOverrideDeadline(30000L)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
+ .setPersisted(true)
+ .build();
+ final int uid1 = SOME_UID;
+ final int uid2 = uid1 + 1;
+ final JobStatus JobStatus1 = JobStatus.createFromJobInfo(task1, uid1, null, -1, null, null);
+ final JobStatus JobStatus2 = JobStatus.createFromJobInfo(task2, uid2, null, -1, null, null);
+ runWritingJobsToDisk(JobStatus1, JobStatus2);
+
+ final File rootDir = new File(mTestContext.getFilesDir(), "system/job");
+ final File file1 = new File(rootDir, JOB_FILE_SPLIT_PREFIX + uid1 + ".xml");
+ final File file2 = new File(rootDir, JOB_FILE_SPLIT_PREFIX + uid2 + ".xml");
+
+ Files.copy(file1.toPath(),
+ new File(rootDir, JOB_FILE_SPLIT_PREFIX + uid1 + ".xml.bak").toPath());
+ Files.copy(file1.toPath(), new File(rootDir, "random.xml").toPath());
+ Files.copy(file2.toPath(),
+ new File(rootDir, "blah" + JOB_FILE_SPLIT_PREFIX + uid1 + ".xml").toPath());
+
+ JobSet jobStatusSet = new JobSet();
+ mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
+ assertEquals("Incorrect # of persisted tasks.", 2, jobStatusSet.size());
+ }
+
/**
* Test that dynamic constraints aren't written to disk.
*/
@@ -254,22 +294,22 @@
file = new File(mTestContext.getFilesDir(), "10000");
assertEquals(JobStore.INVALID_UID, JobStore.extractUidFromJobFileName(file));
- file = new File(mTestContext.getFilesDir(), JobStore.JOB_FILE_SPLIT_PREFIX);
+ file = new File(mTestContext.getFilesDir(), JOB_FILE_SPLIT_PREFIX);
assertEquals(JobStore.INVALID_UID, JobStore.extractUidFromJobFileName(file));
- file = new File(mTestContext.getFilesDir(), JobStore.JOB_FILE_SPLIT_PREFIX + "text.xml");
+ file = new File(mTestContext.getFilesDir(), JOB_FILE_SPLIT_PREFIX + "text.xml");
assertEquals(JobStore.INVALID_UID, JobStore.extractUidFromJobFileName(file));
- file = new File(mTestContext.getFilesDir(), JobStore.JOB_FILE_SPLIT_PREFIX + ".xml");
+ file = new File(mTestContext.getFilesDir(), JOB_FILE_SPLIT_PREFIX + ".xml");
assertEquals(JobStore.INVALID_UID, JobStore.extractUidFromJobFileName(file));
- file = new File(mTestContext.getFilesDir(), JobStore.JOB_FILE_SPLIT_PREFIX + "-10123.xml");
+ file = new File(mTestContext.getFilesDir(), JOB_FILE_SPLIT_PREFIX + "-10123.xml");
assertEquals(JobStore.INVALID_UID, JobStore.extractUidFromJobFileName(file));
- file = new File(mTestContext.getFilesDir(), JobStore.JOB_FILE_SPLIT_PREFIX + "1.xml");
+ file = new File(mTestContext.getFilesDir(), JOB_FILE_SPLIT_PREFIX + "1.xml");
assertEquals(1, JobStore.extractUidFromJobFileName(file));
- file = new File(mTestContext.getFilesDir(), JobStore.JOB_FILE_SPLIT_PREFIX + "101023.xml");
+ file = new File(mTestContext.getFilesDir(), JOB_FILE_SPLIT_PREFIX + "101023.xml");
assertEquals(101023, JobStore.extractUidFromJobFileName(file));
}
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index f94aff7..9f6d7f2 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -27,6 +27,7 @@
import static android.view.Display.INVALID_DISPLAY;
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CAPTURING_IN_PROGRESS;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -39,6 +40,7 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
@@ -133,7 +135,7 @@
private final MediaProjectionManagerService.Injector mMediaProjectionMetricsLoggerInjector =
new MediaProjectionManagerService.Injector() {
@Override
- MediaProjectionMetricsLogger mediaProjectionMetricsLogger() {
+ MediaProjectionMetricsLogger mediaProjectionMetricsLogger(Context context) {
return mMediaProjectionMetricsLogger;
}
};
@@ -311,6 +313,70 @@
}
@Test
+ public void stop_noActiveProjections_doesNotLog() throws Exception {
+ MediaProjectionManagerService service =
+ new MediaProjectionManagerService(mContext, mMediaProjectionMetricsLoggerInjector);
+ MediaProjectionManagerService.MediaProjection projection =
+ startProjectionPreconditions(service);
+
+ projection.stop();
+
+ verifyZeroInteractions(mMediaProjectionMetricsLogger);
+ }
+
+ @Test
+ public void stop_noSession_logsHostUidAndUnknownTargetUid() throws Exception {
+ MediaProjectionManagerService service =
+ new MediaProjectionManagerService(mContext, mMediaProjectionMetricsLoggerInjector);
+ MediaProjectionManagerService.MediaProjection projection =
+ startProjectionPreconditions(service);
+ projection.start(mIMediaProjectionCallback);
+
+ projection.stop();
+
+ verify(mMediaProjectionMetricsLogger)
+ .logStopped(UID, ContentRecordingSession.TARGET_UID_UNKNOWN);
+ }
+
+ @Test
+ public void stop_displaySession_logsHostUidAndUnknownTargetUidFullScreen() throws Exception {
+ MediaProjectionManagerService service =
+ new MediaProjectionManagerService(mContext, mMediaProjectionMetricsLoggerInjector);
+ MediaProjectionManagerService.MediaProjection projection =
+ startProjectionPreconditions(service);
+ projection.start(mIMediaProjectionCallback);
+ doReturn(true)
+ .when(mWindowManagerInternal)
+ .setContentRecordingSession(any(ContentRecordingSession.class));
+ service.setContentRecordingSession(DISPLAY_SESSION);
+
+ projection.stop();
+
+ verify(mMediaProjectionMetricsLogger)
+ .logStopped(UID, ContentRecordingSession.TARGET_UID_FULL_SCREEN);
+ }
+
+ @Test
+ public void stop_taskSession_logsHostUidAndTargetUid() throws Exception {
+ int targetUid = 1234;
+ MediaProjectionManagerService service =
+ new MediaProjectionManagerService(mContext, mMediaProjectionMetricsLoggerInjector);
+ MediaProjectionManagerService.MediaProjection projection =
+ startProjectionPreconditions(service);
+ projection.start(mIMediaProjectionCallback);
+ doReturn(true)
+ .when(mWindowManagerInternal)
+ .setContentRecordingSession(any(ContentRecordingSession.class));
+ ContentRecordingSession taskSession =
+ ContentRecordingSession.createTaskSession(mock(IBinder.class), targetUid);
+ service.setContentRecordingSession(taskSession);
+
+ projection.stop();
+
+ verify(mMediaProjectionMetricsLogger).logStopped(UID, targetUid);
+ }
+
+ @Test
public void testIsValid_multipleStarts_preventionDisabled() throws NameNotFoundException {
MediaProjectionManagerService service = new MediaProjectionManagerService(mContext,
mPreventReusedTokenDisabledInjector);
@@ -586,6 +652,18 @@
/* isSetSessionSuccessful= */ false, RECORD_CANCEL);
}
+ @Test
+ public void notifyPermissionRequestInitiated_forwardsToLogger() {
+ int hostUid = 123;
+ int sessionCreationSource = 456;
+ mService =
+ new MediaProjectionManagerService(mContext, mMediaProjectionMetricsLoggerInjector);
+
+ mService.notifyPermissionRequestInitiated(hostUid, sessionCreationSource);
+
+ verify(mMediaProjectionMetricsLogger).logInitiated(hostUid, sessionCreationSource);
+ }
+
/**
* Executes and validates scenario where the consent result indicates the projection ends.
*/
@@ -749,8 +827,10 @@
public void setContentRecordingSession_success_logsCaptureInProgress()
throws Exception {
mService.addCallback(mWatcherCallback);
- MediaProjectionManagerService service = new MediaProjectionManagerService(mContext, mMediaProjectionMetricsLoggerInjector);
- MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
+ MediaProjectionManagerService service =
+ new MediaProjectionManagerService(mContext, mMediaProjectionMetricsLoggerInjector);
+ MediaProjectionManagerService.MediaProjection projection =
+ startProjectionPreconditions(service);
projection.start(mIMediaProjectionCallback);
doReturn(true).when(mWindowManagerInternal).setContentRecordingSession(
any(ContentRecordingSession.class));
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java
new file mode 100644
index 0000000..73b4cc8
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media.projection;
+
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_STOPPED;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.time.Duration;
+
+/**
+ * Tests for the {@link MediaProjectionMetricsLoggerTest} class.
+ *
+ * <p>Build/Install/Run: atest FrameworksServicesTests:MediaProjectionMetricsLoggerTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class MediaProjectionMetricsLoggerTest {
+
+ private static final int TEST_HOST_UID = 123;
+ private static final int TEST_TARGET_UID = 456;
+ private static final int TEST_CREATION_SOURCE = 789;
+
+ @Mock private FrameworkStatsLogWrapper mFrameworkStatsLogWrapper;
+ @Mock private MediaProjectionSessionIdGenerator mSessionIdGenerator;
+ @Mock private MediaProjectionTimestampStore mTimestampStore;
+
+ private MediaProjectionMetricsLogger mLogger;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mLogger =
+ new MediaProjectionMetricsLogger(
+ mFrameworkStatsLogWrapper, mSessionIdGenerator, mTimestampStore);
+ }
+
+ @Test
+ public void logInitiated_logsStateChangedAtomId() {
+ mLogger.logInitiated(TEST_HOST_UID, TEST_CREATION_SOURCE);
+
+ verifyStateChangedAtomIdLogged();
+ }
+
+ @Test
+ public void logInitiated_logsStateInitiated() {
+ mLogger.logInitiated(TEST_HOST_UID, TEST_CREATION_SOURCE);
+
+ verifyStateLogged(MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED);
+ }
+
+ @Test
+ public void logInitiated_logsHostUid() {
+ mLogger.logInitiated(TEST_HOST_UID, TEST_CREATION_SOURCE);
+
+ verifyHostUidLogged(TEST_HOST_UID);
+ }
+
+ @Test
+ public void logInitiated_logsSessionCreationSource() {
+ mLogger.logInitiated(TEST_HOST_UID, TEST_CREATION_SOURCE);
+
+ verifyCreationSourceLogged(TEST_CREATION_SOURCE);
+ }
+
+ @Test
+ public void logInitiated_logsUnknownTargetUid() {
+ mLogger.logInitiated(TEST_HOST_UID, TEST_CREATION_SOURCE);
+
+ verifyTargetUidLogged(-2);
+ }
+
+ @Test
+ public void logInitiated_noPreviousSession_logsUnknownTimeSinceLastActive() {
+ when(mTimestampStore.timeSinceLastActiveSession()).thenReturn(null);
+
+ mLogger.logInitiated(TEST_HOST_UID, TEST_CREATION_SOURCE);
+
+ verifyTimeSinceLastActiveSessionLogged(-1);
+ }
+
+ @Test
+ public void logInitiated_previousSession_logsTimeSinceLastActiveInSeconds() {
+ Duration timeSinceLastActiveSession = Duration.ofHours(1234);
+ when(mTimestampStore.timeSinceLastActiveSession()).thenReturn(timeSinceLastActiveSession);
+
+ mLogger.logInitiated(TEST_HOST_UID, TEST_CREATION_SOURCE);
+
+ verifyTimeSinceLastActiveSessionLogged((int) timeSinceLastActiveSession.toSeconds());
+ }
+
+ @Test
+ public void logInitiated_logsNewSessionId() {
+ int newSessionId = 123;
+ when(mSessionIdGenerator.createAndGetNewSessionId()).thenReturn(newSessionId);
+
+ mLogger.logInitiated(TEST_HOST_UID, TEST_CREATION_SOURCE);
+
+ verifySessionIdLogged(newSessionId);
+ }
+
+ @Test
+ public void logInitiated_logsPreviousState() {
+ mLogger.logInitiated(TEST_HOST_UID, TEST_CREATION_SOURCE);
+ verifyPreviousStateLogged(
+ MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN);
+
+ mLogger.logInitiated(TEST_HOST_UID, TEST_CREATION_SOURCE);
+ verifyPreviousStateLogged(
+ MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED);
+ }
+
+ @Test
+ public void logStopped_logsStateChangedAtomId() {
+ mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID);
+
+ verifyStateChangedAtomIdLogged();
+ }
+
+ @Test
+ public void logStopped_logsCurrentSessionId() {
+ int currentSessionId = 987;
+ when(mSessionIdGenerator.getCurrentSessionId()).thenReturn(currentSessionId);
+
+ mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID);
+
+ verifySessionIdLogged(currentSessionId);
+ }
+
+ @Test
+ public void logStopped_logsStateStopped() {
+ mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID);
+
+ verifyStateLogged(MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_STOPPED);
+ }
+
+ @Test
+ public void logStopped_logsHostUid() {
+ mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID);
+
+ verifyHostUidLogged(TEST_HOST_UID);
+ }
+
+ @Test
+ public void logStopped_logsTargetUid() {
+ mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID);
+
+ verifyTargetUidLogged(TEST_TARGET_UID);
+ }
+
+ @Test
+ public void logStopped_logsUnknownTimeSinceLastActive() {
+ mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID);
+
+ verifyTimeSinceLastActiveSessionLogged(-1);
+ }
+
+ @Test
+ public void logStopped_logsUnknownSessionCreationSource() {
+ mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID);
+
+ verifyCreationSourceLogged(
+ MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN);
+ }
+
+ @Test
+ public void logStopped_logsPreviousState() {
+ mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID);
+ verifyPreviousStateLogged(
+ MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN);
+
+ mLogger.logInitiated(TEST_HOST_UID, TEST_CREATION_SOURCE);
+ verifyPreviousStateLogged(
+ MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_STOPPED);
+
+ mLogger.logStopped(TEST_HOST_UID, TEST_CREATION_SOURCE);
+ verifyPreviousStateLogged(
+ MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED);
+ }
+
+ @Test
+ public void logStopped_registersActiveSessionEnded_afterLogging() {
+ mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID);
+
+ InOrder inOrder = inOrder(mFrameworkStatsLogWrapper, mTimestampStore);
+ inOrder.verify(mFrameworkStatsLogWrapper)
+ .write(
+ /* code= */ anyInt(),
+ /* sessionId= */ anyInt(),
+ /* state= */ anyInt(),
+ /* previousState= */ anyInt(),
+ /* hostUid= */ anyInt(),
+ /* targetUid= */ anyInt(),
+ /* timeSinceLastActive= */ anyInt(),
+ /* creationSource= */ anyInt());
+ inOrder.verify(mTimestampStore).registerActiveSessionEnded();
+ }
+
+ private void verifyStateChangedAtomIdLogged() {
+ verify(mFrameworkStatsLogWrapper)
+ .write(
+ /* code= */ eq(FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED),
+ /* sessionId= */ anyInt(),
+ /* state= */ anyInt(),
+ /* previousState= */ anyInt(),
+ /* hostUid= */ anyInt(),
+ /* targetUid= */ anyInt(),
+ /* timeSinceLastActive= */ anyInt(),
+ /* creationSource= */ anyInt());
+ }
+
+ private void verifyStateLogged(int state) {
+ verify(mFrameworkStatsLogWrapper)
+ .write(
+ /* code= */ anyInt(),
+ /* sessionId= */ anyInt(),
+ eq(state),
+ /* previousState= */ anyInt(),
+ /* hostUid= */ anyInt(),
+ /* targetUid= */ anyInt(),
+ /* timeSinceLastActive= */ anyInt(),
+ /* creationSource= */ anyInt());
+ }
+
+ private void verifyHostUidLogged(int hostUid) {
+ verify(mFrameworkStatsLogWrapper)
+ .write(
+ /* code= */ anyInt(),
+ /* sessionId= */ anyInt(),
+ /* state= */ anyInt(),
+ /* previousState= */ anyInt(),
+ eq(hostUid),
+ /* targetUid= */ anyInt(),
+ /* timeSinceLastActive= */ anyInt(),
+ /* creationSource= */ anyInt());
+ }
+
+ private void verifyCreationSourceLogged(int creationSource) {
+ verify(mFrameworkStatsLogWrapper)
+ .write(
+ /* code= */ anyInt(),
+ /* sessionId= */ anyInt(),
+ /* state= */ anyInt(),
+ /* previousState= */ anyInt(),
+ /* hostUid= */ anyInt(),
+ /* targetUid= */ anyInt(),
+ /* timeSinceLastActive= */ anyInt(),
+ eq(creationSource));
+ }
+
+ private void verifyTargetUidLogged(int targetUid) {
+ verify(mFrameworkStatsLogWrapper)
+ .write(
+ /* code= */ anyInt(),
+ /* sessionId= */ anyInt(),
+ /* state= */ anyInt(),
+ /* previousState= */ anyInt(),
+ /* hostUid= */ anyInt(),
+ eq(targetUid),
+ /* timeSinceLastActive= */ anyInt(),
+ /* creationSource= */ anyInt());
+ }
+
+ private void verifyTimeSinceLastActiveSessionLogged(int timeSinceLastActiveSession) {
+ verify(mFrameworkStatsLogWrapper)
+ .write(
+ /* code= */ anyInt(),
+ /* sessionId= */ anyInt(),
+ /* state= */ anyInt(),
+ /* previousState= */ anyInt(),
+ /* hostUid= */ anyInt(),
+ /* targetUid= */ anyInt(),
+ /* timeSinceLastActive= */ eq(timeSinceLastActiveSession),
+ /* creationSource= */ anyInt());
+ }
+
+ private void verifySessionIdLogged(int newSessionId) {
+ verify(mFrameworkStatsLogWrapper)
+ .write(
+ /* code= */ anyInt(),
+ /* sessionId= */ eq(newSessionId),
+ /* state= */ anyInt(),
+ /* previousState= */ anyInt(),
+ /* hostUid= */ anyInt(),
+ /* targetUid= */ anyInt(),
+ /* timeSinceLastActive= */ anyInt(),
+ /* creationSource= */ anyInt());
+ }
+
+ private void verifyPreviousStateLogged(int previousState) {
+ verify(mFrameworkStatsLogWrapper)
+ .write(
+ /* code= */ anyInt(),
+ /* sessionId= */ anyInt(),
+ /* state= */ anyInt(),
+ eq(previousState),
+ /* hostUid= */ anyInt(),
+ /* targetUid= */ anyInt(),
+ /* timeSinceLastActive= */ anyInt(),
+ /* creationSource= */ anyInt());
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
index 9bd938f..cf8548c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
@@ -80,7 +80,9 @@
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
import android.view.accessibility.IAccessibilityManagerClient;
+
import androidx.test.runner.AndroidJUnit4;
+
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags;
import com.android.internal.config.sysui.TestableFlagResolver;
import com.android.internal.logging.InstanceIdSequence;
@@ -93,6 +95,7 @@
import java.util.List;
import java.util.Objects;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -190,7 +193,7 @@
assertTrue(mAccessibilityManager.isEnabled());
// TODO (b/291907312): remove feature flag
- mTestFlagResolver.setFlagOverride(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR, true);
+ mSetFlagsRule.enableFlags(Flags.FLAG_REFACTOR_ATTENTION_HELPER);
// Disable feature flags by default. Tests should enable as needed.
mSetFlagsRule.disableFlags(Flags.FLAG_POLITE_NOTIFICATIONS, Flags.FLAG_EXPIRE_BITMAPS);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 75d012a..6792cfe 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -71,6 +71,7 @@
import static android.os.UserManager.USER_TYPE_FULL_SECONDARY;
import static android.os.UserManager.USER_TYPE_PROFILE_CLONE;
import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
+import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE;
import static android.service.notification.Adjustment.KEY_IMPORTANCE;
import static android.service.notification.Adjustment.KEY_USER_SENTIMENT;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
@@ -84,7 +85,6 @@
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
-import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR;
import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.FSI_FORCE_DEMOTE;
import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
@@ -207,6 +207,7 @@
import android.os.UserManager;
import android.os.WorkSource;
import android.permission.PermissionManager;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.DeviceConfig;
import android.provider.MediaStore;
import android.provider.Settings;
@@ -318,6 +319,7 @@
private static final int UID_HEADLESS = 1_000_000;
private static final int TOAST_DURATION = 2_000;
private static final int SECONDARY_DISPLAY_ID = 42;
+ private static final int TEST_PROFILE_USERHANDLE = 12;
private final int mUid = Binder.getCallingUid();
private final @UserIdInt int mUserId = UserHandle.getUserId(mUid);
@@ -445,7 +447,7 @@
TestableNotificationManagerService.StrongAuthTrackerFake mStrongAuthTracker;
TestableFlagResolver mTestFlagResolver = new TestableFlagResolver();
-
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake(
1 << 30);
@Mock
@@ -611,7 +613,8 @@
});
// TODO (b/291907312): remove feature flag
- mTestFlagResolver.setFlagOverride(ENABLE_ATTENTION_HELPER_REFACTOR, false);
+ mSetFlagsRule.disableFlags(Flags.FLAG_REFACTOR_ATTENTION_HELPER,
+ Flags.FLAG_POLITE_NOTIFICATIONS);
initNMS();
}
@@ -652,7 +655,7 @@
verify(mHistoryManager).onBootPhaseAppsCanStart();
// TODO b/291907312: remove feature flag
- if (mTestFlagResolver.isEnabled(ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ if (Flags.refactorAttentionHelper()) {
mService.mAttentionHelper.setAudioManager(mAudioManager);
} else {
mService.setAudioManager(mAudioManager);
@@ -826,6 +829,12 @@
mPackageIntentReceiver.onReceive(getContext(), intent);
}
+ private void simulateProfileAvailabilityActions(String intentAction) {
+ final Intent intent = new Intent(intentAction);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, TEST_PROFILE_USERHANDLE);
+ mUserSwitchIntentReceiver.onReceive(mContext, intent);
+ }
+
private ArrayMap<Boolean, ArrayList<ComponentName>> generateResetComponentValues() {
ArrayMap<Boolean, ArrayList<ComponentName>> changed = new ArrayMap<>();
changed.put(true, new ArrayList<>());
@@ -1683,7 +1692,7 @@
@Test
public void testEnqueueNotificationWithTag_WritesExpectedLogs_NAHRefactor() throws Exception {
// TODO b/291907312: remove feature flag
- mTestFlagResolver.setFlagOverride(ENABLE_ATTENTION_HELPER_REFACTOR, true);
+ mSetFlagsRule.enableFlags(Flags.FLAG_REFACTOR_ATTENTION_HELPER);
// Cleanup NMS before re-initializing
if (mService != null) {
try {
@@ -9146,7 +9155,7 @@
public void testOnBubbleMetadataChangedToSuppressNotification_soundStopped_NAHRefactor()
throws Exception {
// TODO b/291907312: remove feature flag
- mTestFlagResolver.setFlagOverride(ENABLE_ATTENTION_HELPER_REFACTOR, true);
+ mSetFlagsRule.enableFlags(Flags.FLAG_REFACTOR_ATTENTION_HELPER);
// Cleanup NMS before re-initializing
if (mService != null) {
try {
@@ -12751,6 +12760,23 @@
verify(service, times(1)).setDNDMigrationDone(user.id);
}
+ @Test
+ public void testProfileUnavailableIntent() throws RemoteException {
+ mSetFlagsRule.enableFlags(FLAG_ALLOW_PRIVATE_PROFILE);
+ simulateProfileAvailabilityActions(Intent.ACTION_PROFILE_UNAVAILABLE);
+ verify(mWorkerHandler).post(any(Runnable.class));
+ verify(mSnoozeHelper).clearData(anyInt());
+ }
+
+
+ @Test
+ public void testManagedProfileUnavailableIntent() throws RemoteException {
+ mSetFlagsRule.disableFlags(FLAG_ALLOW_PRIVATE_PROFILE);
+ simulateProfileAvailabilityActions(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
+ verify(mWorkerHandler).post(any(Runnable.class));
+ verify(mSnoozeHelper).clearData(anyInt());
+ }
+
private NotificationRecord createAndPostNotification(Notification.Builder nb, String testName)
throws RemoteException {
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, testName, mUid, 0,
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index af39b2f..1b8d746 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -60,6 +60,7 @@
"truth",
"testables",
"hamcrest-library",
+ "flag-junit",
"platform-compat-test-rules",
"CtsSurfaceValidatorLib",
"service-sdksandbox.impl",
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
index 8db09f9..61c4d06 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
@@ -46,7 +46,6 @@
import static java.util.Collections.unmodifiableMap;
import android.content.Context;
-import android.os.Looper;
import android.os.SystemClock;
import android.util.ArrayMap;
import android.view.InputDevice;
@@ -99,10 +98,6 @@
* settings values.
*/
protected final void setUpPhoneWindowManager(boolean supportSettingsUpdate) {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
-
doReturn(mSettingsProviderRule.mockContentResolver(mContext))
.when(mContext).getContentResolver();
mPhoneWindowManager = new TestPhoneWindowManager(mContext, supportSettingsUpdate);
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 2244dbe..261d3cc 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -78,6 +78,7 @@
import android.os.UserHandle;
import android.os.Vibrator;
import android.os.VibratorInfo;
+import android.os.test.TestLooper;
import android.service.dreams.DreamManagerInternal;
import android.telecom.TelecomManager;
import android.util.FeatureFlagUtils;
@@ -160,12 +161,13 @@
@Mock private KeyguardServiceDelegate mKeyguardServiceDelegate;
private StaticMockitoSession mMockitoSession;
+ private TestLooper mTestLooper = new TestLooper();
private HandlerThread mHandlerThread;
private Handler mHandler;
private class TestInjector extends PhoneWindowManager.Injector {
TestInjector(Context context, WindowManagerPolicy.WindowManagerFuncs funcs) {
- super(context, funcs);
+ super(context, funcs, mTestLooper.getLooper());
}
AccessibilityShortcutController getAccessibilityShortcutController(
@@ -184,12 +186,10 @@
TestPhoneWindowManager(Context context, boolean supportSettingsUpdate) {
MockitoAnnotations.initMocks(this);
- mHandlerThread = new HandlerThread("fake window manager");
- mHandlerThread.start();
- mHandler = new Handler(mHandlerThread.getLooper());
+ mHandler = new Handler(mTestLooper.getLooper());
mContext = mockingDetails(context).isSpy() ? context : spy(context);
- mHandler.runWithScissors(() -> setUp(supportSettingsUpdate), 0 /* timeout */);
- waitForIdle();
+ mHandler.post(() -> setUp(supportSettingsUpdate));
+ mTestLooper.dispatchAll();
}
private void setUp(boolean supportSettingsUpdate) {
@@ -301,7 +301,6 @@
}
void tearDown() {
- mHandlerThread.quitSafely();
LocalServices.removeServiceForTest(InputMethodManagerInternal.class);
Mockito.reset(mPhoneWindowManager);
mMockitoSession.finishMocking();
@@ -327,10 +326,6 @@
mPhoneWindowManager.dispatchUnhandledKey(null /*focusedToken*/, event, FLAG_INTERACTIVE);
}
- void waitForIdle() {
- mHandler.runWithScissors(() -> { }, 0 /* timeout */);
- }
-
/**
* Below functions will override the setting or the policy behavior.
*/
@@ -504,13 +499,13 @@
* Below functions will check the policy behavior could be invoked.
*/
void assertTakeScreenshotCalled() {
- waitForIdle();
+ mTestLooper.dispatchAll();
verify(mDisplayPolicy, timeout(SHORTCUT_KEY_DELAY_MILLIS))
.takeScreenshot(anyInt(), anyInt());
}
void assertShowGlobalActionsCalled() {
- waitForIdle();
+ mTestLooper.dispatchAll();
verify(mPhoneWindowManager).showGlobalActions();
verify(mGlobalActions, timeout(SHORTCUT_KEY_DELAY_MILLIS))
.showDialog(anyBoolean(), anyBoolean());
@@ -519,53 +514,53 @@
}
void assertVolumeMute() {
- waitForIdle();
+ mTestLooper.dispatchAll();
verify(mAudioManagerInternal, timeout(SHORTCUT_KEY_DELAY_MILLIS))
.silenceRingerModeInternal(eq("volume_hush"));
}
void assertAccessibilityKeychordCalled() {
- waitForIdle();
+ mTestLooper.dispatchAll();
verify(mAccessibilityShortcutController,
timeout(SHORTCUT_KEY_DELAY_MILLIS)).performAccessibilityShortcut();
}
void assertDreamRequest() {
- waitForIdle();
+ mTestLooper.dispatchAll();
verify(mDreamManagerInternal).requestDream();
}
void assertPowerSleep() {
- waitForIdle();
+ mTestLooper.dispatchAll();
verify(mPowerManager,
timeout(SHORTCUT_KEY_DELAY_MILLIS)).goToSleep(anyLong(), anyInt(), anyInt());
}
void assertPowerWakeUp() {
- waitForIdle();
+ mTestLooper.dispatchAll();
verify(mPowerManager,
timeout(SHORTCUT_KEY_DELAY_MILLIS)).wakeUp(anyLong(), anyInt(), anyString());
}
void assertNoPowerSleep() {
- waitForIdle();
+ mTestLooper.dispatchAll();
verify(mPowerManager, never()).goToSleep(anyLong(), anyInt(), anyInt());
}
void assertCameraLaunch() {
- waitForIdle();
+ mTestLooper.dispatchAll();
// GestureLauncherService should receive interceptPowerKeyDown twice.
verify(mGestureLauncherService, times(2))
.interceptPowerKeyDown(any(), anyBoolean(), any());
}
void assertSearchManagerLaunchAssist() {
- waitForIdle();
+ mTestLooper.dispatchAll();
verify(mSearchManager, timeout(SHORTCUT_KEY_DELAY_MILLIS)).launchAssist(any());
}
void assertLaunchCategory(String category) {
- waitForIdle();
+ mTestLooper.dispatchAll();
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
try {
verify(mContext).startActivityAsUser(intentCaptor.capture(), any());
@@ -578,17 +573,17 @@
}
void assertShowRecentApps() {
- waitForIdle();
+ mTestLooper.dispatchAll();
verify(mStatusBarManagerInternal).showRecentApps(anyBoolean());
}
void assertStatusBarStartAssist() {
- waitForIdle();
+ mTestLooper.dispatchAll();
verify(mStatusBarManagerInternal).startAssist(any());
}
void assertSwitchKeyboardLayout(int direction) {
- waitForIdle();
+ mTestLooper.dispatchAll();
if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_UI)) {
verify(mInputMethodManagerInternal).switchKeyboardLayout(eq(direction));
verify(mWindowManagerFuncsImpl, never()).switchKeyboardLayout(anyInt(), anyInt());
@@ -599,7 +594,7 @@
}
void assertTakeBugreport() {
- waitForIdle();
+ mTestLooper.dispatchAll();
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mContext).sendOrderedBroadcastAsUser(intentCaptor.capture(), any(), any(), any(),
any(), anyInt(), any(), any());
@@ -607,17 +602,17 @@
}
void assertTogglePanel() throws RemoteException {
- waitForIdle();
+ mTestLooper.dispatchAll();
verify(mPhoneWindowManager.mStatusBarService).togglePanel();
}
void assertToggleShortcutsMenu() {
- waitForIdle();
+ mTestLooper.dispatchAll();
verify(mStatusBarManagerInternal).toggleKeyboardShortcutsMenu(anyInt());
}
void assertToggleCapsLock() {
- waitForIdle();
+ mTestLooper.dispatchAll();
verify(mInputManagerInternal).toggleCapsLock(anyInt());
}
@@ -642,12 +637,12 @@
}
void assertGoToHomescreen() {
- waitForIdle();
+ mTestLooper.dispatchAll();
verify(mPhoneWindowManager).launchHomeFromHotKey(anyInt());
}
void assertOpenAllAppView() {
- waitForIdle();
+ mTestLooper.dispatchAll();
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mContext, timeout(TEST_SINGLE_KEY_DELAY_MILLIS))
.startActivityAsUser(intentCaptor.capture(), isNull(), any(UserHandle.class));
@@ -655,13 +650,13 @@
}
void assertNotOpenAllAppView() {
- waitForIdle();
+ mTestLooper.dispatchAll();
verify(mContext, after(TEST_SINGLE_KEY_DELAY_MILLIS).never())
.startActivityAsUser(any(Intent.class), any(), any(UserHandle.class));
}
void assertActivityTargetLaunched(ComponentName targetActivity) {
- waitForIdle();
+ mTestLooper.dispatchAll();
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mContext, timeout(TEST_SINGLE_KEY_DELAY_MILLIS))
.startActivityAsUser(intentCaptor.capture(), isNull(), any(UserHandle.class));
@@ -670,7 +665,7 @@
void assertShortcutLogged(int vendorId, int productId, KeyboardLogEvent logEvent,
int expectedKey, int expectedModifierState, String errorMsg) {
- waitForIdle();
+ mTestLooper.dispatchAll();
verify(() -> FrameworkStatsLog.write(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED,
vendorId, productId, logEvent.getIntValue(), new int[]{expectedKey},
expectedModifierState), description(errorMsg));
diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
index d2eb1cc..78566fb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
@@ -84,31 +84,49 @@
private final ContentRecordingSession mWaitingDisplaySession =
ContentRecordingSession.createDisplaySession(DEFAULT_DISPLAY);
private ContentRecordingSession mTaskSession;
- private static Point sSurfaceSize;
+ private Point mSurfaceSize;
private ContentRecorder mContentRecorder;
@Mock private MediaProjectionManagerWrapper mMediaProjectionManagerWrapper;
private SurfaceControl mRecordedSurface;
+ private boolean mHandleAnisotropicDisplayMirroring = false;
+
@Before public void setUp() {
MockitoAnnotations.initMocks(this);
- // GIVEN SurfaceControl can successfully mirror the provided surface.
- sSurfaceSize = new Point(
- mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().width(),
- mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().height());
- mRecordedSurface = surfaceControlMirrors(sSurfaceSize);
-
doReturn(INVALID_DISPLAY).when(mWm.mDisplayManagerInternal).getDisplayIdToMirror(anyInt());
- // GIVEN the VirtualDisplay associated with the session (so the display has state ON).
+ // Skip unnecessary operations of relayout.
+ spyOn(mWm.mWindowPlacerLocked);
+ doNothing().when(mWm.mWindowPlacerLocked).performSurfacePlacement(anyBoolean());
+ }
+
+ private void defaultInit() {
+ createContentRecorder(createDefaultDisplayInfo());
+ }
+
+ private DisplayInfo createDefaultDisplayInfo() {
+ return createDisplayInfo(mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().width(),
+ mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().height());
+ }
+
+ private DisplayInfo createDisplayInfo(int width, int height) {
+ // GIVEN SurfaceControl can successfully mirror the provided surface.
+ mSurfaceSize = new Point(width, height);
+ mRecordedSurface = surfaceControlMirrors(mSurfaceSize);
+
DisplayInfo displayInfo = mDisplayInfo;
- displayInfo.logicalWidth = sSurfaceSize.x;
- displayInfo.logicalHeight = sSurfaceSize.y;
+ displayInfo.logicalWidth = width;
+ displayInfo.logicalHeight = height;
displayInfo.state = STATE_ON;
+ return displayInfo;
+ }
+
+ private void createContentRecorder(DisplayInfo displayInfo) {
mVirtualDisplayContent = createNewDisplay(displayInfo);
final int displayId = mVirtualDisplayContent.getDisplayId();
mContentRecorder = new ContentRecorder(mVirtualDisplayContent,
- mMediaProjectionManagerWrapper);
+ mMediaProjectionManagerWrapper, mHandleAnisotropicDisplayMirroring);
spyOn(mVirtualDisplayContent);
// GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
@@ -124,14 +142,11 @@
// GIVEN a session is waiting for the user to review consent.
mWaitingDisplaySession.setVirtualDisplayId(displayId);
mWaitingDisplaySession.setWaitingForConsent(true);
-
- // Skip unnecessary operations of relayout.
- spyOn(mWm.mWindowPlacerLocked);
- doNothing().when(mWm.mWindowPlacerLocked).performSurfacePlacement(anyBoolean());
}
@Test
public void testIsCurrentlyRecording() {
+ defaultInit();
assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
mContentRecorder.updateRecording();
@@ -140,6 +155,7 @@
@Test
public void testUpdateRecording_display() {
+ defaultInit();
mContentRecorder.setContentRecordingSession(mDisplaySession);
mContentRecorder.updateRecording();
assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
@@ -147,6 +163,7 @@
@Test
public void testUpdateRecording_display_invalidDisplayIdToMirror() {
+ defaultInit();
ContentRecordingSession session = ContentRecordingSession.createDisplaySession(
INVALID_DISPLAY);
mContentRecorder.setContentRecordingSession(session);
@@ -156,6 +173,7 @@
@Test
public void testUpdateRecording_display_noDisplayContentToMirror() {
+ defaultInit();
doReturn(null).when(
mWm.mRoot).getDisplayContent(anyInt());
mContentRecorder.setContentRecordingSession(mDisplaySession);
@@ -165,6 +183,7 @@
@Test
public void testUpdateRecording_task_nullToken() {
+ defaultInit();
ContentRecordingSession session = mTaskSession;
session.setVirtualDisplayId(mDisplaySession.getVirtualDisplayId());
session.setTokenToRecord(null);
@@ -176,6 +195,7 @@
@Test
public void testUpdateRecording_task_noWindowContainer() {
+ defaultInit();
// Use the window container token of the DisplayContent, rather than task.
ContentRecordingSession invalidTaskSession = ContentRecordingSession.createTaskSession(
new WindowContainer.RemoteToken(mDisplayContent));
@@ -187,6 +207,7 @@
@Test
public void testUpdateRecording_wasPaused() {
+ defaultInit();
mContentRecorder.setContentRecordingSession(mDisplaySession);
mContentRecorder.updateRecording();
@@ -197,6 +218,7 @@
@Test
public void testUpdateRecording_waitingForConsent() {
+ defaultInit();
mContentRecorder.setContentRecordingSession(mWaitingDisplaySession);
mContentRecorder.updateRecording();
assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
@@ -209,6 +231,7 @@
@Test
public void testOnConfigurationChanged_neverRecording() {
+ defaultInit();
mContentRecorder.onConfigurationChanged(ORIENTATION_PORTRAIT);
verify(mTransaction, never()).setPosition(eq(mRecordedSurface), anyFloat(), anyFloat());
@@ -218,6 +241,7 @@
@Test
public void testOnConfigurationChanged_resizesSurface() {
+ defaultInit();
mContentRecorder.setContentRecordingSession(mDisplaySession);
mContentRecorder.updateRecording();
// Ensure a different orientation when we check if something has changed.
@@ -234,13 +258,14 @@
@Test
public void testOnConfigurationChanged_resizesVirtualDisplay() {
+ defaultInit();
final int newWidth = 55;
mContentRecorder.setContentRecordingSession(mDisplaySession);
mContentRecorder.updateRecording();
// The user rotates the device, so the host app resizes the virtual display for the capture.
- resizeDisplay(mDisplayContent, newWidth, sSurfaceSize.y);
- resizeDisplay(mVirtualDisplayContent, newWidth, sSurfaceSize.y);
+ resizeDisplay(mDisplayContent, newWidth, mSurfaceSize.y);
+ resizeDisplay(mVirtualDisplayContent, newWidth, mSurfaceSize.y);
mContentRecorder.onConfigurationChanged(mDisplayContent.getConfiguration().orientation);
verify(mTransaction, atLeast(2)).setPosition(eq(mRecordedSurface), anyFloat(),
@@ -251,6 +276,7 @@
@Test
public void testOnConfigurationChanged_rotateVirtualDisplay() {
+ defaultInit();
mContentRecorder.setContentRecordingSession(mDisplaySession);
mContentRecorder.updateRecording();
@@ -271,12 +297,13 @@
*/
@Test
public void testOnConfigurationChanged_resizeSurface() {
+ defaultInit();
mContentRecorder.setContentRecordingSession(mDisplaySession);
mContentRecorder.updateRecording();
// Resize the output surface.
- final Point newSurfaceSize = new Point(Math.round(sSurfaceSize.x / 2f),
- Math.round(sSurfaceSize.y * 2));
+ final Point newSurfaceSize = new Point(Math.round(mSurfaceSize.x / 2f),
+ Math.round(mSurfaceSize.y * 2));
doReturn(newSurfaceSize).when(mWm.mDisplayManagerInternal).getDisplaySurfaceDefaultSize(
anyInt());
mContentRecorder.onConfigurationChanged(
@@ -292,6 +319,7 @@
@Test
public void testOnTaskOrientationConfigurationChanged_resizesSurface() {
+ defaultInit();
mContentRecorder.setContentRecordingSession(mTaskSession);
mContentRecorder.updateRecording();
@@ -314,6 +342,7 @@
@Test
public void testOnTaskBoundsConfigurationChanged_notifiesCallback() {
+ defaultInit();
mTask.getRootTask().setWindowingMode(WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW);
final int minWidth = 222;
@@ -351,6 +380,7 @@
@Test
public void testTaskWindowingModeChanged_pip_stopsRecording() {
+ defaultInit();
// WHEN a recording is ongoing.
mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
mContentRecorder.setContentRecordingSession(mTaskSession);
@@ -368,6 +398,7 @@
@Test
public void testTaskWindowingModeChanged_fullscreen_startsRecording() {
+ defaultInit();
// WHEN a recording is ongoing.
mTask.setWindowingMode(WINDOWING_MODE_PINNED);
mContentRecorder.setContentRecordingSession(mTaskSession);
@@ -384,6 +415,7 @@
@Test
public void testStartRecording_notifiesCallback_taskSession() {
+ defaultInit();
// WHEN a recording is ongoing.
mContentRecorder.setContentRecordingSession(mTaskSession);
mContentRecorder.updateRecording();
@@ -396,6 +428,7 @@
@Test
public void testStartRecording_notifiesCallback_displaySession() {
+ defaultInit();
// WHEN a recording is ongoing.
mContentRecorder.setContentRecordingSession(mDisplaySession);
mContentRecorder.updateRecording();
@@ -408,6 +441,7 @@
@Test
public void testStartRecording_taskInPIP_recordingNotStarted() {
+ defaultInit();
// GIVEN a task is in PIP.
mContentRecorder.setContentRecordingSession(mTaskSession);
mTask.setWindowingMode(WINDOWING_MODE_PINNED);
@@ -421,6 +455,7 @@
@Test
public void testStartRecording_taskInSplit_recordingStarted() {
+ defaultInit();
// GIVEN a task is in PIP.
mContentRecorder.setContentRecordingSession(mTaskSession);
mTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
@@ -434,6 +469,7 @@
@Test
public void testStartRecording_taskInFullscreen_recordingStarted() {
+ defaultInit();
// GIVEN a task is in PIP.
mContentRecorder.setContentRecordingSession(mTaskSession);
mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
@@ -447,6 +483,7 @@
@Test
public void testOnVisibleRequestedChanged_notifiesCallback() {
+ defaultInit();
// WHEN a recording is ongoing.
mContentRecorder.setContentRecordingSession(mTaskSession);
mContentRecorder.updateRecording();
@@ -471,6 +508,7 @@
@Test
public void testOnVisibleRequestedChanged_noRecording_doesNotNotifyCallback() {
+ defaultInit();
// WHEN a recording is not ongoing.
assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
@@ -493,6 +531,7 @@
@Test
public void testPauseRecording_pausesRecording() {
+ defaultInit();
mContentRecorder.setContentRecordingSession(mDisplaySession);
mContentRecorder.updateRecording();
@@ -502,12 +541,14 @@
@Test
public void testPauseRecording_neverRecording() {
+ defaultInit();
mContentRecorder.pauseRecording();
assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
}
@Test
public void testStopRecording_stopsRecording() {
+ defaultInit();
mContentRecorder.setContentRecordingSession(mDisplaySession);
mContentRecorder.updateRecording();
@@ -517,12 +558,14 @@
@Test
public void testStopRecording_neverRecording() {
+ defaultInit();
mContentRecorder.stopRecording();
assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
}
@Test
public void testRemoveTask_stopsRecording() {
+ defaultInit();
mContentRecorder.setContentRecordingSession(mTaskSession);
mContentRecorder.updateRecording();
@@ -533,6 +576,7 @@
@Test
public void testRemoveTask_stopsRecording_nullSessionShouldNotThrowExceptions() {
+ defaultInit();
mContentRecorder.setContentRecordingSession(mTaskSession);
mContentRecorder.updateRecording();
mContentRecorder.setContentRecordingSession(null);
@@ -541,6 +585,7 @@
@Test
public void testUpdateMirroredSurface_capturedAreaResized() {
+ defaultInit();
mContentRecorder.setContentRecordingSession(mDisplaySession);
mContentRecorder.updateRecording();
assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
@@ -548,9 +593,9 @@
// WHEN attempting to mirror on the virtual display, and the captured content is resized.
float xScale = 0.7f;
float yScale = 2f;
- Rect displayAreaBounds = new Rect(0, 0, Math.round(sSurfaceSize.x * xScale),
- Math.round(sSurfaceSize.y * yScale));
- mContentRecorder.updateMirroredSurface(mTransaction, displayAreaBounds, sSurfaceSize);
+ Rect displayAreaBounds = new Rect(0, 0, Math.round(mSurfaceSize.x * xScale),
+ Math.round(mSurfaceSize.y * yScale));
+ mContentRecorder.updateMirroredSurface(mTransaction, displayAreaBounds, mSurfaceSize);
assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
// THEN content in the captured DisplayArea is scaled to fit the surface size.
@@ -558,7 +603,7 @@
1.0f / yScale);
// THEN captured content is positioned in the centre of the output surface.
int scaledWidth = Math.round((float) displayAreaBounds.width() / xScale);
- int xInset = (sSurfaceSize.x - scaledWidth) / 2;
+ int xInset = (mSurfaceSize.x - scaledWidth) / 2;
verify(mTransaction, atLeastOnce()).setPosition(mRecordedSurface, xInset, 0);
// THEN the resize callback is notified.
verify(mMediaProjectionManagerWrapper).notifyActiveProjectionCapturedContentResized(
@@ -566,7 +611,131 @@
}
@Test
+ public void testUpdateMirroredSurface_isotropicPixel() {
+ mHandleAnisotropicDisplayMirroring = false;
+ DisplayInfo displayInfo = createDefaultDisplayInfo();
+ createContentRecorder(displayInfo);
+ mContentRecorder.setContentRecordingSession(mDisplaySession);
+ mContentRecorder.updateRecording();
+ assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
+
+ verify(mTransaction, atLeastOnce()).setMatrix(mRecordedSurface, 1, 0, 0, 1);
+ }
+
+ @Test
+ public void testUpdateMirroredSurface_anisotropicPixel_compressY() {
+ mHandleAnisotropicDisplayMirroring = true;
+ DisplayInfo displayInfo = createDefaultDisplayInfo();
+ DisplayInfo inputDisplayInfo =
+ mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY).getDisplayInfo();
+ displayInfo.physicalXDpi = 2.0f * inputDisplayInfo.physicalXDpi;
+ displayInfo.physicalYDpi = inputDisplayInfo.physicalYDpi;
+ createContentRecorder(displayInfo);
+ mContentRecorder.setContentRecordingSession(mDisplaySession);
+ mContentRecorder.updateRecording();
+ assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
+
+ float xScale = 1f;
+ float yScale = 0.5f;
+ verify(mTransaction, atLeastOnce()).setMatrix(mRecordedSurface, xScale, 0, 0,
+ yScale);
+ verify(mTransaction, atLeastOnce()).setPosition(mRecordedSurface, 0,
+ Math.round(0.25 * mSurfaceSize.y));
+ }
+
+ @Test
+ public void testUpdateMirroredSurface_anisotropicPixel_compressX() {
+ mHandleAnisotropicDisplayMirroring = true;
+ DisplayInfo displayInfo = createDefaultDisplayInfo();
+ DisplayInfo inputDisplayInfo =
+ mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY).getDisplayInfo();
+ displayInfo.physicalXDpi = inputDisplayInfo.physicalXDpi;
+ displayInfo.physicalYDpi = 2.0f * inputDisplayInfo.physicalYDpi;
+ createContentRecorder(displayInfo);
+ mContentRecorder.setContentRecordingSession(mDisplaySession);
+ mContentRecorder.updateRecording();
+ assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
+
+ float xScale = 0.5f;
+ float yScale = 1f;
+ verify(mTransaction, atLeastOnce()).setMatrix(mRecordedSurface, xScale, 0, 0,
+ yScale);
+ verify(mTransaction, atLeastOnce()).setPosition(mRecordedSurface,
+ Math.round(0.25 * mSurfaceSize.x), 0);
+ }
+
+ @Test
+ public void testUpdateMirroredSurface_anisotropicPixel_scaleOnX() {
+ mHandleAnisotropicDisplayMirroring = true;
+ int width = 2 * mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().width();
+ int height = 6 * mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().height();
+ DisplayInfo displayInfo = createDisplayInfo(width, height);
+ DisplayInfo inputDisplayInfo =
+ mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY).getDisplayInfo();
+ displayInfo.physicalXDpi = inputDisplayInfo.physicalXDpi;
+ displayInfo.physicalYDpi = 2.0f * inputDisplayInfo.physicalYDpi;
+ createContentRecorder(displayInfo);
+ mContentRecorder.setContentRecordingSession(mDisplaySession);
+ mContentRecorder.updateRecording();
+ assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
+
+ float xScale = 2f;
+ float yScale = 4f;
+ verify(mTransaction, atLeastOnce()).setMatrix(mRecordedSurface, xScale, 0, 0,
+ yScale);
+ verify(mTransaction, atLeastOnce()).setPosition(mRecordedSurface, 0,
+ inputDisplayInfo.logicalHeight);
+ }
+
+ @Test
+ public void testUpdateMirroredSurface_anisotropicPixel_scaleOnY() {
+ mHandleAnisotropicDisplayMirroring = true;
+ int width = 6 * mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().width();
+ int height = 2 * mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().height();
+ DisplayInfo displayInfo = createDisplayInfo(width, height);
+ DisplayInfo inputDisplayInfo =
+ mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY).getDisplayInfo();
+ displayInfo.physicalXDpi = 2.0f * inputDisplayInfo.physicalXDpi;
+ displayInfo.physicalYDpi = inputDisplayInfo.physicalYDpi;
+ createContentRecorder(displayInfo);
+ mContentRecorder.setContentRecordingSession(mDisplaySession);
+ mContentRecorder.updateRecording();
+ assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
+
+ float xScale = 4f;
+ float yScale = 2f;
+ verify(mTransaction, atLeastOnce()).setMatrix(mRecordedSurface, xScale, 0, 0,
+ yScale);
+ verify(mTransaction, atLeastOnce()).setPosition(mRecordedSurface,
+ inputDisplayInfo.logicalWidth, 0);
+ }
+
+ @Test
+ public void testUpdateMirroredSurface_anisotropicPixel_shrinkCanvas() {
+ mHandleAnisotropicDisplayMirroring = true;
+ int width = mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().width() / 2;
+ int height = mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().height() / 2;
+ DisplayInfo displayInfo = createDisplayInfo(width, height);
+ DisplayInfo inputDisplayInfo =
+ mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY).getDisplayInfo();
+ displayInfo.physicalXDpi = 2f * inputDisplayInfo.physicalXDpi;
+ displayInfo.physicalYDpi = inputDisplayInfo.physicalYDpi;
+ createContentRecorder(displayInfo);
+ mContentRecorder.setContentRecordingSession(mDisplaySession);
+ mContentRecorder.updateRecording();
+ assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
+
+ float xScale = 0.5f;
+ float yScale = 0.25f;
+ verify(mTransaction, atLeastOnce()).setMatrix(mRecordedSurface, xScale, 0, 0,
+ yScale);
+ verify(mTransaction, atLeastOnce()).setPosition(mRecordedSurface, 0,
+ (mSurfaceSize.y - height / 2) / 2);
+ }
+
+ @Test
public void testDisplayContentUpdatesRecording_withoutSurface() {
+ defaultInit();
// GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
// mirror.
setUpDefaultTaskDisplayAreaWindowToken();
@@ -585,6 +754,7 @@
@Test
public void testDisplayContentUpdatesRecording_withSurface() {
+ defaultInit();
// GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
// mirror.
setUpDefaultTaskDisplayAreaWindowToken();
@@ -602,12 +772,13 @@
@Test
public void testDisplayContentUpdatesRecording_displayMirroring() {
+ defaultInit();
// GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
// mirror.
setUpDefaultTaskDisplayAreaWindowToken();
// GIVEN SurfaceControl can successfully mirror the provided surface.
- surfaceControlMirrors(sSurfaceSize);
+ surfaceControlMirrors(mSurfaceSize);
// Initially disable getDisplayIdToMirror since the DMS may create the DC outside the direct
// call in the test. We need to spy on the DC before updateRecording is called or we can't
// verify setDisplayMirroring is called
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
index 4864868..3cb4a1d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
@@ -19,6 +19,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import android.os.Handler;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.DexmakerShareClassLoaderRule;
import org.junit.Rule;
@@ -27,11 +28,16 @@
/** The base class which provides the common rule for test classes under wm package. */
class SystemServiceTestsBase {
- @Rule
+ @Rule(order = 0)
public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
new DexmakerShareClassLoaderRule();
- @Rule
- public final SystemServicesTestRule mSystemServicesTestRule = new SystemServicesTestRule();
+
+ @Rule(order = 1)
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ @Rule(order = 2)
+ public final SystemServicesTestRule mSystemServicesTestRule = new SystemServicesTestRule(
+ this::onBeforeSystemServicesCreated);
@WindowTestRunner.MethodWrapperRule
public final WindowManagerGlobalLockRule mLockRule =
@@ -65,6 +71,11 @@
}
/**
+ * Called before system services are created
+ */
+ protected void onBeforeSystemServicesCreated() {}
+
+ /**
* Make the system booted, so that {@link ActivityStack#resumeTopActivityInnerLocked} can really
* be executed to update activity state and configuration when resuming the current top.
*/
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 7634d9f..2597465 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -134,11 +134,20 @@
private WindowState.PowerManagerWrapper mPowerManagerWrapper;
private InputManagerService mImService;
private InputChannel mInputChannel;
+ private Runnable mOnBeforeServicesCreated;
/**
* Spied {@link SurfaceControl.Transaction} class than can be used to verify calls.
*/
SurfaceControl.Transaction mTransaction;
+ public SystemServicesTestRule(Runnable onBeforeServicesCreated) {
+ mOnBeforeServicesCreated = onBeforeServicesCreated;
+ }
+
+ public SystemServicesTestRule() {
+ this(/* onBeforeServicesCreated= */ null);
+ }
+
@Override
public Statement apply(Statement base, Description description) {
return new Statement() {
@@ -168,6 +177,10 @@
}
private void setUp() {
+ if (mOnBeforeServicesCreated != null) {
+ mOnBeforeServicesCreated.run();
+ }
+
// Use stubOnly() to reduce memory usage if it doesn't need verification.
final MockSettings spyStubOnly = withSettings().stubOnly()
.defaultAnswer(CALLS_REAL_METHODS);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsHandlerThread.java b/services/usage/java/com/android/server/usage/UsageStatsHandlerThread.java
new file mode 100644
index 0000000..31418d6
--- /dev/null
+++ b/services/usage/java/com/android/server/usage/UsageStatsHandlerThread.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usage;
+
+import android.os.Looper;
+import android.os.Trace;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.ServiceThread;
+
+/**
+ * Shared singleton default priority thread for usage stats message handling.
+ */
+public class UsageStatsHandlerThread extends ServiceThread {
+ private static final long SLOW_DISPATCH_THRESHOLD_MS = 10_000;
+ private static final long SLOW_DELIVERY_THRESHOLD_MS = 30_000;
+
+ private static final Object sLock = new Object();
+
+ @GuardedBy("sLock")
+ private static UsageStatsHandlerThread sInstance;
+
+ private UsageStatsHandlerThread() {
+ super("android.usagestats", android.os.Process.THREAD_PRIORITY_DEFAULT,
+ /* allowIo= */ true);
+ }
+
+ @GuardedBy("sLock")
+ private static void ensureThreadLocked() {
+ if (sInstance != null) {
+ return;
+ }
+
+ sInstance = new UsageStatsHandlerThread();
+ sInstance.start();
+ final Looper looper = sInstance.getLooper();
+ looper.setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
+ looper.setSlowLogThresholdMs(
+ SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
+ }
+
+ /**
+ * Obtain a singleton instance of the UsageStatsHandlerThread.
+ */
+ public static UsageStatsHandlerThread get() {
+ synchronized (sLock) {
+ ensureThreadLocked();
+ return sInstance;
+ }
+ }
+}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 55b5d11..e413663 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -202,6 +202,7 @@
static final int MSG_HANDLE_LAUNCH_TIME_ON_USER_UNLOCK = 8;
static final int MSG_NOTIFY_ESTIMATED_LAUNCH_TIMES_CHANGED = 9;
static final int MSG_UID_REMOVED = 10;
+ static final int MSG_USER_STARTED = 11;
private final Object mLock = new Object();
private Handler mHandler;
@@ -334,7 +335,7 @@
mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
mPackageManager = getContext().getPackageManager();
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
- mHandler = new H(BackgroundThread.get().getLooper());
+ mHandler = getUsageEventProcessingHandler();
mIoHandler = new Handler(IoThread.get().getLooper(), mIoHandlerCallback);
mAppStandby = mInjector.getAppStandbyController(getContext());
@@ -380,10 +381,12 @@
IntentFilter filter = new IntentFilter(Intent.ACTION_USER_REMOVED);
filter.addAction(Intent.ACTION_USER_STARTED);
- getContext().registerReceiverAsUser(new UserActionsReceiver(), UserHandle.ALL,
- filter, null, /* Handler scheduler */ null);
+ getContext().registerReceiverAsUser(new UserActionsReceiver(), UserHandle.ALL, filter,
+ null, /* scheduler= */ Flags.useDedicatedHandlerThread() ? mHandler : null);
+
getContext().registerReceiverAsUser(new UidRemovedReceiver(), UserHandle.ALL,
- new IntentFilter(ACTION_UID_REMOVED), null, /* Handler scheduler */ null);
+ new IntentFilter(ACTION_UID_REMOVED), null,
+ /* scheduler= */ Flags.useDedicatedHandlerThread() ? mHandler : null);
mRealTimeSnapshot = SystemClock.elapsedRealtime();
mSystemTimeSnapshot = System.currentTimeMillis();
@@ -471,6 +474,14 @@
}
}
+ private Handler getUsageEventProcessingHandler() {
+ if (Flags.useDedicatedHandlerThread()) {
+ return new H(UsageStatsHandlerThread.get().getLooper());
+ } else {
+ return new H(BackgroundThread.get().getLooper());
+ }
+ }
+
private void onUserUnlocked(int userId) {
// fetch the installed packages outside the lock so it doesn't block package manager.
final HashMap<String, Long> installedPackages = getInstalledPackages(userId);
@@ -618,7 +629,7 @@
}
} else if (Intent.ACTION_USER_STARTED.equals(action)) {
if (userId >= 0) {
- mAppStandby.postCheckIdleStates(userId);
+ mHandler.obtainMessage(MSG_USER_STARTED, userId, 0).sendToTarget();
}
}
}
@@ -1554,8 +1565,7 @@
synchronized (mLaunchTimeAlarmQueues) {
LaunchTimeAlarmQueue alarmQueue = mLaunchTimeAlarmQueues.get(userId);
if (alarmQueue == null) {
- alarmQueue = new LaunchTimeAlarmQueue(
- userId, getContext(), BackgroundThread.get().getLooper());
+ alarmQueue = new LaunchTimeAlarmQueue(userId, getContext(), mHandler.getLooper());
mLaunchTimeAlarmQueues.put(userId, alarmQueue);
}
@@ -2040,6 +2050,9 @@
case MSG_UID_REMOVED:
mResponseStatsTracker.onUidRemoved(msg.arg1);
break;
+ case MSG_USER_STARTED:
+ mAppStandby.postCheckIdleStates(msg.arg1);
+ break;
case MSG_PACKAGE_REMOVED:
onPackageRemoved(msg.arg1, (String) msg.obj);
break;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 7a0bf90..540cecf 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1056,6 +1056,14 @@
"carrier_use_ims_first_for_emergency_bool";
/**
+ * When {@code true}, this carrier will preferentially dial normal routed emergency calls over
+ * an in-service SIM if one is available.
+ * @hide
+ */
+ public static final String KEY_PREFER_IN_SERVICE_SIM_FOR_NORMAL_ROUTED_EMERGENCY_CALLS_BOOL =
+ "prefer_in_service_sim_for_normal_routed_emergency_calls_bool";
+
+ /**
* When {@code true}, the determination of whether to place a call as an emergency call will be
* based on the known {@link android.telephony.emergency.EmergencyNumber}s for the SIM on which
* the call is being placed. In a dual SIM scenario, if Sim A has the emergency numbers
@@ -9874,6 +9882,8 @@
sDefaults.putBoolean(KEY_CARRIER_IMS_GBA_REQUIRED_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL, true);
+ sDefaults.putBoolean(KEY_PREFER_IN_SERVICE_SIM_FOR_NORMAL_ROUTED_EMERGENCY_CALLS_BOOL,
+ false);
sDefaults.putBoolean(KEY_USE_ONLY_DIALED_SIM_ECC_LIST_BOOL, false);
sDefaults.putString(KEY_CARRIER_NETWORK_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING, "");
sDefaults.putString(KEY_CARRIER_NETWORK_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING, "");
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index e9af486..11cbcb1 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -15,6 +15,7 @@
*/
package android.telephony.data;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -35,6 +36,7 @@
import android.util.ArrayMap;
import android.util.Log;
+import com.android.internal.telephony.flags.Flags;
import com.android.telephony.Rlog;
import java.lang.annotation.Retention;
@@ -121,6 +123,9 @@
public static final int TYPE_BIP = ApnTypes.BIP;
/** APN type for ENTERPRISE. */
public static final int TYPE_ENTERPRISE = ApnTypes.ENTERPRISE;
+ /** APN type for RCS (Rich Communication Services). */
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ public static final int TYPE_RCS = ApnTypes.RCS;
/** @hide */
@IntDef(flag = true, prefix = {"TYPE_"}, value = {
@@ -139,6 +144,7 @@
TYPE_BIP,
TYPE_VSIM,
TYPE_ENTERPRISE,
+ TYPE_RCS
})
@Retention(RetentionPolicy.SOURCE)
public @interface ApnType {
@@ -356,6 +362,17 @@
@SystemApi
public static final String TYPE_ENTERPRISE_STRING = "enterprise";
+ /**
+ * APN type for RCS (Rich Communication Services)
+ *
+ * Note: String representations of APN types are intended for system apps to communicate with
+ * modem components or carriers. Non-system apps should use the integer variants instead.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ @SystemApi
+ public static final String TYPE_RCS_STRING = "rcs";
+
/** @hide */
@IntDef(prefix = { "AUTH_TYPE_" }, value = {
@@ -424,6 +441,26 @@
@Retention(RetentionPolicy.SOURCE)
public @interface MvnoType {}
+ /**
+ * Indicating this APN can be used when the device is using terrestrial cellular networks.
+ * @hide
+ */
+ public static final int INFRASTRUCTURE_CELLULAR = 1 << 0;
+
+ /**
+ * Indicating this APN can be used when the device is attached to satellites.
+ * @hide
+ */
+ public static final int INFRASTRUCTURE_SATELLITE = 1 << 1;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "INFRASTRUCTURE_" }, value = {
+ INFRASTRUCTURE_CELLULAR,
+ INFRASTRUCTURE_SATELLITE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface InfrastructureBitmask {}
+
private static final Map<String, Integer> APN_TYPE_STRING_MAP;
private static final Map<Integer, String> APN_TYPE_INT_MAP;
private static final Map<String, Integer> PROTOCOL_STRING_MAP;
@@ -449,6 +486,7 @@
APN_TYPE_STRING_MAP.put(TYPE_ENTERPRISE_STRING, TYPE_ENTERPRISE);
APN_TYPE_STRING_MAP.put(TYPE_VSIM_STRING, TYPE_VSIM);
APN_TYPE_STRING_MAP.put(TYPE_BIP_STRING, TYPE_BIP);
+ APN_TYPE_STRING_MAP.put(TYPE_RCS_STRING, TYPE_RCS);
APN_TYPE_INT_MAP = new ArrayMap<>();
APN_TYPE_INT_MAP.put(TYPE_DEFAULT, TYPE_DEFAULT_STRING);
@@ -466,6 +504,7 @@
APN_TYPE_INT_MAP.put(TYPE_ENTERPRISE, TYPE_ENTERPRISE_STRING);
APN_TYPE_INT_MAP.put(TYPE_VSIM, TYPE_VSIM_STRING);
APN_TYPE_INT_MAP.put(TYPE_BIP, TYPE_BIP_STRING);
+ APN_TYPE_INT_MAP.put(TYPE_RCS, TYPE_RCS_STRING);
PROTOCOL_STRING_MAP = new ArrayMap<>();
PROTOCOL_STRING_MAP.put("IP", PROTOCOL_IP);
@@ -528,6 +567,7 @@
private final int mCarrierId;
private final int mSkip464Xlat;
private final boolean mAlwaysOn;
+ private final @InfrastructureBitmask int mInfrastructureBitmask;
/**
* Returns the default MTU (Maximum Transmission Unit) size in bytes of the IPv4 routes brought
@@ -916,6 +956,29 @@
return mAlwaysOn;
}
+ /**
+ * Check if this APN can be used when the device is using certain infrastructure(s).
+ *
+ * @param infrastructures The infrastructure(s) the device is using.
+ *
+ * @return {@code true} if this APN can be used.
+ * @hide
+ */
+ public boolean isForInfrastructure(@InfrastructureBitmask int infrastructures) {
+ return (mInfrastructureBitmask & infrastructures) != 0;
+ }
+
+ /**
+ * @return The infrastructure bitmask of which the APN can be used on. For example, some APNs
+ * can only be used when the device is on cellular, on satellite, or both.
+ *
+ * @hide
+ */
+ @InfrastructureBitmask
+ public int getInfrastructureBitmask() {
+ return mInfrastructureBitmask;
+ }
+
private ApnSetting(Builder builder) {
this.mEntryName = builder.mEntryName;
this.mApnName = builder.mApnName;
@@ -952,6 +1015,7 @@
this.mCarrierId = builder.mCarrierId;
this.mSkip464Xlat = builder.mSkip464Xlat;
this.mAlwaysOn = builder.mAlwaysOn;
+ this.mInfrastructureBitmask = builder.mInfrastructureBitmask;
}
/**
@@ -1031,6 +1095,8 @@
cursor.getColumnIndexOrThrow(Telephony.Carriers.CARRIER_ID)))
.setSkip464Xlat(cursor.getInt(cursor.getColumnIndexOrThrow(Carriers.SKIP_464XLAT)))
.setAlwaysOn(cursor.getInt(cursor.getColumnIndexOrThrow(Carriers.ALWAYS_ON)) == 1)
+ .setInfrastructureBitmask(cursor.getInt(cursor.getColumnIndexOrThrow(
+ Telephony.Carriers.INFRASTRUCTURE_BITMASK)))
.buildWithoutCheck();
}
@@ -1070,6 +1136,7 @@
.setCarrierId(apn.mCarrierId)
.setSkip464Xlat(apn.mSkip464Xlat)
.setAlwaysOn(apn.mAlwaysOn)
+ .setInfrastructureBitmask(apn.mInfrastructureBitmask)
.buildWithoutCheck();
}
@@ -1115,6 +1182,7 @@
sb.append(", ").append(mCarrierId);
sb.append(", ").append(mSkip464Xlat);
sb.append(", ").append(mAlwaysOn);
+ sb.append(", ").append(mInfrastructureBitmask);
sb.append(", ").append(Objects.hash(mUser, mPassword));
return sb.toString();
}
@@ -1179,7 +1247,7 @@
mProtocol, mRoamingProtocol, mMtuV4, mMtuV6, mCarrierEnabled, mNetworkTypeBitmask,
mLingeringNetworkTypeBitmask, mProfileId, mPersistent, mMaxConns, mWaitTime,
mMaxConnsTime, mMvnoType, mMvnoMatchData, mApnSetId, mCarrierId, mSkip464Xlat,
- mAlwaysOn);
+ mAlwaysOn, mInfrastructureBitmask);
}
@Override
@@ -1191,36 +1259,37 @@
ApnSetting other = (ApnSetting) o;
return mEntryName.equals(other.mEntryName)
- && Objects.equals(mId, other.mId)
+ && mId == other.mId
&& Objects.equals(mOperatorNumeric, other.mOperatorNumeric)
&& Objects.equals(mApnName, other.mApnName)
&& Objects.equals(mProxyAddress, other.mProxyAddress)
&& Objects.equals(mMmsc, other.mMmsc)
&& Objects.equals(mMmsProxyAddress, other.mMmsProxyAddress)
- && Objects.equals(mMmsProxyPort, other.mMmsProxyPort)
- && Objects.equals(mProxyPort, other.mProxyPort)
+ && mMmsProxyPort == other.mMmsProxyPort
+ && mProxyPort == other.mProxyPort
&& Objects.equals(mUser, other.mUser)
&& Objects.equals(mPassword, other.mPassword)
- && Objects.equals(mAuthType, other.mAuthType)
- && Objects.equals(mApnTypeBitmask, other.mApnTypeBitmask)
- && Objects.equals(mProtocol, other.mProtocol)
- && Objects.equals(mRoamingProtocol, other.mRoamingProtocol)
- && Objects.equals(mCarrierEnabled, other.mCarrierEnabled)
- && Objects.equals(mProfileId, other.mProfileId)
- && Objects.equals(mPersistent, other.mPersistent)
- && Objects.equals(mMaxConns, other.mMaxConns)
- && Objects.equals(mWaitTime, other.mWaitTime)
- && Objects.equals(mMaxConnsTime, other.mMaxConnsTime)
- && Objects.equals(mMtuV4, other.mMtuV4)
- && Objects.equals(mMtuV6, other.mMtuV6)
- && Objects.equals(mMvnoType, other.mMvnoType)
+ && mAuthType == other.mAuthType
+ && mApnTypeBitmask == other.mApnTypeBitmask
+ && mProtocol == other.mProtocol
+ && mRoamingProtocol == other.mRoamingProtocol
+ && mCarrierEnabled == other.mCarrierEnabled
+ && mProfileId == other.mProfileId
+ && mPersistent == other.mPersistent
+ && mMaxConns == other.mMaxConns
+ && mWaitTime == other.mWaitTime
+ && mMaxConnsTime == other.mMaxConnsTime
+ && mMtuV4 == other.mMtuV4
+ && mMtuV6 == other.mMtuV6
+ && mMvnoType == other.mMvnoType
&& Objects.equals(mMvnoMatchData, other.mMvnoMatchData)
- && Objects.equals(mNetworkTypeBitmask, other.mNetworkTypeBitmask)
- && Objects.equals(mLingeringNetworkTypeBitmask, other.mLingeringNetworkTypeBitmask)
- && Objects.equals(mApnSetId, other.mApnSetId)
- && Objects.equals(mCarrierId, other.mCarrierId)
- && Objects.equals(mSkip464Xlat, other.mSkip464Xlat)
- && Objects.equals(mAlwaysOn, other.mAlwaysOn);
+ && mNetworkTypeBitmask == other.mNetworkTypeBitmask
+ && mLingeringNetworkTypeBitmask == other.mLingeringNetworkTypeBitmask
+ && mApnSetId == other.mApnSetId
+ && mCarrierId == other.mCarrierId
+ && mSkip464Xlat == other.mSkip464Xlat
+ && mAlwaysOn == other.mAlwaysOn
+ && mInfrastructureBitmask == other.mInfrastructureBitmask;
}
/**
@@ -1270,7 +1339,8 @@
&& Objects.equals(mApnSetId, other.mApnSetId)
&& Objects.equals(mCarrierId, other.mCarrierId)
&& Objects.equals(mSkip464Xlat, other.mSkip464Xlat)
- && Objects.equals(mAlwaysOn, other.mAlwaysOn);
+ && Objects.equals(mAlwaysOn, other.mAlwaysOn)
+ && Objects.equals(mInfrastructureBitmask, other.mInfrastructureBitmask);
}
/**
@@ -1307,7 +1377,8 @@
&& Objects.equals(this.mApnSetId, other.mApnSetId)
&& Objects.equals(this.mCarrierId, other.mCarrierId)
&& Objects.equals(this.mSkip464Xlat, other.mSkip464Xlat)
- && Objects.equals(this.mAlwaysOn, other.mAlwaysOn);
+ && Objects.equals(this.mAlwaysOn, other.mAlwaysOn)
+ && Objects.equals(this.mInfrastructureBitmask, other.mInfrastructureBitmask);
}
// Equal or one is null.
@@ -1379,6 +1450,7 @@
apnValue.put(Telephony.Carriers.CARRIER_ID, mCarrierId);
apnValue.put(Telephony.Carriers.SKIP_464XLAT, mSkip464Xlat);
apnValue.put(Telephony.Carriers.ALWAYS_ON, mAlwaysOn);
+ apnValue.put(Telephony.Carriers.INFRASTRUCTURE_BITMASK, mInfrastructureBitmask);
return apnValue;
}
@@ -1651,6 +1723,7 @@
dest.writeInt(mCarrierId);
dest.writeInt(mSkip464Xlat);
dest.writeBoolean(mAlwaysOn);
+ dest.writeInt(mInfrastructureBitmask);
}
private static ApnSetting readFromParcel(Parcel in) {
@@ -1686,6 +1759,7 @@
.setCarrierId(in.readInt())
.setSkip464Xlat(in.readInt())
.setAlwaysOn(in.readBoolean())
+ .setInfrastructureBitmask(in.readInt())
.buildWithoutCheck();
}
@@ -1767,6 +1841,7 @@
private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
private int mSkip464Xlat = Carriers.SKIP_464XLAT_DEFAULT;
private boolean mAlwaysOn;
+ private int mInfrastructureBitmask = INFRASTRUCTURE_CELLULAR;
/**
* Default constructor for Builder.
@@ -2189,6 +2264,22 @@
}
/**
+ * Set the infrastructure bitmask.
+ *
+ * @param infrastructureBitmask The infrastructure bitmask of which the APN can be used on.
+ * For example, some APNs can only be used when the device is on cellular, on satellite, or
+ * both.
+ *
+ * @return The builder.
+ * @hide
+ */
+ @NonNull
+ public Builder setInfrastructureBitmask(@InfrastructureBitmask int infrastructureBitmask) {
+ this.mInfrastructureBitmask = infrastructureBitmask;
+ return this;
+ }
+
+ /**
* Builds {@link ApnSetting} from this builder.
*
* @return {@code null} if {@link #setApnName(String)} or {@link #setEntryName(String)}
@@ -2198,7 +2289,7 @@
public ApnSetting build() {
if ((mApnTypeBitmask & (TYPE_DEFAULT | TYPE_MMS | TYPE_SUPL | TYPE_DUN | TYPE_HIPRI
| TYPE_FOTA | TYPE_IMS | TYPE_CBS | TYPE_IA | TYPE_EMERGENCY | TYPE_MCX
- | TYPE_XCAP | TYPE_VSIM | TYPE_BIP | TYPE_ENTERPRISE)) == 0
+ | TYPE_XCAP | TYPE_VSIM | TYPE_BIP | TYPE_ENTERPRISE | TYPE_RCS)) == 0
|| TextUtils.isEmpty(mApnName) || TextUtils.isEmpty(mEntryName)) {
return null;
}
diff --git a/telephony/java/android/telephony/data/DataProfile.java b/telephony/java/android/telephony/data/DataProfile.java
index f346b92..88a32d1 100644
--- a/telephony/java/android/telephony/data/DataProfile.java
+++ b/telephony/java/android/telephony/data/DataProfile.java
@@ -431,6 +431,8 @@
return ApnSetting.TYPE_VSIM;
case NetworkCapabilities.NET_CAPABILITY_ENTERPRISE:
return ApnSetting.TYPE_ENTERPRISE;
+ case NetworkCapabilities.NET_CAPABILITY_RCS:
+ return ApnSetting.TYPE_RCS;
default:
return ApnSetting.TYPE_NONE;
}
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index 82aa85d..f4f2be6 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -53,7 +53,17 @@
}
filegroup {
- name: "FlickerTestsAppLaunch-src",
+ name: "FlickerTestsAppLaunchCommon-src",
+ srcs: ["src/**/launch/common/*.kt"],
+}
+
+filegroup {
+ name: "FlickerTestsAppLaunch1-src",
+ srcs: ["src/**/launch/OpenApp*.kt"],
+}
+
+filegroup {
+ name: "FlickerTestsAppLaunch2-src",
srcs: ["src/**/launch/*.kt"],
}
@@ -119,7 +129,8 @@
exclude_srcs: [
":FlickerTestsAppClose-src",
":FlickerTestsIme-src",
- ":FlickerTestsAppLaunch-src",
+ ":FlickerTestsAppLaunch1-src",
+ ":FlickerTestsAppLaunch2-src",
":FlickerTestsQuickswitch-src",
":FlickerTestsRotation-src",
":FlickerTestsNotification-src",
@@ -162,7 +173,8 @@
instrumentation_target_package: "com.android.server.wm.flicker.launch",
srcs: [
":FlickerTestsBase-src",
- ":FlickerTestsAppLaunch-src",
+ ":FlickerTestsAppLaunchCommon-src",
+ ":FlickerTestsAppLaunch2-src",
],
exclude_srcs: [
":FlickerTestsActivityEmbedding-src",
@@ -170,6 +182,39 @@
}
android_test {
+ name: "FlickerTestsAppLaunch1",
+ defaults: ["FlickerTestsDefault"],
+ additional_manifests: ["manifests/AndroidManifestAppLaunch.xml"],
+ package_name: "com.android.server.wm.flicker.launch",
+ instrumentation_target_package: "com.android.server.wm.flicker.launch",
+ srcs: [
+ ":FlickerTestsBase-src",
+ ":FlickerTestsAppLaunchCommon-src",
+ ":FlickerTestsAppLaunch1-src",
+ ],
+ exclude_srcs: [
+ ":FlickerTestsActivityEmbedding-src",
+ ],
+}
+
+android_test {
+ name: "FlickerTestsAppLaunch2",
+ defaults: ["FlickerTestsDefault"],
+ additional_manifests: ["manifests/AndroidManifestAppLaunch.xml"],
+ package_name: "com.android.server.wm.flicker.launch",
+ instrumentation_target_package: "com.android.server.wm.flicker.launch",
+ srcs: [
+ ":FlickerTestsBase-src",
+ ":FlickerTestsAppLaunchCommon-src",
+ ":FlickerTestsAppLaunch2-src",
+ ],
+ exclude_srcs: [
+ ":FlickerTestsActivityEmbedding-src",
+ ":FlickerTestsAppLaunch1-src",
+ ],
+}
+
+android_test {
name: "FlickerTestsNotification",
defaults: ["FlickerTestsDefault"],
additional_manifests: ["manifests/AndroidManifestNotification.xml"],
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt
index 48d5041..f788efa 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt
@@ -16,13 +16,11 @@
package com.android.server.wm.flicker.launch
-import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
import androidx.test.filters.FlakyTest
+import com.android.server.wm.flicker.launch.common.OpenAppFromIconTransition
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -54,28 +52,7 @@
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
open class OpenAppFromIconColdTest(flicker: LegacyFlickerTest) :
- OpenAppFromLauncherTransition(flicker) {
- /** {@inheritDoc} */
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- super.transition(this)
- setup {
- if (flicker.scenario.isTablet) {
- tapl.setExpectedRotation(flicker.scenario.startRotation.value)
- } else {
- tapl.setExpectedRotation(Rotation.ROTATION_0.value)
- }
- RemoveAllTasksButHomeRule.removeAllTasksButHome()
- }
- transitions {
- tapl
- .goHome()
- .switchToAllApps()
- .getAppIcon(testApp.appName)
- .launch(testApp.packageName)
- }
- teardown { testApp.exit(wmHelper) }
- }
+ OpenAppFromIconTransition(flicker) {
@FlakyTest(bugId = 240916028)
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt
index f575fcc..d86dc50 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt
@@ -21,6 +21,7 @@
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import com.android.server.wm.flicker.launch.common.OpenAppFromLauncherTransition
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
index 93d0520..be07053 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
@@ -25,6 +25,7 @@
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.launch.common.OpenAppFromLauncherTransition
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
index 78b58f4..f66eff9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
@@ -24,6 +24,7 @@
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.launch.common.OpenAppFromLauncherTransition
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
index 3f931c4..65214764 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
@@ -27,6 +27,7 @@
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
+import com.android.server.wm.flicker.launch.common.OpenAppFromLockscreenTransition
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Ignore
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index b85362a..4d31c28 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -25,6 +25,7 @@
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.launch.common.OpenAppFromLauncherTransition
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
index 1bdb6e71..42e34b3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
@@ -30,6 +30,7 @@
import android.view.KeyEvent
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.launch.common.OpenAppFromLauncherTransition
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenTransferSplashscreenAppFromLauncherTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenTransferSplashscreenAppFromLauncherTransition.kt
deleted file mode 100644
index 3d9c067..0000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenTransferSplashscreenAppFromLauncherTransition.kt
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.launch
-
-import android.platform.test.annotations.Presubmit
-import android.tools.common.traces.component.ComponentNameMatcher
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.helpers.TransferSplashscreenAppHelper
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test cold launching an app from launcher
- *
- * To run this test: `atest FlickerTests:OpenTransferSplashscreenAppFromLauncherTransition`
- *
- * Actions:
- * ```
- * Inherit from OpenAppFromIconColdTest, Launch an app [testApp] with an animated splash screen
- * by clicking it's icon on all apps, and wait for transfer splash screen complete
- * ```
- *
- * Notes:
- * ```
- * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
- * are inherited [OpenAppTransition]
- * 2. Verify no flickering when transfer splash screen to app window.
- * ```
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenTransferSplashscreenAppFromLauncherTransition(flicker: LegacyFlickerTest) :
- OpenAppFromIconColdTest(flicker) {
- override val testApp = TransferSplashscreenAppHelper(instrumentation)
-
- /**
- * Checks that [ComponentNameMatcher.LAUNCHER] window is the top window at the start of the
- * transition, and is replaced by [ComponentNameMatcher.SPLASH_SCREEN], then [testApp] remains
- * visible until the end
- */
- @Presubmit
- @Test
- fun appWindowAfterSplash() {
- flicker.assertWm {
- this.isAppWindowOnTop(ComponentNameMatcher.LAUNCHER)
- .then()
- .isAppWindowOnTop(ComponentNameMatcher.SPLASH_SCREEN)
- .then()
- .isAppWindowOnTop(testApp)
- .isAppWindowInvisible(ComponentNameMatcher.SPLASH_SCREEN)
- }
- }
-
- companion object {
- /**
- * Creates the test configurations.
- *
- * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
- * navigation modes.
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenAppFromIconTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenAppFromIconTransition.kt
new file mode 100644
index 0000000..c854701
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenAppFromIconTransition.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.launch.common
+
+import android.tools.common.Rotation
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
+
+abstract class OpenAppFromIconTransition(flicker: LegacyFlickerTest) :
+ OpenAppFromLauncherTransition(flicker) {
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup {
+ if (flicker.scenario.isTablet) {
+ tapl.setExpectedRotation(flicker.scenario.startRotation.value)
+ } else {
+ tapl.setExpectedRotation(Rotation.ROTATION_0.value)
+ }
+ RemoveAllTasksButHomeRule.removeAllTasksButHome()
+ }
+ transitions {
+ tapl
+ .goHome()
+ .switchToAllApps()
+ .getAppIcon(testApp.appName)
+ .launch(testApp.packageName)
+ }
+ teardown { testApp.exit(wmHelper) }
+ }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenAppFromLauncherTransition.kt
similarity index 95%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenAppFromLauncherTransition.kt
index 4fc9bcb..9d7096e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenAppFromLauncherTransition.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.wm.flicker.launch
+package com.android.server.wm.flicker.launch.common
import android.platform.test.annotations.Presubmit
import android.tools.common.traces.component.ComponentNameMatcher
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenAppFromLockscreenTransition.kt
similarity index 97%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenTransition.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenAppFromLockscreenTransition.kt
index cc501e6..7b08843 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenAppFromLockscreenTransition.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.wm.flicker.launch
+package com.android.server.wm.flicker.launch.common
import android.platform.test.annotations.Presubmit
import android.tools.common.traces.component.ComponentNameMatcher
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenAppTransition.kt
similarity index 98%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenAppTransition.kt
index bb11be5..989619e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenAppTransition.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.wm.flicker.launch
+package com.android.server.wm.flicker.launch.common
import android.platform.test.annotations.Presubmit
import android.tools.common.traces.component.ComponentNameMatcher
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenTransferSplashscreenAppFromLauncherTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenTransferSplashscreenAppFromLauncherTransition.kt
new file mode 100644
index 0000000..2e9620b
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenTransferSplashscreenAppFromLauncherTransition.kt
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.launch.common
+
+import android.platform.test.annotations.Presubmit
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.helpers.TransferSplashscreenAppHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test cold launching an app from launcher
+ *
+ * To run this test: `atest FlickerTests:OpenTransferSplashscreenAppFromLauncherTransition`
+ *
+ * Actions:
+ * ```
+ * Inherit from OpenAppFromIconColdTest, Launch an app [testApp] with an animated splash screen
+ * by clicking it's icon on all apps, and wait for transfer splash screen complete
+ * ```
+ *
+ * Notes:
+ * ```
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [OpenAppTransition]
+ * 2. Verify no flickering when transfer splash screen to app window.
+ * ```
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class OpenTransferSplashscreenAppFromLauncherTransition(flicker: LegacyFlickerTest) :
+ OpenAppFromIconTransition(flicker) {
+ override val testApp = TransferSplashscreenAppHelper(instrumentation)
+
+ /**
+ * Checks that [ComponentNameMatcher.LAUNCHER] window is the top window at the start of the
+ * transition, and is replaced by [ComponentNameMatcher.SPLASH_SCREEN], then [testApp] remains
+ * visible until the end
+ */
+ @Presubmit
+ @Test
+ fun appWindowAfterSplash() {
+ flicker.assertWm {
+ this.isAppWindowOnTop(ComponentNameMatcher.LAUNCHER)
+ .then()
+ .isAppWindowOnTop(ComponentNameMatcher.SPLASH_SCREEN)
+ .then()
+ .isAppWindowOnTop(testApp)
+ .isAppWindowInvisible(ComponentNameMatcher.SPLASH_SCREEN)
+ }
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun focusChanges() {
+ super.focusChanges()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun appWindowReplacesLauncherAsTopWindow() {
+ super.appWindowReplacesLauncherAsTopWindow()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun appWindowAsTopWindowAtEnd() {
+ super.appWindowAsTopWindowAtEnd()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun appWindowBecomesTopWindow() {
+ super.appWindowBecomesTopWindow()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun appWindowBecomesVisible() {
+ super.appWindowBecomesVisible()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun appWindowIsTopWindowAtEnd() {
+ super.appWindowIsTopWindowAtEnd()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun appLayerBecomesVisible() {
+ super.appLayerBecomesVisible()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun appLayerReplacesLauncher() {
+ super.appLayerReplacesLauncher()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun cujCompleted() {
+ super.cujCompleted()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun entireScreenCovered() {
+ super.entireScreenCovered()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() {
+ super.navBarLayerIsVisibleAtStartAndEnd()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() {
+ super.navBarLayerPositionAtStartAndEnd()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun navBarWindowIsAlwaysVisible() {
+ super.navBarWindowIsAlwaysVisible()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun navBarWindowIsVisibleAtStartAndEnd() {
+ super.navBarWindowIsVisibleAtStartAndEnd()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() {
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() {
+ super.statusBarLayerPositionAtStartAndEnd()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() {
+ super.statusBarWindowIsAlwaysVisible()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() {
+ super.taskBarLayerIsVisibleAtStartAndEnd()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() {
+ super.taskBarWindowIsAlwaysVisible()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ }
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
+ }
+}