Merge "Add RefactorFlagUtils and an example usage" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 90e9c43..c8046ab 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -294,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/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/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/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/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 a7b3c10..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;
@@ -742,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;
@@ -966,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
@@ -3981,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() {
@@ -6045,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
@@ -6342,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;
}
}
}
@@ -7310,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.
@@ -7331,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;
}
@@ -11934,96 +11848,6 @@
t.clearTrustedPresentationCallback(getSurfaceControl());
}
- private void setPreferredFrameRateCategory(int preferredFrameRateCategory) {
- if (!shouldSetFrameRateCategory()) {
- return;
- }
-
- 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;
- }
-
private void logAndTrace(String msg) {
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.instant(Trace.TRACE_TAG_VIEW, mTag + "-" + msg);
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/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/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/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index 0a2bd63..7e03bd9 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -45,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/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/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/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/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/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/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/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/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/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/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, "");