Merge changes I2539124b,I98093976 into main
* changes:
[sb] use HeadsUpNotificationInteractor to control clock visibility
[sb] Don't show notifs in view model if any chip is showing
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index e184704..0bce29f 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -23,7 +23,6 @@
"aconfig_mediacodec_flags_java_lib",
"aconfig_settingslib_flags_java_lib",
"aconfig_trade_in_mode_flags_java_lib",
- "android-sdk-flags-java",
"android.adaptiveauth.flags-aconfig-java",
"android.app.appfunctions.flags-aconfig-java",
"android.app.assist.flags-aconfig-java",
@@ -63,6 +62,7 @@
"android.os.vibrator.flags-aconfig-java",
"android.permission.flags-aconfig-java",
"android.provider.flags-aconfig-java",
+ "android.sdk.flags-aconfig-java",
"android.security.flags-aconfig-java",
"android.server.app.flags-aconfig-java",
"android.service.autofill.flags-aconfig-java",
diff --git a/android-sdk-flags/Android.bp b/android-sdk-flags/Android.bp
index 79a0b9a..d1df2ca 100644
--- a/android-sdk-flags/Android.bp
+++ b/android-sdk-flags/Android.bp
@@ -17,14 +17,21 @@
}
aconfig_declarations {
- name: "android-sdk-flags",
+ name: "android.sdk.flags-aconfig",
package: "android.sdk",
container: "system",
srcs: ["flags.aconfig"],
}
java_aconfig_library {
- name: "android-sdk-flags-java",
- aconfig_declarations: "android-sdk-flags",
+ name: "android.sdk.flags-aconfig-java",
+ aconfig_declarations: "android.sdk.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
+java_aconfig_library {
+ name: "android.sdk.flags-aconfig-java-host",
+ aconfig_declarations: "android.sdk.flags-aconfig",
+ host_supported: true,
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
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 963307b..a5a08fb 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -573,6 +573,7 @@
case Constants.KEY_MIN_LINEAR_BACKOFF_TIME_MS:
case Constants.KEY_MIN_EXP_BACKOFF_TIME_MS:
case Constants.KEY_SYSTEM_STOP_TO_FAILURE_RATIO:
+ case Constants.KEY_ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF:
mConstants.updateBackoffConstantsLocked();
break;
case Constants.KEY_CONN_CONGESTION_DELAY_FRAC:
@@ -679,6 +680,8 @@
private static final String KEY_MIN_EXP_BACKOFF_TIME_MS = "min_exp_backoff_time_ms";
private static final String KEY_SYSTEM_STOP_TO_FAILURE_RATIO =
"system_stop_to_failure_ratio";
+ private static final String KEY_ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF =
+ "abandoned_job_timeouts_before_aggressive_backoff";
private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac";
private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac";
private static final String KEY_CONN_USE_CELL_SIGNAL_STRENGTH =
@@ -750,6 +753,7 @@
private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME_MS = JobInfo.MIN_BACKOFF_MILLIS;
private static final long DEFAULT_MIN_EXP_BACKOFF_TIME_MS = JobInfo.MIN_BACKOFF_MILLIS;
private static final int DEFAULT_SYSTEM_STOP_TO_FAILURE_RATIO = 3;
+ private static final int DEFAULT_ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF = 3;
private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f;
private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f;
private static final boolean DEFAULT_CONN_USE_CELL_SIGNAL_STRENGTH = true;
@@ -845,7 +849,12 @@
* incremental failure in the backoff policy calculation.
*/
int SYSTEM_STOP_TO_FAILURE_RATIO = DEFAULT_SYSTEM_STOP_TO_FAILURE_RATIO;
-
+ /**
+ * Number of consecutive timeouts by abandoned jobs before we change to aggressive backoff
+ * policy.
+ */
+ int ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF =
+ DEFAULT_ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF;
/**
* The fraction of a job's running window that must pass before we
* consider running it when the network is congested.
@@ -1078,6 +1087,10 @@
SYSTEM_STOP_TO_FAILURE_RATIO = DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
KEY_SYSTEM_STOP_TO_FAILURE_RATIO,
DEFAULT_SYSTEM_STOP_TO_FAILURE_RATIO);
+ ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF = DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+ KEY_ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF,
+ DEFAULT_ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF);
}
// TODO(141645789): move into ConnectivityController.CcConfig
@@ -1287,6 +1300,8 @@
pw.print(KEY_MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME_MS).println();
pw.print(KEY_MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME_MS).println();
pw.print(KEY_SYSTEM_STOP_TO_FAILURE_RATIO, SYSTEM_STOP_TO_FAILURE_RATIO).println();
+ pw.print(KEY_ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF,
+ ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF).println();
pw.print(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println();
pw.print(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println();
pw.print(KEY_CONN_USE_CELL_SIGNAL_STRENGTH, CONN_USE_CELL_SIGNAL_STRENGTH).println();
@@ -2997,6 +3012,7 @@
final long initialBackoffMillis = job.getInitialBackoffMillis();
int numFailures = failureToReschedule.getNumFailures();
+ int numAbandonedFailures = failureToReschedule.getNumAbandonedFailures();
int numSystemStops = failureToReschedule.getNumSystemStops();
// We should back off slowly if JobScheduler keeps stopping the job,
// but back off immediately if the issue appeared to be the app's fault
@@ -3006,9 +3022,19 @@
|| internalStopReason == JobParameters.INTERNAL_STOP_REASON_ANR
|| stopReason == JobParameters.STOP_REASON_USER) {
numFailures++;
+ } else if (android.app.job.Flags.handleAbandonedJobs()
+ && internalStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED) {
+ numAbandonedFailures++;
+ numFailures++;
} else {
numSystemStops++;
}
+
+ int backoffPolicy = job.getBackoffPolicy();
+ if (shouldUseAggressiveBackoff(numAbandonedFailures)) {
+ backoffPolicy = JobInfo.BACKOFF_POLICY_EXPONENTIAL;
+ }
+
final int backoffAttempts =
numFailures + numSystemStops / mConstants.SYSTEM_STOP_TO_FAILURE_RATIO;
final long earliestRuntimeMs;
@@ -3017,7 +3043,7 @@
earliestRuntimeMs = JobStatus.NO_EARLIEST_RUNTIME;
} else {
long delayMillis;
- switch (job.getBackoffPolicy()) {
+ switch (backoffPolicy) {
case JobInfo.BACKOFF_POLICY_LINEAR: {
long backoff = initialBackoffMillis;
if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME_MS) {
@@ -3046,7 +3072,7 @@
}
JobStatus newJob = new JobStatus(failureToReschedule,
earliestRuntimeMs,
- JobStatus.NO_LATEST_RUNTIME, numFailures, numSystemStops,
+ JobStatus.NO_LATEST_RUNTIME, numFailures, numAbandonedFailures, numSystemStops,
failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis(),
failureToReschedule.getCumulativeExecutionTimeMs());
if (stopReason == JobParameters.STOP_REASON_USER) {
@@ -3069,6 +3095,20 @@
}
/**
+ * Returns {@code true} if the given number of abandoned failures indicates that JobScheduler
+ * should use an aggressive backoff policy.
+ *
+ * @param numAbandonedFailures The number of abandoned failures.
+ * @return {@code true} if the given number of abandoned failures indicates that JobScheduler
+ * should use an aggressive backoff policy.
+ */
+ public boolean shouldUseAggressiveBackoff(int numAbandonedFailures) {
+ return android.app.job.Flags.handleAbandonedJobs()
+ && numAbandonedFailures
+ > mConstants.ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF;
+ }
+
+ /**
* Maximum time buffer in which JobScheduler will try to optimize periodic job scheduling. This
* does not cause a job's period to be larger than requested (eg: if the requested period is
* shorter than this buffer). This is used to put a limit on when JobScheduler will intervene
@@ -3147,6 +3187,7 @@
return new JobStatus(periodicToReschedule,
elapsedNow + period - flex, elapsedNow + period,
0 /* numFailures */, 0 /* numSystemStops */,
+ 0 /* numAbandonedFailures */,
sSystemClock.millis() /* lastSuccessfulRunTime */,
periodicToReschedule.getLastFailedRunTime(),
0 /* Reset cumulativeExecutionTime because of successful execution */);
@@ -3163,6 +3204,7 @@
return new JobStatus(periodicToReschedule,
newEarliestRunTimeElapsed, newLatestRuntimeElapsed,
0 /* numFailures */, 0 /* numSystemStops */,
+ 0 /* numAbandonedFailures */,
sSystemClock.millis() /* lastSuccessfulRunTime */,
periodicToReschedule.getLastFailedRunTime(),
0 /* Reset cumulativeExecutionTime because of successful execution */);
@@ -3171,6 +3213,10 @@
@VisibleForTesting
void maybeProcessBuggyJob(@NonNull JobStatus jobStatus, int debugStopReason) {
boolean jobTimedOut = debugStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT;
+ if (android.app.job.Flags.handleAbandonedJobs()) {
+ jobTimedOut |= (debugStopReason
+ == JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED);
+ }
// If madeActive = 0, the job never actually started.
if (!jobTimedOut && jobStatus.madeActive > 0) {
final long executionDurationMs = sUptimeMillisClock.millis() - jobStatus.madeActive;
@@ -3252,9 +3298,12 @@
// we stop it.
final JobStatus rescheduledJob = needsReschedule
? getRescheduleJobForFailureLocked(jobStatus, stopReason, debugStopReason) : null;
+ final boolean isStopReasonAbandoned = android.app.job.Flags.handleAbandonedJobs()
+ && (debugStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED);
if (rescheduledJob != null
&& !rescheduledJob.shouldTreatAsUserInitiatedJob()
&& (debugStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT
+ || isStopReasonAbandoned
|| debugStopReason == JobParameters.INTERNAL_STOP_REASON_PREEMPT)) {
rescheduledJob.disallowRunInBatterySaverAndDoze();
}
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 d8934d8..dfb3681 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
@@ -269,7 +269,9 @@
convertRtcBoundsToElapsed(utcTimes, elapsedNow);
JobStatus newJob = new JobStatus(job,
elapsedRuntimes.first, elapsedRuntimes.second,
- 0, 0, job.getLastSuccessfulRunTime(), job.getLastFailedRunTime(),
+ 0 /* numFailures */, 0 /* numAbandonedFailures */,
+ 0 /* numSystemStops */, job.getLastSuccessfulRunTime(),
+ job.getLastFailedRunTime(),
job.getCumulativeExecutionTimeMs());
newJob.prepareLocked();
toAdd.add(newJob);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index a3eaefd..5a33aa0 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -316,6 +316,12 @@
private final int numFailures;
/**
+ * How many times this job has stopped due to {@link
+ * JobParameters#STOP_REASON_TIMEOUT_ABANDONED}.
+ */
+ private final int mNumAbandonedFailures;
+
+ /**
* The number of times JobScheduler has forced this job to stop due to reasons mostly outside
* of the app's control.
*/
@@ -605,6 +611,8 @@
* @param tag A string associated with the job for debugging/logging purposes.
* @param numFailures Count of how many times this job has requested a reschedule because
* its work was not yet finished.
+ * @param mNumAbandonedFailures Count of how many times this job has requested a reschedule
+ * because it was abandoned.
* @param numSystemStops Count of how many times JobScheduler has forced this job to stop due to
* factors mostly out of the app's control.
* @param earliestRunTimeElapsedMillis Milestone: earliest point in time at which the job
@@ -617,7 +625,7 @@
*/
private JobStatus(JobInfo job, int callingUid, String sourcePackageName,
int sourceUserId, int standbyBucket, @Nullable String namespace, String tag,
- int numFailures, int numSystemStops,
+ int numFailures, int mNumAbandonedFailures, int numSystemStops,
long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis,
long lastSuccessfulRunTime, long lastFailedRunTime, long cumulativeExecutionTimeMs,
int internalFlags,
@@ -677,6 +685,7 @@
this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
this.mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
this.numFailures = numFailures;
+ this.mNumAbandonedFailures = mNumAbandonedFailures;
mNumSystemStops = numSystemStops;
int requiredConstraints = job.getConstraintFlags();
@@ -750,7 +759,8 @@
this(jobStatus.getJob(), jobStatus.getUid(),
jobStatus.getSourcePackageName(), jobStatus.getSourceUserId(),
jobStatus.getStandbyBucket(), jobStatus.getNamespace(),
- jobStatus.getSourceTag(), jobStatus.getNumFailures(), jobStatus.getNumSystemStops(),
+ jobStatus.getSourceTag(), jobStatus.getNumFailures(),
+ jobStatus.getNumAbandonedFailures(), jobStatus.getNumSystemStops(),
jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed(),
jobStatus.getLastSuccessfulRunTime(), jobStatus.getLastFailedRunTime(),
jobStatus.getCumulativeExecutionTimeMs(),
@@ -787,6 +797,7 @@
this(job, callingUid, sourcePkgName, sourceUserId,
standbyBucket, namespace,
sourceTag, /* numFailures */ 0, /* numSystemStops */ 0,
+ /* mNumAbandonedFailures */ 0,
earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
lastSuccessfulRunTime, lastFailedRunTime, cumulativeExecutionTimeMs,
innerFlags, dynamicConstraints);
@@ -806,13 +817,15 @@
/** Create a new job to be rescheduled with the provided parameters. */
public JobStatus(JobStatus rescheduling,
long newEarliestRuntimeElapsedMillis,
- long newLatestRuntimeElapsedMillis, int numFailures, int numSystemStops,
+ long newLatestRuntimeElapsedMillis, int numFailures,
+ int mNumAbandonedFailures, int numSystemStops,
long lastSuccessfulRunTime, long lastFailedRunTime,
long cumulativeExecutionTimeMs) {
this(rescheduling.job, rescheduling.getUid(),
rescheduling.getSourcePackageName(), rescheduling.getSourceUserId(),
rescheduling.getStandbyBucket(), rescheduling.getNamespace(),
- rescheduling.getSourceTag(), numFailures, numSystemStops,
+ rescheduling.getSourceTag(), numFailures,
+ mNumAbandonedFailures, numSystemStops,
newEarliestRuntimeElapsedMillis,
newLatestRuntimeElapsedMillis,
lastSuccessfulRunTime, lastFailedRunTime, cumulativeExecutionTimeMs,
@@ -851,7 +864,8 @@
int standbyBucket = JobSchedulerService.standbyBucketForPackage(jobPackage,
sourceUserId, elapsedNow);
return new JobStatus(job, callingUid, sourcePkg, sourceUserId,
- standbyBucket, namespace, tag, /* numFailures */ 0, /* numSystemStops */ 0,
+ standbyBucket, namespace, tag, /* numFailures */ 0,
+ /* mNumAbandonedFailures */ 0, /* numSystemStops */ 0,
earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */,
/* cumulativeExecutionTime */ 0,
@@ -1146,6 +1160,13 @@
}
/**
+ * Returns the number of times the job stopped previously for STOP_REASON_TIMEOUT_ABANDONED.
+ */
+ public int getNumAbandonedFailures() {
+ return mNumAbandonedFailures;
+ }
+
+ /**
* Returns the number of times the system stopped a previous execution of this job for reasons
* that were likely outside the app's control.
*/
diff --git a/core/api/current.txt b/core/api/current.txt
index 3af8e7e..6aef9dd 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1203,6 +1203,7 @@
field public static final int minResizeHeight = 16843670; // 0x1010396
field public static final int minResizeWidth = 16843669; // 0x1010395
field public static final int minSdkVersion = 16843276; // 0x101020c
+ field @FlaggedApi("android.content.pm.support_minor_versions_in_minsdkversion") public static final int minSdkVersionFull;
field public static final int minWidth = 16843071; // 0x101013f
field public static final int minimumHorizontalAngle = 16843901; // 0x101047d
field public static final int minimumVerticalAngle = 16843902; // 0x101047e
@@ -10089,10 +10090,10 @@
method @FlaggedApi("android.companion.unpair_associated_device") @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean removeBond(int);
method public void requestNotificationAccess(android.content.ComponentName);
method @FlaggedApi("android.companion.association_tag") public void setAssociationTag(int, @NonNull String);
- method @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void startObservingDevicePresence(@NonNull String) throws android.companion.DeviceNotAssociatedException;
+ method @Deprecated @FlaggedApi("android.companion.device_presence") @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void startObservingDevicePresence(@NonNull String) throws android.companion.DeviceNotAssociatedException;
method @FlaggedApi("android.companion.device_presence") @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void startObservingDevicePresence(@NonNull android.companion.ObservingDevicePresenceRequest);
method public void startSystemDataTransfer(int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.companion.CompanionException>) throws android.companion.DeviceNotAssociatedException;
- method @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void stopObservingDevicePresence(@NonNull String) throws android.companion.DeviceNotAssociatedException;
+ method @Deprecated @FlaggedApi("android.companion.device_presence") @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void stopObservingDevicePresence(@NonNull String) throws android.companion.DeviceNotAssociatedException;
method @FlaggedApi("android.companion.device_presence") @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void stopObservingDevicePresence(@NonNull android.companion.ObservingDevicePresenceRequest);
field public static final String EXTRA_ASSOCIATION = "android.companion.extra.ASSOCIATION";
field @Deprecated public static final String EXTRA_DEVICE = "android.companion.extra.DEVICE";
@@ -10120,9 +10121,9 @@
method @RequiresPermission(android.Manifest.permission.DELIVER_COMPANION_MESSAGES) public final void detachSystemDataTransport(int) throws android.companion.DeviceNotAssociatedException;
method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
method @Deprecated @MainThread public void onDeviceAppeared(@NonNull String);
- method @MainThread public void onDeviceAppeared(@NonNull android.companion.AssociationInfo);
+ method @Deprecated @FlaggedApi("android.companion.device_presence") @MainThread public void onDeviceAppeared(@NonNull android.companion.AssociationInfo);
method @Deprecated @MainThread public void onDeviceDisappeared(@NonNull String);
- method @MainThread public void onDeviceDisappeared(@NonNull android.companion.AssociationInfo);
+ method @Deprecated @FlaggedApi("android.companion.device_presence") @MainThread public void onDeviceDisappeared(@NonNull android.companion.AssociationInfo);
method @FlaggedApi("android.companion.device_presence") @MainThread public void onDevicePresenceEvent(@NonNull android.companion.DevicePresenceEvent);
field public static final String SERVICE_INTERFACE = "android.companion.CompanionDeviceService";
}
@@ -13225,8 +13226,8 @@
public abstract class PackageManager {
ctor @Deprecated public PackageManager();
method @Deprecated public abstract void addPackageToPreferred(@NonNull String);
- method public abstract boolean addPermission(@NonNull android.content.pm.PermissionInfo);
- method public abstract boolean addPermissionAsync(@NonNull android.content.pm.PermissionInfo);
+ method @Deprecated @FlaggedApi("android.permission.flags.permission_tree_apis_deprecated") public abstract boolean addPermission(@NonNull android.content.pm.PermissionInfo);
+ method @Deprecated @FlaggedApi("android.permission.flags.permission_tree_apis_deprecated") public abstract boolean addPermissionAsync(@NonNull android.content.pm.PermissionInfo);
method @Deprecated public abstract void addPreferredActivity(@NonNull android.content.IntentFilter, int, @Nullable android.content.ComponentName[], @NonNull android.content.ComponentName);
method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean addWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int);
method public boolean canPackageQuery(@NonNull String, @NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -13367,7 +13368,7 @@
method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryServiceProperty(@NonNull String);
method public void relinquishUpdateOwnership(@NonNull String);
method @Deprecated public abstract void removePackageFromPreferred(@NonNull String);
- method public abstract void removePermission(@NonNull String);
+ method @Deprecated @FlaggedApi("android.permission.flags.permission_tree_apis_deprecated") public abstract void removePermission(@NonNull String);
method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean removeWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int);
method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.pm.PackageManager.OnChecksumsReadyListener) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException;
method @Nullable public abstract android.content.pm.ResolveInfo resolveActivity(@NonNull android.content.Intent, int);
@@ -33612,9 +33613,14 @@
@FlaggedApi("android.os.cpu_gpu_headrooms") public final class CpuHeadroomParams {
ctor public CpuHeadroomParams();
method public int getCalculationType();
+ method @IntRange(from=android.os.CpuHeadroomParams.CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to=android.os.CpuHeadroomParams.CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) public long getCalculationWindowMillis();
method public void setCalculationType(int);
+ method public void setCalculationWindowMillis(@IntRange(from=android.os.CpuHeadroomParams.CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to=android.os.CpuHeadroomParams.CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) int);
+ method public void setTids(@NonNull int...);
field public static final int CPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1; // 0x1
field public static final int CPU_HEADROOM_CALCULATION_TYPE_MIN = 0; // 0x0
+ field public static final int CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000; // 0x2710
+ field public static final int CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50; // 0x32
}
public final class CpuUsageInfo implements android.os.Parcelable {
@@ -33867,9 +33873,13 @@
@FlaggedApi("android.os.cpu_gpu_headrooms") public final class GpuHeadroomParams {
ctor public GpuHeadroomParams();
method public int getCalculationType();
+ method @IntRange(from=android.os.GpuHeadroomParams.GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to=android.os.GpuHeadroomParams.GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) public int getCalculationWindowMillis();
method public void setCalculationType(int);
+ method public void setCalculationWindowMillis(@IntRange(from=android.os.GpuHeadroomParams.GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to=android.os.GpuHeadroomParams.GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) int);
field public static final int GPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1; // 0x1
field public static final int GPU_HEADROOM_CALCULATION_TYPE_MIN = 0; // 0x0
+ field public static final int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000; // 0x2710
+ field public static final int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50; // 0x32
}
public class Handler {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 4b83b43..83699ac 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3940,6 +3940,7 @@
field public static final String WIFI_NL80211_SERVICE = "wifinl80211";
field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager";
field public static final String WIFI_SCANNING_SERVICE = "wifiscanner";
+ field @FlaggedApi("android.net.wifi.flags.usd") public static final String WIFI_USD_SERVICE = "wifi_usd";
}
public final class ContextParams {
@@ -5209,6 +5210,11 @@
method @NonNull public java.util.Collection<android.hardware.contexthub.HubServiceInfo> getServiceInfoCollection();
method @Nullable public String getTag();
method public int getVersion();
+ field public static final int REASON_CLOSE_ENDPOINT_SESSION_REQUESTED = 4; // 0x4
+ field public static final int REASON_ENDPOINT_INVALID = 5; // 0x5
+ field public static final int REASON_ENDPOINT_STOPPED = 6; // 0x6
+ field public static final int REASON_FAILURE = 0; // 0x0
+ field public static final int REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED = 3; // 0x3
}
public static final class HubEndpoint.Builder {
@@ -5295,16 +5301,13 @@
@FlaggedApi("android.chre.flags.offload_api") public interface IHubEndpointDiscoveryCallback {
method public void onEndpointsStarted(@NonNull java.util.List<android.hardware.contexthub.HubDiscoveryInfo>);
- method public void onEndpointsStopped(@NonNull java.util.List<android.hardware.contexthub.HubDiscoveryInfo>);
+ method public void onEndpointsStopped(@NonNull java.util.List<android.hardware.contexthub.HubDiscoveryInfo>, int);
}
@FlaggedApi("android.chre.flags.offload_api") public interface IHubEndpointLifecycleCallback {
method public void onSessionClosed(@NonNull android.hardware.contexthub.HubEndpointSession, int);
method @NonNull public android.hardware.contexthub.HubEndpointSessionResult onSessionOpenRequest(@NonNull android.hardware.contexthub.HubEndpointInfo, @Nullable android.hardware.contexthub.HubServiceInfo);
method public void onSessionOpened(@NonNull android.hardware.contexthub.HubEndpointSession);
- field public static final int REASON_CLOSE_ENDPOINT_SESSION_REQUESTED = 4; // 0x4
- field public static final int REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED = 3; // 0x3
- field public static final int REASON_UNSPECIFIED = 0; // 0x0
}
@FlaggedApi("android.chre.flags.offload_api") public interface IHubEndpointMessageCallback {
@@ -7634,10 +7637,11 @@
method public boolean isActive();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean isMuted();
method public boolean isSpatialized();
- field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_APP_OPS = 8; // 0x8
+ field @Deprecated @FlaggedApi("android.media.audio.muted_by_port_volume_api") @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_APP_OPS = 8; // 0x8
field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_CLIENT_VOLUME = 16; // 0x10
field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_MASTER = 1; // 0x1
field @FlaggedApi("android.media.audio.muted_by_port_volume_api") @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_OP_CONTROL_AUDIO = 128; // 0x80
+ field @FlaggedApi("android.media.audio.muted_by_port_volume_api") @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_OP_PLAY_AUDIO = 8; // 0x8
field @FlaggedApi("android.media.audio.muted_by_port_volume_api") @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_PORT_VOLUME = 64; // 0x40
field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_STREAM_MUTED = 4; // 0x4
field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_STREAM_VOLUME = 2; // 0x2
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 967f6194..3a62ac9 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -541,7 +541,9 @@
method @Nullable public android.graphics.Rect peekBitmapDimensions();
method @Nullable public android.graphics.Rect peekBitmapDimensions(int);
method @FlaggedApi("com.android.window.flags.multi_crop") @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) public int setBitmapWithCrops(@Nullable android.graphics.Bitmap, @NonNull java.util.Map<android.graphics.Point,android.graphics.Rect>, boolean, int) throws java.io.IOException;
+ method @FlaggedApi("android.app.live_wallpaper_content_handling") @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) public int setBitmapWithDescription(@Nullable android.graphics.Bitmap, @NonNull android.app.wallpaper.WallpaperDescription, boolean, int) throws java.io.IOException;
method @FlaggedApi("com.android.window.flags.multi_crop") @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) public int setStreamWithCrops(@NonNull java.io.InputStream, @NonNull java.util.Map<android.graphics.Point,android.graphics.Rect>, boolean, int) throws java.io.IOException;
+ method @FlaggedApi("android.app.live_wallpaper_content_handling") @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) public int setStreamWithDescription(@NonNull java.io.InputStream, @NonNull android.app.wallpaper.WallpaperDescription, boolean, int) throws java.io.IOException;
method public void setWallpaperZoomOut(@NonNull android.os.IBinder, float);
method public boolean shouldEnableWideColorGamut();
method public boolean wallpaperSupportsWcg(int);
@@ -907,6 +909,15 @@
}
+package android.app.wallpaper {
+
+ public static final class WallpaperDescription.Builder {
+ method @NonNull public android.app.wallpaper.WallpaperDescription.Builder setCropHints(@NonNull java.util.Map<android.graphics.Point,android.graphics.Rect>);
+ method @NonNull public android.app.wallpaper.WallpaperDescription.Builder setCropHints(@NonNull android.util.SparseArray<android.graphics.Rect>);
+ }
+
+}
+
package android.appwidget {
public class AppWidgetManager {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 7e0a9b6..3cbea87 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -130,7 +130,6 @@
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.Immutable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import com.android.internal.pm.RoSystemFeatures;
@@ -1020,6 +1019,33 @@
}
}
+ @Override
+ public void setPageSizeAppCompatFlagsSettingsOverride(String packageName, boolean enabled) {
+ try {
+ mPM.setPageSizeAppCompatFlagsSettingsOverride(packageName, enabled);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public boolean isPageSizeCompatEnabled(String packageName) {
+ try {
+ return mPM.isPageSizeCompatEnabled(packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public String getPageSizeCompatWarningMessage(String packageName) {
+ try {
+ return mPM.getPageSizeCompatWarningMessage(packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private static List<byte[]> encodeCertificates(List<Certificate> certs) throws
CertificateEncodingException {
if (certs == null) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index f3d999a..e2d20cb 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -817,16 +817,16 @@
R.layout.notification_2025_template_expanded_base,
R.layout.notification_2025_template_heads_up_base,
R.layout.notification_2025_template_header,
+ R.layout.notification_2025_template_conversation,
+ R.layout.notification_2025_template_collapsed_call,
+ R.layout.notification_2025_template_expanded_call,
+ R.layout.notification_2025_template_collapsed_messaging,
+ R.layout.notification_2025_template_collapsed_media,
R.layout.notification_template_material_big_picture,
R.layout.notification_template_material_big_text,
R.layout.notification_template_material_inbox,
- R.layout.notification_template_material_messaging,
R.layout.notification_template_material_big_messaging,
- R.layout.notification_template_material_conversation,
- R.layout.notification_template_material_media,
R.layout.notification_template_material_big_media,
- R.layout.notification_template_material_call,
- R.layout.notification_template_material_big_call,
R.layout.notification_template_header -> true;
case R.layout.notification_template_material_progress -> Flags.apiRichOngoing();
default -> false;
@@ -5924,7 +5924,7 @@
|| resId == getCompactHeadsUpBaseLayoutResource()
|| resId == getMessagingCompactHeadsUpLayoutResource()
|| resId == getCollapsedMessagingLayoutResource()
- || resId == R.layout.notification_template_material_media);
+ || resId == getCollapsedMediaLayoutResource());
RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
resetStandardTemplate(contentView);
@@ -7573,15 +7573,47 @@
}
private int getCollapsedMessagingLayoutResource() {
- return R.layout.notification_template_material_messaging;
+ if (Flags.notificationsRedesignTemplates()) {
+ return R.layout.notification_2025_template_collapsed_messaging;
+ } else {
+ return R.layout.notification_template_material_messaging;
+ }
}
private int getExpandedMessagingLayoutResource() {
return R.layout.notification_template_material_big_messaging;
}
+ private int getCollapsedMediaLayoutResource() {
+ if (Flags.notificationsRedesignTemplates()) {
+ return R.layout.notification_2025_template_collapsed_media;
+ } else {
+ return R.layout.notification_template_material_media;
+ }
+ }
+
private int getConversationLayoutResource() {
- return R.layout.notification_template_material_conversation;
+ if (Flags.notificationsRedesignTemplates()) {
+ return R.layout.notification_2025_template_conversation;
+ } else {
+ return R.layout.notification_template_material_conversation;
+ }
+ }
+
+ private int getCollapsedCallLayoutResource() {
+ if (Flags.notificationsRedesignTemplates()) {
+ return R.layout.notification_2025_template_collapsed_call;
+ } else {
+ return R.layout.notification_template_material_call;
+ }
+ }
+
+ private int getExpandedCallLayoutResource() {
+ if (Flags.notificationsRedesignTemplates()) {
+ return R.layout.notification_2025_template_expanded_call;
+ } else {
+ return R.layout.notification_template_material_big_call;
+ }
}
private int getProgressLayoutResource() {
@@ -10480,7 +10512,7 @@
.fillTextsFrom(mBuilder);
TemplateBindResult result = new TemplateBindResult();
RemoteViews template = mBuilder.applyStandardTemplate(
- R.layout.notification_template_material_media, p,
+ mBuilder.getCollapsedMediaLayoutResource(), p,
null /* result */);
for (int i = 0; i < MAX_MEDIA_BUTTONS_IN_COMPACT; i++) {
@@ -10932,10 +10964,10 @@
final RemoteViews contentView;
if (isCollapsed) {
contentView = mBuilder.applyStandardTemplate(
- R.layout.notification_template_material_call, p, null /* result */);
+ mBuilder.getCollapsedCallLayoutResource(), p, null /* result */);
} else {
contentView = mBuilder.applyStandardTemplateWithActions(
- R.layout.notification_template_material_big_call, p, null /* result */);
+ mBuilder.getExpandedCallLayoutResource(), p, null /* result */);
}
// Bind some extra conversation-specific header fields.
@@ -14786,12 +14818,23 @@
} else {
mBackgroundColor = rawColor;
}
- mPrimaryTextColor = ContrastColorUtil.findAlphaToMeetContrast(
- ContrastColorUtil.resolvePrimaryColor(ctx, mBackgroundColor, nightMode),
- mBackgroundColor, 4.5);
- mSecondaryTextColor = ContrastColorUtil.findAlphaToMeetContrast(
- ContrastColorUtil.resolveSecondaryColor(ctx, mBackgroundColor, nightMode),
- mBackgroundColor, 4.5);
+ if (Flags.uiRichOngoing()) {
+ boolean isBgDark = Notification.Builder.isColorDark(mBackgroundColor);
+ int onSurfaceColorExtreme = isBgDark ? Color.WHITE : Color.BLACK;
+ mPrimaryTextColor = ContrastColorUtil.ensureContrast(
+ ColorUtils.blendARGB(mBackgroundColor, onSurfaceColorExtreme, 0.9f),
+ mBackgroundColor, isBgDark, 4.5);
+ mSecondaryTextColor = ContrastColorUtil.ensureContrast(
+ ColorUtils.blendARGB(mBackgroundColor, onSurfaceColorExtreme, 0.8f),
+ mBackgroundColor, isBgDark, 4.5);
+ } else {
+ mPrimaryTextColor = ContrastColorUtil.findAlphaToMeetContrast(
+ ContrastColorUtil.resolvePrimaryColor(ctx, mBackgroundColor, nightMode),
+ mBackgroundColor, 4.5);
+ mSecondaryTextColor = ContrastColorUtil.findAlphaToMeetContrast(
+ ContrastColorUtil.resolveSecondaryColor(ctx,
+ mBackgroundColor, nightMode), mBackgroundColor, 4.5);
+ }
mContrastColor = mPrimaryTextColor;
mPrimaryAccentColor = mPrimaryTextColor;
mSecondaryAccentColor = mSecondaryTextColor;
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index e218418..3973c58 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -61,6 +61,8 @@
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
/**
@@ -680,12 +682,17 @@
@GuardedBy("mLock")
private boolean mTestMode = false;
- /**
- * The local value of the handler, used during testing but also used directly by the
- * NonceLocal handler.
- */
+ // This is the local value of the nonce, as last set by the NonceHandler. It is always
+ // updated by the setNonce() operation. The getNonce() operation returns this value in
+ // NonceLocal handlers and handlers in test mode.
@GuardedBy("mLock")
- protected long mTestNonce = NONCE_UNSET;
+ protected long mShadowNonce = NONCE_UNSET;
+
+ // A list of watchers to be notified of changes. This is null until at least one watcher
+ // registers. Checking for null is meant to be the fastest way the handler can determine
+ // that there are no watchers to be notified.
+ @GuardedBy("mLock")
+ private ArrayList<Semaphore> mWatchers;
/**
* The methods to get and set a nonce from whatever storage is being used. mLock may be
@@ -701,27 +708,60 @@
/**
* Get a nonce from storage. If the handler is in test mode, the nonce is returned from
- * the local mTestNonce.
+ * the local mShadowNonce.
*/
long getNonce() {
synchronized (mLock) {
- if (mTestMode) return mTestNonce;
+ if (mTestMode) return mShadowNonce;
}
return getNonceInternal();
}
/**
- * Write a nonce to storage. If the handler is in test mode, the nonce is written to the
- * local mTestNonce and storage is not affected.
+ * Write a nonce to storage. The nonce is always written to the local mShadowNonce. If
+ * the handler is not in test mode the nonce is also written to storage.
*/
void setNonce(long val) {
synchronized (mLock) {
- if (mTestMode) {
- mTestNonce = val;
- return;
+ mShadowNonce = val;
+ if (!mTestMode) {
+ setNonceInternal(val);
+ }
+ wakeAllWatchersLocked();
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void wakeAllWatchersLocked() {
+ if (mWatchers != null) {
+ for (int i = 0; i < mWatchers.size(); i++) {
+ mWatchers.get(i).release();
}
}
- setNonceInternal(val);
+ }
+
+ /**
+ * Register a watcher to be notified when a nonce changes. There is no check for
+ * duplicates. In general, this method is called only from {@link NonceWatcher}.
+ */
+ void registerWatcher(Semaphore s) {
+ synchronized (mLock) {
+ if (mWatchers == null) {
+ mWatchers = new ArrayList<>();
+ }
+ mWatchers.add(s);
+ }
+ }
+
+ /**
+ * Unregister a watcher. Nothing happens if the watcher is not registered.
+ */
+ void unregisterWatcher(Semaphore s) {
+ synchronized (mLock) {
+ if (mWatchers != null) {
+ mWatchers.remove(s);
+ }
+ }
}
/**
@@ -854,7 +894,7 @@
void setTestMode(boolean mode) {
synchronized (mLock) {
mTestMode = mode;
- mTestNonce = NONCE_UNSET;
+ mShadowNonce = NONCE_UNSET;
}
}
@@ -1028,7 +1068,7 @@
/**
* SystemProperties and shared storage are protected and cannot be written by random
* processes. So, for testing purposes, the NonceLocal handler stores the nonce locally. The
- * NonceLocal uses the mTestNonce in the superclass, regardless of test mode.
+ * NonceLocal uses the mShadowNonce in the superclass, regardless of test mode.
*/
private static class NonceLocal extends NonceHandler {
// The saved nonce.
@@ -1040,16 +1080,130 @@
@Override
long getNonceInternal() {
- return mTestNonce;
+ return mShadowNonce;
}
@Override
void setNonceInternal(long value) {
- mTestNonce = value;
+ mShadowNonce = value;
}
}
/**
+ * A NonceWatcher lets an external client test if a nonce value has changed from the last time
+ * the watcher was checked.
+ * @hide
+ */
+ public static class NonceWatcher implements AutoCloseable {
+ // The handler for the key.
+ private final NonceHandler mHandler;
+
+ // The last-seen value. This is initialized to "unset".
+ private long mLastSeen = NONCE_UNSET;
+
+ // The semaphore that the watcher waits on. A permit is released every time the nonce
+ // changes. Permits are acquired in the wait method.
+ private final Semaphore mSem = new Semaphore(0);
+
+ /**
+ * Create a watcher for a handler. The last-seen value is not set here and will be
+ * "unset". Therefore, a call to isChanged() will return true if the nonce has ever been
+ * set, no matter when the watcher is first created. Clients may want to flush that
+ * change by calling isChanged() immediately after constructing the object.
+ */
+ private NonceWatcher(@NonNull NonceHandler handler) {
+ mHandler = handler;
+ mHandler.registerWatcher(mSem);
+ }
+
+ /**
+ * Unregister to be notified when a nonce changes. NonceHandler allows a call to
+ * unregisterWatcher with a semaphore that is not registered, so there is no check inside
+ * this method to guard against multiple closures.
+ */
+ @Override
+ public void close() {
+ mHandler.unregisterWatcher(mSem);
+ }
+
+ /**
+ * Return the last seen value of the nonce. This does not update that value. Only
+ * {@link #isChanged()} updates the value.
+ */
+ public long lastSeen() {
+ return mLastSeen;
+ }
+
+ /**
+ * Return true if the nonce has changed from the last time isChanged() was called. The
+ * method is not thread safe.
+ * @hide
+ */
+ public boolean isChanged() {
+ long current = mHandler.getNonce();
+ if (current != mLastSeen) {
+ mLastSeen = current;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Wait for the nonce value to change. It is not guaranteed that the nonce has changed when
+ * this returns: clients must confirm with {@link #isChanged}. The wait operation is only
+ * effective in a process that writes the nonces. The function returns the number of times
+ * the nonce had changed since the last call to the method.
+ * @hide
+ */
+ public int waitForChange() throws InterruptedException {
+ mSem.acquire(1);
+ return 1 + mSem.drainPermits();
+ }
+
+ /**
+ * Wait for the nonce value to change. It is not guaranteed that the nonce has changed when
+ * this returns: clients must confirm with {@link #isChanged}. The wait operation is only
+ * effective in a process that writes the nonces. The function returns the number of times
+ * the nonce changed since the last call to the method. A return value of zero means the
+ * timeout expired. Beware that a timeout of 0 means the function will not wait at all.
+ * @hide
+ */
+ public int waitForChange(long timeout, TimeUnit timeUnit) throws InterruptedException {
+ if (mSem.tryAcquire(1, timeout, timeUnit)) {
+ return 1 + mSem.drainPermits();
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Wake the watcher by releasing the semaphore. This can be used to wake clients that are
+ * blocked in {@link #waitForChange} without affecting the underlying nonce.
+ * @hide
+ */
+ public void wakeUp() {
+ mSem.release();
+ }
+ }
+
+ /**
+ * Return a NonceWatcher for the cache.
+ * @hide
+ */
+ public NonceWatcher getNonceWatcher() {
+ return new NonceWatcher(mNonce);
+ }
+
+ /**
+ * Return a NonceWatcher for the given property. If a handler does not exist for the
+ * property, one is created. This throws if the property name is not a valid cache key.
+ * @hide
+ */
+ public static NonceWatcher getNonceWatcher(@NonNull String propertyName) {
+ return new NonceWatcher(getNonceHandler(propertyName));
+ }
+
+ /**
* Complete key prefixes.
*/
private static final String PREFIX_TEST = CACHE_KEY_PREFIX + "." + MODULE_TEST + ".";
@@ -1663,6 +1817,26 @@
}
/**
+ * Non-static version of corkInvalidations() for situations in which the cache instance is
+ * available. This is slightly faster than than the static versions because it does not have
+ * to look up the NonceHandler for a given property name.
+ * @hide
+ */
+ public void corkInvalidations() {
+ mNonce.cork();
+ }
+
+ /**
+ * Non-static version of uncorkInvalidations() for situations in which the cache instance is
+ * available. This is slightly faster than than the static versions because it does not have
+ * to look up the NonceHandler for a given property name.
+ * @hide
+ */
+ public void uncorkInvalidations() {
+ mNonce.uncork();
+ }
+
+ /**
* Invalidate caches in all processes that are keyed for the module and api.
* @hide
*/
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index abb2dd4..a8671cf 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -2491,6 +2491,27 @@
return result.getInt(EXTRA_NEW_WALLPAPER_ID, 0);
}
+ /**
+ * Version of setBitmap that allows specification of wallpaper metadata including how the
+ * wallpaper will be positioned for different display sizes.
+ *
+ * @param fullImage A bitmap that will supply the wallpaper imagery.
+ * @param description Wallpaper metadata including desired cropping
+ * @param allowBackup {@code true} if the OS is permitted to back up this wallpaper
+ * image for restore to a future device; {@code false} otherwise.
+ * @param which Flags indicating which wallpaper(s) to configure with the new imagery.
+ * @hide
+ */
+ @FlaggedApi(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING)
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.SET_WALLPAPER)
+ public int setBitmapWithDescription(@Nullable Bitmap fullImage,
+ @NonNull WallpaperDescription description, boolean allowBackup,
+ @SetWallpaperFlags int which) throws IOException {
+ return setBitmapWithCrops(fullImage, description.getCropHints(), allowBackup, which,
+ mContext.getUserId());
+ }
+
private final void validateRect(Rect rect) {
if (rect != null && rect.isEmpty()) {
throw new IllegalArgumentException("visibleCrop rectangle must be valid and non-empty");
@@ -2700,6 +2721,27 @@
}
/**
+ * Version of setStream that allows specification of wallpaper metadata including how the
+ * wallpaper will be positioned for different display sizes.
+ *
+ * @param bitmapData A stream containing the raw data to install as a wallpaper. This
+ * data can be in any format handled by {@link BitmapRegionDecoder}.
+ * @param description Wallpaper metadata including desired cropping
+ * @param allowBackup {@code true} if the OS is permitted to back up this wallpaper
+ * image for restore to a future device; {@code false} otherwise.
+ * @param which Flags indicating which wallpaper(s) to configure with the new imagery.
+ * @hide
+ */
+ @FlaggedApi(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING)
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.SET_WALLPAPER)
+ public int setStreamWithDescription(@NonNull InputStream bitmapData,
+ @NonNull WallpaperDescription description, boolean allowBackup,
+ @SetWallpaperFlags int which) throws IOException {
+ return setStreamWithCrops(bitmapData, description.getCropHints(), allowBackup, which);
+ }
+
+ /**
* Return whether any users are currently set to use the wallpaper
* with the given resource ID. That is, their wallpaper has been
* set through {@link #setResource(int)} with the same resource id.
diff --git a/core/java/android/app/wallpaper/WallpaperDescription.java b/core/java/android/app/wallpaper/WallpaperDescription.java
index 4a142bb..3ee00ca 100644
--- a/core/java/android/app/wallpaper/WallpaperDescription.java
+++ b/core/java/android/app/wallpaper/WallpaperDescription.java
@@ -19,8 +19,14 @@
import static android.app.Flags.FLAG_LIVE_WALLPAPER_CONTENT_HANDLING;
import android.annotation.FlaggedApi;
+import android.annotation.SuppressLint;
+import android.annotation.TestApi;
import android.app.WallpaperInfo;
+import android.app.WallpaperManager;
+import android.app.WallpaperManager.ScreenOrientation;
import android.content.ComponentName;
+import android.graphics.Point;
+import android.graphics.Rect;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
@@ -29,6 +35,8 @@
import android.text.Spanned;
import android.text.SpannedString;
import android.util.Log;
+import android.util.Pair;
+import android.util.SparseArray;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -43,6 +51,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
/**
@@ -71,12 +80,15 @@
@Nullable private final Uri mContextUri;
@Nullable private final CharSequence mContextDescription;
@NonNull private final PersistableBundle mContent;
+ @NonNull private final SparseArray<Rect> mCropHints;
+ private final float mSampleSize;
private WallpaperDescription(@Nullable ComponentName component,
@Nullable String id, @Nullable Uri thumbnail, @Nullable CharSequence title,
@Nullable List<CharSequence> description, @Nullable Uri contextUri,
@Nullable CharSequence contextDescription,
- @Nullable PersistableBundle content) {
+ @Nullable PersistableBundle content, @NonNull SparseArray<Rect> cropHints,
+ float sampleSize) {
this.mComponent = component;
this.mId = id;
this.mThumbnail = thumbnail;
@@ -85,6 +97,8 @@
this.mContextUri = contextUri;
this.mContextDescription = contextDescription;
this.mContent = (content != null) ? content : new PersistableBundle();
+ this.mCropHints = cropHints;
+ this.mSampleSize = sampleSize;
}
/** @return the component for this wallpaper, or {@code null} for a static wallpaper */
@@ -134,6 +148,24 @@
return mContent;
}
+ /**
+ * @return the cropping for the current image as described in
+ * {@link Builder#setCropHints(SparseArray)}
+ * @hide
+ */
+ @NonNull
+ public SparseArray<Rect> getCropHints() {
+ return mCropHints;
+ }
+
+ /**
+ * @return the subsamling size as described in {@link Builder#setSampleSize(float)}.
+ * @hide
+ */
+ public float getSampleSize() {
+ return mSampleSize;
+ }
+
////// Comparison overrides
@Override
@@ -163,9 +195,23 @@
if (mContextDescription != null) {
out.attribute(null, "contextdescription", toHtml(mContextDescription));
}
+
+ for (Pair<Integer, String> pair : screenDimensionPairs()) {
+ @ScreenOrientation int orientation = pair.first;
+ String attrName = pair.second;
+ Rect cropHint = mCropHints.get(orientation);
+ if (cropHint == null) continue;
+ out.attributeInt(null, "cropLeft" + attrName, cropHint.left);
+ out.attributeInt(null, "cropTop" + attrName, cropHint.top);
+ out.attributeInt(null, "cropRight" + attrName, cropHint.right);
+ out.attributeInt(null, "cropBottom" + attrName, cropHint.bottom);
+ }
+ out.attributeFloat(null, "sampleSize", mSampleSize);
+
out.startTag(null, XML_TAG_DESCRIPTION);
for (CharSequence s : mDescription) out.attribute(null, "descriptionline", toHtml(s));
out.endTag(null, XML_TAG_DESCRIPTION);
+
try {
out.startTag(null, XML_TAG_CONTENT);
mContent.saveToXml(out);
@@ -194,6 +240,19 @@
CharSequence contextDescription = fromHtml(
in.getAttributeValue(null, "contextdescription"));
+ SparseArray<Rect> cropHints = new SparseArray<>();
+ screenDimensionPairs().forEach(pair -> {
+ @ScreenOrientation int orientation = pair.first;
+ String attrName = pair.second;
+ Rect crop = new Rect(
+ in.getAttributeInt(null, "cropLeft" + attrName, 0),
+ in.getAttributeInt(null, "cropTop" + attrName, 0),
+ in.getAttributeInt(null, "cropRight" + attrName, 0),
+ in.getAttributeInt(null, "cropBottom" + attrName, 0));
+ if (!crop.isEmpty()) cropHints.put(orientation, crop);
+ });
+ float sampleSize = in.getAttributeFloat(null, "sampleSize", 1f);
+
List<CharSequence> description = new ArrayList<>();
PersistableBundle content = null;
int type;
@@ -213,7 +272,7 @@
}
return new WallpaperDescription(componentName, id, thumbnail, title, description,
- contextUri, contextDescription, content);
+ contextUri, contextDescription, content, cropHints, sampleSize);
}
private static String toHtml(@NonNull CharSequence c) {
@@ -253,6 +312,13 @@
mContextUri = Uri.CREATOR.createFromParcel(in);
mContextDescription = in.readCharSequence();
mContent = PersistableBundle.CREATOR.createFromParcel(in);
+ mCropHints = new SparseArray<>();
+ screenDimensionPairs().forEach(pair -> {
+ int orientation = pair.first;
+ Rect crop = in.readTypedObject(Rect.CREATOR);
+ if (crop != null) mCropHints.put(orientation, crop);
+ });
+ mSampleSize = in.readFloat();
}
@NonNull
@@ -283,6 +349,11 @@
Uri.writeToParcel(dest, mContextUri);
dest.writeCharSequence(mContextDescription);
dest.writePersistableBundle(mContent);
+ screenDimensionPairs().forEach(pair -> {
+ int orientation = pair.first;
+ dest.writeTypedObject(mCropHints.get(orientation), flags);
+ });
+ dest.writeFloat(mSampleSize);
}
////// Builder
@@ -293,9 +364,17 @@
*/
@NonNull
public Builder toBuilder() {
- return new Builder().setComponent(mComponent).setId(mId).setThumbnail(mThumbnail).setTitle(
- mTitle).setDescription(mDescription).setContextUri(
- mContextUri).setContextDescription(mContextDescription).setContent(mContent);
+ return new Builder()
+ .setComponent(mComponent)
+ .setId(mId)
+ .setThumbnail(mThumbnail)
+ .setTitle(mTitle)
+ .setDescription(mDescription)
+ .setContextUri(mContextUri)
+ .setContextDescription(mContextDescription)
+ .setContent(mContent)
+ .setCropHints(mCropHints)
+ .setSampleSize(mSampleSize);
}
/** Builder for the immutable {@link WallpaperDescription} class */
@@ -308,6 +387,9 @@
@Nullable private Uri mContextUri;
@Nullable private CharSequence mContextDescription;
@NonNull private PersistableBundle mContent = new PersistableBundle();
+ @NonNull
+ private SparseArray<Rect> mCropHints = new SparseArray<>();
+ private float mSampleSize = 1f;
/** Creates a new, empty {@link Builder}. */
public Builder() {}
@@ -416,11 +498,69 @@
return this;
}
+ /**
+ * Defines which part of the source wallpaper image is in the stored crop file.
+ *
+ * @param cropHints map from screen dimensions to a sub-region of the image to display
+ * for those dimensions. The {@code Rect} sub-region may have a larger
+ * width/height ratio than the screen dimensions to apply a horizontal
+ * parallax effect. If the map is empty or some entries are missing, the
+ * system will apply a default strategy to position the wallpaper for
+ * any unspecified screen dimensions.
+ * @hide
+ */
+ @NonNull
+ @TestApi
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder setCropHints(@NonNull Map<Point, Rect> cropHints) {
+ mCropHints = new SparseArray<>();
+ cropHints.forEach(
+ (point, rect) -> mCropHints.put(WallpaperManager.getOrientation(point), rect));
+ return this;
+ }
+
+ /**
+ * Defines which part of the source wallpaper image is in the stored crop file.
+ *
+ * @param cropHints map from {@link ScreenOrientation} to a sub-region of the image to
+ * display for that screen orientation.
+ * @hide
+ */
+ @NonNull
+ @TestApi
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder setCropHints(@NonNull SparseArray<Rect> cropHints) {
+ mCropHints = cropHints;
+ return this;
+ }
+
+ /**
+ * How much the crop is sub-sampled. A value > 1 means that the image quality was reduced.
+ * This is the ratio between the cropHint height and the actual stored crop file height.
+ * height.
+ *
+ * @param sampleSize Sub-sampling value
+ * @hide
+ */
+ @NonNull
+ public Builder setSampleSize(float sampleSize) {
+ mSampleSize = sampleSize;
+ return this;
+ }
+
/** Creates and returns the {@link WallpaperDescription} represented by this builder. */
@NonNull
public WallpaperDescription build() {
return new WallpaperDescription(mComponent, mId, mThumbnail, mTitle, mDescription,
- mContextUri, mContextDescription, mContent);
+ mContextUri, mContextDescription, mContent, mCropHints, mSampleSize);
}
}
+
+ private static List<Pair<Integer, String>> screenDimensionPairs() {
+ return List.of(
+ new Pair<>(WallpaperManager.ORIENTATION_PORTRAIT, "Portrait"),
+ new Pair<>(WallpaperManager.ORIENTATION_LANDSCAPE, "Landscape"),
+ new Pair<>(WallpaperManager.ORIENTATION_SQUARE_PORTRAIT, "SquarePortrait"),
+ new Pair<>(WallpaperManager.ORIENTATION_SQUARE_LANDSCAPE, "SquareLandscape"));
+ }
}
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 4472c3d..0466847 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -1229,7 +1229,6 @@
}
}
- // TODO(b/315163162) Add @Deprecated keyword after 24Q2 cut.
/**
* Register to receive callbacks whenever the associated device comes in and out of range.
*
@@ -1261,7 +1260,12 @@
*
* @throws DeviceNotAssociatedException if the given device was not previously associated
* with this app.
+ *
+ * @deprecated use {@link #startObservingDevicePresence(ObservingDevicePresenceRequest)}
+ * instead.
*/
+ @FlaggedApi(Flags.FLAG_DEVICE_PRESENCE)
+ @Deprecated
@RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE)
public void startObservingDevicePresence(@NonNull String deviceAddress)
throws DeviceNotAssociatedException {
@@ -1288,7 +1292,7 @@
callingUid, callingPid);
}
}
- // TODO(b/315163162) Add @Deprecated keyword after 24Q2 cut.
+
/**
* Unregister for receiving callbacks whenever the associated device comes in and out of range.
*
@@ -1305,7 +1309,12 @@
*
* @throws DeviceNotAssociatedException if the given device was not previously associated
* with this app.
+ *
+ * @deprecated use {@link #stopObservingDevicePresence(ObservingDevicePresenceRequest)}
+ * instead.
*/
+ @FlaggedApi(Flags.FLAG_DEVICE_PRESENCE)
+ @Deprecated
@RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE)
public void stopObservingDevicePresence(@NonNull String deviceAddress)
throws DeviceNotAssociatedException {
diff --git a/core/java/android/companion/CompanionDeviceService.java b/core/java/android/companion/CompanionDeviceService.java
index db080fc..316d129 100644
--- a/core/java/android/companion/CompanionDeviceService.java
+++ b/core/java/android/companion/CompanionDeviceService.java
@@ -247,12 +247,14 @@
.detachSystemDataTransport(associationId);
}
- // TODO(b/315163162) Add @Deprecated keyword after 24Q2 cut.
/**
* Called by the system when an associated device is nearby or connected.
*
* @param associationInfo A record for the companion device.
+ * @deprecated use {@link #onDevicePresenceEvent(DevicePresenceEvent)}} instead.
*/
+ @FlaggedApi(Flags.FLAG_DEVICE_PRESENCE)
+ @Deprecated
@MainThread
public void onDeviceAppeared(@NonNull AssociationInfo associationInfo) {
if (!associationInfo.isSelfManaged()) {
@@ -260,12 +262,14 @@
}
}
- // TODO(b/315163162) Add @Deprecated keyword after 24Q2 cut.
/**
* Called by the system when an associated device is out of range or disconnected.
*
* @param associationInfo A record for the companion device.
+ * @deprecated use {@link #onDevicePresenceEvent(DevicePresenceEvent)}} instead.
*/
+ @FlaggedApi(Flags.FLAG_DEVICE_PRESENCE)
+ @Deprecated
@MainThread
public void onDeviceDisappeared(@NonNull AssociationInfo associationInfo) {
if (!associationInfo.isSelfManaged()) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index acad92c9..6e2ca2c 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4250,6 +4250,7 @@
//@hide: WIFI_RTT_SERVICE,
//@hide: ETHERNET_SERVICE,
WIFI_RTT_RANGING_SERVICE,
+ WIFI_USD_SERVICE,
NSD_SERVICE,
AUDIO_SERVICE,
AUDIO_DEVICE_VOLUME_SERVICE,
@@ -5096,6 +5097,19 @@
*/
public static final String WIFI_RTT_RANGING_SERVICE = "wifirtt";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
+ * android.net.wifi.usd.UsdManager} for Unsynchronized Service Discovery (USD) operation.
+ *
+ * @see #getSystemService(String)
+ * @see android.net.wifi.usd.UsdManager
+ * @hide
+ */
+ @FlaggedApi(android.net.wifi.flags.Flags.FLAG_USD)
+ @SystemApi
+ public static final String WIFI_USD_SERVICE = "wifi_usd";
+
/**
* Use with {@link #getSystemService(String)} to retrieve a {@link
* android.net.lowpan.LowpanManager} for handling management of
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index cccfdb0..9478422 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1449,6 +1449,97 @@
}
}
+ /**
+ * Use this to report any errors during alignment checks
+ *
+ * @hide
+ */
+ public static final int PAGE_SIZE_APP_COMPAT_FLAG_ERROR = -1;
+
+ /**
+ * Initial value for mPageSizeAppCompatFlags
+ *
+ * @hide
+ */
+ public static final int PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED = 0;
+
+ /**
+ * if set, extract libs forcefully for 16 KB device and show warning dialog.
+ *
+ * @hide
+ */
+ public static final int PAGE_SIZE_APP_COMPAT_FLAG_UNCOMPRESSED_LIBS_NOT_ALIGNED = 1 << 1;
+
+ /**
+ * if set, load 4 KB aligned ELFs on 16 KB device in compat mode and show warning dialog.
+ *
+ * @hide
+ */
+ public static final int PAGE_SIZE_APP_COMPAT_FLAG_ELF_NOT_ALIGNED = 1 << 2;
+
+ /**
+ * Run in 16 KB app compat mode. This flag will be set explicitly through settings. If set, 16
+ * KB app compat warning dialogs will still show up.
+ *
+ * @hide
+ */
+ public static final int PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_ENABLED = 1 << 3;
+
+ /**
+ * Disable 16 KB app compat mode through settings. It should only affect ELF loading as app is
+ * already installed.
+ *
+ * @hide
+ */
+ public static final int PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_DISABLED = 1 << 4;
+
+ /**
+ * Run in 16 KB app compat mode. This flag will be set explicitly through manifest. If set, hide
+ * the 16 KB app compat warning dialogs. This has the highest priority to enable compat mode.
+ *
+ * @hide
+ */
+ public static final int PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_ENABLED = 1 << 5;
+
+ /**
+ * Disable 16 KB app compat mode. This has the highest priority to disable compat mode.
+ *
+ * @hide
+ */
+ public static final int PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_DISABLED = 1 << 6;
+
+ /**
+ * Max value for page size app compat
+ *
+ * @hide
+ */
+ public static final int PAGE_SIZE_APP_COMPAT_FLAG_MAX = 1 << 7;
+
+ /**
+ * 16 KB app compat status for the app. App can have native shared libs which are not page
+ * aligned, LOAD segments inside the shared libs have to be page aligned. Apps can specify the
+ * override in manifest file as well.
+ */
+ private @PageSizeAppCompatFlags int mPageSizeAppCompatFlags =
+ ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED;
+
+ /** {@hide} */
+ @IntDef(
+ prefix = {"PAGE_SIZE_APP_COMPAT_FLAG_"},
+ value = {
+ PAGE_SIZE_APP_COMPAT_FLAG_ERROR,
+ PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED,
+ PAGE_SIZE_APP_COMPAT_FLAG_UNCOMPRESSED_LIBS_NOT_ALIGNED,
+ PAGE_SIZE_APP_COMPAT_FLAG_ELF_NOT_ALIGNED,
+ PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_ENABLED,
+ PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_DISABLED,
+ PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_ENABLED,
+ PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_DISABLED,
+ PAGE_SIZE_APP_COMPAT_FLAG_MAX,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PageSizeAppCompatFlags {}
+
/** @hide */
public String classLoaderName;
@@ -1777,7 +1868,7 @@
pw.println(prefix + "enableOnBackInvokedCallback=" + isOnBackInvokedCallbackEnabled());
pw.println(prefix + "allowCrossUidActivitySwitchFromBelow="
+ allowCrossUidActivitySwitchFromBelow);
-
+ pw.println(prefix + "mPageSizeAppCompatFlags=" + mPageSizeAppCompatFlags);
}
pw.println(prefix + "createTimestamp=" + createTimestamp);
if (mKnownActivityEmbeddingCerts != null) {
@@ -1897,6 +1988,10 @@
}
proto.write(ApplicationInfoProto.Detail.ALLOW_CROSS_UID_ACTIVITY_SWITCH_FROM_BELOW,
allowCrossUidActivitySwitchFromBelow);
+
+ proto.write(ApplicationInfoProto.Detail.ENABLE_PAGE_SIZE_APP_COMPAT,
+ mPageSizeAppCompatFlags);
+
proto.end(detailToken);
}
if (!ArrayUtils.isEmpty(mKnownActivityEmbeddingCerts)) {
@@ -2024,6 +2119,7 @@
localeConfigRes = orig.localeConfigRes;
allowCrossUidActivitySwitchFromBelow = orig.allowCrossUidActivitySwitchFromBelow;
createTimestamp = SystemClock.uptimeMillis();
+ mPageSizeAppCompatFlags = orig.mPageSizeAppCompatFlags;
}
public String toString() {
@@ -2128,6 +2224,7 @@
}
dest.writeInt(localeConfigRes);
dest.writeInt(allowCrossUidActivitySwitchFromBelow ? 1 : 0);
+ dest.writeInt(mPageSizeAppCompatFlags);
sForStringSet.parcel(mKnownActivityEmbeddingCerts, dest, flags);
}
@@ -2228,6 +2325,7 @@
}
localeConfigRes = source.readInt();
allowCrossUidActivitySwitchFromBelow = source.readInt() != 0;
+ mPageSizeAppCompatFlags = source.readInt();
mKnownActivityEmbeddingCerts = sForStringSet.unparcel(source);
if (mKnownActivityEmbeddingCerts.isEmpty()) {
@@ -2765,6 +2863,11 @@
requestRawExternalStorageAccess = value;
}
+ /** {@hide} */
+ public void setPageSizeAppCompatFlags(@PageSizeAppCompatFlags int value) {
+ mPageSizeAppCompatFlags |= value;
+ }
+
/**
* Replaces {@link #mAppClassNamesByProcess}. This takes over the ownership of the passed map.
* Do not modify the argument at the callsite.
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 5d4babb..9f898b8 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -848,4 +848,12 @@
int getAppMetadataSource(String packageName, int userId);
ComponentName getDomainVerificationAgent(int userId);
+
+ void setPageSizeAppCompatFlagsSettingsOverride(in String packageName, boolean enabled);
+
+ boolean isPageSizeCompatEnabled(in String packageName);
+
+ String getPageSizeCompatWarningMessage(in String packageName);
+
+ List<String> getAllApexDirectories();
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index d2b43b9..23d3693 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -804,7 +804,6 @@
@Deprecated
private void __metadata() {}
-
//@formatter:on
// End of generated code
@@ -6729,6 +6728,11 @@
* If the given permission already exists, the info you supply here
* will be used to update it.
*
+ * @deprecated Support for dynamic permissions is going to be removed, and apps that use dynamic
+ * permissions should declare their permissions statically inside their app manifest instead.
+ * This method will become a no-op in a future Android release and eventually be removed from
+ * the SDK.
+ *
* @param info Description of the permission to be added.
*
* @return Returns true if a new permission was created, false if an
@@ -6739,7 +6743,9 @@
*
* @see #removePermission(String)
*/
- //@Deprecated
+ @SuppressWarnings("HiddenAbstractMethod")
+ @FlaggedApi(android.permission.flags.Flags.FLAG_PERMISSION_TREE_APIS_DEPRECATED)
+ @Deprecated
public abstract boolean addPermission(@NonNull PermissionInfo info);
/**
@@ -6748,8 +6754,15 @@
* allowing it to return quicker and batch a series of adds at the
* expense of no guarantee the added permission will be retained if
* the device is rebooted before it is written.
+ *
+ * @deprecated Support for dynamic permissions is going to be removed, and apps that use dynamic
+ * permissions should declare their permissions statically inside their app manifest instead.
+ * This method will become a no-op in a future Android release and eventually be removed from
+ * the SDK.
*/
- //@Deprecated
+ @SuppressWarnings("HiddenAbstractMethod")
+ @FlaggedApi(android.permission.flags.Flags.FLAG_PERMISSION_TREE_APIS_DEPRECATED)
+ @Deprecated
public abstract boolean addPermissionAsync(@NonNull PermissionInfo info);
/**
@@ -6758,6 +6771,11 @@
* -- you are only allowed to remove permissions that you are allowed
* to add.
*
+ * @deprecated Support for dynamic permissions is going to be removed, and apps that use dynamic
+ * permissions should declare their permissions statically inside their app manifest instead.
+ * This method will become a no-op in a future Android release and eventually be removed from
+ * the SDK.
+ *
* @param permName The name of the permission to remove.
*
* @throws SecurityException if you are not allowed to remove the
@@ -6765,7 +6783,9 @@
*
* @see #addPermission(PermissionInfo)
*/
- //@Deprecated
+ @SuppressWarnings("HiddenAbstractMethod")
+ @FlaggedApi(android.permission.flags.Flags.FLAG_PERMISSION_TREE_APIS_DEPRECATED)
+ @Deprecated
public abstract void removePermission(@NonNull String permName);
/**
@@ -10987,6 +11007,41 @@
}
/**
+ * Set the page compat mode override for given package
+ *
+ * @hide
+ */
+ @FlaggedApi(android.content.pm.Flags.FLAG_APP_COMPAT_OPTION_16KB)
+ public void setPageSizeAppCompatFlagsSettingsOverride(@NonNull String packageName,
+ boolean enabled) {
+ throw new UnsupportedOperationException(
+ "setPageSizeAppCompatFlagsSettingsOverride not implemented in subclass");
+ }
+
+ /**
+ * Check whether page size app compat mode is enabled for given package
+ *
+ * @hide
+ */
+ @FlaggedApi(android.content.pm.Flags.FLAG_APP_COMPAT_OPTION_16KB)
+ public boolean isPageSizeCompatEnabled(@NonNull String packageName) {
+ throw new UnsupportedOperationException(
+ "isPageSizeCompatEnabled not implemented in subclass");
+ }
+
+ /**
+ * Get the page size app compat warning dialog to show at app launch time
+ *
+ * @hide
+ */
+ @Nullable
+ @FlaggedApi(android.content.pm.Flags.FLAG_APP_COMPAT_OPTION_16KB)
+ public String getPageSizeCompatWarningMessage(@NonNull String packageName) {
+ throw new UnsupportedOperationException(
+ "getPageSizeCompatWarningMessage not implemented in subclass");
+ }
+
+ /**
* Returns the harmful app warning string for the given app, or null if there is none set.
*
* @param packageName The full name of the desired package.
diff --git a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
index 153dd9a..e30f871 100644
--- a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
@@ -330,6 +330,36 @@
}
/**
+ * Check if a package is compatible with this platform with regards to its
+ * its minSdkVersionFull.
+ *
+ * @param minSdkVersionFullString A string representation of a major.minor version,
+ * e.g. "12.34"
+ * @param platformMinSdkVersionFull The major and minor version of the platform, i.e. the value
+ * of Build.VERSION.SDK_INT_FULL
+ * @param input A ParseInput object to report success or failure
+ */
+ public static ParseResult<Void> verifyMinSdkVersionFull(@NonNull String minSdkVersionFullString,
+ int platformMinSdkVersionFull, @NonNull ParseInput input) {
+ int minSdkVersionFull;
+ try {
+ minSdkVersionFull = Build.parseFullVersion(minSdkVersionFullString);
+ } catch (IllegalStateException e) {
+ return input.error(PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+ e.getMessage());
+ }
+ if (minSdkVersionFull <= platformMinSdkVersionFull) {
+ return input.success(null);
+ }
+ return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
+ "Requires newer sdk version "
+ + Build.fullVersionToString(minSdkVersionFull)
+ + " (current version is "
+ + Build.fullVersionToString(platformMinSdkVersionFull)
+ + ")");
+ }
+
+ /**
* Computes the targetSdkVersion to use at runtime. If the package is not compatible with this
* platform, populates {@code outError[0]} with an error message.
* <p>
diff --git a/core/java/android/hardware/camera2/TotalCaptureResult.java b/core/java/android/hardware/camera2/TotalCaptureResult.java
index 7e42f43..29eaab8 100644
--- a/core/java/android/hardware/camera2/TotalCaptureResult.java
+++ b/core/java/android/hardware/camera2/TotalCaptureResult.java
@@ -86,7 +86,8 @@
mPhysicalCaptureResults = new HashMap<String, TotalCaptureResult>();
for (PhysicalCaptureResultInfo onePhysicalResult : physicalResults) {
TotalCaptureResult physicalResult = new TotalCaptureResult(
- onePhysicalResult.getCameraId(), onePhysicalResult.getCameraMetadata(),
+ onePhysicalResult.getCameraId(),
+ onePhysicalResult.getCameraMetadata(),
parent, extras, /*partials*/null, sessionId, new PhysicalCaptureResultInfo[0]);
mPhysicalCaptureResults.put(onePhysicalResult.getCameraId(),
physicalResult);
@@ -115,7 +116,8 @@
mPhysicalCaptureResults = new HashMap<String, TotalCaptureResult>();
for (PhysicalCaptureResultInfo onePhysicalResult : physicalResults) {
TotalCaptureResult physicalResult = new TotalCaptureResult(
- onePhysicalResult.getCameraId(), onePhysicalResult.getCameraMetadata(),
+ onePhysicalResult.getCameraId(),
+ onePhysicalResult.getCameraMetadata(),
parent, requestId, frameNumber, /*partials*/null, sessionId,
new PhysicalCaptureResultInfo[0]);
mPhysicalCaptureResults.put(onePhysicalResult.getCameraId(),
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index ea70abb..34c0f7b 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -34,6 +34,7 @@
import android.hardware.camera2.CameraExtensionCharacteristics;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CameraMetadataInfo;
import android.hardware.camera2.CameraOfflineSession;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
@@ -59,6 +60,8 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.SystemClock;
@@ -129,6 +132,8 @@
final Object mInterfaceLock = new Object(); // access from this class and Session only!
private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
+ private long mFMQReader; // native fmq reader ptr
+
private final StateCallback mDeviceCallback;
private volatile StateCallbackKK mSessionStateCallback;
private final Executor mDeviceExecutor;
@@ -476,6 +481,9 @@
if (mInError) return;
mRemoteDevice = new ICameraDeviceUserWrapper(remoteDevice);
+ Parcel resultParcel = Parcel.obtain();
+ mRemoteDevice.getCaptureResultMetadataQueue().writeToParcel(resultParcel, 0);
+ mFMQReader = nativeCreateFMQReader(resultParcel);
IBinder remoteDeviceBinder = remoteDevice.asBinder();
// For legacy camera device, remoteDevice is in the same process, and
@@ -1682,6 +1690,7 @@
if (mRemoteDevice != null || mInError) {
mDeviceExecutor.execute(mCallOnClosed);
}
+ nativeClose(mFMQReader);
mRemoteDevice = null;
}
@@ -2416,27 +2425,61 @@
}
}
}
+ private PhysicalCaptureResultInfo[] readMetadata(
+ PhysicalCaptureResultInfo[] srcPhysicalResults) {
+ PhysicalCaptureResultInfo[] retVal =
+ new PhysicalCaptureResultInfo[srcPhysicalResults.length];
+ int i = 0;
+ long fmqSize = 0;
+ for (PhysicalCaptureResultInfo srcPhysicalResult : srcPhysicalResults) {
+ CameraMetadataNative physicalCameraMetadata = null;
+ if (srcPhysicalResult.getCameraMetadataInfo().getTag() ==
+ CameraMetadataInfo.fmqSize) {
+ fmqSize = srcPhysicalResult.getCameraMetadataInfo().getFmqSize();
+ physicalCameraMetadata =
+ new CameraMetadataNative(nativeReadResultMetadata(mFMQReader, fmqSize));
+ } else {
+ physicalCameraMetadata = srcPhysicalResult.getCameraMetadata();
+ }
+ PhysicalCaptureResultInfo physicalResultInfo =
+ new PhysicalCaptureResultInfo(
+ srcPhysicalResult.getCameraId(), physicalCameraMetadata);
+ retVal[i] = physicalResultInfo;
+ i++;
+ }
+ return retVal;
+ }
@Override
- public void onResultReceived(CameraMetadataNative result,
+ public void onResultReceived(CameraMetadataInfo resultInfo,
CaptureResultExtras resultExtras, PhysicalCaptureResultInfo physicalResults[])
throws RemoteException {
int requestId = resultExtras.getRequestId();
long frameNumber = resultExtras.getFrameNumber();
-
- if (DEBUG) {
- Log.v(TAG, "Received result frame " + frameNumber + " for id "
- + requestId);
- }
-
synchronized(mInterfaceLock) {
if (mRemoteDevice == null) return; // Camera already closed
-
+ PhysicalCaptureResultInfo savedPhysicalResults[] = physicalResults;
+ CameraMetadataNative result;
+ if (resultInfo.getTag() == CameraMetadataInfo.fmqSize) {
+ CameraMetadataNative fmqMetadata =
+ new CameraMetadataNative(
+ nativeReadResultMetadata(mFMQReader, resultInfo.getFmqSize()));
+ result = fmqMetadata;
+ } else {
+ result = resultInfo.getMetadata();
+ }
+ physicalResults = readMetadata(savedPhysicalResults);
+ if (DEBUG) {
+ Log.v(TAG, "Received result frame " + frameNumber + " for id "
+ + requestId);
+ }
// Redirect device callback to the offline session in case we are in the middle
// of an offline switch
if (mOfflineSessionImpl != null) {
- mOfflineSessionImpl.getCallbacks().onResultReceived(result, resultExtras,
+ CameraMetadataInfo resultInfoOffline = CameraMetadataInfo.metadata(result);
+ mOfflineSessionImpl.getCallbacks().onResultReceived(resultInfoOffline,
+ resultExtras,
physicalResults);
return;
}
@@ -2824,6 +2867,11 @@
}
}
+ private static native long nativeCreateFMQReader(Parcel resultQueue);
+ //TODO: Investigate adding FastNative b/62791857
+ private static native long nativeReadResultMetadata(long ptr, long metadataSize);
+ private static native void nativeClose(long ptr);
+
@Override
public @CAMERA_AUDIO_RESTRICTION int getCameraAudioRestriction() throws CameraAccessException {
synchronized(mInterfaceLock) {
@@ -2870,4 +2918,4 @@
}
}
}
-}
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index c0a5928..d7b6f11 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -391,6 +391,18 @@
}
/**
+ * Take ownership of native metadata
+ */
+ public CameraMetadataNative(long metadataPtr) {
+ super();
+ mMetadataPtr = metadataPtr;
+ if (mMetadataPtr == 0) {
+ throw new OutOfMemoryError("Failed to allocate native CameraMetadata");
+ }
+ updateNativeAllocation();
+ }
+
+ /**
* Move the contents from {@code other} into a new camera metadata instance.</p>
*
* <p>After this call, {@code other} will become empty.</p>
diff --git a/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java
index 1769c46..e660d6a 100644
--- a/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java
@@ -28,6 +28,7 @@
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.CameraMetadataInfo;
import android.hardware.camera2.ICameraDeviceCallbacks;
import android.hardware.camera2.ICameraOfflineSession;
import android.hardware.camera2.TotalCaptureResult;
@@ -291,10 +292,10 @@
}
@Override
- public void onResultReceived(CameraMetadataNative result,
+ public void onResultReceived(CameraMetadataInfo resultInfo,
CaptureResultExtras resultExtras, PhysicalCaptureResultInfo physicalResults[])
throws RemoteException {
-
+ CameraMetadataNative result = resultInfo.getMetadata();
int requestId = resultExtras.getRequestId();
long frameNumber = resultExtras.getFrameNumber();
diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
index 831c75ec..a79e084 100644
--- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
+++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
@@ -26,6 +26,7 @@
import android.hardware.camera2.params.SessionConfiguration;
import android.hardware.camera2.utils.ExceptionUtils;
import android.hardware.camera2.utils.SubmitInfo;
+import android.hardware.common.fmq.MQDescriptor;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
@@ -313,4 +314,15 @@
throw ExceptionUtils.throwAsPublicException(e);
}
}
-}
+
+ public MQDescriptor<Byte, Byte> getCaptureResultMetadataQueue() throws CameraAccessException {
+ try {
+ return mRemoteDevice.getCaptureResultMetadataQueue();
+ } catch (ServiceSpecificException e) {
+ throw ExceptionUtils.throwAsPublicException(e);
+ } catch (RemoteException e) {
+ throw ExceptionUtils.throwAsPublicException(e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/camera2/impl/PhysicalCaptureResultInfo.java b/core/java/android/hardware/camera2/impl/PhysicalCaptureResultInfo.java
index 09619d0..77296d1 100644
--- a/core/java/android/hardware/camera2/impl/PhysicalCaptureResultInfo.java
+++ b/core/java/android/hardware/camera2/impl/PhysicalCaptureResultInfo.java
@@ -15,6 +15,7 @@
*/
package android.hardware.camera2.impl;
+import android.hardware.camera2.CameraMetadataInfo;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.os.Parcel;
@@ -25,7 +26,7 @@
*/
public class PhysicalCaptureResultInfo implements Parcelable {
private String cameraId;
- private CameraMetadataNative cameraMetadata;
+ private CameraMetadataInfo cameraMetadataInfo;
public static final @android.annotation.NonNull Parcelable.Creator<PhysicalCaptureResultInfo> CREATOR =
new Parcelable.Creator<PhysicalCaptureResultInfo>() {
@@ -46,7 +47,7 @@
public PhysicalCaptureResultInfo(String cameraId, CameraMetadataNative cameraMetadata) {
this.cameraId = cameraId;
- this.cameraMetadata = cameraMetadata;
+ this.cameraMetadataInfo = CameraMetadataInfo.metadata(cameraMetadata);
}
@Override
@@ -57,13 +58,12 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(cameraId);
- cameraMetadata.writeToParcel(dest, flags);
+ cameraMetadataInfo.writeToParcel(dest, flags);
}
public void readFromParcel(Parcel in) {
cameraId = in.readString();
- cameraMetadata = new CameraMetadataNative();
- cameraMetadata.readFromParcel(in);
+ cameraMetadataInfo = CameraMetadataInfo.CREATOR.createFromParcel(in);
}
public String getCameraId() {
@@ -71,6 +71,11 @@
}
public CameraMetadataNative getCameraMetadata() {
- return cameraMetadata;
+ return cameraMetadataInfo.getMetadata();
}
-}
+
+ public CameraMetadataInfo getCameraMetadataInfo() {
+ return cameraMetadataInfo;
+ }
+
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/contexthub/HubEndpoint.java b/core/java/android/hardware/contexthub/HubEndpoint.java
index 078b4d4..7efdd6d 100644
--- a/core/java/android/hardware/contexthub/HubEndpoint.java
+++ b/core/java/android/hardware/contexthub/HubEndpoint.java
@@ -18,6 +18,7 @@
import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -31,6 +32,8 @@
import androidx.annotation.GuardedBy;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -48,6 +51,46 @@
public class HubEndpoint {
private static final String TAG = "HubEndpoint";
+ /**
+ * Constants describing the outcome of operations through HubEndpoints (like opening/closing of
+ * sessions or stopping of endpoints).
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = {"REASON_"},
+ value = {
+ REASON_FAILURE,
+ REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED,
+ REASON_CLOSE_ENDPOINT_SESSION_REQUESTED,
+ REASON_ENDPOINT_INVALID,
+ REASON_ENDPOINT_STOPPED,
+ })
+ public @interface Reason {}
+
+ /** Unclassified failure */
+ public static final int REASON_FAILURE = 0;
+
+ // The values 1 and 2 are reserved at the Context Hub HAL but not exposed to apps.
+
+ /** The peer rejected the request to open this endpoint session. */
+ public static final int REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED = 3;
+
+ /** The peer closed this endpoint session. */
+ public static final int REASON_CLOSE_ENDPOINT_SESSION_REQUESTED = 4;
+
+ /** The peer endpoint is invalid. */
+ public static final int REASON_ENDPOINT_INVALID = 5;
+
+ /**
+ * The endpoint is now stopped. The app should retrieve the endpoint info using {@link
+ * android.hardware.location.ContextHubManager#findEndpoints} or register updates through
+ * {@link android.hardware.location.ContextHubManager#registerEndpointDiscoveryCallback}
+ * to get notified if the endpoint restarts.
+ */
+ public static final int REASON_ENDPOINT_STOPPED = 6;
+
private final Object mLock = new Object();
private final HubEndpointInfo mPendingHubEndpointInfo;
@Nullable private final IHubEndpointLifecycleCallback mLifecycleCallback;
@@ -173,9 +216,7 @@
try {
mServiceToken.closeSession(
- sessionId,
- IHubEndpointLifecycleCallback
- .REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED);
+ sessionId, REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
@@ -396,9 +437,7 @@
try {
// Oneway notification to system service
- serviceToken.closeSession(
- session.getId(),
- IHubEndpointLifecycleCallback.REASON_CLOSE_ENDPOINT_SESSION_REQUESTED);
+ serviceToken.closeSession(session.getId(), REASON_CLOSE_ENDPOINT_SESSION_REQUESTED);
} catch (RemoteException e) {
Log.e(TAG, "closeSession: failed to close session " + session, e);
e.rethrowFromSystemServer();
diff --git a/core/java/android/hardware/contexthub/IContextHubEndpointDiscoveryCallback.aidl b/core/java/android/hardware/contexthub/IContextHubEndpointDiscoveryCallback.aidl
index 85775c0..245be93 100644
--- a/core/java/android/hardware/contexthub/IContextHubEndpointDiscoveryCallback.aidl
+++ b/core/java/android/hardware/contexthub/IContextHubEndpointDiscoveryCallback.aidl
@@ -31,6 +31,7 @@
/**
* Called when endpoint(s) stopped.
* @param hubEndpointInfoList The list of endpoints that started.
+ * @param reason The reason why the endpoints stopped.
*/
- void onEndpointsStopped(in HubEndpointInfo[] hubEndpointInfoList);
+ void onEndpointsStopped(in HubEndpointInfo[] hubEndpointInfoList, int reason);
}
diff --git a/core/java/android/hardware/contexthub/IHubEndpointDiscoveryCallback.java b/core/java/android/hardware/contexthub/IHubEndpointDiscoveryCallback.java
index 0b77ddb..a61a7eb 100644
--- a/core/java/android/hardware/contexthub/IHubEndpointDiscoveryCallback.java
+++ b/core/java/android/hardware/contexthub/IHubEndpointDiscoveryCallback.java
@@ -42,7 +42,8 @@
* Called when a list of hub endpoints have stopped.
*
* @param discoveryInfoList The list containing hub discovery information.
+ * @param reason The reason the endpoints stopped.
*/
- // TODO(b/375487784): Add endpoint stop reason
- void onEndpointsStopped(@NonNull List<HubDiscoveryInfo> discoveryInfoList);
+ void onEndpointsStopped(
+ @NonNull List<HubDiscoveryInfo> discoveryInfoList, @HubEndpoint.Reason int reason);
}
diff --git a/core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java b/core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java
index 4688439..fe449bb 100644
--- a/core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java
+++ b/core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java
@@ -17,15 +17,11 @@
package android.hardware.contexthub;
import android.annotation.FlaggedApi;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.chre.flags.Flags;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
/**
* Interface for listening to lifecycle events of a hub endpoint.
*
@@ -34,24 +30,6 @@
@SystemApi
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
public interface IHubEndpointLifecycleCallback {
- /** Unknown reason. */
- int REASON_UNSPECIFIED = 0;
-
- /** The peer rejected the request to open this endpoint session. */
- int REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED = 3;
-
- /** The peer closed this endpoint session. */
- int REASON_CLOSE_ENDPOINT_SESSION_REQUESTED = 4;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({
- REASON_UNSPECIFIED,
- REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED,
- REASON_CLOSE_ENDPOINT_SESSION_REQUESTED,
- })
- @interface EndpointLifecycleReason {}
-
/**
* Called when an endpoint is requesting a session be opened with another endpoint.
*
@@ -78,5 +56,5 @@
* used.
* @param reason The reason why this session was closed.
*/
- void onSessionClosed(@NonNull HubEndpointSession session, @EndpointLifecycleReason int reason);
+ void onSessionClosed(@NonNull HubEndpointSession session, @HubEndpoint.Reason int reason);
}
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 5e8a187..117d8fe 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -791,7 +791,7 @@
}
@Override
- public void onEndpointsStopped(HubEndpointInfo[] hubEndpointInfoList) {
+ public void onEndpointsStopped(HubEndpointInfo[] hubEndpointInfoList, int reason) {
if (hubEndpointInfoList.length == 0) {
Log.w(TAG, "onEndpointsStopped: received empty discovery list");
return;
@@ -815,7 +815,7 @@
if (discoveryList.isEmpty()) {
Log.w(TAG, "onEndpointsStopped: no matching service descriptor");
} else {
- callback.onEndpointsStopped(discoveryList);
+ callback.onEndpointsStopped(discoveryList, reason);
}
});
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 4bde8e2..5f3c15d 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1199,6 +1199,11 @@
// when the stylus is not down.
mPrivOps.setHandwritingSurfaceNotTouchable(true);
break;
+ case MotionEvent.ACTION_OUTSIDE:
+ // TODO(b/350047836): determine if there is use-case for simultaneous touch
+ // and stylus handwriting and we shouldn't finish for that.
+ finishStylusHandwriting();
+ break;
}
}
@@ -3207,6 +3212,7 @@
Log.d(TAG, "Setting new handwriting region for stylus handwriting "
+ handwritingRegion + " from last " + mLastHandwritingRegion);
}
+ mPrivOps.setHandwritingTouchableRegion(handwritingRegion);
mLastHandwritingRegion = handwritingRegion;
}
diff --git a/core/java/android/os/AggregateBatteryConsumer.java b/core/java/android/os/AggregateBatteryConsumer.java
index f0e12ca..f5fee4f 100644
--- a/core/java/android/os/AggregateBatteryConsumer.java
+++ b/core/java/android/os/AggregateBatteryConsumer.java
@@ -17,7 +17,6 @@
package android.os;
import android.annotation.NonNull;
-import android.util.proto.ProtoOutputStream;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
@@ -100,19 +99,6 @@
}
}
- void writePowerComponentModelProto(@NonNull ProtoOutputStream proto) {
- for (int i = 0; i < POWER_COMPONENT_COUNT; i++) {
- final int powerModel = getPowerModel(i);
- if (powerModel == BatteryConsumer.POWER_MODEL_UNDEFINED) continue;
-
- final long token = proto.start(BatteryUsageStatsAtomsProto.COMPONENT_MODELS);
- proto.write(BatteryUsageStatsAtomsProto.PowerComponentModel.COMPONENT, i);
- proto.write(BatteryUsageStatsAtomsProto.PowerComponentModel.POWER_MODEL,
- powerModelToProtoEnum(powerModel));
- proto.end(token);
- }
- }
-
/**
* Builder for DeviceBatteryConsumer.
*/
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index 14b67f6..96ea168 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -161,18 +161,27 @@
/**
* Unspecified power model.
+ *
+ * @deprecated PowerModel is no longer supported
*/
+ @Deprecated
public static final int POWER_MODEL_UNDEFINED = 0;
/**
* Power model that is based on average consumption rates that hardware components
* consume in various states.
+ *
+ * @deprecated PowerModel is no longer supported
*/
+ @Deprecated
public static final int POWER_MODEL_POWER_PROFILE = 1;
/**
* Power model that is based on energy consumption stats provided by PowerStats HAL.
+ *
+ * @deprecated PowerModel is no longer supported
*/
+ @Deprecated
public static final int POWER_MODEL_ENERGY_CONSUMPTION = 2;
/**
@@ -380,19 +389,17 @@
public final @ScreenState int screenState;
public final @PowerState int powerState;
- final int mPowerModelColumnIndex;
final int mPowerColumnIndex;
final int mDurationColumnIndex;
private Key(@PowerComponentId int powerComponentId, @ProcessState int processState,
- @ScreenState int screenState, @PowerState int powerState, int powerModelColumnIndex,
+ @ScreenState int screenState, @PowerState int powerState,
int powerColumnIndex, int durationColumnIndex) {
this.powerComponentId = powerComponentId;
this.processState = processState;
this.screenState = screenState;
this.powerState = powerState;
- mPowerModelColumnIndex = powerModelColumnIndex;
mPowerColumnIndex = powerColumnIndex;
mDurationColumnIndex = durationColumnIndex;
}
@@ -577,11 +584,11 @@
*
* @param componentId The ID of the power component, e.g.
* {@link BatteryConsumer#POWER_COMPONENT_CPU}.
+ * @deprecated PowerModel is no longer supported
*/
+ @Deprecated
public @PowerModel int getPowerModel(@PowerComponentId int componentId) {
- return mPowerComponents.getPowerModel(
- mData.layout.getKeyOrThrow(componentId, PROCESS_STATE_UNSPECIFIED,
- SCREEN_STATE_UNSPECIFIED, POWER_STATE_UNSPECIFIED));
+ return POWER_MODEL_UNDEFINED;
}
/**
@@ -589,9 +596,11 @@
*
* @param key The key of the power component, obtained by calling {@link #getKey} or
* {@link #getKeys} method.
+ * @deprecated PowerModel is no longer supported
*/
+ @Deprecated
public @PowerModel int getPowerModel(@NonNull BatteryConsumer.Key key) {
- return mPowerComponents.getPowerModel(key);
+ return POWER_MODEL_UNDEFINED;
}
/**
@@ -657,20 +666,6 @@
}
/**
- * Returns the name of the specified power model. Intended for logging and debugging.
- */
- public static String powerModelToString(@BatteryConsumer.PowerModel int powerModel) {
- switch (powerModel) {
- case BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION:
- return "energy consumption";
- case BatteryConsumer.POWER_MODEL_POWER_PROFILE:
- return "power profile";
- default:
- return "";
- }
- }
-
- /**
* Returns the equivalent PowerModel enum for the specified power model.
* {@see BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage.PowerModel}
*/
@@ -857,10 +852,8 @@
static class BatteryConsumerDataLayout {
private static final Key[] KEY_ARRAY = new Key[0];
- public static final int POWER_MODEL_NOT_INCLUDED = -1;
public final String[] customPowerComponentNames;
public final int customPowerComponentCount;
- public final boolean powerModelsIncluded;
public final boolean processStateDataIncluded;
public final boolean screenStateDataIncluded;
public final boolean powerStateDataIncluded;
@@ -872,11 +865,10 @@
private SparseArray<Key[]> mPerComponentKeys;
private BatteryConsumerDataLayout(int firstColumn, String[] customPowerComponentNames,
- boolean powerModelsIncluded, boolean includeProcessStateData,
- boolean includeScreenState, boolean includePowerState) {
+ boolean includeProcessStateData, boolean includeScreenState,
+ boolean includePowerState) {
this.customPowerComponentNames = customPowerComponentNames;
this.customPowerComponentCount = customPowerComponentNames.length;
- this.powerModelsIncluded = powerModelsIncluded;
this.processStateDataIncluded = includeProcessStateData;
this.screenStateDataIncluded = includeScreenState;
this.powerStateDataIncluded = includePowerState;
@@ -904,7 +896,7 @@
continue;
}
for (int i = 0; i < powerComponentIds.length; i++) {
- columnIndex = addKeys(keyList, powerModelsIncluded, includeProcessStateData,
+ columnIndex = addKeys(keyList, includeProcessStateData,
powerComponentIds[i], screenState, powerState, columnIndex);
}
}
@@ -934,13 +926,10 @@
}
}
- private int addKeys(List<Key> keys, boolean powerModelsIncluded,
- boolean includeProcessStateData, @PowerComponentId int componentId,
- int screenState, int powerState, int columnIndex) {
+ private int addKeys(List<Key> keys, boolean includeProcessStateData,
+ @PowerComponentId int componentId, int screenState, int powerState,
+ int columnIndex) {
keys.add(new Key(componentId, PROCESS_STATE_UNSPECIFIED, screenState, powerState,
- powerModelsIncluded
- ? columnIndex++
- : POWER_MODEL_NOT_INCLUDED, // power model
columnIndex++, // power
columnIndex++ // usage duration
));
@@ -956,9 +945,6 @@
continue;
}
keys.add(new Key(componentId, processState, screenState, powerState,
- powerModelsIncluded
- ? columnIndex++
- : POWER_MODEL_NOT_INCLUDED, // power model
columnIndex++, // power
columnIndex++ // usage duration
));
@@ -1016,7 +1002,7 @@
}
static BatteryConsumerDataLayout createBatteryConsumerDataLayout(
- String[] customPowerComponentNames, boolean includePowerModels,
+ String[] customPowerComponentNames,
boolean includeProcessStateData, boolean includeScreenStateData,
boolean includePowerStateData) {
int columnCount = BatteryConsumer.COLUMN_COUNT;
@@ -1025,8 +1011,7 @@
columnCount = Math.max(columnCount, UserBatteryConsumer.COLUMN_COUNT);
return new BatteryConsumerDataLayout(columnCount, customPowerComponentNames,
- includePowerModels, includeProcessStateData, includeScreenStateData,
- includePowerStateData);
+ includeProcessStateData, includeScreenStateData, includePowerStateData);
}
protected abstract static class BaseBuilder<T extends BaseBuilder<?>> {
@@ -1086,7 +1071,7 @@
public T setConsumedPower(@PowerComponentId int componentId, double componentPower,
@PowerModel int powerModel) {
mPowerComponentsBuilder.setConsumedPower(getKey(componentId, PROCESS_STATE_UNSPECIFIED),
- componentPower, powerModel);
+ componentPower);
return (T) this;
}
@@ -1095,14 +1080,14 @@
public T addConsumedPower(@PowerComponentId int componentId, double componentPower,
@PowerModel int powerModel) {
mPowerComponentsBuilder.addConsumedPower(getKey(componentId, PROCESS_STATE_UNSPECIFIED),
- componentPower, powerModel);
+ componentPower);
return (T) this;
}
@SuppressWarnings("unchecked")
@NonNull
public T setConsumedPower(Key key, double componentPower, @PowerModel int powerModel) {
- mPowerComponentsBuilder.setConsumedPower(key, componentPower, powerModel);
+ mPowerComponentsBuilder.setConsumedPower(key, componentPower);
return (T) this;
}
@@ -1110,21 +1095,14 @@
@NonNull
public T addConsumedPower(@PowerComponentId int componentId, double componentPower) {
mPowerComponentsBuilder.addConsumedPower(getKey(componentId, PROCESS_STATE_UNSPECIFIED),
- componentPower, POWER_MODEL_UNDEFINED);
+ componentPower);
return (T) this;
}
@SuppressWarnings("unchecked")
@NonNull
public T addConsumedPower(Key key, double componentPower) {
- mPowerComponentsBuilder.addConsumedPower(key, componentPower, POWER_MODEL_UNDEFINED);
- return (T) this;
- }
-
- @SuppressWarnings("unchecked")
- @NonNull
- public T addConsumedPower(Key key, double componentPower, @PowerModel int powerModel) {
- mPowerComponentsBuilder.addConsumedPower(key, componentPower, powerModel);
+ mPowerComponentsBuilder.addConsumedPower(key, componentPower);
return (T) this;
}
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index 72e4cef..f913fcf 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -114,7 +114,6 @@
static final String XML_ATTR_POWER_STATE = "power_state";
static final String XML_ATTR_POWER = "power";
static final String XML_ATTR_DURATION = "duration";
- static final String XML_ATTR_MODEL = "model";
static final String XML_ATTR_BATTERY_CAPACITY = "battery_capacity";
static final String XML_ATTR_DISCHARGE_PERCENT = "discharge_pct";
static final String XML_ATTR_DISCHARGE_LOWER = "discharge_lower";
@@ -155,7 +154,6 @@
private final long mBatteryTimeRemainingMs;
private final long mChargeTimeRemainingMs;
private final String[] mCustomPowerComponentNames;
- private final boolean mIncludesPowerModels;
private final boolean mIncludesProcessStateData;
private final boolean mIncludesScreenStateData;
private final boolean mIncludesPowerStateData;
@@ -179,7 +177,6 @@
mBatteryTimeRemainingMs = builder.mBatteryTimeRemainingMs;
mChargeTimeRemainingMs = builder.mChargeTimeRemainingMs;
mCustomPowerComponentNames = builder.mCustomPowerComponentNames;
- mIncludesPowerModels = builder.mIncludePowerModels;
mIncludesProcessStateData = builder.mIncludesProcessStateData;
mIncludesScreenStateData = builder.mIncludesScreenStateData;
mIncludesPowerStateData = builder.mIncludesPowerStateData;
@@ -364,14 +361,13 @@
mBatteryTimeRemainingMs = source.readLong();
mChargeTimeRemainingMs = source.readLong();
mCustomPowerComponentNames = source.readStringArray();
- mIncludesPowerModels = source.readBoolean();
mIncludesProcessStateData = source.readBoolean();
mIncludesScreenStateData = source.readBoolean();
mIncludesPowerStateData = source.readBoolean();
mBatteryConsumersCursorWindow = CursorWindow.newFromParcel(source);
mBatteryConsumerDataLayout = BatteryConsumer.createBatteryConsumerDataLayout(
- mCustomPowerComponentNames, mIncludesPowerModels, mIncludesProcessStateData,
+ mCustomPowerComponentNames, mIncludesProcessStateData,
mIncludesScreenStateData, mIncludesPowerStateData);
final int numRows = mBatteryConsumersCursorWindow.getNumRows();
@@ -424,7 +420,6 @@
dest.writeLong(mBatteryTimeRemainingMs);
dest.writeLong(mChargeTimeRemainingMs);
dest.writeStringArray(mCustomPowerComponentNames);
- dest.writeBoolean(mIncludesPowerModels);
dest.writeBoolean(mIncludesProcessStateData);
dest.writeBoolean(mIncludesScreenStateData);
dest.writeBoolean(mIncludesPowerStateData);
@@ -506,9 +501,6 @@
getDischargeDurationMs());
deviceBatteryConsumer.writeStatsProto(proto,
BatteryUsageStatsAtomsProto.DEVICE_BATTERY_CONSUMER);
- if (mIncludesPowerModels) {
- deviceBatteryConsumer.writePowerComponentModelProto(proto);
- }
writeUidBatteryConsumersProto(proto, maxRawSize);
}
@@ -629,7 +621,7 @@
printPowerComponent(pw, prefix,
mBatteryConsumerDataLayout.getPowerComponentName(powerComponent),
- devicePowerMah, appsPowerMah, BatteryConsumer.POWER_MODEL_UNDEFINED,
+ devicePowerMah, appsPowerMah,
deviceConsumer.getUsageDurationMillis(powerComponent));
}
@@ -716,23 +708,15 @@
printPowerComponent(pw, prefix,
mBatteryConsumerDataLayout.getPowerComponentName(powerComponent),
devicePowerMah, appsPowerMah,
- mIncludesPowerModels ? deviceConsumer.getPowerModel(powerComponent)
- : BatteryConsumer.POWER_MODEL_UNDEFINED,
deviceConsumer.getUsageDurationMillis(dimensions));
}
}
private void printPowerComponent(PrintWriter pw, String prefix, String label,
- double devicePowerMah, double appsPowerMah, int powerModel, long durationMs) {
+ double devicePowerMah, double appsPowerMah, long durationMs) {
StringBuilder sb = new StringBuilder();
sb.append(prefix).append(" ").append(label).append(": ")
.append(BatteryStats.formatCharge(devicePowerMah));
- if (powerModel != BatteryConsumer.POWER_MODEL_UNDEFINED
- && powerModel != BatteryConsumer.POWER_MODEL_POWER_PROFILE) {
- sb.append(" [");
- sb.append(BatteryConsumer.powerModelToString(powerModel));
- sb.append("]");
- }
sb.append(" apps: ").append(BatteryStats.formatCharge(appsPowerMah));
if (durationMs != 0) {
sb.append(" duration: ");
@@ -828,7 +812,7 @@
final boolean includesPowerStateData = parser.getAttributeBoolean(null,
XML_ATTR_PREFIX_INCLUDES_POWER_STATE_DATA, false);
- builder = new Builder(customComponentNames.toArray(new String[0]), true,
+ builder = new Builder(customComponentNames.toArray(new String[0]),
includesProcStateData, includesScreenStateData, includesPowerStateData, 0);
builder.setStatsStartTimestamp(
@@ -913,7 +897,6 @@
private final CursorWindow mBatteryConsumersCursorWindow;
@NonNull
private final String[] mCustomPowerComponentNames;
- private final boolean mIncludePowerModels;
private final boolean mIncludesProcessStateData;
private final boolean mIncludesScreenStateData;
private final boolean mIncludesPowerStateData;
@@ -938,22 +921,21 @@
private BatteryStatsHistory mBatteryStatsHistory;
public Builder(@NonNull String[] customPowerComponentNames) {
- this(customPowerComponentNames, false, false, false, false, 0);
+ this(customPowerComponentNames, false, false, false, 0);
}
- public Builder(@NonNull String[] customPowerComponentNames, boolean includePowerModels,
+ public Builder(@NonNull String[] customPowerComponentNames,
boolean includeProcessStateData, boolean includeScreenStateData,
boolean includesPowerStateData, double minConsumedPowerThreshold) {
mBatteryConsumersCursorWindow =
new CursorWindow(null, BATTERY_CONSUMER_CURSOR_WINDOW_SIZE);
onCursorWindowAllocated(mBatteryConsumersCursorWindow);
mBatteryConsumerDataLayout = BatteryConsumer.createBatteryConsumerDataLayout(
- customPowerComponentNames, includePowerModels, includeProcessStateData,
+ customPowerComponentNames, includeProcessStateData,
includeScreenStateData, includesPowerStateData);
mBatteryConsumersCursorWindow.setNumColumns(mBatteryConsumerDataLayout.columnCount);
mCustomPowerComponentNames = customPowerComponentNames;
- mIncludePowerModels = includePowerModels;
mIncludesProcessStateData = includeProcessStateData;
mIncludesScreenStateData = includeScreenStateData;
mIncludesPowerStateData = includesPowerStateData;
diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java
index 6325b00..6e67578 100644
--- a/core/java/android/os/BatteryUsageStatsQuery.java
+++ b/core/java/android/os/BatteryUsageStatsQuery.java
@@ -24,6 +24,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
/**
* Query parameters for the {@link BatteryStatsManager#getBatteryUsageStats()} call.
@@ -65,12 +66,6 @@
*/
public static final int FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY = 0x0002;
- /**
- * Indicates that identifiers of power models used for computations of power
- * consumption should be included in the BatteryUsageStats.
- */
- public static final int FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS = 0x0004;
-
public static final int FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA = 0x0008;
public static final int FLAG_BATTERY_USAGE_STATS_INCLUDE_VIRTUAL_UIDS = 0x0010;
@@ -202,7 +197,24 @@
return mAggregatedToTimestamp;
}
+ @Override
+ public String toString() {
+ return "BatteryUsageStatsQuery{"
+ + "mFlags=" + Integer.toHexString(mFlags)
+ + ", mUserIds=" + Arrays.toString(mUserIds)
+ + ", mMaxStatsAgeMs=" + mMaxStatsAgeMs
+ + ", mAggregatedFromTimestamp=" + mAggregatedFromTimestamp
+ + ", mAggregatedToTimestamp=" + mAggregatedToTimestamp
+ + ", mMonotonicStartTime=" + mMonotonicStartTime
+ + ", mMonotonicEndTime=" + mMonotonicEndTime
+ + ", mMinConsumedPowerThreshold=" + mMinConsumedPowerThreshold
+ + ", mPowerComponents=" + Arrays.toString(mPowerComponents)
+ + '}';
+ }
+
private BatteryUsageStatsQuery(Parcel in) {
+ mMonotonicStartTime = in.readLong();
+ mMonotonicEndTime = in.readLong();
mFlags = in.readInt();
mUserIds = new int[in.readInt()];
in.readIntArray(mUserIds);
@@ -215,6 +227,8 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(mMonotonicStartTime);
+ dest.writeLong(mMonotonicEndTime);
dest.writeInt(mFlags);
dest.writeInt(mUserIds.length);
dest.writeIntArray(mUserIds);
@@ -311,7 +325,10 @@
* power monitoring data is available.
*
* Should only be used for testing and debugging.
+ *
+ * @deprecated PowerModel is no longer supported
*/
+ @Deprecated
public Builder powerProfileModeledOnly() {
mFlags |= BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL;
return this;
@@ -322,9 +339,10 @@
* of power consumption.
*
* Should only be used for testing and debugging.
+ * @deprecated PowerModel is no longer supported
*/
+ @Deprecated
public Builder includePowerModels() {
- mFlags |= BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS;
return this;
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index c2e9260..9b39c62 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1546,6 +1546,57 @@
}
/**
+ * Convert a major.minor version String like "36.1" to an int that
+ * represents both major and minor version.
+ *
+ * @param version the String to parse
+ * @return an int encoding the major and minor version
+ * @throws IllegalArgumentException if the string could not be converted into an int
+ *
+ * @hide
+ */
+ @SuppressWarnings("FlaggedApi") // SDK_INT_MULTIPLIER is defined in this file
+ public static @SdkIntFull int parseFullVersion(@NonNull String version) {
+ int index = version.indexOf('.');
+ int major;
+ int minor = 0;
+ try {
+ if (index == -1) {
+ major = Integer.parseInt(version);
+ } else {
+ major = Integer.parseInt(version.substring(0, index));
+ minor = Integer.parseInt(version.substring(index + 1));
+ }
+ if (major < 0 || minor < 0) {
+ throw new NumberFormatException();
+ }
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("failed to parse '" + version
+ + "' as a major.minor version code");
+ }
+ return major * VERSION_CODES_FULL.SDK_INT_MULTIPLIER + minor;
+ }
+
+ /**
+ * Convert an int representing a major.minor version like SDK_INT_FULL to a
+ * human readable string. The returned string is only intended for debug
+ * and error messages.
+ *
+ * @param version the int to convert to a string
+ * @return a String representing the same major.minor version as the int passed in
+ * @throws IllegalArgumentException if {@code version} is negative
+ *
+ * @hide
+ */
+ public static String fullVersionToString(@SdkIntFull int version) {
+ if (version < 0) {
+ throw new IllegalArgumentException("failed to convert '" + version
+ + "' to string: not a valid major.minor version code");
+ }
+ return String.format("%d.%d", getMajorSdkVersion(version), getMinorSdkVersion(version));
+ }
+
+ /**
* The vendor API for 2024 Q2
*
* <p>For Android 14-QPR3 and later, the vendor API level is completely decoupled from the SDK
diff --git a/core/java/android/os/CpuHeadroomParams.java b/core/java/android/os/CpuHeadroomParams.java
index f0d4f7d..8e78b7e 100644
--- a/core/java/android/os/CpuHeadroomParams.java
+++ b/core/java/android/os/CpuHeadroomParams.java
@@ -18,10 +18,13 @@
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
import android.os.health.SystemHealthManager;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
/**
* Headroom request params used by {@link SystemHealthManager#getCpuHeadroom(CpuHeadroomParams)}.
@@ -54,6 +57,16 @@
public static final int CPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1;
/**
+ * Minimum CPU headroom calculation window size.
+ */
+ public static final int CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50;
+
+ /**
+ * Maximum CPU headroom calculation window size.
+ */
+ public static final int CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000;
+
+ /**
* Sets the headroom calculation type.
* <p>
*
@@ -83,6 +96,63 @@
}
/**
+ * Sets the headroom calculation window size in milliseconds.
+ * <p>
+ *
+ * @param windowMillis the window size in milliseconds, ranged from
+ * [{@link #CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN},
+ * {@link #CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX}]. The smaller
+ * the value, the larger fluctuation in value should be expected. The
+ * default value can be retrieved from the
+ * {@link #getCalculationWindowMillis}. The device will try to use the
+ * closest feasible window size to this param.
+ * @throws IllegalArgumentException if the window size is not in allowed range.
+ */
+ public void setCalculationWindowMillis(
+ @IntRange(from = CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to =
+ CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) int windowMillis) {
+ if (windowMillis < CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN
+ || windowMillis > CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) {
+ throw new IllegalArgumentException("Invalid calculation window: " + windowMillis);
+ }
+ mInternal.calculationWindowMillis = windowMillis;
+ }
+
+ /**
+ * Gets the headroom calculation window size in milliseconds.
+ * <p>
+ * This will return the default value chosen by the device if not set.
+ */
+ public @IntRange(from = CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to =
+ CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) long getCalculationWindowMillis() {
+ return mInternal.calculationWindowMillis;
+ }
+
+ /**
+ * Sets the thread TIDs to track.
+ * <p>
+ * The TIDs should belong to the same of the process that will the headroom call. And they
+ * should not have different core affinity.
+ * <p>
+ * If not set, the headroom will be based on the PID of the process making the call.
+ *
+ * @param tids non-empty list of TIDs, maximum 5.
+ * @throws IllegalArgumentException if the list size is not in allowed range or TID is not
+ * positive.
+ */
+ public void setTids(@NonNull int... tids) {
+ if (tids.length == 0 || tids.length > 5) {
+ throw new IllegalArgumentException("Invalid number of TIDs: " + tids.length);
+ }
+ for (int tid : tids) {
+ if (tid <= 0) {
+ throw new IllegalArgumentException("Invalid TID: " + tid);
+ }
+ }
+ mInternal.tids = Arrays.copyOf(tids, tids.length);
+ }
+
+ /**
* @hide
*/
public CpuHeadroomParamsInternal getInternal() {
diff --git a/core/java/android/os/CpuHeadroomParamsInternal.aidl b/core/java/android/os/CpuHeadroomParamsInternal.aidl
index 6cc4699..d572f965 100644
--- a/core/java/android/os/CpuHeadroomParamsInternal.aidl
+++ b/core/java/android/os/CpuHeadroomParamsInternal.aidl
@@ -25,6 +25,8 @@
@JavaDerive(equals = true, toString = true)
parcelable CpuHeadroomParamsInternal {
boolean usesDeviceHeadroom = false;
+ int[] tids;
+ int calculationWindowMillis = 1000;
CpuHeadroomParams.CalculationType calculationType = CpuHeadroomParams.CalculationType.MIN;
CpuHeadroomParams.SelectionType selectionType = CpuHeadroomParams.SelectionType.ALL;
}
diff --git a/core/java/android/os/GpuHeadroomParams.java b/core/java/android/os/GpuHeadroomParams.java
index efb2a28..4dc9826 100644
--- a/core/java/android/os/GpuHeadroomParams.java
+++ b/core/java/android/os/GpuHeadroomParams.java
@@ -18,6 +18,7 @@
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.os.health.SystemHealthManager;
import java.lang.annotation.Retention;
@@ -54,6 +55,16 @@
public static final int GPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1;
/**
+ * Minimum GPU headroom calculation window size.
+ */
+ public static final int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50;
+
+ /**
+ * Maximum GPU headroom calculation window size.
+ */
+ public static final int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000;
+
+ /**
* Sets the headroom calculation type.
* <p>
*
@@ -83,6 +94,39 @@
}
/**
+ * Sets the headroom calculation window size in milliseconds.
+ * <p>
+ *
+ * @param windowMillis the window size in milliseconds, ranged from
+ * [{@link #GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN},
+ * {@link #GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX}]. The smaller
+ * the value, the larger fluctuation in value should be expected. The
+ * default value can be retrieved from the
+ * {@link #getCalculationWindowMillis}. If the device will try to use the
+ * closest feasible window size to this param.
+ * @throws IllegalArgumentException if the window is invalid.
+ */
+ public void setCalculationWindowMillis(
+ @IntRange(from = GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to =
+ GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) int windowMillis) {
+ if (windowMillis < GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN
+ || windowMillis > GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) {
+ throw new IllegalArgumentException("Invalid calculation window: " + windowMillis);
+ }
+ mInternal.calculationWindowMillis = windowMillis;
+ }
+
+ /**
+ * Gets the headroom calculation window size in milliseconds.
+ * <p>
+ * This will return the default value chosen by the device if not set.
+ */
+ public @IntRange(from = GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to =
+ GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) int getCalculationWindowMillis() {
+ return mInternal.calculationWindowMillis;
+ }
+
+ /**
* @hide
*/
public GpuHeadroomParamsInternal getInternal() {
diff --git a/core/java/android/os/GpuHeadroomParamsInternal.aidl b/core/java/android/os/GpuHeadroomParamsInternal.aidl
index 20309e7..40d5d8e 100644
--- a/core/java/android/os/GpuHeadroomParamsInternal.aidl
+++ b/core/java/android/os/GpuHeadroomParamsInternal.aidl
@@ -24,5 +24,6 @@
*/
@JavaDerive(equals = true, toString = true)
parcelable GpuHeadroomParamsInternal {
+ int calculationWindowMillis = 1000;
GpuHeadroomParams.CalculationType calculationType = GpuHeadroomParams.CalculationType.MIN;
}
diff --git a/core/java/android/os/IHintManager.aidl b/core/java/android/os/IHintManager.aidl
index a043da9..f1936b5 100644
--- a/core/java/android/os/IHintManager.aidl
+++ b/core/java/android/os/IHintManager.aidl
@@ -21,7 +21,9 @@
import android.os.GpuHeadroomParamsInternal;
import android.os.IHintSession;
import android.os.SessionCreationConfig;
+import android.hardware.power.CpuHeadroomResult;
import android.hardware.power.ChannelConfig;
+import android.hardware.power.GpuHeadroomResult;
import android.hardware.power.SessionConfig;
import android.hardware.power.SessionTag;
@@ -53,9 +55,9 @@
*/
@nullable ChannelConfig getSessionChannel(in IBinder token);
oneway void closeSessionChannel();
- float[] getCpuHeadroom(in CpuHeadroomParamsInternal params);
+ @nullable CpuHeadroomResult getCpuHeadroom(in CpuHeadroomParamsInternal params);
long getCpuHeadroomMinIntervalMillis();
- float getGpuHeadroom(in GpuHeadroomParamsInternal params);
+ @nullable GpuHeadroomResult getGpuHeadroom(in GpuHeadroomParamsInternal params);
long getGpuHeadroomMinIntervalMillis();
/**
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 94259d7..f9789c1 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -91,6 +91,8 @@
# PerformanceHintManager
per-file CpuHeadroom*.aidl = file:/ADPF_OWNERS
per-file GpuHeadroom*.aidl = file:/ADPF_OWNERS
+per-file CpuHeadroom*.java = file:/ADPF_OWNERS
+per-file GpuHeadroom*.java = file:/ADPF_OWNERS
per-file PerformanceHintManager.java = file:/ADPF_OWNERS
per-file WorkDuration.java = file:/ADPF_OWNERS
per-file IHintManager.aidl = file:/ADPF_OWNERS
diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java
index d116e07..4db1f1b 100644
--- a/core/java/android/os/PowerComponents.java
+++ b/core/java/android/os/PowerComponents.java
@@ -15,7 +15,6 @@
*/
package android.os;
-import static android.os.BatteryConsumer.BatteryConsumerDataLayout.POWER_MODEL_NOT_INCLUDED;
import static android.os.BatteryConsumer.POWER_COMPONENT_ANY;
import static android.os.BatteryConsumer.POWER_COMPONENT_BASE;
import static android.os.BatteryConsumer.POWER_STATE_ANY;
@@ -156,15 +155,6 @@
return mData.layout.getPowerComponentName(componentId);
}
- @BatteryConsumer.PowerModel
- int getPowerModel(BatteryConsumer.Key key) {
- if (key.mPowerModelColumnIndex == POWER_MODEL_NOT_INCLUDED) {
- throw new IllegalStateException(
- "Power model IDs were not requested in the BatteryUsageStatsQuery");
- }
- return mData.getInt(key.mPowerModelColumnIndex);
- }
-
/**
* Returns the amount of time used by the specified component, e.g. CPU, WiFi etc.
*
@@ -378,10 +368,6 @@
if (durationMs != 0) {
serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_DURATION, durationMs);
}
- if (mData.layout.powerModelsIncluded) {
- serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_MODEL,
- getPowerModel(key));
- }
serializer.endTag(null, BatteryUsageStats.XML_TAG_COMPONENT);
}
serializer.endTag(null, BatteryUsageStats.XML_TAG_POWER_COMPONENTS);
@@ -411,7 +397,6 @@
int powerState = POWER_STATE_UNSPECIFIED;
double powerMah = 0;
long durationMs = 0;
- int model = BatteryConsumer.POWER_MODEL_UNDEFINED;
for (int i = 0; i < parser.getAttributeCount(); i++) {
switch (parser.getAttributeName(i)) {
case BatteryUsageStats.XML_ATTR_ID:
@@ -432,14 +417,11 @@
case BatteryUsageStats.XML_ATTR_DURATION:
durationMs = parser.getAttributeLong(i);
break;
- case BatteryUsageStats.XML_ATTR_MODEL:
- model = parser.getAttributeInt(i);
- break;
}
}
final BatteryConsumer.Key key = builder.mData.layout.getKey(componentId,
processState, screenState, powerState);
- builder.addConsumedPower(key, powerMah, model);
+ builder.addConsumedPower(key, powerMah);
builder.addUsageDurationMillis(key, durationMs);
break;
}
@@ -453,43 +435,28 @@
* Builder for PowerComponents.
*/
static final class Builder {
- private static final byte POWER_MODEL_UNINITIALIZED = -1;
-
private final BatteryConsumer.BatteryConsumerData mData;
private final double mMinConsumedPowerThreshold;
Builder(BatteryConsumer.BatteryConsumerData data, double minConsumedPowerThreshold) {
mData = data;
mMinConsumedPowerThreshold = minConsumedPowerThreshold;
- for (BatteryConsumer.Key key : mData.layout.keys) {
- if (key.mPowerModelColumnIndex != POWER_MODEL_NOT_INCLUDED) {
- mData.putInt(key.mPowerModelColumnIndex, POWER_MODEL_UNINITIALIZED);
- }
- }
}
/**
- * @deprecated use {@link #addConsumedPower(BatteryConsumer.Key, double, int)}
+ * @deprecated use {@link #addConsumedPower(BatteryConsumer.Key, double)}
*/
@Deprecated
@NonNull
- public Builder setConsumedPower(BatteryConsumer.Key key, double componentPower,
- int powerModel) {
+ public Builder setConsumedPower(BatteryConsumer.Key key, double componentPower) {
mData.putDouble(key.mPowerColumnIndex, componentPower);
- if (key.mPowerModelColumnIndex != POWER_MODEL_NOT_INCLUDED) {
- mData.putInt(key.mPowerModelColumnIndex, powerModel);
- }
return this;
}
@NonNull
- public Builder addConsumedPower(BatteryConsumer.Key key, double componentPower,
- int powerModel) {
+ public Builder addConsumedPower(BatteryConsumer.Key key, double componentPower) {
mData.putDouble(key.mPowerColumnIndex,
mData.getDouble(key.mPowerColumnIndex) + componentPower);
- if (key.mPowerModelColumnIndex != POWER_MODEL_NOT_INCLUDED) {
- mData.putInt(key.mPowerModelColumnIndex, powerModel);
- }
return this;
}
@@ -547,28 +514,6 @@
mData.getLong(key.mDurationColumnIndex)
+ otherData.getLong(otherKey.mDurationColumnIndex));
}
- if (key.mPowerModelColumnIndex == POWER_MODEL_NOT_INCLUDED) {
- continue;
- }
-
- boolean undefined = false;
- if (otherKey.mPowerModelColumnIndex == POWER_MODEL_NOT_INCLUDED) {
- undefined = true;
- } else {
- final int powerModel = mData.getInt(key.mPowerModelColumnIndex);
- int otherPowerModel = otherData.getInt(otherKey.mPowerModelColumnIndex);
- if (powerModel == POWER_MODEL_UNINITIALIZED) {
- mData.putInt(key.mPowerModelColumnIndex, otherPowerModel);
- } else if (powerModel != otherPowerModel
- && otherPowerModel != POWER_MODEL_UNINITIALIZED) {
- undefined = true;
- }
- }
-
- if (undefined) {
- mData.putInt(key.mPowerModelColumnIndex,
- BatteryConsumer.POWER_MODEL_UNDEFINED);
- }
}
}
@@ -594,13 +539,6 @@
@NonNull
public PowerComponents build() {
for (BatteryConsumer.Key key : mData.layout.keys) {
- if (key.mPowerModelColumnIndex != POWER_MODEL_NOT_INCLUDED) {
- if (mData.getInt(key.mPowerModelColumnIndex) == POWER_MODEL_UNINITIALIZED) {
- mData.putInt(key.mPowerModelColumnIndex,
- BatteryConsumer.POWER_MODEL_UNDEFINED);
- }
- }
-
if (mMinConsumedPowerThreshold != 0) {
if (mData.getDouble(key.mPowerColumnIndex) < mMinConsumedPowerThreshold) {
mData.putDouble(key.mPowerColumnIndex, 0);
diff --git a/core/java/android/os/health/SystemHealthManager.java b/core/java/android/os/health/SystemHealthManager.java
index 4db9bc3..cd79e41 100644
--- a/core/java/android/os/health/SystemHealthManager.java
+++ b/core/java/android/os/health/SystemHealthManager.java
@@ -23,6 +23,8 @@
import android.annotation.SystemService;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.hardware.power.CpuHeadroomResult;
+import android.hardware.power.GpuHeadroomResult;
import android.os.BatteryStats;
import android.os.Build;
import android.os.Bundle;
@@ -110,15 +112,16 @@
}
/**
- * Provides an estimate of global available CPU headroom of the calling thread.
+ * Provides an estimate of global available CPU headroom.
* <p>
*
* @param params params to customize the CPU headroom calculation, null to use default params.
- * @return a single value a {@code Float.NaN} if it's temporarily unavailable.
+ * @return a single value headroom or a {@code Float.NaN} if it's temporarily unavailable.
* A valid value is ranged from [0, 100], where 0 indicates no more CPU resources can be
* granted.
- * @throws UnsupportedOperationException if the API is unsupported or the request params can't
- * be served.
+ * @throws UnsupportedOperationException if the API is unsupported.
+ * @throws SecurityException if the TIDs of the params don't belong to the same process.
+ * @throws IllegalStateException if the TIDs of the params don't have the same affinity setting.
*/
@FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS)
public @FloatRange(from = 0f, to = 100f) float getCpuHeadroom(
@@ -127,8 +130,12 @@
throw new UnsupportedOperationException();
}
try {
- return mHintManager.getCpuHeadroom(
- params != null ? params.getInternal() : new CpuHeadroomParamsInternal())[0];
+ final CpuHeadroomResult ret = mHintManager.getCpuHeadroom(
+ params != null ? params.getInternal() : new CpuHeadroomParamsInternal());
+ if (ret == null || ret.getTag() != CpuHeadroomResult.globalHeadroom) {
+ return Float.NaN;
+ }
+ return ret.getGlobalHeadroom();
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -144,8 +151,7 @@
* @return a single value headroom or a {@code Float.NaN} if it's temporarily unavailable.
* A valid value is ranged from [0, 100], where 0 indicates no more GPU resources can be
* granted.
- * @throws UnsupportedOperationException if the API is unsupported or the request params can't
- * be served.
+ * @throws UnsupportedOperationException if the API is unsupported.
*/
@FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS)
public @FloatRange(from = 0f, to = 100f) float getGpuHeadroom(
@@ -154,8 +160,12 @@
throw new UnsupportedOperationException();
}
try {
- return mHintManager.getGpuHeadroom(
+ final GpuHeadroomResult ret = mHintManager.getGpuHeadroom(
params != null ? params.getInternal() : new GpuHeadroomParamsInternal());
+ if (ret == null || ret.getTag() != GpuHeadroomResult.globalHeadroom) {
+ return Float.NaN;
+ }
+ return ret.getGlobalHeadroom();
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
diff --git a/core/java/android/security/responsible_apis_flags.aconfig b/core/java/android/security/responsible_apis_flags.aconfig
index 3c35701..a5c837b 100644
--- a/core/java/android/security/responsible_apis_flags.aconfig
+++ b/core/java/android/security/responsible_apis_flags.aconfig
@@ -132,3 +132,9 @@
description: "Android Advanced Protection Mode Feature: Memory Tagging Extension"
bug: "378931989"
}
+flag {
+ name: "aapm_feature_disable_cellular_2g"
+ namespace: "responsible_apis"
+ description: "Android Advanced Protection Mode Feature: Disable Cellular 2G"
+ bug: "377748286"
+}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index b6e114b..a0feccd 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1236,6 +1236,8 @@
private @ActivityInfo.ColorMode int mCurrentColorMode = ActivityInfo.COLOR_MODE_DEFAULT;
private long mColorModeLastSetMillis = -1;
+ private final boolean mIsSubscribeGranularDisplayEventsEnabled;
+
public ViewRootImpl(Context context, Display display) {
this(context, display, WindowManagerGlobal.getWindowSession(), new WindowLayout());
}
@@ -1333,6 +1335,8 @@
// Disable DRAW_WAKE_LOCK starting U.
mDisableDrawWakeLock =
CompatChanges.isChangeEnabled(DISABLE_DRAW_WAKE_LOCK) && disableDrawWakeLock();
+ mIsSubscribeGranularDisplayEventsEnabled =
+ com.android.server.display.feature.flags.Flags.subscribeGranularDisplayEvents();
}
public static void addFirstDrawHandler(Runnable callback) {
@@ -1810,14 +1814,22 @@
mAccessibilityInteractionConnectionManager, mHandler);
mAccessibilityManager.addHighContrastTextStateChangeListener(
mExecutor, mHighContrastTextManager);
+
+
+ long eventsToBeRegistered =
+ (mIsSubscribeGranularDisplayEventsEnabled)
+ ? DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_STATE
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED
+ : DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED;
DisplayManagerGlobal
.getInstance()
.registerDisplayListener(
mDisplayListener,
mHandler,
- DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED
- | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED
- | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED,
+ eventsToBeRegistered,
mBasePackageName);
if (forceInvertColor()) {
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 595eb26..7e3b904 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1298,7 +1298,13 @@
@Override
public void visitUris(@NonNull Consumer<Uri> visitor) {
- if (mIntentId != -1 || mItems == null) {
+ if (mItems == null) {
+ // Null item indicates adapter conversion took place, so the URIs in cached items
+ // need to be validated.
+ RemoteCollectionItems cachedItems = mCollectionCache.getItemsForId(mIntentId);
+ if (cachedItems != null) {
+ cachedItems.visitUris(visitor);
+ }
return;
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index cb70466..d7750bd 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -106,7 +106,6 @@
import android.os.ParcelableParcel;
import android.os.Process;
import android.os.SystemClock;
-import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.BoringLayout;
@@ -9230,179 +9229,174 @@
@Override
protected void onDraw(Canvas canvas) {
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, "TextView.onDraw");
- try {
- restartMarqueeIfNeeded();
+ restartMarqueeIfNeeded();
- // Draw the background for this view
- super.onDraw(canvas);
+ // Draw the background for this view
+ super.onDraw(canvas);
- final int compoundPaddingLeft = getCompoundPaddingLeft();
- final int compoundPaddingTop = getCompoundPaddingTop();
- final int compoundPaddingRight = getCompoundPaddingRight();
- final int compoundPaddingBottom = getCompoundPaddingBottom();
- final int scrollX = mScrollX;
- final int scrollY = mScrollY;
- final int right = mRight;
- final int left = mLeft;
- final int bottom = mBottom;
- final int top = mTop;
- final boolean isLayoutRtl = isLayoutRtl();
- final int offset = getHorizontalOffsetForDrawables();
- final int leftOffset = isLayoutRtl ? 0 : offset;
- final int rightOffset = isLayoutRtl ? offset : 0;
+ final int compoundPaddingLeft = getCompoundPaddingLeft();
+ final int compoundPaddingTop = getCompoundPaddingTop();
+ final int compoundPaddingRight = getCompoundPaddingRight();
+ final int compoundPaddingBottom = getCompoundPaddingBottom();
+ final int scrollX = mScrollX;
+ final int scrollY = mScrollY;
+ final int right = mRight;
+ final int left = mLeft;
+ final int bottom = mBottom;
+ final int top = mTop;
+ final boolean isLayoutRtl = isLayoutRtl();
+ final int offset = getHorizontalOffsetForDrawables();
+ final int leftOffset = isLayoutRtl ? 0 : offset;
+ final int rightOffset = isLayoutRtl ? offset : 0;
- final Drawables dr = mDrawables;
- if (dr != null) {
- /*
- * Compound, not extended, because the icon is not clipped
- * if the text height is smaller.
- */
+ final Drawables dr = mDrawables;
+ if (dr != null) {
+ /*
+ * Compound, not extended, because the icon is not clipped
+ * if the text height is smaller.
+ */
- int vspace = bottom - top - compoundPaddingBottom - compoundPaddingTop;
- int hspace = right - left - compoundPaddingRight - compoundPaddingLeft;
+ int vspace = bottom - top - compoundPaddingBottom - compoundPaddingTop;
+ int hspace = right - left - compoundPaddingRight - compoundPaddingLeft;
- // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
- // Make sure to update invalidateDrawable() when changing this code.
- if (dr.mShowing[Drawables.LEFT] != null) {
- canvas.save();
- canvas.translate(scrollX + mPaddingLeft + leftOffset,
- scrollY + compoundPaddingTop + (vspace - dr.mDrawableHeightLeft) / 2);
- dr.mShowing[Drawables.LEFT].draw(canvas);
- canvas.restore();
- }
-
- // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
- // Make sure to update invalidateDrawable() when changing this code.
- if (dr.mShowing[Drawables.RIGHT] != null) {
- canvas.save();
- canvas.translate(scrollX + right - left - mPaddingRight
- - dr.mDrawableSizeRight - rightOffset,
- scrollY + compoundPaddingTop + (vspace - dr.mDrawableHeightRight) / 2);
- dr.mShowing[Drawables.RIGHT].draw(canvas);
- canvas.restore();
- }
-
- // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
- // Make sure to update invalidateDrawable() when changing this code.
- if (dr.mShowing[Drawables.TOP] != null) {
- canvas.save();
- canvas.translate(scrollX + compoundPaddingLeft
- + (hspace - dr.mDrawableWidthTop) / 2, scrollY + mPaddingTop);
- dr.mShowing[Drawables.TOP].draw(canvas);
- canvas.restore();
- }
-
- // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
- // Make sure to update invalidateDrawable() when changing this code.
- if (dr.mShowing[Drawables.BOTTOM] != null) {
- canvas.save();
- canvas.translate(scrollX + compoundPaddingLeft
- + (hspace - dr.mDrawableWidthBottom) / 2,
- scrollY + bottom - top - mPaddingBottom - dr.mDrawableSizeBottom);
- dr.mShowing[Drawables.BOTTOM].draw(canvas);
- canvas.restore();
- }
+ // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
+ // Make sure to update invalidateDrawable() when changing this code.
+ if (dr.mShowing[Drawables.LEFT] != null) {
+ canvas.save();
+ canvas.translate(scrollX + mPaddingLeft + leftOffset,
+ scrollY + compoundPaddingTop + (vspace - dr.mDrawableHeightLeft) / 2);
+ dr.mShowing[Drawables.LEFT].draw(canvas);
+ canvas.restore();
}
- int color = mCurTextColor;
-
- if (mLayout == null) {
- assumeLayout();
+ // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
+ // Make sure to update invalidateDrawable() when changing this code.
+ if (dr.mShowing[Drawables.RIGHT] != null) {
+ canvas.save();
+ canvas.translate(scrollX + right - left - mPaddingRight
+ - dr.mDrawableSizeRight - rightOffset,
+ scrollY + compoundPaddingTop + (vspace - dr.mDrawableHeightRight) / 2);
+ dr.mShowing[Drawables.RIGHT].draw(canvas);
+ canvas.restore();
}
- Layout layout = mLayout;
-
- if (mHint != null && !mHideHint && mText.length() == 0) {
- if (mHintTextColor != null) {
- color = mCurHintTextColor;
- }
-
- layout = mHintLayout;
+ // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
+ // Make sure to update invalidateDrawable() when changing this code.
+ if (dr.mShowing[Drawables.TOP] != null) {
+ canvas.save();
+ canvas.translate(scrollX + compoundPaddingLeft
+ + (hspace - dr.mDrawableWidthTop) / 2, scrollY + mPaddingTop);
+ dr.mShowing[Drawables.TOP].draw(canvas);
+ canvas.restore();
}
- mTextPaint.setColor(color);
- mTextPaint.drawableState = getDrawableState();
-
- canvas.save();
- /* Would be faster if we didn't have to do this. Can we chop the
- (displayable) text so that we don't need to do this ever?
- */
-
- int extendedPaddingTop = getExtendedPaddingTop();
- int extendedPaddingBottom = getExtendedPaddingBottom();
-
- final int vspace = mBottom - mTop - compoundPaddingBottom - compoundPaddingTop;
- final int maxScrollY = mLayout.getHeight() - vspace;
-
- float clipLeft = compoundPaddingLeft + scrollX;
- float clipTop = (scrollY == 0) ? 0 : extendedPaddingTop + scrollY;
- float clipRight = right - left - getCompoundPaddingRight() + scrollX;
- float clipBottom = bottom - top + scrollY
- - ((scrollY == maxScrollY) ? 0 : extendedPaddingBottom);
-
- if (mShadowRadius != 0) {
- clipLeft += Math.min(0, mShadowDx - mShadowRadius);
- clipRight += Math.max(0, mShadowDx + mShadowRadius);
-
- clipTop += Math.min(0, mShadowDy - mShadowRadius);
- clipBottom += Math.max(0, mShadowDy + mShadowRadius);
+ // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
+ // Make sure to update invalidateDrawable() when changing this code.
+ if (dr.mShowing[Drawables.BOTTOM] != null) {
+ canvas.save();
+ canvas.translate(scrollX + compoundPaddingLeft
+ + (hspace - dr.mDrawableWidthBottom) / 2,
+ scrollY + bottom - top - mPaddingBottom - dr.mDrawableSizeBottom);
+ dr.mShowing[Drawables.BOTTOM].draw(canvas);
+ canvas.restore();
}
-
- canvas.clipRect(clipLeft, clipTop, clipRight, clipBottom);
-
- int voffsetText = 0;
- int voffsetCursor = 0;
-
- // translate in by our padding
- /* shortcircuit calling getVerticaOffset() */
- if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) {
- voffsetText = getVerticalOffset(false);
- voffsetCursor = getVerticalOffset(true);
- }
- canvas.translate(compoundPaddingLeft, extendedPaddingTop + voffsetText);
-
- final int layoutDirection = getLayoutDirection();
- final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
- if (isMarqueeFadeEnabled()) {
- if (!mSingleLine && getLineCount() == 1 && canMarquee()
- && (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) != Gravity.LEFT) {
- final int width = mRight - mLeft;
- final int padding = getCompoundPaddingLeft() + getCompoundPaddingRight();
- final float dx = mLayout.getLineRight(0) - (width - padding);
- canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f);
- }
-
- if (mMarquee != null && mMarquee.isRunning()) {
- final float dx = -mMarquee.getScroll();
- canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f);
- }
- }
-
- final int cursorOffsetVertical = voffsetCursor - voffsetText;
-
- maybeUpdateHighlightPaths();
- // If there is a gesture preview highlight, then the selection or cursor is not drawn.
- Path highlight = hasGesturePreviewHighlight() ? null : getUpdatedHighlightPath();
- if (mEditor != null) {
- mEditor.onDraw(canvas, layout, mHighlightPaths, mHighlightPaints, highlight,
- mHighlightPaint, cursorOffsetVertical);
- } else {
- layout.draw(canvas, mHighlightPaths, mHighlightPaints, highlight, mHighlightPaint,
- cursorOffsetVertical);
- }
-
- if (mMarquee != null && mMarquee.shouldDrawGhost()) {
- final float dx = mMarquee.getGhostOffset();
- canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f);
- layout.draw(canvas, mHighlightPaths, mHighlightPaints, highlight, mHighlightPaint,
- cursorOffsetVertical);
- }
-
- canvas.restore();
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
+
+ int color = mCurTextColor;
+
+ if (mLayout == null) {
+ assumeLayout();
+ }
+
+ Layout layout = mLayout;
+
+ if (mHint != null && !mHideHint && mText.length() == 0) {
+ if (mHintTextColor != null) {
+ color = mCurHintTextColor;
+ }
+
+ layout = mHintLayout;
+ }
+
+ mTextPaint.setColor(color);
+ mTextPaint.drawableState = getDrawableState();
+
+ canvas.save();
+ /* Would be faster if we didn't have to do this. Can we chop the
+ (displayable) text so that we don't need to do this ever?
+ */
+
+ int extendedPaddingTop = getExtendedPaddingTop();
+ int extendedPaddingBottom = getExtendedPaddingBottom();
+
+ final int vspace = mBottom - mTop - compoundPaddingBottom - compoundPaddingTop;
+ final int maxScrollY = mLayout.getHeight() - vspace;
+
+ float clipLeft = compoundPaddingLeft + scrollX;
+ float clipTop = (scrollY == 0) ? 0 : extendedPaddingTop + scrollY;
+ float clipRight = right - left - getCompoundPaddingRight() + scrollX;
+ float clipBottom = bottom - top + scrollY
+ - ((scrollY == maxScrollY) ? 0 : extendedPaddingBottom);
+
+ if (mShadowRadius != 0) {
+ clipLeft += Math.min(0, mShadowDx - mShadowRadius);
+ clipRight += Math.max(0, mShadowDx + mShadowRadius);
+
+ clipTop += Math.min(0, mShadowDy - mShadowRadius);
+ clipBottom += Math.max(0, mShadowDy + mShadowRadius);
+ }
+
+ canvas.clipRect(clipLeft, clipTop, clipRight, clipBottom);
+
+ int voffsetText = 0;
+ int voffsetCursor = 0;
+
+ // translate in by our padding
+ /* shortcircuit calling getVerticaOffset() */
+ if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) {
+ voffsetText = getVerticalOffset(false);
+ voffsetCursor = getVerticalOffset(true);
+ }
+ canvas.translate(compoundPaddingLeft, extendedPaddingTop + voffsetText);
+
+ final int layoutDirection = getLayoutDirection();
+ final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
+ if (isMarqueeFadeEnabled()) {
+ if (!mSingleLine && getLineCount() == 1 && canMarquee()
+ && (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) != Gravity.LEFT) {
+ final int width = mRight - mLeft;
+ final int padding = getCompoundPaddingLeft() + getCompoundPaddingRight();
+ final float dx = mLayout.getLineRight(0) - (width - padding);
+ canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f);
+ }
+
+ if (mMarquee != null && mMarquee.isRunning()) {
+ final float dx = -mMarquee.getScroll();
+ canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f);
+ }
+ }
+
+ final int cursorOffsetVertical = voffsetCursor - voffsetText;
+
+ maybeUpdateHighlightPaths();
+ // If there is a gesture preview highlight, then the selection or cursor is not drawn.
+ Path highlight = hasGesturePreviewHighlight() ? null : getUpdatedHighlightPath();
+ if (mEditor != null) {
+ mEditor.onDraw(canvas, layout, mHighlightPaths, mHighlightPaints, highlight,
+ mHighlightPaint, cursorOffsetVertical);
+ } else {
+ layout.draw(canvas, mHighlightPaths, mHighlightPaints, highlight, mHighlightPaint,
+ cursorOffsetVertical);
+ }
+
+ if (mMarquee != null && mMarquee.shouldDrawGhost()) {
+ final float dx = mMarquee.getGhostOffset();
+ canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f);
+ layout.draw(canvas, mHighlightPaths, mHighlightPaints, highlight, mHighlightPaint,
+ cursorOffsetVertical);
+ }
+
+ canvas.restore();
}
@Override
@@ -11260,201 +11254,192 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, "TextView.onMeasure");
- try {
- int widthMode = MeasureSpec.getMode(widthMeasureSpec);
- int heightMode = MeasureSpec.getMode(heightMeasureSpec);
- int widthSize = MeasureSpec.getSize(widthMeasureSpec);
- int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+ int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+ int heightSize = MeasureSpec.getSize(heightMeasureSpec);
- int width;
- int height;
+ int width;
+ int height;
- BoringLayout.Metrics boring = UNKNOWN_BORING;
- BoringLayout.Metrics hintBoring = UNKNOWN_BORING;
+ BoringLayout.Metrics boring = UNKNOWN_BORING;
+ BoringLayout.Metrics hintBoring = UNKNOWN_BORING;
- if (mTextDir == null) {
- mTextDir = getTextDirectionHeuristic();
+ if (mTextDir == null) {
+ mTextDir = getTextDirectionHeuristic();
+ }
+
+ int des = -1;
+ boolean fromexisting = false;
+ final float widthLimit = (widthMode == MeasureSpec.AT_MOST)
+ ? (float) widthSize : Float.MAX_VALUE;
+
+ if (widthMode == MeasureSpec.EXACTLY) {
+ // Parent has told us how big to be. So be it.
+ width = widthSize;
+ } else {
+ if (mLayout != null && mEllipsize == null) {
+ des = desired(mLayout, mUseBoundsForWidth);
}
- int des = -1;
- boolean fromexisting = false;
- final float widthLimit = (widthMode == MeasureSpec.AT_MOST)
- ? (float) widthSize : Float.MAX_VALUE;
-
- if (widthMode == MeasureSpec.EXACTLY) {
- // Parent has told us how big to be. So be it.
- width = widthSize;
+ if (des < 0) {
+ boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir,
+ isFallbackLineSpacingForBoringLayout(), getResolvedMinimumFontMetrics(),
+ mBoring);
+ if (boring != null) {
+ mBoring = boring;
+ }
} else {
- if (mLayout != null && mEllipsize == null) {
- des = desired(mLayout, mUseBoundsForWidth);
- }
+ fromexisting = true;
+ }
+ if (boring == null || boring == UNKNOWN_BORING) {
if (des < 0) {
- boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir,
- isFallbackLineSpacingForBoringLayout(), getResolvedMinimumFontMetrics(),
- mBoring);
- if (boring != null) {
- mBoring = boring;
- }
+ des = (int) Math.ceil(Layout.getDesiredWidthWithLimit(mTransformed, 0,
+ mTransformed.length(), mTextPaint, mTextDir, widthLimit,
+ mUseBoundsForWidth));
+ }
+ width = des;
+ } else {
+ if (mUseBoundsForWidth) {
+ RectF bbox = boring.getDrawingBoundingBox();
+ float rightMax = Math.max(bbox.right, boring.width);
+ float leftMin = Math.min(bbox.left, 0);
+ width = Math.max(boring.width, (int) Math.ceil(rightMax - leftMin));
} else {
- fromexisting = true;
+ width = boring.width;
+ }
+ }
+
+ final Drawables dr = mDrawables;
+ if (dr != null) {
+ width = Math.max(width, dr.mDrawableWidthTop);
+ width = Math.max(width, dr.mDrawableWidthBottom);
+ }
+
+ if (mHint != null) {
+ int hintDes = -1;
+ int hintWidth;
+
+ if (mHintLayout != null && mEllipsize == null) {
+ hintDes = desired(mHintLayout, mUseBoundsForWidth);
}
- if (boring == null || boring == UNKNOWN_BORING) {
- if (des < 0) {
- des = (int) Math.ceil(Layout.getDesiredWidthWithLimit(mTransformed, 0,
- mTransformed.length(), mTextPaint, mTextDir, widthLimit,
+ if (hintDes < 0) {
+ hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir,
+ isFallbackLineSpacingForBoringLayout(), getResolvedMinimumFontMetrics(),
+ mHintBoring);
+ if (hintBoring != null) {
+ mHintBoring = hintBoring;
+ }
+ }
+
+ if (hintBoring == null || hintBoring == UNKNOWN_BORING) {
+ if (hintDes < 0) {
+ hintDes = (int) Math.ceil(Layout.getDesiredWidthWithLimit(mHint, 0,
+ mHint.length(), mTextPaint, mTextDir, widthLimit,
mUseBoundsForWidth));
}
- width = des;
+ hintWidth = hintDes;
} else {
- if (mUseBoundsForWidth) {
- RectF bbox = boring.getDrawingBoundingBox();
- float rightMax = Math.max(bbox.right, boring.width);
- float leftMin = Math.min(bbox.left, 0);
- width = Math.max(boring.width, (int) Math.ceil(rightMax - leftMin));
- } else {
- width = boring.width;
- }
+ hintWidth = hintBoring.width;
}
- final Drawables dr = mDrawables;
- if (dr != null) {
- width = Math.max(width, dr.mDrawableWidthTop);
- width = Math.max(width, dr.mDrawableWidthBottom);
- }
-
- if (mHint != null) {
- int hintDes = -1;
- int hintWidth;
-
- if (mHintLayout != null && mEllipsize == null) {
- hintDes = desired(mHintLayout, mUseBoundsForWidth);
- }
-
- if (hintDes < 0) {
- hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir,
- isFallbackLineSpacingForBoringLayout(),
- getResolvedMinimumFontMetrics(),
- mHintBoring);
- if (hintBoring != null) {
- mHintBoring = hintBoring;
- }
- }
-
- if (hintBoring == null || hintBoring == UNKNOWN_BORING) {
- if (hintDes < 0) {
- hintDes = (int) Math.ceil(Layout.getDesiredWidthWithLimit(mHint, 0,
- mHint.length(), mTextPaint, mTextDir, widthLimit,
- mUseBoundsForWidth));
- }
- hintWidth = hintDes;
- } else {
- hintWidth = hintBoring.width;
- }
-
- if (hintWidth > width) {
- width = hintWidth;
- }
- }
-
- width += getCompoundPaddingLeft() + getCompoundPaddingRight();
-
- if (mMaxWidthMode == EMS) {
- width = Math.min(width, mMaxWidth * getLineHeight());
- } else {
- width = Math.min(width, mMaxWidth);
- }
-
- if (mMinWidthMode == EMS) {
- width = Math.max(width, mMinWidth * getLineHeight());
- } else {
- width = Math.max(width, mMinWidth);
- }
-
- // Check against our minimum width
- width = Math.max(width, getSuggestedMinimumWidth());
-
- if (widthMode == MeasureSpec.AT_MOST) {
- width = Math.min(widthSize, width);
+ if (hintWidth > width) {
+ width = hintWidth;
}
}
- int want = width - getCompoundPaddingLeft() - getCompoundPaddingRight();
- int unpaddedWidth = want;
+ width += getCompoundPaddingLeft() + getCompoundPaddingRight();
- if (mHorizontallyScrolling) want = VERY_WIDE;
-
- int hintWant = want;
- int hintWidth = (mHintLayout == null) ? hintWant : mHintLayout.getWidth();
-
- if (mLayout == null) {
- makeNewLayout(want, hintWant, boring, hintBoring,
- width - getCompoundPaddingLeft() - getCompoundPaddingRight(), false);
+ if (mMaxWidthMode == EMS) {
+ width = Math.min(width, mMaxWidth * getLineHeight());
} else {
- final boolean layoutChanged =
- (mLayout.getWidth() != want) || (hintWidth != hintWant)
- || (mLayout.getEllipsizedWidth()
- != width - getCompoundPaddingLeft() - getCompoundPaddingRight());
-
- final boolean widthChanged = (mHint == null) && (mEllipsize == null)
- && (want > mLayout.getWidth())
- && (mLayout instanceof BoringLayout
- || (fromexisting && des >= 0 && des <= want));
-
- final boolean maximumChanged =
- (mMaxMode != mOldMaxMode) || (mMaximum != mOldMaximum);
-
- if (layoutChanged || maximumChanged) {
- if (!maximumChanged && widthChanged) {
- mLayout.increaseWidthTo(want);
- } else {
- makeNewLayout(want, hintWant, boring, hintBoring,
- width - getCompoundPaddingLeft() - getCompoundPaddingRight(),
- false);
- }
- } else {
- // Nothing has changed
- }
+ width = Math.min(width, mMaxWidth);
}
- if (heightMode == MeasureSpec.EXACTLY) {
- // Parent has told us how big to be. So be it.
- height = heightSize;
- mDesiredHeightAtMeasure = -1;
+ if (mMinWidthMode == EMS) {
+ width = Math.max(width, mMinWidth * getLineHeight());
} else {
- int desired = getDesiredHeight();
-
- height = desired;
- mDesiredHeightAtMeasure = desired;
-
- if (heightMode == MeasureSpec.AT_MOST) {
- height = Math.min(desired, heightSize);
- }
+ width = Math.max(width, mMinWidth);
}
- int unpaddedHeight = height - getCompoundPaddingTop() - getCompoundPaddingBottom();
- if (mMaxMode == LINES && mLayout.getLineCount() > mMaximum) {
- unpaddedHeight = Math.min(unpaddedHeight, mLayout.getLineTop(mMaximum));
- }
+ // Check against our minimum width
+ width = Math.max(width, getSuggestedMinimumWidth());
- /*
- * We didn't let makeNewLayout() register to bring the cursor into view,
- * so do it here if there is any possibility that it is needed.
- */
- if (mMovement != null
- || mLayout.getWidth() > unpaddedWidth
- || mLayout.getHeight() > unpaddedHeight) {
- registerForPreDraw();
- } else {
- scrollTo(0, 0);
+ if (widthMode == MeasureSpec.AT_MOST) {
+ width = Math.min(widthSize, width);
}
-
- setMeasuredDimension(width, height);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
+
+ int want = width - getCompoundPaddingLeft() - getCompoundPaddingRight();
+ int unpaddedWidth = want;
+
+ if (mHorizontallyScrolling) want = VERY_WIDE;
+
+ int hintWant = want;
+ int hintWidth = (mHintLayout == null) ? hintWant : mHintLayout.getWidth();
+
+ if (mLayout == null) {
+ makeNewLayout(want, hintWant, boring, hintBoring,
+ width - getCompoundPaddingLeft() - getCompoundPaddingRight(), false);
+ } else {
+ final boolean layoutChanged = (mLayout.getWidth() != want) || (hintWidth != hintWant)
+ || (mLayout.getEllipsizedWidth()
+ != width - getCompoundPaddingLeft() - getCompoundPaddingRight());
+
+ final boolean widthChanged = (mHint == null) && (mEllipsize == null)
+ && (want > mLayout.getWidth())
+ && (mLayout instanceof BoringLayout
+ || (fromexisting && des >= 0 && des <= want));
+
+ final boolean maximumChanged = (mMaxMode != mOldMaxMode) || (mMaximum != mOldMaximum);
+
+ if (layoutChanged || maximumChanged) {
+ if (!maximumChanged && widthChanged) {
+ mLayout.increaseWidthTo(want);
+ } else {
+ makeNewLayout(want, hintWant, boring, hintBoring,
+ width - getCompoundPaddingLeft() - getCompoundPaddingRight(), false);
+ }
+ } else {
+ // Nothing has changed
+ }
+ }
+
+ if (heightMode == MeasureSpec.EXACTLY) {
+ // Parent has told us how big to be. So be it.
+ height = heightSize;
+ mDesiredHeightAtMeasure = -1;
+ } else {
+ int desired = getDesiredHeight();
+
+ height = desired;
+ mDesiredHeightAtMeasure = desired;
+
+ if (heightMode == MeasureSpec.AT_MOST) {
+ height = Math.min(desired, heightSize);
+ }
+ }
+
+ int unpaddedHeight = height - getCompoundPaddingTop() - getCompoundPaddingBottom();
+ if (mMaxMode == LINES && mLayout.getLineCount() > mMaximum) {
+ unpaddedHeight = Math.min(unpaddedHeight, mLayout.getLineTop(mMaximum));
+ }
+
+ /*
+ * We didn't let makeNewLayout() register to bring the cursor into view,
+ * so do it here if there is any possibility that it is needed.
+ */
+ if (mMovement != null
+ || mLayout.getWidth() > unpaddedWidth
+ || mLayout.getHeight() > unpaddedHeight) {
+ registerForPreDraw();
+ } else {
+ scrollTo(0, 0);
+ }
+
+ setMeasuredDimension(width, height);
}
/**
diff --git a/core/java/android/window/WindowContext.java b/core/java/android/window/WindowContext.java
index fdaab66..2b370b9 100644
--- a/core/java/android/window/WindowContext.java
+++ b/core/java/android/window/WindowContext.java
@@ -94,6 +94,23 @@
mController.attachToDisplayArea(mType, getDisplayId(), mOptions);
}
+ /**
+ * Updates this context to a new displayId.
+ * <p>
+ * Note that this doesn't re-parent previously attached windows (they should be removed and
+ * re-added manually after this is called). Resources associated with this context will have
+ * the correct value and configuration for the new display after this is called.
+ */
+ @Override
+ public void updateDisplay(int displayId) {
+ if (displayId == getDisplayId()) {
+ return;
+ }
+ super.updateDisplay(displayId);
+ mController.detachIfNeeded();
+ mController.attachToDisplayArea(mType, displayId, mOptions);
+ }
+
@Override
public Object getSystemService(String name) {
if (WINDOW_SERVICE.equals(name)) {
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
index ac4c066..5fbb92d 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
@@ -16,6 +16,7 @@
package com.android.internal.inputmethod;
+import android.graphics.Region;
import android.net.Uri;
import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputMethodSubtype;
@@ -51,4 +52,5 @@
void resetStylusHandwriting(int requestId);
void switchKeyboardLayoutAsync(int direction);
void setHandwritingSurfaceNotTouchable(boolean notTouchable);
+ void setHandwritingTouchableRegion(in Region region);
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index b009c99..36333a9 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -20,6 +20,7 @@
import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.graphics.Region;
import android.inputmethodservice.InputMethodService.BackDispositionMode;
import android.inputmethodservice.InputMethodService.ImeWindowVisibility;
import android.net.Uri;
@@ -159,6 +160,25 @@
}
}
+
+ /**
+ * Calls {@link IInputMethodPrivilegedOperations#setHandwritingTouchableRegion(Region)}.
+ *
+ * @param region {@link Region} to set handwritable.
+ */
+ @AnyThread
+ public void setHandwritingTouchableRegion(Region region) {
+ final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
+ if (ops == null) {
+ return;
+ }
+ try {
+ ops.setHandwritingTouchableRegion(region);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* Calls {@link IInputMethodPrivilegedOperations#createInputContentUriToken(Uri, String,
* AndroidFuture)}.
diff --git a/core/java/com/android/internal/jank/Cuj.java b/core/java/com/android/internal/jank/Cuj.java
index 1204ef3..fc41537 100644
--- a/core/java/com/android/internal/jank/Cuj.java
+++ b/core/java/com/android/internal/jank/Cuj.java
@@ -235,8 +235,19 @@
*/
public static final int CUJ_DESKTOP_MODE_SNAP_RESIZE = 118;
+ /**
+ * Track unmaximize window interaction in desktop mode.
+ *
+ * <p> Tracking starts when the maximize button or option is clicked {@link
+ * com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel#onClick}
+ * and finishes when the animation finishes {@link
+ * com.android.wm.shell.windowdecor.ToggleResizeDesktopTaskTransitionHandler#startAnimation}
+ * </p>
+ */
+ public static final int CUJ_DESKTOP_MODE_UNMAXIMIZE_WINDOW = 119;
+
// When adding a CUJ, update this and make sure to also update CUJ_TO_STATSD_INTERACTION_TYPE.
- @VisibleForTesting static final int LAST_CUJ = CUJ_DESKTOP_MODE_SNAP_RESIZE;
+ @VisibleForTesting static final int LAST_CUJ = CUJ_DESKTOP_MODE_UNMAXIMIZE_WINDOW;
/** @hide */
@IntDef({
@@ -346,7 +357,8 @@
CUJ_LAUNCHER_KEYBOARD_QUICK_SWITCH_APP_LAUNCH,
CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE,
CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE,
- CUJ_DESKTOP_MODE_SNAP_RESIZE
+ CUJ_DESKTOP_MODE_SNAP_RESIZE,
+ CUJ_DESKTOP_MODE_UNMAXIMIZE_WINDOW
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {}
@@ -467,6 +479,7 @@
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE;
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE;
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DESKTOP_MODE_SNAP_RESIZE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DESKTOP_MODE_SNAP_RESIZE;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DESKTOP_MODE_UNMAXIMIZE_WINDOW] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DESKTOP_MODE_UNMAXIMIZE_WINDOW;
}
private Cuj() {
@@ -699,6 +712,8 @@
return "DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE";
case CUJ_DESKTOP_MODE_SNAP_RESIZE:
return "DESKTOP_MODE_SNAP_RESIZE";
+ case CUJ_DESKTOP_MODE_UNMAXIMIZE_WINDOW:
+ return "DESKTOP_MODE_UNMAXIMIZE_WINDOW";
}
return "UNKNOWN";
}
diff --git a/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
index 48d0d6c..5ec5762 100644
--- a/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
+++ b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
@@ -392,6 +392,10 @@
private int memtagMode;
@ApplicationInfo.NativeHeapZeroInitialized
private int nativeHeapZeroInitialized;
+
+ @ApplicationInfo.PageSizeAppCompatFlags private int mPageSizeAppCompatFlags =
+ ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED;
+
@Nullable
@DataClass.ParcelWith(Parcelling.BuiltIn.ForBoolean.class)
private Boolean requestRawExternalStorageAccess;
@@ -1118,6 +1122,12 @@
return nativeHeapZeroInitialized;
}
+ @ApplicationInfo.PageSizeAppCompatFlags
+ @Override
+ public int getPageSizeAppCompatFlags() {
+ return mPageSizeAppCompatFlags;
+ }
+
@Override
public int getNetworkSecurityConfigResourceId() {
return networkSecurityConfigRes;
@@ -2221,6 +2231,12 @@
}
@Override
+ public PackageImpl setPageSizeAppCompatFlags(@ApplicationInfo.PageSizeAppCompatFlags int flag) {
+ mPageSizeAppCompatFlags = flag;
+ return this;
+ }
+
+ @Override
public PackageImpl setNetworkSecurityConfigResourceId(int value) {
networkSecurityConfigRes = value;
return this;
@@ -2703,6 +2719,7 @@
appInfo.setKnownActivityEmbeddingCerts(mKnownActivityEmbeddingCerts);
}
appInfo.allowCrossUidActivitySwitchFromBelow = mAllowCrossUidActivitySwitchFromBelow;
+ appInfo.setPageSizeAppCompatFlags(mPageSizeAppCompatFlags);
return appInfo;
}
@@ -3305,6 +3322,7 @@
dest.writeInt(this.mIntentMatchingFlags);
dest.writeIntArray(this.mAlternateLauncherIconResIds);
dest.writeIntArray(this.mAlternateLauncherLabelResIds);
+ dest.writeInt(this.mPageSizeAppCompatFlags);
}
private void writeFeatureFlagState(@NonNull Parcel dest) {
@@ -3499,6 +3517,7 @@
this.mIntentMatchingFlags = in.readInt();
this.mAlternateLauncherIconResIds = in.createIntArray();
this.mAlternateLauncherLabelResIds = in.createIntArray();
+ this.mPageSizeAppCompatFlags = in.readInt();
assignDerivedFields();
assignDerivedFields2();
diff --git a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java
index 67b985a..5062d58 100644
--- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java
@@ -31,7 +31,6 @@
import android.util.SparseArray;
import android.util.SparseIntArray;
-import com.android.internal.R;
import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.internal.pm.pkg.component.ParsedActivity;
import com.android.internal.pm.pkg.component.ParsedApexSystemService;
@@ -280,6 +279,9 @@
ParsingPackage setNativeHeapZeroInitialized(
@ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized);
+ /** Manifest option pageSizeCompat will populate this field */
+ ParsingPackage setPageSizeAppCompatFlags(@ApplicationInfo.PageSizeAppCompatFlags int value);
+
ParsingPackage setRequestRawExternalStorageAccess(
@Nullable Boolean requestRawExternalStorageAccess);
diff --git a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
index 5fc1276..c160b42 100644
--- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
@@ -29,6 +29,7 @@
import static android.os.Build.VERSION_CODES.DONUT;
import static android.os.Build.VERSION_CODES.O;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+import static android.sdk.Flags.majorMinorVersioningScheme;
import static com.android.internal.pm.pkg.parsing.ParsingUtils.parseKnownActivityEmbeddingCerts;
@@ -541,6 +542,7 @@
pkg.setGwpAsanMode(-1);
pkg.setMemtagMode(-1);
+ pkg.setPageSizeAppCompatFlags(ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED);
afterParseBaseApplication(pkg);
@@ -1688,6 +1690,21 @@
targetCode = minCode;
}
+ if (majorMinorVersioningScheme()) {
+ val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_minSdkVersionFull);
+ if (val != null) {
+ if (val.type == TypedValue.TYPE_STRING && val.string != null) {
+ String minSdkVersionFullString = val.string.toString();
+ ParseResult<Void> minSdkVersionFullResult =
+ FrameworkParsingPackageUtils.verifyMinSdkVersionFull(
+ minSdkVersionFullString, Build.VERSION.SDK_INT_FULL, input);
+ if (minSdkVersionFullResult.isError()) {
+ return input.error(minSdkVersionFullResult);
+ }
+ }
+ }
+ }
+
if (isApkInApex) {
val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_maxSdkVersion);
if (val != null) {
@@ -2182,6 +2199,13 @@
pkg.setGwpAsanMode(sa.getInt(R.styleable.AndroidManifestApplication_gwpAsanMode, -1));
pkg.setMemtagMode(sa.getInt(R.styleable.AndroidManifestApplication_memtagMode, -1));
+
+ if (Flags.appCompatOption16kb()) {
+ pkg.setPageSizeAppCompatFlags(
+ sa.getInt(R.styleable.AndroidManifestApplication_pageSizeCompat,
+ ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED));
+ }
+
if (sa.hasValue(R.styleable.AndroidManifestApplication_nativeHeapZeroInitialized)) {
final boolean v = sa.getBoolean(
R.styleable.AndroidManifestApplication_nativeHeapZeroInitialized, false);
diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java
index 97a2d3b..4305ba7 100644
--- a/core/java/com/android/internal/widget/MessagingGroup.java
+++ b/core/java/com/android/internal/widget/MessagingGroup.java
@@ -261,7 +261,7 @@
MessagingGroup createdGroup = sInstancePool.acquire();
if (createdGroup == null) {
createdGroup = (MessagingGroup) LayoutInflater.from(layout.getContext()).inflate(
- R.layout.notification_template_messaging_group, layout,
+ getMessagingGroupLayoutResource(), layout,
false);
createdGroup.addOnLayoutChangeListener(MessagingLayout.MESSAGING_PROPERTY_ANIMATOR);
}
@@ -269,6 +269,14 @@
return createdGroup;
}
+ private static int getMessagingGroupLayoutResource() {
+ if (Flags.notificationsRedesignTemplates()) {
+ return R.layout.notification_2025_messaging_group;
+ } else {
+ return R.layout.notification_template_messaging_group;
+ }
+ }
+
public void removeMessage(MessagingMessage messagingMessage,
ArrayList<MessagingLinearLayout.MessagingChild> toRecycle) {
View view = messagingMessage.getView();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/Operation.java b/core/java/com/android/internal/widget/remotecompose/core/Operation.java
index 102003e..6f6a0a8 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Operation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Operation.java
@@ -18,19 +18,19 @@
import android.annotation.NonNull;
/** Base interface for RemoteCompose operations */
-public interface Operation {
+public abstract class Operation {
/** add the operation to the buffer */
- void write(@NonNull WireBuffer buffer);
+ public abstract void write(@NonNull WireBuffer buffer);
/**
* paint an operation
*
* @param context the paint context used to paint the operation
*/
- void apply(@NonNull RemoteContext context);
+ public abstract void apply(@NonNull RemoteContext context);
/** Debug utility to display an operation + indentation */
@NonNull
- String deepToString(@NonNull String indent);
+ public abstract String deepToString(@NonNull String indent);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/OperationInterface.java b/core/java/com/android/internal/widget/remotecompose/core/OperationInterface.java
new file mode 100644
index 0000000..741303a
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/OperationInterface.java
@@ -0,0 +1,36 @@
+/*
+ * 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.internal.widget.remotecompose.core;
+
+import android.annotation.NonNull;
+
+/** Base interface for RemoteCompose operations */
+public interface OperationInterface {
+
+ /** add the operation to the buffer */
+ void write(@NonNull WireBuffer buffer);
+
+ /**
+ * paint an operation
+ *
+ * @param context the paint context used to paint the operation
+ */
+ void apply(@NonNull RemoteContext context);
+
+ /** Debug utility to display an operation + indentation */
+ @NonNull
+ String deepToString(@NonNull String indent);
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/Operations.java b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
index 687a99b..006fe3a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
@@ -55,7 +55,10 @@
import com.android.internal.widget.remotecompose.core.operations.MatrixTranslate;
import com.android.internal.widget.remotecompose.core.operations.NamedVariable;
import com.android.internal.widget.remotecompose.core.operations.PaintData;
+import com.android.internal.widget.remotecompose.core.operations.PathAppend;
+import com.android.internal.widget.remotecompose.core.operations.PathCreate;
import com.android.internal.widget.remotecompose.core.operations.PathData;
+import com.android.internal.widget.remotecompose.core.operations.PathTween;
import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior;
import com.android.internal.widget.remotecompose.core.operations.RootContentDescription;
import com.android.internal.widget.remotecompose.core.operations.ShaderData;
@@ -178,6 +181,9 @@
public static final int TEXT_MEASURE = 155;
public static final int TEXT_LENGTH = 156;
public static final int TOUCH_EXPRESSION = 157;
+ public static final int PATH_TWEEN = 158;
+ public static final int PATH_CREATE = 159;
+ public static final int PATH_ADD = 160;
///////////////////////////////////////// ======================
@@ -353,5 +359,8 @@
map.put(TEXT_MEASURE, TextMeasure::read);
map.put(TEXT_LENGTH, TextLength::read);
map.put(TOUCH_EXPRESSION, TouchExpression::read);
+ map.put(PATH_TWEEN, PathTween::read);
+ map.put(PATH_CREATE, PathCreate::read);
+ map.put(PATH_ADD, PathAppend::read);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
index 38b08e9..7ecd118 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
@@ -157,6 +157,8 @@
public abstract void drawTweenPath(
int path1Id, int path2Id, float tween, float start, float stop);
+ public abstract void tweenPath(int out, int path1, int path2, float tween);
+
/**
* This applies changes to the current paint
*
diff --git a/core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java b/core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java
index 9999182..cfdd522 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java
@@ -21,7 +21,7 @@
* PaintOperation interface, used for operations aimed at painting (while any operation _can_ paint,
* this make it a little more explicit)
*/
-public abstract class PaintOperation implements Operation {
+public abstract class PaintOperation extends Operation {
@Override
public void apply(@NonNull RemoteContext context) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
index c05079e..3a5d68d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
@@ -59,7 +59,10 @@
import com.android.internal.widget.remotecompose.core.operations.MatrixTranslate;
import com.android.internal.widget.remotecompose.core.operations.NamedVariable;
import com.android.internal.widget.remotecompose.core.operations.PaintData;
+import com.android.internal.widget.remotecompose.core.operations.PathAppend;
+import com.android.internal.widget.remotecompose.core.operations.PathCreate;
import com.android.internal.widget.remotecompose.core.operations.PathData;
+import com.android.internal.widget.remotecompose.core.operations.PathTween;
import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior;
import com.android.internal.widget.remotecompose.core.operations.RootContentDescription;
import com.android.internal.widget.remotecompose.core.operations.TextData;
@@ -644,6 +647,37 @@
}
/**
+ * interpolate the two paths to produce a 3rd
+ *
+ * @param pid1 the first path
+ * @param pid2 the second path
+ * @param tween path is the path1+(pat2-path1)*tween
+ * @return id of the tweened path
+ */
+ public int pathTween(int pid1, int pid2, float tween) {
+ int out = mRemoteComposeState.nextId();
+ PathTween.apply(mBuffer, out, pid1, pid2, tween);
+ return out;
+ }
+
+ /**
+ * Create a path with an initial moveTo
+ *
+ * @param x x coordinate of the moveto
+ * @param y y coordinate of the moveto
+ * @return id of the created path
+ */
+ public int pathCreate(float x, float y) {
+ int out = mRemoteComposeState.nextId();
+ PathCreate.apply(mBuffer, out, x, y);
+ return out;
+ }
+
+ public void pathAppend(int id, float... path) {
+ PathAppend.apply(mBuffer, id, path);
+ }
+
+ /**
* Draw the specified path
*
* @param pathId
@@ -1154,6 +1188,16 @@
}
/**
+ * Reserve a float and returns a NaN number pointing to that float
+ *
+ * @return the nan id of float
+ */
+ public float reserveFloatVariable() {
+ int id = mRemoteComposeState.cacheFloat(0f);
+ return Utils.asNan(id);
+ }
+
+ /**
* Add a Integer return an id number pointing to that float.
*
* @param value adds an integer and assigns it an id
@@ -1651,8 +1695,8 @@
*/
public void addModifierScroll(int direction, float positionId, int notches) {
// TODO: add support for non-notch behaviors etc.
- float max = this.addFloat(0f);
- float notchMax = this.addFloat(0f);
+ float max = this.reserveFloatVariable();
+ float notchMax = this.reserveFloatVariable();
float touchExpressionDirection =
direction != 0 ? RemoteContext.FLOAT_TOUCH_POS_X : RemoteContext.FLOAT_TOUCH_POS_Y;
this.addTouchExpression(
@@ -1807,8 +1851,8 @@
ClipRectModifierOperation.apply(mBuffer);
}
- public void addLoopStart(float count, float from, float step, int indexId) {
- LoopOperation.apply(mBuffer, count, from, step, indexId);
+ public void addLoopStart(int indexId, float from, float step, float until) {
+ LoopOperation.apply(mBuffer, indexId, from, step, until);
}
public void addLoopEnd() {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeOperation.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeOperation.java
index e60695f..97487e6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeOperation.java
@@ -15,4 +15,4 @@
*/
package com.android.internal.widget.remotecompose.core;
-public interface RemoteComposeOperation extends Operation {}
+public interface RemoteComposeOperation {}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
index a903e6e..f5f9e21 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
@@ -131,6 +131,16 @@
}
}
+ private final IntMap<float[]> mPathData = new IntMap<>();
+
+ public void putPathData(int id, float[] data) {
+ mPathData.put(id, data);
+ }
+
+ public float[] getPathData(int id) {
+ return mPathData.get(id);
+ }
+
/**
* Adds a data Override.
*
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
index 26305bf..6eb8463 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
@@ -93,12 +93,20 @@
/**
* Load a path under an id. Paths can be use in clip drawPath and drawTweenPath
*
- * @param instanceId
- * @param floatPath
+ * @param instanceId the id to save this path under
+ * @param floatPath the path as a float array
*/
public abstract void loadPathData(int instanceId, @NonNull float[] floatPath);
/**
+ * Load a path under an id. Paths can be use in clip drawPath and drawTweenPath
+ *
+ * @param instanceId
+ * @return the a
+ */
+ public abstract @Nullable float[] getPathData(int instanceId);
+
+ /**
* Associate a name with a give id.
*
* @param varName the name
diff --git a/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java
index a64b706..2f1502c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java
@@ -28,11 +28,17 @@
int mStartingIndex = 0;
int mSize = 0;
+ /**
+ * Create a wire buffer
+ *
+ * @param size the initial size of the buffer
+ */
public WireBuffer(int size) {
mMaxSize = size;
mBuffer = new byte[mMaxSize];
}
+ /** Create a wire buffer of default size */
public WireBuffer() {
this(BUFFER_SIZE);
}
@@ -44,37 +50,74 @@
}
}
+ /**
+ * get the wire buffer's underlying byte array. Note the array will be bigger that the used
+ * portion
+ *
+ * @return byte array of the wire buffer
+ */
public @NonNull byte[] getBuffer() {
return mBuffer;
}
+ /**
+ * The current mix size of the buffer
+ *
+ * @return max size
+ */
public int getMax_size() {
return mMaxSize;
}
+ /**
+ * The current point in the buffer which will be written to
+ *
+ * @return index pointing into the buffer
+ */
public int getIndex() {
return mIndex;
}
+ /**
+ * The size of the buffer
+ *
+ * @return the size of the buffer
+ */
public int getSize() {
return mSize;
}
+ /**
+ * Reposition the pointer
+ *
+ * @param index the new position of the index
+ */
public void setIndex(int index) {
this.mIndex = index;
}
+ /**
+ * Write a byte representing the command into the buffer
+ *
+ * @param type the command id
+ */
public void start(int type) {
mStartingIndex = mIndex;
writeByte(type);
}
+ /**
+ * Unused Todo remove?
+ *
+ * @param type the type of object to write
+ */
public void startWithSize(int type) {
mStartingIndex = mIndex;
writeByte(type);
mIndex += 4; // skip ahead for the future size
}
+ /** Unused Todo remove? */
public void endWithSize() {
int size = mIndex - mStartingIndex;
int currentIndex = mIndex;
@@ -97,10 +140,20 @@
}
}
+ /**
+ * return the size of the buffer todo rename to getSize
+ *
+ * @return the size of the buffer
+ */
public int size() {
return mSize;
}
+ /**
+ * Bytes available
+ *
+ * @return the size - index
+ */
public boolean available() {
return mSize - mIndex > 0;
}
@@ -109,28 +162,53 @@
// Read values
///////////////////////////////////////////////////////////////////////////
+ /**
+ * read the operation type (reads a single byte)
+ *
+ * @return the byte cast to an integer
+ */
public int readOperationType() {
return readByte();
}
+ /**
+ * Read a boolean (stored as a byte 1 = true)
+ *
+ * @return boolean of the byte
+ */
public boolean readBoolean() {
byte value = mBuffer[mIndex];
mIndex++;
return (value == 1);
}
+ /**
+ * read a single byte byte
+ *
+ * @return byte from 0..255 as an Integer
+ */
public int readByte() {
int value = 0xFF & mBuffer[mIndex];
mIndex++;
return value;
}
+ /**
+ * read a short [byte n] << 8 | [byte n+1]; index increast by 2
+ *
+ * @return return a short cast as an integer
+ */
public int readShort() {
int v1 = (mBuffer[mIndex++] & 0xFF) << 8;
int v2 = (mBuffer[mIndex++] & 0xFF) << 0;
return v1 + v2;
}
+ /**
+ * Read an integer without incrementing the index
+ *
+ * @return the integer
+ */
public int peekInt() {
int tmp = mIndex;
int v1 = (mBuffer[tmp++] & 0xFF) << 24;
@@ -140,6 +218,11 @@
return v1 + v2 + v3 + v4;
}
+ /**
+ * Read an integer. index increased by 4
+ *
+ * @return integer
+ */
public int readInt() {
int v1 = (mBuffer[mIndex++] & 0xFF) << 24;
int v2 = (mBuffer[mIndex++] & 0xFF) << 16;
@@ -148,6 +231,11 @@
return v1 + v2 + v3 + v4;
}
+ /**
+ * Read a long index is increased by 8
+ *
+ * @return long
+ */
public long readLong() {
long v1 = (mBuffer[mIndex++] & 0xFFL) << 56;
long v2 = (mBuffer[mIndex++] & 0xFFL) << 48;
@@ -160,14 +248,30 @@
return v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8;
}
+ /**
+ * Read a 32 bit float IEEE standard index is increased by 4
+ *
+ * @return the float
+ */
public float readFloat() {
return java.lang.Float.intBitsToFloat(readInt());
}
+ /**
+ * Read a 64 bit double index is increased by 8
+ *
+ * @return double
+ */
public double readDouble() {
return java.lang.Double.longBitsToDouble(readLong());
}
+ /**
+ * Read a byte buffer bytes are encoded as 4 byte length followed by length bytes index is
+ * increased by 4 + number of bytes
+ *
+ * @return byte array
+ */
public @NonNull byte[] readBuffer() {
int count = readInt();
byte[] b = Arrays.copyOfRange(mBuffer, mIndex, mIndex + count);
@@ -175,6 +279,13 @@
return b;
}
+ /**
+ * Read a byte buffer limited to max size. bytes are encoded as 4 byte length followed by length
+ * bytes index is increased by 4 + number of bytes Throw an exception if the read excedes the
+ * max size. This is the preferred form of read buffer.
+ *
+ * @return byte array
+ */
public @NonNull byte[] readBuffer(int maxSize) {
int count = readInt();
if (count < 0 || count > maxSize) {
@@ -186,12 +297,23 @@
return b;
}
+ /**
+ * Read a string encoded in UTF8 The buffer is red with readBuffer and converted to a String
+ *
+ * @return unicode string
+ */
@NonNull
public String readUTF8() {
byte[] stringBuffer = readBuffer();
return new String(stringBuffer);
}
+ /**
+ * Read a string encoded in UTF8 The buffer is red with readBuffer and converted to a String
+ * This is the preferred readUTF8 because it catches errors
+ *
+ * @return unicode string
+ */
@NonNull
public String readUTF8(int maxSize) {
byte[] stringBuffer = readBuffer(maxSize);
@@ -202,18 +324,33 @@
// Write values
///////////////////////////////////////////////////////////////////////////
+ /**
+ * Write a boolean value. (written as a byte 1=true)
+ *
+ * @param value value to write
+ */
public void writeBoolean(boolean value) {
resize(1);
mBuffer[mIndex++] = (byte) (value ? 1 : 0);
mSize++;
}
+ /**
+ * Write a byte value
+ *
+ * @param value value to write
+ */
public void writeByte(int value) {
resize(1);
mBuffer[mIndex++] = (byte) value;
mSize++;
}
+ /**
+ * Write a short value
+ *
+ * @param value value to write
+ */
public void writeShort(int value) {
int need = 2;
resize(need);
@@ -222,6 +359,11 @@
mSize += need;
}
+ /**
+ * Write a int (4 byte) value
+ *
+ * @param value value to write
+ */
public void writeInt(int value) {
int need = 4;
resize(need);
@@ -232,6 +374,11 @@
mSize += need;
}
+ /**
+ * Write a long (8 byte) value
+ *
+ * @param value value to write
+ */
public void writeLong(long value) {
int need = 8;
resize(need);
@@ -246,14 +393,29 @@
mSize += need;
}
+ /**
+ * Write a 32 bit IEEE float value
+ *
+ * @param value value to write
+ */
public void writeFloat(float value) {
writeInt(Float.floatToRawIntBits(value));
}
+ /**
+ * Write a 64 bit IEEE double value
+ *
+ * @param value value to write
+ */
public void writeDouble(double value) {
writeLong(Double.doubleToRawLongBits(value));
}
+ /**
+ * Write a buffer The buffer length is first written followed by the bytes
+ *
+ * @param b array of bytes write
+ */
public void writeBuffer(@NonNull byte[] b) {
resize(b.length + 4);
writeInt(b.length);
@@ -263,6 +425,11 @@
mSize += b.length;
}
+ /**
+ * Write a string is encoded as UTF8
+ *
+ * @param content the string to write
+ */
public void writeUTF8(@NonNull String content) {
byte[] buffer = content.getBytes();
writeBuffer(buffer);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
index 9480076..27ba652 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
@@ -36,7 +36,7 @@
* Operation to deal with bitmap data On getting an Image during a draw call the bitmap is
* compressed and saved in playback the image is decompressed
*/
-public class BitmapData implements Operation, SerializableToString {
+public class BitmapData extends Operation implements SerializableToString {
private static final int OP_CODE = Operations.DATA_BITMAP;
private static final String CLASS_NAME = "BitmapData";
int mImageId;
@@ -85,6 +85,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -119,6 +124,12 @@
buffer.writeBuffer(bitmap);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int imageId = buffer.readInt();
int width = buffer.readInt();
@@ -133,6 +144,11 @@
operations.add(new BitmapData(imageId, width, height, bitmap));
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Bitmap data")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java
index 310b194..5e5e565 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java
@@ -28,7 +28,7 @@
import java.util.List;
/** Add a click area to the document */
-public class ClickArea implements RemoteComposeOperation {
+public class ClickArea extends Operation implements RemoteComposeOperation {
private static final int OP_CODE = Operations.CLICK_AREA;
private static final String CLASS_NAME = "ClickArea";
int mId;
@@ -118,6 +118,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -141,6 +146,12 @@
buffer.writeInt(metadata);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int id = buffer.readInt();
int contentDescription = buffer.readInt();
@@ -154,6 +165,11 @@
operations.add(clickArea);
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Define a region you can click on")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java
index db93829..2fe56d3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java
@@ -69,6 +69,12 @@
return "ClipPath " + mId + ";";
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int pack = buffer.readInt();
int id = pack & 0xFFFFF;
@@ -82,6 +88,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -91,6 +102,11 @@
buffer.writeInt(id);
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Intersect the current clip with the path")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java
index df54fb1..defa656 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java
@@ -31,11 +31,22 @@
public static final int OP_CODE = Operations.CLIP_RECT;
public static final String CLASS_NAME = "ClipRect";
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Maker m = ClipRect::new;
read(m, buffer, operations);
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -50,6 +61,11 @@
apply(buffer, v1, v2, v3, v4);
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("Intersect the current clip with rectangle")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java
index 34e93f5..d86576d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java
@@ -29,12 +29,22 @@
import java.util.List;
/** Operation that defines a simple Color based on ID Mainly for colors in theming. */
-public class ColorConstant implements Operation {
+public class ColorConstant extends Operation {
private static final int OP_CODE = Operations.COLOR_CONSTANT;
private static final String CLASS_NAME = "ColorConstant";
+
+ /** the id of the color */
public int mColorId;
+
+ /** the color value (AARRGGBB) */
public int mColor;
+ /**
+ * Creat a color constant
+ *
+ * @param colorId id of color
+ * @param color AARRGGBB value
+ */
public ColorConstant(int colorId, int color) {
this.mColorId = colorId;
this.mColor = color;
@@ -56,6 +66,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -73,12 +88,23 @@
buffer.writeInt(color);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int colorId = buffer.readInt();
int color = buffer.readInt();
operations.add(new ColorConstant(colorId, color));
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("Define a Color")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java
index c947d11..66f128f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java
@@ -34,7 +34,7 @@
* Operation to Colors Color modes mMode = 0 two colors and a tween mMode = 1 color1 is a colorID.
* mMode = 2 color2 is a colorID. mMode = 3 color1 & color2 are ids mMode = 4 H S V mode
*/
-public class ColorExpression implements Operation, VariableSupport {
+public class ColorExpression extends Operation implements VariableSupport {
private static final int OP_CODE = Operations.COLOR_EXPRESSIONS;
private static final String CLASS_NAME = "ColorExpression";
public int mId;
@@ -204,6 +204,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -228,6 +233,12 @@
buffer.writeFloat(tween);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int id = buffer.readInt();
int mode = buffer.readInt();
@@ -238,6 +249,11 @@
operations.add(new ColorExpression(id, mode, color1, color2, tween));
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("A Color defined by an expression")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java
index b0ccd187..19c219b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java
@@ -30,7 +30,7 @@
import java.util.List;
-public class ComponentValue implements Operation, SerializableToString {
+public class ComponentValue extends Operation implements SerializableToString {
public static final int OP_CODE = Operations.COMPONENT_VALUE;
public static final String CLASS_NAME = "ComponentValue";
@@ -41,6 +41,11 @@
private int mComponentID = -1;
private int mValueId = -1;
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -78,6 +83,12 @@
// Nothing
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int type = buffer.readInt();
int componentId = buffer.readInt();
@@ -86,6 +97,11 @@
operations.add(op);
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("Encode a component-related value (eg its width, height etc.)")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java
index bfaf139..ac6271c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java
@@ -32,7 +32,7 @@
import java.util.Arrays;
import java.util.List;
-public class DataListFloat implements VariableSupport, ArrayAccess, Operation {
+public class DataListFloat extends Operation implements VariableSupport, ArrayAccess {
private static final int OP_CODE = Operations.FLOAT_LIST;
private static final String CLASS_NAME = "IdListData";
private final int mId;
@@ -79,6 +79,12 @@
}
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int id = buffer.readInt();
int len = buffer.readInt();
@@ -93,6 +99,11 @@
operations.add(data);
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("a list of Floats")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java
index 9b286b9..47cbff3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java
@@ -33,7 +33,7 @@
import java.util.Arrays;
import java.util.List;
-public class DataListIds implements VariableSupport, ArrayAccess, Operation {
+public class DataListIds extends Operation implements VariableSupport, ArrayAccess {
private static final int OP_CODE = Operations.ID_LIST;
private static final String CLASS_NAME = "IdListData";
private final int mId;
@@ -71,6 +71,12 @@
}
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int id = buffer.readInt();
int len = buffer.readInt();
@@ -85,6 +91,11 @@
operations.add(data);
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("a list of id's")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java
index 643afc8..e888074 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java
@@ -31,7 +31,7 @@
import java.util.List;
/** This is a map of strings to type & Id */
-public class DataMapIds implements Operation {
+public class DataMapIds extends Operation {
private static final int OP_CODE = Operations.ID_MAP;
private static final String CLASS_NAME = "DataMapIds";
int mId;
@@ -105,6 +105,12 @@
}
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int id = buffer.readInt();
int len = buffer.readInt();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapLookup.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapLookup.java
index eae532c..9af2343 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapLookup.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapLookup.java
@@ -31,7 +31,7 @@
import java.util.List;
/** This can lookup in a map given a string writing the results to an id. */
-public class DataMapLookup implements Operation {
+public class DataMapLookup extends Operation {
private static final int OP_CODE = Operations.DATA_MAP_LOOKUP;
private static final String CLASS_NAME = "DataMapLookup";
public int mId;
@@ -109,12 +109,17 @@
operations.add(new DataMapLookup(id, mapId, stringId));
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
- .description("A float and its associated id")
+ .description("Look up a value in a data map")
.field(INT, "id", "id of float")
.field(INT, "dataMapId", "32-bit float value")
- .field(INT, "value", "32-bit float value");
+ .field(INT, "stringId", "32-bit float value");
}
@Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java
index 629f786..3f95f02 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java
@@ -30,11 +30,22 @@
public static final int OP_CODE = Operations.DRAW_ARC;
private static final String CLASS_NAME = "DrawArc";
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Maker m = DrawArc::new;
read(m, buffer, operations);
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -79,6 +90,11 @@
apply(buffer, v1, v2, v3, v4, v5, v6);
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description(
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java
index 9c23c95..69f5cc5 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java
@@ -100,15 +100,21 @@
+ ";";
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int id = buffer.readInt();
float sLeft = buffer.readFloat();
float srcTop = buffer.readFloat();
float srcRight = buffer.readFloat();
float srcBottom = buffer.readFloat();
- int discriptionId = buffer.readInt();
+ int descriptionId = buffer.readInt();
- DrawBitmap op = new DrawBitmap(id, sLeft, srcTop, srcRight, srcBottom, discriptionId);
+ DrawBitmap op = new DrawBitmap(id, sLeft, srcTop, srcRight, srcBottom, descriptionId);
operations.add(op);
}
@@ -117,6 +123,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -138,6 +149,11 @@
buffer.writeInt(descriptionId);
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
.description("Draw a bitmap")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java
index da9fe24..66646d7 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java
@@ -111,6 +111,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -140,6 +145,12 @@
buffer.writeInt(cdId);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int imageId = buffer.readInt();
int sLeft = buffer.readInt();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java
index e9f81d5..1701486 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java
@@ -196,6 +196,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -232,6 +237,12 @@
buffer.writeInt(cdId);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int imageId = buffer.readInt();
@@ -265,6 +276,11 @@
operations.add(op);
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
.description("Draw a bitmap using integer coordinates")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java
index 11bd49a..e6aecdb 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java
@@ -30,11 +30,22 @@
private static final int OP_CODE = Operations.DRAW_CIRCLE;
private static final String CLASS_NAME = "DrawCircle";
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Maker m = DrawCircle::new;
read(m, buffer, operations);
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -44,6 +55,11 @@
return CLASS_NAME;
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Draw a Circle")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java
index 7310a9d..04f3264 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java
@@ -32,11 +32,22 @@
private static final int OP_CODE = Operations.DRAW_LINE;
private static final String CLASS_NAME = "DrawLine";
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Maker m = DrawLine::new;
read(m, buffer, operations);
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -46,6 +57,11 @@
return CLASS_NAME;
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Draw a line segment")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java
index aa5116e..0a50042 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java
@@ -30,11 +30,22 @@
private static final int OP_CODE = Operations.DRAW_OVAL;
private static final String CLASS_NAME = "DrawOval";
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Maker m = DrawOval::new;
read(m, buffer, operations);
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -44,6 +55,11 @@
return CLASS_NAME;
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Draw the specified oval")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java
index d35094b..41b8243 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java
@@ -50,6 +50,12 @@
return "DrawPath " + "[" + mId + "]" + ", " + mStart + ", " + mEnd;
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int id = buffer.readInt();
DrawPath op = new DrawPath(id);
@@ -61,6 +67,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return Operations.DRAW_PATH;
}
@@ -70,6 +81,11 @@
buffer.writeInt(id);
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
.description("Draw a bitmap using integer coordinates")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java
index db7633c..7e22550 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java
@@ -31,11 +31,22 @@
private static final int OP_CODE = Operations.DRAW_RECT;
private static final String CLASS_NAME = "DrawRect";
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Maker m = DrawRect::new;
read(m, buffer, operations);
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -45,6 +56,11 @@
return CLASS_NAME;
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Draw the specified rectangle")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java
index c306e2b..a41e46e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java
@@ -31,11 +31,22 @@
private static final int OP_CODE = Operations.DRAW_ROUND_RECT;
private static final String CLASS_NAME = "DrawRoundRect";
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Maker m = DrawRoundRect::new;
read(m, buffer, operations);
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -80,6 +91,11 @@
apply(buffer, v1, v2, v3, v4, v5, v6);
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Draw the specified round-rect")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawSector.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawSector.java
index 3b60df7..7616df0 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawSector.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawSector.java
@@ -30,11 +30,22 @@
public static final int OP_CODE = Operations.DRAW_SECTOR;
private static final String CLASS_NAME = "DrawSector";
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Maker m = DrawSector::new;
read(m, buffer, operations);
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -79,6 +90,11 @@
apply(buffer, v1, v2, v3, v4, v5, v6);
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description(
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java
index 9c587ab..2c5d790 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java
@@ -101,6 +101,12 @@
+ floatToString(mY, mOutY);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int text = buffer.readInt();
int start = buffer.readInt();
@@ -120,6 +126,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -158,6 +169,11 @@
buffer.writeBoolean(rtl);
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Draw Operations", id(), CLASS_NAME)
.description("Draw a run of text, all in a single direction")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java
index 8b70181..7de52b8 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java
@@ -111,6 +111,12 @@
return Float.toString(v);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int textID = buffer.readInt();
float x = buffer.readFloat();
@@ -129,6 +135,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -161,6 +172,11 @@
buffer.writeInt(flags);
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
.description("Draw text centered about an anchor point")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java
index e90122b..18d9fdf 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java
@@ -83,6 +83,12 @@
+ Utils.floatToString(mVOffset, mOutVOffset);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int textId = buffer.readInt();
int pathId = buffer.readInt();
@@ -97,6 +103,11 @@
return "DrawTextOnPath";
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return Operations.DRAW_TEXT_ON_PATH;
}
@@ -110,6 +121,11 @@
buffer.writeFloat(hOffset);
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
.description("Draw text along path object")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java
index 0aaaf42..b83e4c2 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java
@@ -92,6 +92,12 @@
+ floatToString(mStop, mOutStop);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int path1Id = buffer.readInt();
int path2Id = buffer.readInt();
@@ -107,6 +113,11 @@
return "DrawTweenPath";
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return Operations.DRAW_TWEEN_PATH;
}
@@ -126,6 +137,11 @@
buffer.writeFloat(stop);
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
.description("Draw text along path object")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java
index 17aaf3b..7dd435a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java
@@ -29,7 +29,7 @@
import java.util.List;
/** Operation to deal with Text data */
-public class FloatConstant implements com.android.internal.widget.remotecompose.core.Operation {
+public class FloatConstant extends Operation {
private static final int OP_CODE = Operations.DATA_FLOAT;
private static final String CLASS_NAME = "FloatConstant";
public int mTextId;
@@ -56,6 +56,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -73,6 +78,12 @@
buffer.writeFloat(value);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int textId = buffer.readInt();
@@ -80,6 +91,11 @@
operations.add(new FloatConstant(textId, value));
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("A float and its associated id")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java
index eef9746..3d92e12 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java
@@ -42,7 +42,7 @@
* like injecting the width of the component int draw rect As well as supporting generalized
* animation floats. The floats represent a RPN style calculator
*/
-public class FloatExpression implements Operation, VariableSupport {
+public class FloatExpression extends Operation implements VariableSupport {
private static final int OP_CODE = Operations.ANIMATED_FLOAT;
private static final String CLASS_NAME = "FloatExpression";
public int mId;
@@ -146,16 +146,19 @@
if (Float.isNaN(mLastChange)) {
mLastChange = t;
}
- if (mFloatAnimation != null) {
+ if (mFloatAnimation != null && !Float.isNaN(mLastCalculatedValue)) {
float f = mFloatAnimation.get(t - mLastChange);
context.loadFloat(mId, f);
} else if (mSpring != null) {
float f = mSpring.get(t - mLastChange);
context.loadFloat(mId, f);
} else {
- context.loadFloat(
- mId,
- mExp.eval(context.getCollectionsAccess(), mPreCalcValue, mPreCalcValue.length));
+ float v =
+ mExp.eval(context.getCollectionsAccess(), mPreCalcValue, mPreCalcValue.length);
+ if (mFloatAnimation != null) {
+ mFloatAnimation.setTargetValue(v);
+ }
+ context.loadFloat(mId, v);
}
}
@@ -207,6 +210,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -243,12 +251,18 @@
}
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int id = buffer.readInt();
int len = buffer.readInt();
int valueLen = len & 0xFFFF;
if (valueLen > MAX_EXPRESSION_SIZE) {
- throw new RuntimeException("Float expression to long");
+ throw new RuntimeException("Float expression too long");
}
int animLen = (len >> 16) & 0xFFFF;
float[] values = new float[valueLen];
@@ -268,6 +282,11 @@
operations.add(new FloatExpression(id, values, animation));
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("A Float expression")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java
index 009aa03..04e4346 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java
@@ -35,7 +35,7 @@
* <p>It encodes the version of the document (following semantic versioning) as well as the
* dimensions of the document in pixels.
*/
-public class Header implements RemoteComposeOperation {
+public class Header extends Operation implements RemoteComposeOperation {
private static final int OP_CODE = Operations.HEADER;
private static final String CLASS_NAME = "Header";
public static final int MAJOR_VERSION = 0;
@@ -120,6 +120,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -136,6 +141,12 @@
buffer.writeLong(capabilities);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int majorVersion = buffer.readInt();
int minorVersion = buffer.readInt();
@@ -157,6 +168,11 @@
operations.add(header);
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Protocol Operations", OP_CODE, CLASS_NAME)
.description(
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java
index c49f74d..67274af 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java
@@ -38,7 +38,7 @@
* like injecting the width of the component int draw rect As well as supporting generalized
* animation floats. The floats represent a RPN style calculator
*/
-public class IntegerExpression implements Operation, VariableSupport {
+public class IntegerExpression extends Operation implements VariableSupport {
private static final int OP_CODE = Operations.INTEGER_EXPRESSION;
private static final String CLASS_NAME = "IntegerExpression";
public int mId;
@@ -141,6 +141,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -163,6 +168,12 @@
}
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int id = buffer.readInt();
int mask = buffer.readInt();
@@ -178,6 +189,11 @@
operations.add(new IntegerExpression(id, mask, values));
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Expression that computes an integer")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java
index 4e7ee4d..aed597a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java
@@ -37,6 +37,12 @@
apply(buffer);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
MatrixRestore op = new MatrixRestore();
operations.add(op);
@@ -53,6 +59,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -61,6 +72,11 @@
buffer.start(OP_CODE);
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Restore the matrix and clip");
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java
index 438a2aa..fece143 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java
@@ -30,6 +30,12 @@
public static final int OP_CODE = Operations.MATRIX_ROTATE;
private static final String CLASS_NAME = "MatrixRotate";
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Maker m =
new Maker() {
@@ -42,6 +48,11 @@
read(m, buffer, operations);
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -51,6 +62,11 @@
return CLASS_NAME;
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("apply rotation to matrix")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java
index 09f54a5..7eb7b3f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java
@@ -41,6 +41,12 @@
return "MatrixSave;";
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
MatrixSave op = new MatrixSave();
operations.add(op);
@@ -51,6 +57,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -59,6 +70,11 @@
buffer.start(Operations.MATRIX_SAVE);
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Save the matrix and clip to a stack");
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java
index 6304584..49bdd1b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java
@@ -30,11 +30,22 @@
public static final int OP_CODE = Operations.MATRIX_SCALE;
public static final String CLASS_NAME = "MatrixScale";
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Maker m = MatrixScale::new;
read(m, buffer, operations);
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -49,9 +60,14 @@
apply(buffer, v1, v2, v3, v4);
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
- .description("Draw the specified Oval")
+ .description("Scale the following draw commands")
.field(DocumentedOperation.FLOAT, "scaleX", "The amount to scale in X")
.field(DocumentedOperation.FLOAT, "scaleY", "The amount to scale in Y")
.field(DocumentedOperation.FLOAT, "pivotX", "The x-coordinate for the pivot point")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java
index 675cf0d..54b6fd1 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java
@@ -31,11 +31,22 @@
public static final int OP_CODE = Operations.MATRIX_SKEW;
public static final String CLASS_NAME = "MatrixSkew";
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Maker m = MatrixSkew::new;
read(m, buffer, operations);
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -50,6 +61,11 @@
apply(buffer, v1, v2);
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Current matrix with the specified skew.")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java
index b0a7d35..b57d83b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java
@@ -30,11 +30,22 @@
public static final int OP_CODE = Operations.MATRIX_TRANSLATE;
public static final String CLASS_NAME = "MatrixTranslate";
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Maker m = MatrixTranslate::new;
read(m, buffer, operations);
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -49,6 +60,11 @@
apply(buffer, v1, v2);
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, "MatrixTranslate")
.description("Preconcat the current matrix with the specified translation")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java b/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java
index 5ca91af..3c82f2b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java
@@ -30,7 +30,7 @@
import java.util.List;
/** Operation to deal with Text data */
-public class NamedVariable implements Operation {
+public class NamedVariable extends Operation {
private static final int OP_CODE = Operations.NAMED_VARIABLE;
private static final String CLASS_NAME = "NamedVariable";
public final int mVarId;
@@ -69,6 +69,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -89,6 +94,12 @@
buffer.writeUTF8(text);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int varId = buffer.readInt();
int varType = buffer.readInt();
@@ -96,6 +107,11 @@
operations.add(new NamedVariable(varId, varType, text));
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Add a string name for an ID")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java
index b24df8a..3c0a842 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java
@@ -66,6 +66,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -75,6 +80,12 @@
paintBundle.writeBundle(buffer);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
PaintData data = new PaintData();
data.mPaintData.readBundle(buffer);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathAppend.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathAppend.java
new file mode 100644
index 0000000..2b00001
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathAppend.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class PathAppend extends PaintOperation implements VariableSupport {
+ private static final int OP_CODE = Operations.PATH_ADD;
+ private static final String CLASS_NAME = "PathAppend";
+ int mInstanceId;
+ float[] mFloatPath;
+ float[] mOutputPath;
+
+ PathAppend(int instanceId, float[] floatPath) {
+ mInstanceId = instanceId;
+ mFloatPath = floatPath;
+ mOutputPath = Arrays.copyOf(mFloatPath, mFloatPath.length);
+ }
+
+ @Override
+ public void updateVariables(@NonNull RemoteContext context) {
+ for (int i = 0; i < mFloatPath.length; i++) {
+ float v = mFloatPath[i];
+ if (Utils.isVariable(v)) {
+ mOutputPath[i] = Float.isNaN(v) ? context.getFloat(Utils.idFromNan(v)) : v;
+ } else {
+ mOutputPath[i] = v;
+ }
+ }
+ }
+
+ @Override
+ public void registerListening(@NonNull RemoteContext context) {
+ for (float v : mFloatPath) {
+ if (Float.isNaN(v)) {
+ context.listensTo(Utils.idFromNan(v), this);
+ }
+ }
+ }
+
+ @Override
+ public void write(@NonNull WireBuffer buffer) {
+ apply(buffer, mInstanceId, mOutputPath);
+ }
+
+ @NonNull
+ @Override
+ public String deepToString(String indent) {
+ return PathData.pathString(mFloatPath);
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "PathAppend[" + mInstanceId + "] += " + "\"" + pathString(mOutputPath) + "\"";
+ }
+
+ /**
+ * public float[] getFloatPath(PaintContext context) { float[] ret = mRetFloats; // Assume
+ * retFloats is declared elsewhere if (ret == null) { return mFloatPath; // Assume floatPath is
+ * declared elsewhere } float[] localRef = mRef; // Assume ref is of type Float[] if (localRef
+ * == null) { for (int i = 0; i < mFloatPath.length; i++) { ret[i] = mFloatPath[i]; } } else {
+ * for (int i = 0; i < mFloatPath.length; i++) { float lr = localRef[i]; if (Float.isNaN(lr)) {
+ * ret[i] = Utils.getActualValue(lr); } else { ret[i] = mFloatPath[i]; } } } return ret; }
+ */
+ public static final int MOVE = 10;
+
+ public static final int LINE = 11;
+ public static final int QUADRATIC = 12;
+ public static final int CONIC = 13;
+ public static final int CUBIC = 14;
+ public static final int CLOSE = 15;
+ public static final int DONE = 16;
+ public static final float MOVE_NAN = Utils.asNan(MOVE);
+ public static final float LINE_NAN = Utils.asNan(LINE);
+ public static final float QUADRATIC_NAN = Utils.asNan(QUADRATIC);
+ public static final float CONIC_NAN = Utils.asNan(CONIC);
+ public static final float CUBIC_NAN = Utils.asNan(CUBIC);
+ public static final float CLOSE_NAN = Utils.asNan(CLOSE);
+ public static final float DONE_NAN = Utils.asNan(DONE);
+
+ @NonNull
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
+ public static int id() {
+ return OP_CODE;
+ }
+
+ public static void apply(@NonNull WireBuffer buffer, int id, @NonNull float[] data) {
+ buffer.start(OP_CODE);
+ buffer.writeInt(id);
+ buffer.writeInt(data.length);
+ for (float datum : data) {
+ buffer.writeFloat(datum);
+ }
+ }
+
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
+ int id = buffer.readInt();
+ int len = buffer.readInt();
+ float[] data = new float[len];
+ for (int i = 0; i < data.length; i++) {
+ data[i] = buffer.readFloat();
+ }
+ operations.add(new PathAppend(id, data));
+ }
+
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
+ public static void documentation(@NonNull DocumentationBuilder doc) {
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
+ .description("Append to a Path")
+ .field(DocumentedOperation.INT, "id", "id string")
+ .field(INT, "length", "id string")
+ .field(FLOAT_ARRAY, "pathData", "length", "path encoded as floats");
+ }
+
+ @Override
+ public void paint(PaintContext context) {}
+
+ @Override
+ public void apply(@NonNull RemoteContext context) {
+ updateVariables(context);
+ float[] data = context.getPathData(mInstanceId);
+ float[] out = mOutputPath;
+ if (data != null) {
+ out = new float[data.length + mOutputPath.length];
+
+ for (int i = 0; i < data.length; i++) {
+ out[i] = data[i];
+ }
+ for (int i = 0; i < mOutputPath.length; i++) {
+ out[i + data.length] = mOutputPath[i];
+ }
+ } else {
+ System.out.println(">>>>>>>>>>> DATA IS NULL");
+ }
+ context.loadPathData(mInstanceId, out);
+ }
+
+ @NonNull
+ public static String pathString(@Nullable float[] path) {
+ if (path == null) {
+ return "null";
+ }
+ StringBuilder str = new StringBuilder();
+ for (int i = 0; i < path.length; i++) {
+ if (Float.isNaN(path[i])) {
+ int id = Utils.idFromNan(path[i]); // Assume idFromNan is defined elsewhere
+ if (id <= DONE) { // Assume DONE is a constant
+ switch (id) {
+ case MOVE:
+ str.append("M");
+ break;
+ case LINE:
+ str.append("L");
+ break;
+ case QUADRATIC:
+ str.append("Q");
+ break;
+ case CONIC:
+ str.append("R");
+ break;
+ case CUBIC:
+ str.append("C");
+ break;
+ case CLOSE:
+ str.append("Z");
+ break;
+ case DONE:
+ str.append(".");
+ break;
+ default:
+ str.append("[" + id + "]");
+ break;
+ }
+ } else {
+ str.append("(" + id + ")");
+ }
+ }
+ }
+ return str.toString();
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathCreate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathCreate.java
new file mode 100644
index 0000000..b62f36b
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathCreate.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class PathCreate extends PaintOperation implements VariableSupport {
+ private static final int OP_CODE = Operations.PATH_CREATE;
+ private static final String CLASS_NAME = "PathCreate";
+ int mInstanceId;
+ float[] mFloatPath;
+ float[] mOutputPath;
+
+ PathCreate(int instanceId, float startX, float startY) {
+ mInstanceId = instanceId;
+ mFloatPath = new float[] {PathData.MOVE_NAN, startX, startY};
+ mOutputPath = Arrays.copyOf(mFloatPath, mFloatPath.length);
+ }
+
+ @Override
+ public void updateVariables(@NonNull RemoteContext context) {
+ for (int i = 0; i < mFloatPath.length; i++) {
+ float v = mFloatPath[i];
+ if (Utils.isVariable(v)) {
+ mOutputPath[i] = Float.isNaN(v) ? context.getFloat(Utils.idFromNan(v)) : v;
+ } else {
+ mOutputPath[i] = v;
+ }
+ }
+ }
+
+ @Override
+ public void registerListening(@NonNull RemoteContext context) {
+ for (float v : mFloatPath) {
+ if (Float.isNaN(v)) {
+ context.listensTo(Utils.idFromNan(v), this);
+ }
+ }
+ }
+
+ @Override
+ public void write(@NonNull WireBuffer buffer) {
+ apply(buffer, mInstanceId, mFloatPath[1], mFloatPath[2]);
+ }
+
+ @NonNull
+ @Override
+ public String deepToString(String indent) {
+ return pathString(mFloatPath);
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "PathCreate[" + mInstanceId + "] = " + "\"" + deepToString(" ") + "\"";
+ }
+
+ public static final int MOVE = 10;
+ public static final int LINE = 11;
+ public static final int QUADRATIC = 12;
+ public static final int CONIC = 13;
+ public static final int CUBIC = 14;
+ public static final int CLOSE = 15;
+ public static final int DONE = 16;
+ public static final float MOVE_NAN = Utils.asNan(MOVE);
+ public static final float LINE_NAN = Utils.asNan(LINE);
+ public static final float QUADRATIC_NAN = Utils.asNan(QUADRATIC);
+ public static final float CONIC_NAN = Utils.asNan(CONIC);
+ public static final float CUBIC_NAN = Utils.asNan(CUBIC);
+ public static final float CLOSE_NAN = Utils.asNan(CLOSE);
+ public static final float DONE_NAN = Utils.asNan(DONE);
+
+ @NonNull
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
+ public static int id() {
+ return OP_CODE;
+ }
+
+ public static void apply(@NonNull WireBuffer buffer, int id, float startX, float startY) {
+ buffer.start(OP_CODE);
+ buffer.writeInt(id);
+ buffer.writeFloat(startX);
+ buffer.writeFloat(startY);
+ }
+
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
+
+ int id = buffer.readInt();
+ float startX = buffer.readFloat();
+ float startY = buffer.readFloat();
+ operations.add(new PathCreate(id, startX, startY));
+ }
+
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
+ public static void documentation(@NonNull DocumentationBuilder doc) {
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
+ .description("Encode a Path ")
+ .field(DocumentedOperation.INT, "id", "id of path")
+ .field(FLOAT, "startX", "initial start x")
+ .field(FLOAT, "startX", "initial start y");
+ }
+
+ @NonNull
+ public static String pathString(@Nullable float[] path) {
+ if (path == null) {
+ return "null";
+ }
+ StringBuilder str = new StringBuilder();
+ for (int i = 0; i < path.length; i++) {
+ if (i != 0) {
+ str.append(" ");
+ }
+ if (Float.isNaN(path[i])) {
+ int id = Utils.idFromNan(path[i]); // Assume idFromNan is defined elsewhere
+ if (id <= DONE) { // Assume DONE is a constant
+ switch (id) {
+ case MOVE:
+ str.append("M");
+ break;
+ case LINE:
+ str.append("L");
+ break;
+ case QUADRATIC:
+ str.append("Q");
+ break;
+ case CONIC:
+ str.append("R");
+ break;
+ case CUBIC:
+ str.append("C");
+ break;
+ case CLOSE:
+ str.append("Z");
+ break;
+ case DONE:
+ str.append(".");
+ break;
+ default:
+ str.append("[" + id + "]");
+ break;
+ }
+ } else {
+ str.append("(" + id + ")");
+ }
+ } else {
+ str.append(path[i]);
+ }
+ }
+ return str.toString();
+ }
+
+ @Override
+ public void paint(PaintContext context) {}
+
+ @Override
+ public void apply(@NonNull RemoteContext context) {
+ context.loadPathData(mInstanceId, mOutputPath);
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
index 509f362..4ec5436 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
@@ -32,7 +32,7 @@
import java.util.Arrays;
import java.util.List;
-public class PathData implements Operation, VariableSupport {
+public class PathData extends Operation implements VariableSupport {
private static final int OP_CODE = Operations.DATA_PATH;
private static final String CLASS_NAME = "PathData";
int mInstanceId;
@@ -112,6 +112,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -125,6 +130,12 @@
}
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int imageId = buffer.readInt();
int len = buffer.readInt();
@@ -135,6 +146,11 @@
operations.add(new PathData(imageId, data));
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Encode a Path ")
@@ -143,6 +159,12 @@
.field(FLOAT_ARRAY, "pathData", "length", "path encoded as floats");
}
+ /**
+ * Render a path as a string
+ *
+ * @param path path as a array of floats
+ * @return string describing the path
+ */
@NonNull
public static String pathString(@Nullable float[] path) {
if (path == null) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathTween.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathTween.java
new file mode 100644
index 0000000..a6fa680
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathTween.java
@@ -0,0 +1,153 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
+
+import android.annotation.NonNull;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+
+import java.util.List;
+
+/** Operation to deal with Path data */
+public class PathTween extends PaintOperation implements VariableSupport {
+ private static final int OP_CODE = Operations.PATH_TWEEN;
+ private static final String CLASS_NAME = "PathTween";
+ public int mOutId;
+ public int mPathId1;
+ public int mPathId2;
+ public float mTween;
+ public float mTweenOut;
+
+ public PathTween(int outId, int pathId1, int pathId2, float tween) {
+ this.mOutId = outId;
+ this.mPathId1 = pathId1;
+ this.mPathId2 = pathId2;
+ this.mTween = tween;
+ this.mTweenOut = mTween;
+ }
+
+ @Override
+ public void updateVariables(@NonNull RemoteContext context) {
+ mTweenOut = Float.isNaN(mTween) ? context.getFloat(Utils.idFromNan(mTween)) : mTween;
+ }
+
+ @Override
+ public void registerListening(@NonNull RemoteContext context) {
+ if (Float.isNaN(mTween)) {
+ context.listensTo(Utils.idFromNan(mTween), this);
+ }
+ }
+
+ @Override
+ public void write(@NonNull WireBuffer buffer) {
+ apply(buffer, mOutId, mPathId1, mPathId2, mTween);
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "PathTween["
+ + mOutId
+ + "] = ["
+ + mPathId1
+ + " ] + [ "
+ + mPathId2
+ + "], "
+ + floatToString(mTween, mTweenOut);
+ }
+
+ @NonNull
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
+ public static int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ *
+ * @param buffer buffer to write to
+ * @param outId id of the path
+ * @param pathId1 source path 1
+ * @param pathId2 source path 2
+ * @param tween interpolate between two paths
+ */
+ public static void apply(
+ @NonNull WireBuffer buffer, int outId, int pathId1, int pathId2, float tween) {
+ buffer.start(OP_CODE);
+ buffer.writeInt(outId);
+ buffer.writeInt(pathId1);
+ buffer.writeInt(pathId2);
+ buffer.writeFloat(tween);
+ }
+
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
+ int outId1 = buffer.readInt();
+ int pathId1 = buffer.readInt();
+ int pathId2 = buffer.readInt();
+ float tween = buffer.readFloat();
+
+ operations.add(new PathTween(outId1, pathId1, pathId2, tween));
+ }
+
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
+ public static void documentation(@NonNull DocumentationBuilder doc) {
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
+ .description("Merge two string into one")
+ .field(DocumentedOperation.INT, "pathId", "id of the path")
+ .field(INT, "srcPathId1", "id of the path")
+ .field(INT, "srcPathId1", "x Shift of the path");
+ }
+
+ @NonNull
+ @Override
+ public String deepToString(String indent) {
+ return indent + toString();
+ }
+
+ @Override
+ public void paint(PaintContext context) {
+ context.tweenPath(mOutId, mPathId1, mPathId2, mTweenOut);
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java
index 8494126..aaa7176 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java
@@ -33,7 +33,7 @@
* <p>It encodes the version of the document (following semantic versioning) as well as the
* dimensions of the document in pixels.
*/
-public class RootContentBehavior implements RemoteComposeOperation {
+public class RootContentBehavior extends Operation implements RemoteComposeOperation {
private static final int OP_CODE = Operations.ROOT_CONTENT_BEHAVIOR;
private static final String CLASS_NAME = "RootContentBehavior";
int mScroll = NONE;
@@ -205,6 +205,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -218,6 +223,12 @@
buffer.writeInt(mode);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int scroll = buffer.readInt();
int alignment = buffer.readInt();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java
index 109945f..e92daa3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java
@@ -28,7 +28,7 @@
import java.util.List;
/** Describe a content description for the document */
-public class RootContentDescription implements RemoteComposeOperation {
+public class RootContentDescription extends Operation implements RemoteComposeOperation {
private static final int OP_CODE = Operations.ROOT_CONTENT_DESCRIPTION;
private static final String CLASS_NAME = "RootContentDescription";
int mContentDescription;
@@ -69,6 +69,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -78,12 +83,23 @@
buffer.writeInt(contentDescription);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int contentDescription = buffer.readInt();
RootContentDescription header = new RootContentDescription(contentDescription);
operations.add(header);
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Protocol Operations", OP_CODE, CLASS_NAME)
.description("Content description of root")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java
index e967ff4..e2502fe 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java
@@ -41,7 +41,7 @@
* Operation to deal with bitmap data On getting an Image during a draw call the bitmap is
* compressed and saved in playback the image is decompressed
*/
-public class ShaderData implements Operation, VariableSupport {
+public class ShaderData extends Operation implements VariableSupport {
private static final int OP_CODE = Operations.DATA_SHADER;
private static final String CLASS_NAME = "ShaderData";
int mShaderTextId; // the actual text of a shader
@@ -203,6 +203,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -266,6 +271,12 @@
}
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int shaderID = buffer.readInt();
int shaderTextId = buffer.readInt();
@@ -318,6 +329,11 @@
operations.add(new ShaderData(shaderID, shaderTextId, floatMap, intMap, bitmapMap));
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Shader")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
index ade008e..3f679bf 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
@@ -31,7 +31,7 @@
import java.util.List;
/** Operation to deal with Text data */
-public class TextData implements Operation, SerializableToString {
+public class TextData extends Operation implements SerializableToString {
private static final int OP_CODE = Operations.DATA_TEXT;
private static final String CLASS_NAME = "TextData";
public final int mTextId;
@@ -59,6 +59,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -69,6 +74,12 @@
buffer.writeUTF8(text);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int textId = buffer.readInt();
@@ -76,6 +87,11 @@
operations.add(new TextData(textId, text));
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Encode a string ")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java
index 865ee81f..4d01e0c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java
@@ -36,7 +36,7 @@
* [command][textID][before,after][flags] before and after define number of digits before and after
* the decimal point
*/
-public class TextFromFloat implements Operation, VariableSupport {
+public class TextFromFloat extends Operation implements VariableSupport {
private static final int OP_CODE = Operations.TEXT_FROM_FLOAT;
private static final String CLASS_NAME = "TextFromFloat";
public int mTextId;
@@ -127,6 +127,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -155,6 +160,12 @@
buffer.writeInt(flags);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int textId = buffer.readInt();
float value = buffer.readFloat();
@@ -166,6 +177,11 @@
operations.add(new TextFromFloat(textId, value, pre, post, flags));
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("Draw text along path object")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLength.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLength.java
index 6ff687b..37ea567 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLength.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLength.java
@@ -28,7 +28,7 @@
import java.util.List;
/** Operation to measure the length of the text */
-public class TextLength implements Operation {
+public class TextLength extends Operation {
private static final int OP_CODE = Operations.TEXT_LENGTH;
private static final String CLASS_NAME = "TextLength";
public int mLengthId;
@@ -54,6 +54,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -71,12 +76,23 @@
buffer.writeInt(textId);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int lengthId = buffer.readInt();
int textId = buffer.readInt();
operations.add(new TextLength(lengthId, textId));
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("get the length of the text and store in float table")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java
index cc812a8..3ec6f01 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java
@@ -34,7 +34,7 @@
* [command][textID][before,after][flags] before and after define number of digits before and after
* the decimal point
*/
-public class TextLookup implements Operation, VariableSupport {
+public class TextLookup extends Operation implements VariableSupport {
private static final int OP_CODE = Operations.TEXT_LOOKUP;
private static final String CLASS_NAME = "TextFromFloat";
public int mTextId;
@@ -84,6 +84,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -103,6 +108,12 @@
buffer.writeFloat(index);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int textId = buffer.readInt();
int dataSetId = buffer.readInt();
@@ -110,6 +121,11 @@
operations.add(new TextLookup(textId, dataSetId, index));
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("Look an array and turn into a text object")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java
index 74be698..9c0ee53 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java
@@ -30,7 +30,7 @@
import java.util.List;
/** Operation convert int index of a list to text */
-public class TextLookupInt implements Operation, VariableSupport {
+public class TextLookupInt extends Operation implements VariableSupport {
private static final int OP_CODE = Operations.TEXT_LOOKUP_INT;
private static final String CLASS_NAME = "TextFromINT";
public int mTextId;
@@ -77,6 +77,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -96,6 +101,12 @@
buffer.writeInt(indexId);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int textId = buffer.readInt();
int dataSetId = buffer.readInt();
@@ -103,6 +114,11 @@
operations.add(new TextLookupInt(textId, dataSetId, indexId));
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("Look up an array and turn into a text object")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMeasure.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMeasure.java
index 6d48f67..d51b389 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMeasure.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMeasure.java
@@ -70,6 +70,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -89,6 +94,12 @@
buffer.writeInt(type);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int id = buffer.readInt();
int textId = buffer.readInt();
@@ -96,9 +107,14 @@
operations.add(new TextMeasure(id, textId, type));
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
- .description("A float and its associated id")
+ .description("Measure text")
.field(INT, "id", "id of float result of the measure")
.field(INT, "textId", "id of text")
.field(INT, "type", "type: measure 0=width,1=height");
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java
index ecd5baa..5b0c38f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java
@@ -29,7 +29,7 @@
import java.util.List;
/** Operation to deal with Text data */
-public class TextMerge implements Operation {
+public class TextMerge extends Operation {
private static final int OP_CODE = Operations.TEXT_MERGE;
private static final String CLASS_NAME = "TextMerge";
public int mTextId;
@@ -58,6 +58,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -77,6 +82,12 @@
buffer.writeInt(srcId2);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int textId = buffer.readInt();
int srcId1 = buffer.readInt();
@@ -85,6 +96,11 @@
operations.add(new TextMerge(textId, srcId1, srcId2));
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Merge two string into one")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java
index d265070..e329c38d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java
@@ -33,7 +33,7 @@
* "tag" the subsequent operations to a given theme. On playback, we can then filter operations
* depending on the chosen theme.
*/
-public class Theme implements RemoteComposeOperation {
+public class Theme extends Operation implements RemoteComposeOperation {
private static final int OP_CODE = Operations.THEME;
private static final String CLASS_NAME = "Theme";
int mTheme;
@@ -77,6 +77,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -86,11 +91,22 @@
buffer.writeInt(theme);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int theme = buffer.readInt();
operations.add(new Theme(theme));
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Protocol Operations", OP_CODE, CLASS_NAME)
.description("Set a theme")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java
index 1bb7b2a..e2e20bc 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java
@@ -42,12 +42,12 @@
* touch behaviours. Including animating to Notched, positions. and tweaking the dynamics of the
* animation.
*/
-public class TouchExpression implements Operation, VariableSupport, TouchListener {
+public class TouchExpression extends Operation implements VariableSupport, TouchListener {
private static final int OP_CODE = Operations.TOUCH_EXPRESSION;
private static final String CLASS_NAME = "TouchExpression";
private float mDefValue;
private float mOutDefValue;
- public int mId;
+ private int mId;
public float[] mSrcExp;
int mMode = 1; // 0 = delta, 1 = absolute
float mMax = 1;
@@ -56,11 +56,14 @@
float mOutMin = 1;
float mValue = 0;
boolean mUnmodified = true;
- public float[] mPreCalcValue;
+ private float[] mPreCalcValue;
private float mLastChange = Float.NaN;
private float mLastCalculatedValue = Float.NaN;
AnimatedFloatExpression mExp = new AnimatedFloatExpression();
+
+ /** The maximum number of floats in the expression */
public static final int MAX_EXPRESSION_SIZE = 32;
+
private VelocityEasing mEasyTouch = new VelocityEasing();
private boolean mEasingToStop = false;
private float mTouchUpTime = 0;
@@ -495,6 +498,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -505,6 +513,14 @@
* @param buffer The buffer to write to
* @param id the id of the resulting float
* @param value the float expression array
+ * @param min the minimum allowed value
+ * @param max the maximum allowed value
+ * @param velocityId the velocity id
+ * @param touchEffects the type touch effect
+ * @param exp the expression the maps touch drags to movement
+ * @param touchMode the touch mode e.g. notch modes
+ * @param touchSpec the spec of the touch modes
+ * @param easingSpec the spec of when the object comes to an easing
*/
public static void apply(
WireBuffer buffer,
@@ -549,7 +565,13 @@
}
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int id = buffer.readInt();
float startValue = buffer.readFloat();
float min = buffer.readFloat();
@@ -594,6 +616,11 @@
easingData));
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(DocumentationBuilder doc) {
doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("A Float expression")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
index baca3e0..de43b90 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
@@ -19,34 +19,60 @@
/** Utilities to be used across all core operations */
public class Utils {
+ /**
+ * Convert an integer id into a float
+ *
+ * @param v the integer id to convert
+ * @return the id as an float
+ */
public static float asNan(int v) {
return Float.intBitsToFloat(v | -0x800000);
}
+ /**
+ * convert a float into an integer id
+ *
+ * @param value the float id to convert
+ * @return the id as an integer
+ */
public static int idFromNan(float value) {
int b = Float.floatToRawIntBits(value);
return b & 0x3FFFFF;
}
+ /**
+ * convert a long into an ID
+ *
+ * @param v the long to convert
+ * @return the id still as a long
+ */
public static long idFromLong(long v) {
return v - 0x100000000L;
}
+ /**
+ * convert a float id and turn it into a string
+ *
+ * @param value float to convert
+ * @return string form of an id
+ */
@NonNull
public static String idStringFromNan(float value) {
int b = Float.floatToRawIntBits(value) & 0x3FFFFF;
return idString(b);
}
+ /**
+ * print an id as a string
+ *
+ * @param b the id
+ * @return the id as a string
+ */
@NonNull
public static String idString(int b) {
return (b > 0xFFFFF) ? "A_" + (b & 0xFFFFF) : "" + b;
}
- public static float getActualValue(float lr) {
- return 0;
- }
-
/**
* trim a string to n characters if needing to trim end in "..."
*
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ActionOperation.java
index 7f1d101..0f84059 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ActionOperation.java
@@ -18,12 +18,11 @@
import android.annotation.NonNull;
import com.android.internal.widget.remotecompose.core.CoreDocument;
-import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
/** Operations representing actions on the document */
-public interface ActionOperation extends Operation {
+public interface ActionOperation {
void serializeToString(int indent, @NonNull StringSerializer serializer);
void runAction(
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java
index aa8f7580..121b180 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java
@@ -46,6 +46,11 @@
return "CanvasContent";
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return Operations.LAYOUT_CANVAS_CONTENT;
}
@@ -61,6 +66,12 @@
buffer.writeInt(componentId);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int componentId = buffer.readInt();
operations.add(new CanvasContent(componentId, 0, 0, 0, 0, null, -1));
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java
index f44e20d..34c4249 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java
@@ -182,6 +182,12 @@
buffer.start(OP_CODE);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
operations.add(new ClickModifierOperation());
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
index fbfc796..faa259f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
@@ -26,7 +26,6 @@
import com.android.internal.widget.remotecompose.core.SerializableToString;
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
-import com.android.internal.widget.remotecompose.core.operations.BitmapData;
import com.android.internal.widget.remotecompose.core.operations.ComponentValue;
import com.android.internal.widget.remotecompose.core.operations.TextData;
import com.android.internal.widget.remotecompose.core.operations.layout.animation.AnimateMeasure;
@@ -468,11 +467,6 @@
value[0] += mX;
value[1] += mY;
if (mParent != null) {
- if (mParent instanceof LayoutComponent) {
- LayoutComponent parent = (LayoutComponent) mParent;
- value[0] += parent.getMarginLeft() + parent.getPaddingLeft();
- value[1] += parent.getMarginTop() + parent.getPaddingTop();
- }
mParent.getLocationInWindow(value);
}
}
@@ -658,12 +652,7 @@
debugBox(this, context);
}
for (Operation op : mList) {
- if (op instanceof BitmapData) {
- ((BitmapData) op).apply(context.getContext());
- }
- if (op instanceof PaintOperation) {
- ((PaintOperation) op).paint(context);
- }
+ op.apply(context.getContext());
}
context.restore();
context.getContext().mLastComponent = prev;
@@ -701,7 +690,7 @@
if (applyAnimationAsNeeded(context)) {
return;
}
- if (mVisibility == Visibility.GONE) {
+ if (mVisibility == Visibility.GONE || mVisibility == Visibility.INVISIBLE) {
return;
}
paintingComponent(context);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java
index 476b73c..396644c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java
@@ -25,7 +25,7 @@
import java.util.List;
-public class ComponentEnd implements Operation {
+public class ComponentEnd extends Operation {
@Override
public void write(@NonNull WireBuffer buffer) {
@@ -54,6 +54,11 @@
return "ComponentEnd";
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return Operations.COMPONENT_END;
}
@@ -66,6 +71,12 @@
return 1 + 4 + 4 + 4;
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
operations.add(new ComponentEnd());
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java
index def9f78..a85ae27 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java
@@ -28,7 +28,7 @@
import java.util.List;
-public class ComponentStart implements ComponentStartOperation {
+public class ComponentStart extends Operation implements ComponentStartOperation {
int mType = DEFAULT;
float mX;
@@ -162,6 +162,11 @@
return "ComponentStart";
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return Operations.COMPONENT_START;
}
@@ -179,6 +184,12 @@
return 1 + 4 + 4 + 4;
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int type = buffer.readInt();
int componentId = buffer.readInt();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStartOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStartOperation.java
index abf2356a..a257d46 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStartOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStartOperation.java
@@ -15,6 +15,4 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout;
-import com.android.internal.widget.remotecompose.core.Operation;
-
-public interface ComponentStartOperation extends Operation {}
+public interface ComponentStartOperation {}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
index 0041582..7b0e4a2 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
@@ -19,8 +19,10 @@
import android.annotation.Nullable;
import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.OperationInterface;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.operations.BitmapData;
+import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
import com.android.internal.widget.remotecompose.core.operations.MatrixRestore;
import com.android.internal.widget.remotecompose.core.operations.MatrixSave;
import com.android.internal.widget.remotecompose.core.operations.MatrixTranslate;
@@ -47,12 +49,6 @@
@Nullable protected ZIndexModifierOperation mZIndexModifier = null;
@Nullable protected GraphicsLayerModifierOperation mGraphicsLayerModifier = null;
- // Margins
- protected float mMarginLeft = 0f;
- protected float mMarginRight = 0f;
- protected float mMarginTop = 0f;
- protected float mMarginBottom = 0f;
-
protected float mPaddingLeft = 0f;
protected float mPaddingRight = 0f;
protected float mPaddingTop = 0f;
@@ -76,22 +72,6 @@
super(parent, componentId, animationId, x, y, width, height);
}
- public float getMarginLeft() {
- return mMarginLeft;
- }
-
- public float getMarginRight() {
- return mMarginRight;
- }
-
- public float getMarginTop() {
- return mMarginTop;
- }
-
- public float getMarginBottom() {
- return mMarginBottom;
- }
-
public float getPaddingLeft() {
return mPaddingLeft;
}
@@ -133,8 +113,7 @@
public void inflate() {
ArrayList<TextData> data = new ArrayList<>();
- ArrayList<TouchExpression> touchExpressions = new ArrayList<>();
- ArrayList<PaintData> paintData = new ArrayList<>();
+ ArrayList<Operation> supportedOperations = new ArrayList<>();
for (Operation op : mList) {
if (op instanceof LayoutComponentContent) {
@@ -179,10 +158,10 @@
mComponentModifiers.add((ModifierOperation) op);
} else if (op instanceof TextData) {
data.add((TextData) op);
- } else if (op instanceof TouchExpression) {
- touchExpressions.add((TouchExpression) op);
- } else if (op instanceof PaintData) {
- paintData.add((PaintData) op);
+ } else if (op instanceof TouchExpression
+ || (op instanceof PaintData)
+ || (op instanceof FloatExpression)) {
+ supportedOperations.add(op);
} else {
// nothing
}
@@ -190,8 +169,7 @@
mList.clear();
mList.addAll(data);
- mList.addAll(touchExpressions);
- mList.addAll(paintData);
+ mList.addAll(supportedOperations);
mList.add(mComponentModifiers);
for (Component c : mChildrenComponents) {
c.mParent = this;
@@ -203,10 +181,6 @@
mX = 0f;
mY = 0f;
- mMarginLeft = 0f;
- mMarginTop = 0f;
- mMarginRight = 0f;
- mMarginBottom = 0f;
mPaddingLeft = 0f;
mPaddingTop = 0f;
mPaddingRight = 0f;
@@ -214,7 +188,7 @@
boolean applyHorizontalMargin = true;
boolean applyVerticalMargin = true;
- for (Operation op : mComponentModifiers.getList()) {
+ for (OperationInterface op : mComponentModifiers.getList()) {
if (op instanceof PaddingModifierOperation) {
// We are accumulating padding modifiers to compute the margin
// until we hit a dimension; the computed padding for the
@@ -223,31 +197,17 @@
float right = ((PaddingModifierOperation) op).getRight();
float top = ((PaddingModifierOperation) op).getTop();
float bottom = ((PaddingModifierOperation) op).getBottom();
- if (applyHorizontalMargin) {
- mMarginLeft += left;
- mMarginRight += right;
- }
- if (applyVerticalMargin) {
- mMarginTop += top;
- mMarginBottom += bottom;
- }
mPaddingLeft += left;
mPaddingTop += top;
mPaddingRight += right;
mPaddingBottom += bottom;
- }
- if (op instanceof WidthModifierOperation && mWidthModifier == null) {
+ } else if (op instanceof WidthModifierOperation && mWidthModifier == null) {
mWidthModifier = (WidthModifierOperation) op;
- applyHorizontalMargin = false;
- }
- if (op instanceof HeightModifierOperation && mHeightModifier == null) {
+ } else if (op instanceof HeightModifierOperation && mHeightModifier == null) {
mHeightModifier = (HeightModifierOperation) op;
- applyVerticalMargin = false;
- }
- if (op instanceof ZIndexModifierOperation) {
+ } else if (op instanceof ZIndexModifierOperation) {
mZIndexModifier = (ZIndexModifierOperation) op;
- }
- if (op instanceof GraphicsLayerModifierOperation) {
+ } else if (op instanceof GraphicsLayerModifierOperation) {
mGraphicsLayerModifier = (GraphicsLayerModifierOperation) op;
}
}
@@ -339,7 +299,7 @@
float s = 0f;
float e = 0f;
float w = 0f;
- for (Operation c : mComponentModifiers.getList()) {
+ for (OperationInterface c : mComponentModifiers.getList()) {
if (c instanceof WidthModifierOperation) {
WidthModifierOperation o = (WidthModifierOperation) c;
if (o.getType() == DimensionModifierOperation.Type.EXACT
@@ -366,7 +326,7 @@
public float computeModifierDefinedPaddingWidth(@NonNull float[] padding) {
float s = 0f;
float e = 0f;
- for (Operation c : mComponentModifiers.getList()) {
+ for (OperationInterface c : mComponentModifiers.getList()) {
if (c instanceof PaddingModifierOperation) {
PaddingModifierOperation pop = (PaddingModifierOperation) c;
s += pop.getLeft();
@@ -383,7 +343,7 @@
float t = 0f;
float b = 0f;
float h = 0f;
- for (Operation c : mComponentModifiers.getList()) {
+ for (OperationInterface c : mComponentModifiers.getList()) {
if (c instanceof HeightModifierOperation) {
HeightModifierOperation o = (HeightModifierOperation) c;
if (o.getType() == DimensionModifierOperation.Type.EXACT
@@ -410,7 +370,7 @@
public float computeModifierDefinedPaddingHeight(@NonNull float[] padding) {
float t = 0f;
float b = 0f;
- for (Operation c : mComponentModifiers.getList()) {
+ for (OperationInterface c : mComponentModifiers.getList()) {
if (c instanceof PaddingModifierOperation) {
PaddingModifierOperation pop = (PaddingModifierOperation) c;
t += pop.getTop();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java
index 7eea885..20e4688 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java
@@ -46,6 +46,11 @@
return "LayoutContent";
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return Operations.LAYOUT_CONTENT;
}
@@ -61,6 +66,12 @@
buffer.writeInt(componentId);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int componentId = buffer.readInt();
operations.add(new LayoutComponentContent(componentId, 0, 0, 0, 0, null, -1));
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java
index 71de285..d88f711 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java
@@ -25,7 +25,7 @@
import java.util.List;
-public class LoopEnd implements Operation {
+public class LoopEnd extends Operation {
@Override
public void write(@NonNull WireBuffer buffer) {
@@ -54,6 +54,11 @@
return "LoopEnd";
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return Operations.LOOP_END;
}
@@ -62,6 +67,12 @@
buffer.start(id());
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
operations.add(new LoopEnd());
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java
index d88382d..83a2f0e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java
@@ -21,31 +21,57 @@
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+import com.android.internal.widget.remotecompose.core.operations.Utils;
import java.util.ArrayList;
import java.util.List;
/** Represents a loop of operations */
-public class LoopOperation extends PaintOperation {
+public class LoopOperation extends PaintOperation implements VariableSupport {
private static final int OP_CODE = Operations.LOOP_START;
@NonNull public ArrayList<Operation> mList = new ArrayList<>();
int mIndexVariableId;
- float mUntil = 12;
- float mFrom = 0;
- float mStep = 1;
+ float mUntil;
+ float mFrom;
+ float mStep;
+ float mUntilOut;
+ float mFromOut;
+ float mStepOut;
public LoopOperation(int count, int indexId) {
mUntil = count;
mIndexVariableId = indexId;
}
- public LoopOperation(float count, float from, float step, int indexId) {
- mUntil = count;
+ @Override
+ public void registerListening(RemoteContext context) {
+ if (Float.isNaN(mUntil)) {
+ context.listensTo(Utils.idFromNan(mUntil), this);
+ }
+ if (Float.isNaN(mFrom)) {
+ context.listensTo(Utils.idFromNan(mFrom), this);
+ }
+ if (Float.isNaN(mStep)) {
+ context.listensTo(Utils.idFromNan(mStep), this);
+ }
+ }
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+ mUntilOut = Float.isNaN(mUntil) ? context.getFloat(Utils.idFromNan(mUntil)) : mUntil;
+ mFromOut = Float.isNaN(mFrom) ? context.getFloat(Utils.idFromNan(mFrom)) : mFrom;
+ mStepOut = Float.isNaN(mStep) ? context.getFloat(Utils.idFromNan(mStep)) : mStep;
+ }
+
+ public LoopOperation(int indexId, float from, float step, float until) {
+ mUntil = until;
mFrom = from;
mStep = step;
mIndexVariableId = indexId;
@@ -58,13 +84,19 @@
@Override
public void write(@NonNull WireBuffer buffer) {
- apply(buffer, mUntil, mFrom, mStep, mIndexVariableId);
+ apply(buffer, mIndexVariableId, mFrom, mStep, mUntil);
}
@NonNull
@Override
public String toString() {
- return "LoopOperation";
+ StringBuilder builder = new StringBuilder("LoopOperation\n");
+ for (Operation operation : mList) {
+ builder.append(" ");
+ builder.append(operation);
+ builder.append("\n");
+ }
+ return builder.toString();
}
@NonNull
@@ -76,13 +108,13 @@
@Override
public void paint(@NonNull PaintContext context) {
if (mIndexVariableId == 0) {
- for (float i = mFrom; i < mUntil; i += mStep) {
+ for (float i = mFromOut; i < mUntilOut; i += mStepOut) {
for (Operation op : mList) {
op.apply(context.getContext());
}
}
} else {
- for (float i = mFrom; i < mUntil; i += mStep) {
+ for (float i = mFromOut; i < mUntilOut; i += mStepOut) {
context.getContext().loadFloat(mIndexVariableId, i);
for (Operation op : mList) {
if (op instanceof VariableSupport) {
@@ -100,24 +132,34 @@
}
public static void apply(
- @NonNull WireBuffer buffer, float count, float from, float step, int indexId) {
+ @NonNull WireBuffer buffer, int indexId, float from, float step, float until) {
buffer.start(OP_CODE);
- buffer.writeFloat(count);
+ buffer.writeInt(indexId);
buffer.writeFloat(from);
buffer.writeFloat(step);
- buffer.writeInt(indexId);
+ buffer.writeFloat(until);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
- float count = buffer.readFloat();
+ int indexId = buffer.readInt();
float from = buffer.readFloat();
float step = buffer.readFloat();
- int indexId = buffer.readInt();
- operations.add(new LoopOperation(count, from, step, indexId));
+ float until = buffer.readFloat();
+ operations.add(new LoopOperation(indexId, from, step, until));
}
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Operations", OP_CODE, name())
- .description("Loop. This operation execute" + " a list of action in a loop");
+ .description("Loop. This operation execute" + " a list of action in a loop")
+ .field(DocumentedOperation.INT, "id", "if not 0 write value")
+ .field(DocumentedOperation.FLOAT, "from", "values starts at")
+ .field(DocumentedOperation.FLOAT, "step", "value step")
+ .field(DocumentedOperation.FLOAT, "until", "stops less than or equal");
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/OperationsListEnd.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/OperationsListEnd.java
index ca79003..99b7e68 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/OperationsListEnd.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/OperationsListEnd.java
@@ -25,7 +25,7 @@
import java.util.List;
-public class OperationsListEnd implements Operation {
+public class OperationsListEnd extends Operation {
@Override
public void write(@NonNull WireBuffer buffer) {
@@ -54,6 +54,11 @@
return "ListEnd";
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return Operations.OPERATIONS_LIST_END;
}
@@ -62,6 +67,12 @@
buffer.start(id());
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
operations.add(new OperationsListEnd());
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java
index 85c7153..fd16287 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java
@@ -197,6 +197,11 @@
return "RootLayout";
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return Operations.LAYOUT_ROOT;
}
@@ -206,6 +211,12 @@
buffer.writeInt(componentId);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int componentId = buffer.readInt();
operations.add(new RootLayoutComponent(componentId, 0, 0, 0, 0, null, -1));
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchCancelModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchCancelModifierOperation.java
index 0316f96..3185bb5 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchCancelModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchCancelModifierOperation.java
@@ -84,6 +84,12 @@
buffer.start(OP_CODE);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(WireBuffer buffer, List<Operation> operations) {
operations.add(new TouchCancelModifierOperation());
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java
index 6fb7059..b230b09 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java
@@ -29,7 +29,7 @@
import java.util.List;
/** Basic component animation spec */
-public class AnimationSpec implements Operation {
+public class AnimationSpec extends Operation {
int mAnimationId = -1;
int mMotionDuration = 300;
int mMotionEasingType = GeneralEasing.CUBIC_STANDARD;
@@ -142,6 +142,11 @@
return "AnimationSpec";
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return Operations.ANIMATION_SPEC;
}
@@ -193,6 +198,12 @@
buffer.writeInt(animationToInt(exitAnimation));
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int animationId = buffer.readInt();
int motionDuration = buffer.readInt();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java
index 47a9421..01cd7cc 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java
@@ -108,6 +108,8 @@
@NonNull PaintContext context,
float maxWidth,
float maxHeight,
+ boolean horizontalWrap,
+ boolean verticalWrap,
@NonNull MeasurePass measure,
@NonNull Size size) {
for (Component c : mChildrenComponents) {
@@ -175,6 +177,11 @@
return "BoxLayout";
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return Operations.LAYOUT_BOX;
}
@@ -192,6 +199,12 @@
buffer.writeInt(verticalPositioning);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int componentId = buffer.readInt();
int animationId = buffer.readInt();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java
index 476b1a66..665db26 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java
@@ -77,6 +77,11 @@
return "CanvasLayout";
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return Operations.LAYOUT_CANVAS;
}
@@ -87,6 +92,12 @@
buffer.writeInt(animationId);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int componentId = buffer.readInt();
int animationId = buffer.readInt();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
index 68e18c6..5b9ee0f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
@@ -125,17 +125,21 @@
@NonNull PaintContext context,
float maxWidth,
float maxHeight,
+ boolean horizontalWrap,
+ boolean verticalWrap,
@NonNull MeasurePass measure,
@NonNull Size size) {
DebugLog.s(() -> "COMPUTE WRAP SIZE in " + this + " (" + mComponentId + ")");
int visibleChildrens = 0;
+ float currentMaxHeight = maxHeight;
for (Component c : mChildrenComponents) {
- c.measure(context, 0f, maxWidth, 0f, maxHeight, measure);
+ c.measure(context, 0f, maxWidth, 0f, currentMaxHeight, measure);
ComponentMeasure m = measure.get(c);
if (m.getVisibility() != Visibility.GONE) {
size.setWidth(Math.max(size.getWidth(), m.getW()));
size.setHeight(size.getHeight() + m.getH());
visibleChildrens++;
+ currentMaxHeight -= m.getH();
}
}
if (!mChildrenComponents.isEmpty()) {
@@ -342,6 +346,11 @@
return "ColumnLayout";
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return Operations.LAYOUT_COLUMN;
}
@@ -361,6 +370,12 @@
buffer.writeFloat(spacedBy);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int componentId = buffer.readInt();
int animationId = buffer.readInt();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
index 3b5aaf3..6a15b7f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
@@ -53,6 +53,8 @@
@NonNull PaintContext context,
float maxWidth,
float maxHeight,
+ boolean horizontalWrap,
+ boolean verticalWrap,
@NonNull MeasurePass measure,
@NonNull Size size) {
// nothing here
@@ -129,42 +131,67 @@
float maxHeight,
@NonNull MeasurePass measure) {
boolean hasWrap = true;
- float measuredWidth =
- Math.min(maxWidth, computeModifierDefinedWidth() - mMarginLeft - mMarginRight);
- float measuredHeight =
- Math.min(maxHeight, computeModifierDefinedHeight() - mMarginTop - mMarginBottom);
- float insetMaxWidth = maxWidth - mMarginLeft - mMarginRight;
- float insetMaxHeight = maxHeight - mMarginTop - mMarginBottom;
+
+ float measuredWidth = Math.min(maxWidth, computeModifierDefinedWidth());
+ float measuredHeight = Math.min(maxHeight, computeModifierDefinedHeight());
+ float insetMaxWidth = maxWidth - mPaddingLeft - mPaddingRight;
+ float insetMaxHeight = maxHeight - mPaddingTop - mPaddingBottom;
+
if (mWidthModifier.isIntrinsicMin()) {
maxWidth = intrinsicWidth();
}
if (mHeightModifier.isIntrinsicMin()) {
maxHeight = intrinsicHeight();
}
- if (mWidthModifier.isWrap() || mHeightModifier.isWrap()) { // TODO: potential npe -- bbade@
+
+ boolean hasHorizontalWrap = mWidthModifier.isWrap();
+ boolean hasVerticalWrap = mHeightModifier.isWrap();
+ if (hasHorizontalWrap || hasVerticalWrap) { // TODO: potential npe -- bbade@
mCachedWrapSize.setWidth(0f);
mCachedWrapSize.setHeight(0f);
- computeWrapSize(context, maxWidth, maxHeight, measure, mCachedWrapSize);
+ float wrapMaxWidth = insetMaxWidth;
+ float wrapMaxHeight = insetMaxHeight;
+ if (hasHorizontalWrap) {
+ wrapMaxWidth = insetMaxWidth - mPaddingLeft - mPaddingRight;
+ }
+ if (hasVerticalWrap) {
+ wrapMaxHeight = insetMaxHeight - mPaddingTop - mPaddingBottom;
+ }
+ computeWrapSize(
+ context,
+ wrapMaxWidth,
+ wrapMaxHeight,
+ mWidthModifier.isWrap(),
+ mHeightModifier.isWrap(),
+ measure,
+ mCachedWrapSize);
measuredWidth = mCachedWrapSize.getWidth();
+ if (hasHorizontalWrap) {
+ measuredWidth += mPaddingLeft + mPaddingRight;
+ }
measuredHeight = mCachedWrapSize.getHeight();
+ if (hasVerticalWrap) {
+ measuredHeight += mPaddingTop + mPaddingBottom;
+ }
} else {
hasWrap = false;
}
+
if (isInHorizontalFill()) {
- measuredWidth = insetMaxWidth;
+ measuredWidth = maxWidth;
} else if (mWidthModifier.hasWeight()) {
measuredWidth = Math.max(measuredWidth, computeModifierDefinedWidth());
} else {
measuredWidth = Math.max(measuredWidth, minWidth);
- measuredWidth = Math.min(measuredWidth, insetMaxWidth);
+ measuredWidth = Math.min(measuredWidth, maxWidth);
}
if (isInVerticalFill()) { // todo: potential npe -- bbade@
- measuredHeight = insetMaxHeight;
+ measuredHeight = maxHeight;
} else if (mHeightModifier.hasWeight()) {
measuredHeight = Math.max(measuredHeight, computeModifierDefinedHeight());
} else {
measuredHeight = Math.max(measuredHeight, minHeight);
- measuredHeight = Math.min(measuredHeight, insetMaxHeight);
+ measuredHeight = Math.min(measuredHeight, maxHeight);
}
if (minWidth == maxWidth) {
measuredWidth = maxWidth;
@@ -172,20 +199,27 @@
if (minHeight == maxHeight) {
measuredHeight = maxHeight;
}
- measuredWidth = Math.min(measuredWidth, insetMaxWidth);
- measuredHeight = Math.min(measuredHeight, insetMaxHeight);
+
if (!hasWrap) {
if (hasHorizontalScroll()) {
mCachedWrapSize.setWidth(0f);
mCachedWrapSize.setHeight(0f);
- computeWrapSize(context, Float.MAX_VALUE, maxHeight, measure, mCachedWrapSize);
+ computeWrapSize(
+ context,
+ Float.MAX_VALUE,
+ maxHeight,
+ false,
+ false,
+ measure,
+ mCachedWrapSize);
float w = mCachedWrapSize.getWidth();
computeSize(context, 0f, w, 0, measuredHeight, measure);
mComponentModifiers.setHorizontalScrollDimension(measuredWidth, w);
} else if (hasVerticalScroll()) {
mCachedWrapSize.setWidth(0f);
mCachedWrapSize.setHeight(0f);
- computeWrapSize(context, maxWidth, Float.MAX_VALUE, measure, mCachedWrapSize);
+ computeWrapSize(
+ context, maxWidth, Float.MAX_VALUE, false, false, measure, mCachedWrapSize);
float h = mCachedWrapSize.getHeight();
computeSize(context, 0f, measuredWidth, 0, h, measure);
mComponentModifiers.setVerticalScrollDimension(measuredHeight, h);
@@ -202,9 +236,6 @@
cm.setH(measuredHeight);
}
- measuredWidth += mMarginLeft + mMarginRight;
- measuredHeight += mMarginTop + mMarginBottom;
-
ComponentMeasure m = measure.get(this);
m.setW(measuredWidth);
m.setH(measuredHeight);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
index 0ce634f..0ec820b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
@@ -123,21 +123,25 @@
@NonNull PaintContext context,
float maxWidth,
float maxHeight,
+ boolean horizontalWrap,
+ boolean verticalWrap,
@NonNull MeasurePass measure,
@NonNull Size size) {
DebugLog.s(() -> "COMPUTE WRAP SIZE in " + this + " (" + mComponentId + ")");
- // int visibleChildrens = 0;
+ int visibleChildrens = 0;
+ float currentMaxWidth = maxWidth;
for (Component c : mChildrenComponents) {
- c.measure(context, 0f, maxWidth, 0f, maxHeight, measure);
+ c.measure(context, 0f, currentMaxWidth, 0f, maxHeight, measure);
ComponentMeasure m = measure.get(c);
if (m.getVisibility() != Visibility.GONE) {
size.setWidth(size.getWidth() + m.getW());
size.setHeight(Math.max(size.getHeight(), m.getH()));
- // visibleChildrens++;
+ visibleChildrens++;
+ currentMaxWidth -= m.getW();
}
}
if (!mChildrenComponents.isEmpty()) {
- size.setWidth(size.getWidth() + (mSpacedBy * (mChildrenComponents.size() - 1)));
+ size.setWidth(size.getWidth() + (mSpacedBy * (visibleChildrens - 1)));
}
DebugLog.e();
}
@@ -345,6 +349,11 @@
return "RowLayout";
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return Operations.LAYOUT_ROW;
}
@@ -364,6 +373,12 @@
buffer.writeFloat(spacedBy);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int componentId = buffer.readInt();
int animationId = buffer.readInt();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java
index 73a104b..61a3ec9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java
@@ -159,10 +159,13 @@
@NonNull PaintContext context,
float maxWidth,
float maxHeight,
+ boolean horizontalWrap,
+ boolean verticalWrap,
@NonNull MeasurePass measure,
@NonNull Size size) {
LayoutManager layout = getLayout(currentLayoutIndex);
- layout.computeWrapSize(context, maxWidth, maxHeight, measure, size);
+ layout.computeWrapSize(
+ context, maxWidth, maxHeight, horizontalWrap, verticalWrap, measure, size);
}
@Override
@@ -442,11 +445,7 @@
int id = c.getPaintId();
for (int i = 0; i < idIndex; i++) {
if (cacheListElementsId[i] == id) {
- context.translate(
- previousLayout.getMarginLeft(), previousLayout.getMarginTop());
c.paint(context);
- context.translate(
- -currentLayout.getMarginLeft(), -currentLayout.getMarginTop());
break;
}
}
@@ -472,16 +471,10 @@
// and fade in the new one
Component previousComponent = stateComponents[previousLayoutIndex];
if (previousComponent != null && component != previousComponent) {
- context.translate(
- currentLayout.getMarginLeft(), currentLayout.getMarginTop());
previousComponent.paint(context);
- context.translate(
- -currentLayout.getMarginLeft(), -currentLayout.getMarginTop());
}
}
- context.translate(currentLayout.getMarginLeft(), currentLayout.getMarginTop());
component.paint(context);
- context.translate(-currentLayout.getMarginLeft(), -currentLayout.getMarginTop());
} else if (op instanceof PaintOperation) {
((PaintOperation) op).paint(context);
}
@@ -563,6 +556,12 @@
buffer.writeInt(indexId);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int componentId = buffer.readInt();
int animationId = buffer.readInt();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
index a527e5a..8e7f538 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
@@ -52,8 +52,8 @@
private int mType = -1;
private float mTextX;
private float mTextY;
- private float mTextW;
- private float mTextH;
+ private float mTextW = -1;
+ private float mTextH = -1;
@Nullable private String mCachedString = "";
@@ -66,7 +66,11 @@
@Override
public void updateVariables(@NonNull RemoteContext context) {
- mCachedString = context.getText(mTextId);
+ String cachedString = context.getText(mTextId);
+ if (cachedString != null && cachedString.equalsIgnoreCase(mCachedString)) {
+ return;
+ }
+ mCachedString = cachedString;
if (mType == -1) {
if (mFontFamilyId != -1) {
String fontFamily = context.getText(mFontFamilyId);
@@ -86,8 +90,9 @@
mType = 0;
}
}
- mNeedsMeasure = true;
- needsRepaint();
+ mTextW = -1;
+ mTextH = -1;
+ invalidateMeasure();
}
public TextLayout(
@@ -168,7 +173,14 @@
return;
}
int length = mCachedString.length();
- context.drawTextRun(mTextId, 0, length, 0, 0, mTextX, mTextY, false);
+ if (mTextW > mWidth) {
+ context.save();
+ context.translate(getScrollX(), getScrollY());
+ context.drawTextRun(mTextId, 0, length, 0, 0, mTextX, mTextY, false);
+ context.restore();
+ } else {
+ context.drawTextRun(mTextId, 0, length, 0, 0, mTextX, mTextY, false);
+ }
if (DEBUG) {
mPaint.setStyle(PaintBundle.STYLE_FILL_AND_STROKE);
mPaint.setColor(1f, 1F, 1F, 1F);
@@ -246,6 +258,8 @@
@NonNull PaintContext context,
float maxWidth,
float maxHeight,
+ boolean horizontalWrap,
+ boolean verticalWrap,
@NonNull MeasurePass measure,
@NonNull Size size) {
context.savePaint();
@@ -262,9 +276,9 @@
context.restorePaint();
float w = bounds[2] - bounds[0];
float h = bounds[3] - bounds[1];
- size.setWidth(w);
+ size.setWidth(Math.min(maxWidth, w));
mTextX = -bounds[0];
- size.setHeight(h);
+ size.setHeight(Math.min(maxHeight, h));
mTextY = -bounds[1];
mTextW = w;
mTextH = h;
@@ -285,6 +299,11 @@
return "TextLayout";
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return Operations.LAYOUT_TEXT;
}
@@ -312,6 +331,12 @@
buffer.writeInt(textAlign);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int componentId = buffer.readInt();
int animationId = buffer.readInt();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java
index 71d2ba6..5df16c5 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java
@@ -114,6 +114,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -142,6 +147,12 @@
buffer.writeInt(shapeType);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
float x = buffer.readFloat();
float y = buffer.readFloat();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java
index 0707cd6..bfadd2f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java
@@ -160,6 +160,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -192,6 +197,12 @@
buffer.writeInt(shapeType);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
float x = buffer.readFloat();
float y = buffer.readFloat();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java
index e05b027..d0af872 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java
@@ -60,6 +60,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -68,6 +73,12 @@
buffer.start(OP_CODE);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
operations.add(new ClipRectModifierOperation());
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java
index 471db0b..1e6ccfc 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java
@@ -34,7 +34,7 @@
import java.util.List;
/** Allows setting visibility on a component */
-public class ComponentVisibilityOperation
+public class ComponentVisibilityOperation extends Operation
implements ModifierOperation, VariableSupport, DecoratorComponent {
private static final int OP_CODE = Operations.MODIFIER_VISIBILITY;
@@ -79,6 +79,12 @@
buffer.writeInt(valueId);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int valueId = buffer.readInt();
operations.add(new ComponentVisibilityOperation(valueId));
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java
index b9324f0..b11deae 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java
@@ -17,13 +17,15 @@
import android.annotation.NonNull;
+import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.operations.Utils;
import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
/** Base class for dimension modifiers */
-public abstract class DimensionModifierOperation implements ModifierOperation, VariableSupport {
+public abstract class DimensionModifierOperation extends Operation
+ implements ModifierOperation, VariableSupport {
public enum Type {
EXACT,
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/GraphicsLayerModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/GraphicsLayerModifierOperation.java
index 571e554..4252309 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/GraphicsLayerModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/GraphicsLayerModifierOperation.java
@@ -206,6 +206,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java
index 7bb4a75..692b526 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java
@@ -37,6 +37,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -47,6 +52,12 @@
buffer.writeFloat(value);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Type type = Type.fromInt(buffer.readInt());
float value = buffer.readFloat();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java
index d239bc8..333e281 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java
@@ -32,7 +32,7 @@
import java.util.List;
/** Capture a host action information. This can be triggered on eg. a click. */
-public class HostActionOperation implements ActionOperation {
+public class HostActionOperation extends Operation implements ActionOperation {
private static final int OP_CODE = Operations.HOST_ACTION;
int mActionId = -1;
@@ -88,6 +88,12 @@
buffer.writeInt(actionId);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int actionId = buffer.readInt();
operations.add(new HostActionOperation(actionId));
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java
index 3268e5e..f9a4270 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java
@@ -32,7 +32,7 @@
import java.util.List;
/** Capture a host action information. This can be triggered on eg. a click. */
-public class HostNamedActionOperation implements ActionOperation {
+public class HostNamedActionOperation extends Operation implements ActionOperation {
private static final int OP_CODE = Operations.HOST_NAMED_ACTION;
public static final int FLOAT_TYPE = 0;
@@ -112,6 +112,12 @@
buffer.writeInt(valueId);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int textId = buffer.readInt();
int type = buffer.readInt();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java
index 8f08f14..f8926fe 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java
@@ -17,10 +17,10 @@
import android.annotation.NonNull;
-import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.OperationInterface;
import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
/** Represents a modifier */
-public interface ModifierOperation extends Operation {
+public interface ModifierOperation extends OperationInterface {
void serializeToString(int indent, @NonNull StringSerializer serializer);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/OffsetModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/OffsetModifierOperation.java
index 8c07059..69c4e9a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/OffsetModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/OffsetModifierOperation.java
@@ -94,6 +94,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java
index 2b6621e..545df64 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java
@@ -32,7 +32,7 @@
* Represents a padding modifier. Padding modifiers can be chained and will impact following
* modifiers.
*/
-public class PaddingModifierOperation implements ModifierOperation {
+public class PaddingModifierOperation extends Operation implements ModifierOperation {
private static final int OP_CODE = Operations.MODIFIER_PADDING;
public static final String CLASS_NAME = "PaddingModifierOperation";
float mLeft;
@@ -118,6 +118,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return Operations.MODIFIER_PADDING;
}
@@ -131,6 +136,12 @@
buffer.writeFloat(bottom);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
float left = buffer.readFloat();
float top = buffer.readFloat();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java
index 3fefc58..681501d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java
@@ -37,11 +37,22 @@
public static final int OP_CODE = Operations.MODIFIER_ROUNDED_CLIP_RECT;
public static final String CLASS_NAME = "RoundedClipRectModifierOperation";
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Maker m = RoundedClipRectModifierOperation::new;
read(m, buffer, operations);
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java
index 8dcfed9..0b66320 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java
@@ -134,6 +134,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatChangeActionOperation.java
index a97fcff..b96d3cc 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatChangeActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatChangeActionOperation.java
@@ -33,7 +33,7 @@
import java.util.List;
/** Apply a value change on an float variable. */
-public class ValueFloatChangeActionOperation implements ActionOperation {
+public class ValueFloatChangeActionOperation extends Operation implements ActionOperation {
private static final int OP_CODE = Operations.VALUE_FLOAT_CHANGE_ACTION;
int mTargetValueId = -1;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatExpressionChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatExpressionChangeActionOperation.java
index 41586b4..d81b7ff 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatExpressionChangeActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatExpressionChangeActionOperation.java
@@ -32,7 +32,8 @@
import java.util.List;
/** Apply a value change on an integer variable. */
-public class ValueFloatExpressionChangeActionOperation implements ActionOperation {
+public class ValueFloatExpressionChangeActionOperation extends Operation
+ implements ActionOperation {
private static final int OP_CODE = Operations.VALUE_FLOAT_EXPRESSION_CHANGE_ACTION;
int mTargetValueId = -1;
@@ -88,6 +89,12 @@
buffer.writeInt(value);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int valueId = buffer.readInt();
int value = buffer.readInt();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java
index c2cd2ab..fb13b42 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java
@@ -32,7 +32,7 @@
import java.util.List;
/** Apply a value change on an integer variable. */
-public class ValueIntegerChangeActionOperation implements ActionOperation {
+public class ValueIntegerChangeActionOperation extends Operation implements ActionOperation {
private static final int OP_CODE = Operations.VALUE_INTEGER_CHANGE_ACTION;
int mTargetValueId = -1;
@@ -87,6 +87,12 @@
buffer.writeInt(value);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int valueId = buffer.readInt();
int value = buffer.readInt();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java
index 43fbb85..0fe88ad 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java
@@ -32,7 +32,8 @@
import java.util.List;
/** Apply a value change on an integer variable. */
-public class ValueIntegerExpressionChangeActionOperation implements ActionOperation {
+public class ValueIntegerExpressionChangeActionOperation extends Operation
+ implements ActionOperation {
private static final int OP_CODE = Operations.VALUE_INTEGER_EXPRESSION_CHANGE_ACTION;
long mTargetValueId = -1;
@@ -88,6 +89,12 @@
buffer.writeLong(value);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
long valueId = buffer.readLong();
long value = buffer.readLong();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java
index 1107889..a8d3b87 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java
@@ -32,7 +32,7 @@
import java.util.List;
/** Apply a value change on a string variable. */
-public class ValueStringChangeActionOperation implements ActionOperation {
+public class ValueStringChangeActionOperation extends Operation implements ActionOperation {
private static final int OP_CODE = Operations.VALUE_STRING_CHANGE_ACTION;
int mTargetValueId = -1;
@@ -91,6 +91,12 @@
buffer.writeInt(value);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int valueId = buffer.readInt();
int value = buffer.readInt();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java
index 3c757a8..f6d743f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java
@@ -37,6 +37,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
@@ -47,6 +52,12 @@
buffer.writeFloat(value);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Type type = Type.fromInt(buffer.readInt());
float value = buffer.readFloat();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ZIndexModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ZIndexModifierOperation.java
index 82c8f34..96ed2cd 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ZIndexModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ZIndexModifierOperation.java
@@ -83,6 +83,11 @@
return CLASS_NAME;
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return OP_CODE;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java
index 07cf762..9543469 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java
@@ -498,6 +498,11 @@
return ret;
}
+ /**
+ * Write a bundle of paint changes to the buffer
+ *
+ * @param buffer bundle to write
+ */
public void writeBundle(@NonNull WireBuffer buffer) {
buffer.writeInt(mPos);
for (int index = 0; index < mPos; index++) {
@@ -505,6 +510,11 @@
}
}
+ /**
+ * This will read the paint bundle off the wire buffer
+ *
+ * @param buffer the buffer to read
+ */
public void readBundle(@NonNull WireBuffer buffer) {
int len = buffer.readInt();
if (len <= 0 || len > 1024) {
@@ -587,6 +597,9 @@
public static final int RADIAL_GRADIENT = 1;
public static final int SWEEP_GRADIENT = 2;
+ private int mLastShaderSet = -1;
+ private boolean mColorFilterSet = false;
+
/**
* sets a shader that draws a linear gradient along a line.
*
@@ -722,12 +735,14 @@
mArray[mPos] = COLOR_FILTER_ID | (mode << 16);
mPos++;
mArray[mPos++] = color;
+ mColorFilterSet = true;
}
/** This sets the color filter to null */
public void clearColorFilter() {
mArray[mPos] = CLEAR_COLOR_FILTER;
mPos++;
+ mColorFilterSet = false;
}
/**
@@ -843,6 +858,7 @@
* @param shaderId
*/
public void setShader(int shaderId) {
+ mLastShaderSet = shaderId;
mArray[mPos] = SHADER;
mPos++;
mArray[mPos] = shaderId;
@@ -909,11 +925,23 @@
mPos++;
}
+ /**
+ * clear a series of paint parameters. Currently not used
+ *
+ * @param mask bit pattern of the attributes to clear
+ */
public void clear(long mask) { // unused for now
}
+ /** Reset the content of the paint bundle so that it can be reused */
public void reset() {
mPos = 0;
+ if (mColorFilterSet) {
+ clearColorFilter();
+ }
+ if (mLastShaderSet != -1 && mLastShaderSet != 0) {
+ setShader(0);
+ }
}
public static @NonNull String blendModeString(int mode) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java
index e5633c7..a568747 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import com.android.internal.widget.remotecompose.core.operations.Utils;
import com.android.internal.widget.remotecompose.core.operations.utilities.easing.MonotonicSpline;
/** high performance floating point expression evaluator used in animation */
@@ -141,7 +140,7 @@
for (int i = 0; i < mStack.length; i++) {
float v = mStack[i];
if (Float.isNaN(v)) {
- sp = mOps[fromNaN(v) - OFFSET].eval(sp);
+ sp = opEval(sp, fromNaN(v));
} else {
mStack[++sp] = v;
}
@@ -170,7 +169,7 @@
if (Float.isNaN(v)) {
int id = fromNaN(v);
if ((id & NanMap.ID_REGION_MASK) != NanMap.ID_REGION_ARRAY) {
- sp = mOps[id - OFFSET].eval(sp);
+ sp = opEval(sp, id);
} else {
mStack[++sp] = v;
}
@@ -199,7 +198,7 @@
if (Float.isNaN(v)) {
int id = fromNaN(v);
if ((id & NanMap.ID_REGION_MASK) != NanMap.ID_REGION_ARRAY) {
- sp = mOps[id - OFFSET].eval(sp);
+ sp = opEval(sp, id);
} else {
mStack[++sp] = v;
}
@@ -228,10 +227,11 @@
mStack = mLocalStack;
mVar = var;
int sp = -1;
+
for (int i = 0; i < len; i++) {
float v = mStack[i];
if (Float.isNaN(v)) {
- sp = mOps[fromNaN(v) - OFFSET].eval(sp);
+ sp = opEval(sp, fromNaN(v));
} else {
mStack[++sp] = v;
}
@@ -250,9 +250,10 @@
mStack = exp;
mVar = var;
int sp = -1;
+
for (float v : exp) {
if (Float.isNaN(v)) {
- sp = mOps[fromNaN(v) - OFFSET].eval(sp);
+ sp = opEval(sp, fromNaN(v));
} else {
System.out.print(" " + v);
mStack[++sp] = v;
@@ -261,294 +262,6 @@
return mStack[sp];
}
- @NonNull Op[] mOps;
-
- {
- Op mADD =
- (sp) -> { // ADD
- mStack[sp - 1] = mStack[sp - 1] + mStack[sp];
- return sp - 1;
- };
- Op mSUB =
- (sp) -> { // SUB
- mStack[sp - 1] = mStack[sp - 1] - mStack[sp];
- return sp - 1;
- };
- Op mMUL =
- (sp) -> { // MUL
- mStack[sp - 1] = mStack[sp - 1] * mStack[sp];
- return sp - 1;
- };
- Op mDIV =
- (sp) -> { // DIV
- mStack[sp - 1] = mStack[sp - 1] / mStack[sp];
- return sp - 1;
- };
- Op mMOD =
- (sp) -> { // MOD
- mStack[sp - 1] = mStack[sp - 1] % mStack[sp];
- return sp - 1;
- };
- Op mMIN =
- (sp) -> { // MIN
- mStack[sp - 1] = (float) Math.min(mStack[sp - 1], mStack[sp]);
- return sp - 1;
- };
- Op mMAX =
- (sp) -> { // MAX
- mStack[sp - 1] = (float) Math.max(mStack[sp - 1], mStack[sp]);
- return sp - 1;
- };
- Op mPOW =
- (sp) -> { // POW
- mStack[sp - 1] = (float) Math.pow(mStack[sp - 1], mStack[sp]);
- return sp - 1;
- };
- Op mSQRT =
- (sp) -> { // SQRT
- mStack[sp] = (float) Math.sqrt(mStack[sp]);
- return sp;
- };
- Op mABS =
- (sp) -> { // ABS
- mStack[sp] = (float) Math.abs(mStack[sp]);
- return sp;
- };
- Op mSIGN =
- (sp) -> { // SIGN
- mStack[sp] = (float) Math.signum(mStack[sp]);
- return sp;
- };
- Op mCOPY_SIGN =
- (sp) -> { // copySign
- mStack[sp - 1] = (float) Math.copySign(mStack[sp - 1], mStack[sp]);
- return sp - 1;
- };
- Op mEXP =
- (sp) -> { // EXP
- mStack[sp] = (float) Math.exp(mStack[sp]);
- return sp;
- };
- Op mFLOOR =
- (sp) -> { // FLOOR
- mStack[sp] = (float) Math.floor(mStack[sp]);
- return sp;
- };
- Op mLOG =
- (sp) -> { // LOG
- mStack[sp] = (float) Math.log10(mStack[sp]);
- return sp;
- };
- Op mLN =
- (sp) -> { // LN
- mStack[sp] = (float) Math.log(mStack[sp]);
- return sp;
- };
- Op mROUND =
- (sp) -> { // ROUND
- mStack[sp] = (float) Math.round(mStack[sp]);
- return sp;
- };
- Op mSIN =
- (sp) -> { // SIN
- mStack[sp] = (float) Math.sin(mStack[sp]);
- return sp;
- };
- Op mCOS =
- (sp) -> { // COS
- mStack[sp] = (float) Math.cos(mStack[sp]);
- return sp;
- };
- Op mTAN =
- (sp) -> { // TAN
- mStack[sp] = (float) Math.tan(mStack[sp]);
- return sp;
- };
- Op mASIN =
- (sp) -> { // ASIN
- mStack[sp] = (float) Math.asin(mStack[sp]);
- return sp;
- };
- Op mACOS =
- (sp) -> { // ACOS
- mStack[sp] = (float) Math.acos(mStack[sp]);
- return sp;
- };
- Op mATAN =
- (sp) -> { // ATAN
- mStack[sp] = (float) Math.atan(mStack[sp]);
- return sp;
- };
- Op mATAN2 =
- (sp) -> { // ATAN2
- mStack[sp - 1] = (float) Math.atan2(mStack[sp - 1], mStack[sp]);
- return sp - 1;
- };
- Op mMAD =
- (sp) -> { // MAD
- mStack[sp - 2] = mStack[sp] + mStack[sp - 1] * mStack[sp - 2];
- return sp - 2;
- };
- Op mTERNARY_CONDITIONAL =
- (sp) -> { // TERNARY_CONDITIONAL
- mStack[sp - 2] = (mStack[sp] > 0) ? mStack[sp - 1] : mStack[sp - 2];
- return sp - 2;
- };
- Op mCLAMP =
- (sp) -> { // CLAMP (min, max, value)
- mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]), mStack[sp - 1]);
- return sp - 2;
- };
- Op mCBRT =
- (sp) -> { // CBRT is cube root
- mStack[sp] = (float) Math.pow(mStack[sp], 1 / 3.);
- return sp;
- };
- Op mDEG =
- (sp) -> { // DEG
- mStack[sp] = mStack[sp] * FP_TO_RAD;
- return sp;
- };
- Op mRAD =
- (sp) -> { // RAD
- mStack[sp] = mStack[sp] * FP_TO_DEG;
- return sp;
- };
- Op mCEIL =
- (sp) -> { // CEIL
- mStack[sp] = (float) Math.ceil(mStack[sp]);
- return sp;
- };
- Op mA_DEREF =
- (sp) -> { // A_DEREF
- Utils.log(" \n >>> DREF " + Integer.toHexString(fromNaN(mStack[sp - 1])));
- Utils.log(" >>> DREF " + mStack[sp] + " " + mStack[sp - 1]);
- int id = fromNaN(mStack[sp - 1]);
- mStack[sp - 1] = mCollectionsAccess.getFloatValue(id, (int) mStack[sp]);
- return sp - 1;
- };
- Op mA_MAX =
- (sp) -> { // A_MAX
- int id = fromNaN(mStack[sp]);
- float[] array = mCollectionsAccess.getFloats(id);
- float max = array[0];
- for (int i = 1; i < array.length; i++) {
- max = Math.max(max, array[i]);
- }
- mStack[sp] = max;
- return sp;
- };
- Op mA_MIN =
- (sp) -> { // A_MIN
- int id = fromNaN(mStack[sp]);
- float[] array = mCollectionsAccess.getFloats(id);
- if (array.length == 0) {
- return sp;
- }
- float min = array[0];
- for (int i = 1; i < array.length; i++) {
- min = Math.min(min, array[i]);
- }
- mStack[sp] = min;
- return sp;
- };
- Op mA_SUM =
- (sp) -> { // A_SUM
- int id = fromNaN(mStack[sp]);
- float[] array = mCollectionsAccess.getFloats(id);
- float sum = 0;
- for (int i = 0; i < array.length; i++) {
- sum += array[i];
- }
- mStack[sp] = sum;
- return sp;
- };
- Op mA_AVG =
- (sp) -> { // A_AVG
- int id = fromNaN(mStack[sp]);
- float[] array = mCollectionsAccess.getFloats(id);
- float sum = 0;
- for (int i = 0; i < array.length; i++) {
- sum += array[i];
- }
- mStack[sp] = sum / array.length;
- return sp;
- };
- Op mA_LEN =
- (sp) -> { // A_LEN
- int id = fromNaN(mStack[sp]);
- mStack[sp] = mCollectionsAccess.getListLength(id);
- return sp;
- };
- Op mA_SPLINE =
- (sp) -> { // A_SPLINE
- int id = fromNaN(mStack[sp - 1]);
- mStack[sp - 1] = getSplineValue(id, mStack[sp]);
- return sp - 1;
- };
- Op mFIRST_VAR =
- (sp) -> { // FIRST_VAR
- mStack[sp] = mVar[0];
- return sp;
- };
- Op mSECOND_VAR =
- (sp) -> { // SECOND_VAR
- mStack[sp] = mVar[1];
- return sp;
- };
- Op mTHIRD_VAR =
- (sp) -> { // THIRD_VAR
- mStack[sp] = mVar[2];
- return sp;
- };
-
- Op[] ops = {
- null,
- mADD,
- mSUB,
- mMUL,
- mDIV,
- mMOD,
- mMIN,
- mMAX,
- mPOW,
- mSQRT,
- mABS,
- mSIGN,
- mCOPY_SIGN,
- mEXP,
- mFLOOR,
- mLOG,
- mLN,
- mROUND,
- mSIN,
- mCOS,
- mTAN,
- mASIN,
- mACOS,
- mATAN,
- mATAN2,
- mMAD,
- mTERNARY_CONDITIONAL,
- mCLAMP,
- mCBRT,
- mDEG,
- mRAD,
- mCEIL,
- mA_DEREF,
- mA_MAX,
- mA_MIN,
- mA_SUM,
- mA_AVG,
- mA_LEN,
- mA_SPLINE,
- mFIRST_VAR,
- mSECOND_VAR,
- mTHIRD_VAR,
- };
- mOps = ops;
- }
-
static {
int k = 0;
sNames.put(k++, "NOP");
@@ -765,4 +478,248 @@
int b = Float.floatToRawIntBits(v);
return b & 0x7FFFFF;
}
+
+ // ================= New approach ========
+ private static final int OP_ADD = OFFSET + 1;
+ private static final int OP_SUB = OFFSET + 2;
+ private static final int OP_MUL = OFFSET + 3;
+ private static final int OP_DIV = OFFSET + 4;
+ private static final int OP_MOD = OFFSET + 5;
+ private static final int OP_MIN = OFFSET + 6;
+ private static final int OP_MAX = OFFSET + 7;
+ private static final int OP_POW = OFFSET + 8;
+ private static final int OP_SQRT = OFFSET + 9;
+ private static final int OP_ABS = OFFSET + 10;
+ private static final int OP_SIGN = OFFSET + 11;
+ private static final int OP_COPY_SIGN = OFFSET + 12;
+ private static final int OP_EXP = OFFSET + 13;
+ private static final int OP_FLOOR = OFFSET + 14;
+ private static final int OP_LOG = OFFSET + 15;
+ private static final int OP_LN = OFFSET + 16;
+ private static final int OP_ROUND = OFFSET + 17;
+ private static final int OP_SIN = OFFSET + 18;
+ private static final int OP_COS = OFFSET + 19;
+ private static final int OP_TAN = OFFSET + 20;
+ private static final int OP_ASIN = OFFSET + 21;
+ private static final int OP_ACOS = OFFSET + 22;
+ private static final int OP_ATAN = OFFSET + 23;
+ private static final int OP_ATAN2 = OFFSET + 24;
+ private static final int OP_MAD = OFFSET + 25;
+ private static final int OP_TERNARY_CONDITIONAL = OFFSET + 26;
+ private static final int OP_CLAMP = OFFSET + 27;
+ private static final int OP_CBRT = OFFSET + 28;
+ private static final int OP_DEG = OFFSET + 29;
+ private static final int OP_RAD = OFFSET + 30;
+ private static final int OP_CEIL = OFFSET + 31;
+ private static final int OP_A_DEREF = OFFSET + 32;
+ private static final int OP_A_MAX = OFFSET + 33;
+ private static final int OP_A_MIN = OFFSET + 34;
+ private static final int OP_A_SUM = OFFSET + 35;
+ private static final int OP_A_AVG = OFFSET + 36;
+ private static final int OP_A_LEN = OFFSET + 37;
+ private static final int OP_A_SPLINE = OFFSET + 38;
+ private static final int OP_FIRST_VAR = OFFSET + 39;
+ private static final int OP_SECOND_VAR = OFFSET + 40;
+ private static final int OP_THIRD_VAR = OFFSET + 41;
+
+ int opEval(int sp, int id) {
+ float[] array;
+
+ switch (id) {
+ case OP_ADD:
+ mStack[sp - 1] = mStack[sp - 1] + mStack[sp];
+ return sp - 1;
+
+ case OP_SUB:
+ mStack[sp - 1] = mStack[sp - 1] - mStack[sp];
+ return sp - 1;
+
+ case OP_MUL:
+ mStack[sp - 1] = mStack[sp - 1] * mStack[sp];
+ return sp - 1;
+
+ case OP_DIV:
+ mStack[sp - 1] = mStack[sp - 1] / mStack[sp];
+ return sp - 1;
+
+ case OP_MOD:
+ mStack[sp - 1] = mStack[sp - 1] % mStack[sp];
+ return sp - 1;
+
+ case OP_MIN:
+ mStack[sp - 1] = (float) Math.min(mStack[sp - 1], mStack[sp]);
+ return sp - 1;
+
+ case OP_MAX:
+ mStack[sp - 1] = (float) Math.max(mStack[sp - 1], mStack[sp]);
+ return sp - 1;
+
+ case OP_POW:
+ mStack[sp - 1] = (float) Math.pow(mStack[sp - 1], mStack[sp]);
+ return sp - 1;
+
+ case OP_SQRT:
+ mStack[sp] = (float) Math.sqrt(mStack[sp]);
+ return sp;
+
+ case OP_ABS:
+ mStack[sp] = (float) Math.abs(mStack[sp]);
+ return sp;
+
+ case OP_SIGN:
+ mStack[sp] = (float) Math.signum(mStack[sp]);
+ return sp;
+
+ case OP_COPY_SIGN:
+ mStack[sp - 1] = (float) Math.copySign(mStack[sp - 1], mStack[sp]);
+ return sp - 1;
+
+ case OP_EXP:
+ mStack[sp] = (float) Math.exp(mStack[sp]);
+ return sp;
+
+ case OP_FLOOR:
+ mStack[sp] = (float) Math.floor(mStack[sp]);
+ return sp;
+
+ case OP_LOG:
+ mStack[sp] = (float) Math.log10(mStack[sp]);
+ return sp;
+
+ case OP_LN:
+ mStack[sp] = (float) Math.log(mStack[sp]);
+ return sp;
+
+ case OP_ROUND:
+ mStack[sp] = (float) Math.round(mStack[sp]);
+ return sp;
+
+ case OP_SIN:
+ mStack[sp] = (float) Math.sin(mStack[sp]);
+ return sp;
+
+ case OP_COS:
+ mStack[sp] = (float) Math.cos(mStack[sp]);
+ return sp;
+
+ case OP_TAN:
+ mStack[sp] = (float) Math.tan(mStack[sp]);
+ return sp;
+
+ case OP_ASIN:
+ mStack[sp] = (float) Math.asin(mStack[sp]);
+ return sp;
+
+ case OP_ACOS:
+ mStack[sp] = (float) Math.acos(mStack[sp]);
+ return sp;
+
+ case OP_ATAN:
+ mStack[sp] = (float) Math.atan(mStack[sp]);
+ return sp;
+
+ case OP_ATAN2:
+ mStack[sp - 1] = (float) Math.atan2(mStack[sp - 1], mStack[sp]);
+ return sp - 1;
+
+ case OP_MAD:
+ mStack[sp - 2] = mStack[sp] + mStack[sp - 1] * mStack[sp - 2];
+ return sp - 2;
+
+ case OP_TERNARY_CONDITIONAL:
+ mStack[sp - 2] = (mStack[sp] > 0) ? mStack[sp - 1] : mStack[sp - 2];
+ return sp - 2;
+
+ case OP_CLAMP:
+ mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]), mStack[sp - 1]);
+ return sp - 2;
+
+ case OP_CBRT:
+ mStack[sp] = (float) Math.pow(mStack[sp], 1 / 3.);
+ return sp;
+
+ case OP_DEG:
+ mStack[sp] = mStack[sp] * FP_TO_RAD;
+ return sp;
+
+ case OP_RAD:
+ mStack[sp] = mStack[sp] * FP_TO_DEG;
+ return sp;
+
+ case OP_CEIL:
+ mStack[sp] = (float) Math.ceil(mStack[sp]);
+ return sp;
+
+ case OP_A_DEREF:
+ id = fromNaN(mStack[sp - 1]);
+ mStack[sp - 1] = mCollectionsAccess.getFloatValue(id, (int) mStack[sp]);
+ return sp - 1;
+
+ case OP_A_MAX:
+ id = fromNaN(mStack[sp]);
+ array = mCollectionsAccess.getFloats(id);
+ float max = array[0];
+ for (int i = 1; i < array.length; i++) {
+ max = Math.max(max, array[i]);
+ }
+ mStack[sp] = max;
+ return sp;
+
+ case OP_A_MIN:
+ id = fromNaN(mStack[sp]);
+ array = mCollectionsAccess.getFloats(id);
+ if (array.length == 0) {
+ return sp;
+ }
+ float min = array[0];
+ for (int i = 1; i < array.length; i++) {
+ min = Math.min(min, array[i]);
+ }
+ mStack[sp] = min;
+ return sp;
+
+ case OP_A_SUM:
+ id = fromNaN(mStack[sp]);
+ array = mCollectionsAccess.getFloats(id);
+ float sum = 0;
+ for (int i = 0; i < array.length; i++) {
+ sum += array[i];
+ }
+ mStack[sp] = sum;
+ return sp;
+
+ case OP_A_AVG:
+ id = fromNaN(mStack[sp]);
+ array = mCollectionsAccess.getFloats(id);
+ sum = 0;
+ for (int i = 0; i < array.length; i++) {
+ sum += array[i];
+ }
+ mStack[sp] = sum / array.length;
+ return sp;
+
+ case OP_A_LEN:
+ id = fromNaN(mStack[sp]);
+ mStack[sp] = mCollectionsAccess.getListLength(id);
+ return sp;
+
+ case OP_A_SPLINE:
+ id = fromNaN(mStack[sp - 1]);
+ mStack[sp - 1] = getSplineValue(id, mStack[sp]);
+ return sp - 1;
+
+ case OP_FIRST_VAR:
+ mStack[sp] = mVar[0];
+ return sp;
+
+ case OP_SECOND_VAR:
+ mStack[sp] = mVar[1];
+ return sp;
+
+ case OP_THIRD_VAR:
+ mStack[sp] = mVar[2];
+ return sp;
+ }
+ return sp;
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java
index 4f12872..b92f96f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java
@@ -24,13 +24,39 @@
public interface CollectionsAccess {
float getFloatValue(int id, int index);
+ /**
+ * Get the array of float if it is a float array
+ *
+ * @param id the id of the float array
+ * @return
+ */
@Nullable
float[] getFloats(int id);
+ /**
+ * Get the number of entries in the list
+ *
+ * @param id the id of the list
+ * @return
+ */
int getListLength(int id);
+ /**
+ * get the id of an entry if the list is a list of id's
+ *
+ * @param listId the list id
+ * @param index the index into the list
+ * @return
+ */
int getId(int listId, int index);
+ /**
+ * Get the value as an integer
+ *
+ * @param listId the list id to access
+ * @param index the index into the list
+ * @return
+ */
default int getIntValue(int listId, int index) {
return (int) getFloatValue(listId, index);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java
index f73ab39..0a33511 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java
@@ -82,7 +82,7 @@
for (int i = 0; i < mStack.length; i++) {
int v = mStack[i];
if (((1 << i) & mask) != 0) {
- sp = mOps[v - OFFSET].eval(sp);
+ sp = opEval(sp, v);
} else {
mStack[++sp] = v;
}
@@ -107,7 +107,7 @@
for (int i = 0; i < len; i++) {
int v = mStack[i];
if (((1 << i) & mask) != 0) {
- sp = mOps[v - OFFSET].eval(sp);
+ sp = opEval(sp, v);
} else {
mStack[++sp] = v;
}
@@ -130,7 +130,7 @@
for (int i = 0; i < exp.length; i++) {
int v = mStack[i];
if (((1 << i) & opMask) != 0) {
- sp = mOps[v - OFFSET].eval(sp);
+ sp = opEval(sp, v);
} else {
mStack[++sp] = v;
}
@@ -138,171 +138,140 @@
return mStack[sp];
}
- @NonNull Op[] mOps;
+ private static final int OP_ADD = OFFSET + 1;
+ private static final int OP_SUB = OFFSET + 2;
+ private static final int OP_MUL = OFFSET + 3;
+ private static final int OP_DIV = OFFSET + 4;
+ private static final int OP_MOD = OFFSET + 5;
+ private static final int OP_SHL = OFFSET + 6;
+ private static final int OP_SHR = OFFSET + 7;
+ private static final int OP_USHR = OFFSET + 8;
+ private static final int OP_OR = OFFSET + 9;
+ private static final int OP_AND = OFFSET + 10;
+ private static final int OP_XOR = OFFSET + 11;
+ private static final int OP_COPY_SIGN = OFFSET + 12;
+ private static final int OP_MIN = OFFSET + 13;
+ private static final int OP_MAX = OFFSET + 14;
+ private static final int OP_NEG = OFFSET + 15;
+ private static final int OP_ABS = OFFSET + 16;
+ private static final int OP_INCR = OFFSET + 17;
+ private static final int OP_DECR = OFFSET + 18;
+ private static final int OP_NOT = OFFSET + 19;
+ private static final int OP_SIGN = OFFSET + 20;
+ private static final int OP_CLAMP = OFFSET + 21;
+ private static final int OP_TERNARY_CONDITIONAL = OFFSET + 22;
+ private static final int OP_MAD = OFFSET + 23;
+ private static final int OP_FIRST_VAR = OFFSET + 24;
+ private static final int OP_SECOND_VAR = OFFSET + 25;
+ private static final int OP_THIRD_VAR = OFFSET + 26;
- {
- Op mADD =
- (sp) -> { // ADD
- mStack[sp - 1] = mStack[sp - 1] + mStack[sp];
- return sp - 1;
- };
- Op mSUB =
- (sp) -> { // SUB
- mStack[sp - 1] = mStack[sp - 1] - mStack[sp];
- return sp - 1;
- };
- Op mMUL =
- (sp) -> { // MUL
- mStack[sp - 1] = mStack[sp - 1] * mStack[sp];
- return sp - 1;
- };
- Op mDIV =
- (sp) -> { // DIV
- mStack[sp - 1] = mStack[sp - 1] / mStack[sp];
- return sp - 1;
- };
- Op mMOD =
- (sp) -> { // MOD
- mStack[sp - 1] = mStack[sp - 1] % mStack[sp];
- return sp - 1;
- };
- Op mSHL =
- (sp) -> { // SHL
- mStack[sp - 1] = mStack[sp - 1] << mStack[sp];
- return sp - 1;
- };
- Op mSHR =
- (sp) -> { // SHR
- mStack[sp - 1] = mStack[sp - 1] >> mStack[sp];
- return sp - 1;
- };
- Op mUSHR =
- (sp) -> { // USHR
- mStack[sp - 1] = mStack[sp - 1] >>> mStack[sp];
- return sp - 1;
- };
- Op mOR =
- (sp) -> { // OR
- mStack[sp - 1] = mStack[sp - 1] | mStack[sp];
- return sp - 1;
- };
- Op mAND =
- (sp) -> { // AND
- mStack[sp - 1] = mStack[sp - 1] & mStack[sp];
- return sp - 1;
- };
- Op mXOR =
- (sp) -> { // XOR
- mStack[sp - 1] = mStack[sp - 1] ^ mStack[sp];
- return sp - 1;
- };
- Op mCOPY_SIGN =
- (sp) -> { // COPY_SIGN copy the sign via bit manipulation
- mStack[sp - 1] = (mStack[sp - 1] ^ (mStack[sp] >> 31)) - (mStack[sp] >> 31);
- return sp - 1;
- };
- Op mMIN =
- (sp) -> { // MIN
- mStack[sp - 1] = Math.min(mStack[sp - 1], mStack[sp]);
- return sp - 1;
- };
- Op mMAX =
- (sp) -> { // MAX
- mStack[sp - 1] = Math.max(mStack[sp - 1], mStack[sp]);
- return sp - 1;
- };
- Op mNEG =
- (sp) -> { // NEG
- mStack[sp] = -mStack[sp];
- return sp;
- };
- Op mABS =
- (sp) -> { // ABS
- mStack[sp] = Math.abs(mStack[sp]);
- return sp;
- };
- Op mINCR =
- (sp) -> { // INCR
- mStack[sp] = mStack[sp] + 1;
- return sp;
- };
- Op mDECR =
- (sp) -> { // DECR
- mStack[sp] = mStack[sp] - 1;
- return sp;
- };
- Op mNOT =
- (sp) -> { // NOT
- mStack[sp] = ~mStack[sp];
- return sp;
- };
- Op mSIGN =
- (sp) -> { // SIGN x<0 = -1,x==0 = 0 , x>0 = 1
- mStack[sp] = (mStack[sp] >> 31) | (-mStack[sp] >>> 31);
- return sp;
- };
- Op mCLAMP =
- (sp) -> { // CLAMP(min,max, val)
- mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]), mStack[sp - 1]);
- return sp - 2;
- };
- Op mTERNARY_CONDITIONAL =
- (sp) -> { // TERNARY_CONDITIONAL
- mStack[sp - 2] = (mStack[sp] > 0) ? mStack[sp - 1] : mStack[sp - 2];
- return sp - 2;
- };
- Op mMAD =
- (sp) -> { // MAD
- mStack[sp - 2] = mStack[sp] + mStack[sp - 1] * mStack[sp - 2];
- return sp - 2;
- };
- Op mFIRST_VAR =
- (sp) -> { // FIRST_VAR
- mStack[sp] = mVar[0];
- return sp;
- };
- Op mSECOND_VAR =
- (sp) -> { // SECOND_VAR
- mStack[sp] = mVar[1];
- return sp;
- };
- Op mTHIRD_VAR =
- (sp) -> { // THIRD_VAR
- mStack[sp] = mVar[2];
- return sp;
- };
+ int opEval(int sp, int id) {
+ switch (id) {
+ case OP_ADD: // ADD
+ mStack[sp - 1] = mStack[sp - 1] + mStack[sp];
+ return sp - 1;
- Op[] ops = {
- null,
- mADD,
- mSUB,
- mMUL,
- mDIV,
- mMOD,
- mSHL,
- mSHR,
- mUSHR,
- mOR,
- mAND,
- mXOR,
- mCOPY_SIGN,
- mMIN,
- mMAX,
- mNEG,
- mABS,
- mINCR,
- mDECR,
- mNOT,
- mSIGN,
- mCLAMP,
- mTERNARY_CONDITIONAL,
- mMAD,
- mFIRST_VAR,
- mSECOND_VAR,
- mTHIRD_VAR,
- };
+ case OP_SUB: // SUB
+ mStack[sp - 1] = mStack[sp - 1] - mStack[sp];
+ return sp - 1;
- mOps = ops;
+ case OP_MUL: // MUL
+ mStack[sp - 1] = mStack[sp - 1] * mStack[sp];
+ return sp - 1;
+
+ case OP_DIV: // DIV
+ mStack[sp - 1] = mStack[sp - 1] / mStack[sp];
+ return sp - 1;
+
+ case OP_MOD: // MOD
+ mStack[sp - 1] = mStack[sp - 1] % mStack[sp];
+ return sp - 1;
+
+ case OP_SHL: // SHL
+ mStack[sp - 1] = mStack[sp - 1] << mStack[sp];
+ return sp - 1;
+
+ case OP_SHR: // SHR
+ mStack[sp - 1] = mStack[sp - 1] >> mStack[sp];
+ return sp - 1;
+
+ case OP_USHR: // USHR
+ mStack[sp - 1] = mStack[sp - 1] >>> mStack[sp];
+ return sp - 1;
+
+ case OP_OR: // OR
+ mStack[sp - 1] = mStack[sp - 1] | mStack[sp];
+ return sp - 1;
+
+ case OP_AND: // AND
+ mStack[sp - 1] = mStack[sp - 1] & mStack[sp];
+ return sp - 1;
+
+ case OP_XOR: // XOR
+ mStack[sp - 1] = mStack[sp - 1] ^ mStack[sp];
+ return sp - 1;
+
+ case OP_COPY_SIGN: // COPY_SIGN copy the sign via bit manipulation
+ mStack[sp - 1] = (mStack[sp - 1] ^ (mStack[sp] >> 31)) - (mStack[sp] >> 31);
+ return sp - 1;
+
+ case OP_MIN: // MIN
+ mStack[sp - 1] = Math.min(mStack[sp - 1], mStack[sp]);
+ return sp - 1;
+
+ case OP_MAX: // MAX
+ mStack[sp - 1] = Math.max(mStack[sp - 1], mStack[sp]);
+ return sp - 1;
+
+ case OP_NEG: // NEG
+ mStack[sp] = -mStack[sp];
+ return sp;
+
+ case OP_ABS: // ABS
+ mStack[sp] = Math.abs(mStack[sp]);
+ return sp;
+
+ case OP_INCR: // INCR
+ mStack[sp] = mStack[sp] + 1;
+ return sp;
+
+ case OP_DECR: // DECR
+ mStack[sp] = mStack[sp] - 1;
+ return sp;
+
+ case OP_NOT: // NOT
+ mStack[sp] = ~mStack[sp];
+ return sp;
+
+ case OP_SIGN: // SIGN x<0 = -1,x==0 = 0 , x>0 = 1
+ mStack[sp] = (mStack[sp] >> 31) | (-mStack[sp] >>> 31);
+ return sp;
+
+ case OP_CLAMP: // CLAMP(min,max, val)
+ mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]), mStack[sp - 1]);
+ return sp - 2;
+
+ case OP_TERNARY_CONDITIONAL: // TERNARY_CONDITIONAL
+ mStack[sp - 2] = (mStack[sp] > 0) ? mStack[sp - 1] : mStack[sp - 2];
+ return sp - 2;
+
+ case OP_MAD: // MAD
+ mStack[sp - 2] = mStack[sp] + mStack[sp - 1] * mStack[sp - 2];
+ return sp - 2;
+
+ case OP_FIRST_VAR: // FIRST_VAR
+ mStack[sp] = mVar[0];
+ return sp;
+
+ case OP_SECOND_VAR: // SECOND_VAR
+ mStack[sp] = mVar[1];
+ return sp;
+
+ case OP_THIRD_VAR: // THIRD_VAR
+ mStack[sp] = mVar[2];
+ return sp;
+ }
+ return 0;
}
static {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/touch/VelocityEasing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/touch/VelocityEasing.java
index 7e02bc9..c7e2442 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/touch/VelocityEasing.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/touch/VelocityEasing.java
@@ -31,6 +31,11 @@
* limitations under the License.
*/
+/**
+ * This computes an form of easing such that the values constrained to be consistent in velocity The
+ * easing function is also constrained by the configure To have: a maximum time to stop, a maximum
+ * velocity, a maximum acceleration
+ */
public class VelocityEasing {
private float mStartPos = 0;
private float mStartV = 0;
@@ -46,6 +51,11 @@
private boolean mOneDimension = true;
private float mTotalEasingDuration = 0;
+ /**
+ * get the duration the easing will take
+ *
+ * @return the duration for the easing
+ */
public float getDuration() {
if (mEasing != null) {
return mTotalEasingDuration;
@@ -53,6 +63,12 @@
return mDuration;
}
+ /**
+ * Get the velocity at time t
+ *
+ * @param t time in seconds
+ * @return the velocity units/second
+ */
public float getV(float t) {
if (mEasing == null) {
for (int i = 0; i < mNumberOfStages; i++) {
@@ -71,6 +87,12 @@
return (float) getEasingDiff((t - mStage[lastStages].mStartTime));
}
+ /**
+ * Get the position t seconds after the configure
+ *
+ * @param t time in seconds
+ * @return the position at time t
+ */
public float getPos(float t) {
if (mEasing == null) {
for (int i = 0; i < mNumberOfStages; i++) {
@@ -91,6 +113,7 @@
return ret;
}
+ @Override
public String toString() {
var s = " ";
for (int i = 0; i < mNumberOfStages; i++) {
@@ -100,6 +123,17 @@
return s;
}
+ /**
+ * Configure the Velocity easing curve The system is in arbitrary units
+ *
+ * @param currentPos the current position
+ * @param destination the destination
+ * @param currentVelocity the current velocity units/seconds
+ * @param maxTime the max time to achieve position
+ * @param maxAcceleration the max acceleration units/s^2
+ * @param maxVelocity the maximum velocity
+ * @param easing End in using this easing curve
+ */
public void config(
float currentPos,
float destination,
diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java
index 4af79f3..975213f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java
@@ -29,7 +29,7 @@
import java.util.List;
/** Used to represent a boolean */
-public class BooleanConstant implements Operation {
+public class BooleanConstant extends Operation {
private static final int OP_CODE = Operations.DATA_BOOLEAN;
private boolean mValue = false;
private int mId;
@@ -73,6 +73,11 @@
return "OrigamiBoolean";
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return Operations.DATA_BOOLEAN;
}
@@ -90,6 +95,12 @@
buffer.writeBoolean(value);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int id = buffer.readInt();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java
index 613e732..210a15a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java
@@ -29,7 +29,7 @@
import java.util.List;
/** Represents a single integer typically used for states or named for input into the system */
-public class IntegerConstant implements Operation {
+public class IntegerConstant extends Operation {
private int mValue = 0;
private int mId;
@@ -65,6 +65,11 @@
return "IntegerConstant";
}
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
public static int id() {
return Operations.DATA_INT;
}
@@ -82,6 +87,12 @@
buffer.writeInt(value);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int id = buffer.readInt();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java
index 745caa3..9875c93 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java
@@ -29,7 +29,7 @@
import java.util.List;
/** Used to represent a long */
-public class LongConstant implements Operation {
+public class LongConstant extends Operation {
private static final int OP_CODE = Operations.DATA_LONG;
private long mValue;
private int mId;
@@ -83,6 +83,12 @@
buffer.writeLong(value);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int id = buffer.readInt();
diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
index 648f7bf..19b4b36 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
@@ -659,4 +659,14 @@
}
mListener = null;
}
+
+ /**
+ * This returns the amount of time in ms the player used to evalueate a pass it is averaged over
+ * a number of evaluations.
+ *
+ * @return time in ms
+ */
+ public float getEvalTime() {
+ return mInner.getEvalTime();
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
index 3c91cff..bc7d5e1 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
@@ -529,6 +529,7 @@
@Override
public void setImageFilterQuality(int quality) {
Utils.log(" quality =" + quality);
+ mPaint.setFilterBitmap(quality == 1);
}
@Override
@@ -711,20 +712,32 @@
}
@Override
+ public void tweenPath(int out, int path1, int path2, float tween) {
+ float[] p = getPathArray(path1, path2, tween);
+ AndroidRemoteContext androidContext = (AndroidRemoteContext) mContext;
+ androidContext.mRemoteComposeState.putPathData(out, p);
+ }
+
+ @Override
public void reset() {
mPaint.reset();
}
private Path getPath(int path1Id, int path2Id, float tween, float start, float end) {
+ return getPath(getPathArray(path1Id, path2Id, tween), start, end);
+ }
+
+ private float[] getPathArray(int path1Id, int path2Id, float tween) {
+ AndroidRemoteContext androidContext = (AndroidRemoteContext) mContext;
if (tween == 0.0f) {
- return getPath(path1Id, start, end);
+ return androidContext.mRemoteComposeState.getPathData(path1Id);
}
if (tween == 1.0f) {
- return getPath(path2Id, start, end);
+ return androidContext.mRemoteComposeState.getPathData(path2Id);
}
- AndroidRemoteContext androidContext = (AndroidRemoteContext) mContext;
- float[] data1 = (float[]) androidContext.mRemoteComposeState.getFromId(path1Id);
- float[] data2 = (float[]) androidContext.mRemoteComposeState.getFromId(path2Id);
+
+ float[] data1 = androidContext.mRemoteComposeState.getPathData(path1Id);
+ float[] data2 = androidContext.mRemoteComposeState.getPathData(path2Id);
float[] tmp = new float[data2.length];
for (int i = 0; i < tmp.length; i++) {
if (Float.isNaN(data1[i]) || Float.isNaN(data2[i])) {
@@ -733,6 +746,10 @@
tmp[i] = (data2[i] - data1[i]) * tween + data1[i];
}
}
+ return tmp;
+ }
+
+ private Path getPath(float[] tmp, float start, float end) {
Path path = new Path();
FloatsToPath.genPath(path, tmp, start, end);
return path;
@@ -741,9 +758,9 @@
private Path getPath(int id, float start, float end) {
AndroidRemoteContext androidContext = (AndroidRemoteContext) mContext;
Path path = new Path();
- if (androidContext.mRemoteComposeState.containsId(id)) {
- float[] data = (float[]) androidContext.mRemoteComposeState.getFromId(id);
- FloatsToPath.genPath(path, data, start, end);
+ float[] pathData = androidContext.mRemoteComposeState.getPathData(id);
+ if (pathData != null) {
+ FloatsToPath.genPath(path, pathData, start, end);
}
return path;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
index 77c2514..0fb0a28 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
@@ -60,9 +60,12 @@
@Override
public void loadPathData(int instanceId, @NonNull float[] floatPath) {
- if (!mRemoteComposeState.containsId(instanceId)) {
- mRemoteComposeState.cacheData(instanceId, floatPath);
- }
+ mRemoteComposeState.putPathData(instanceId, floatPath);
+ }
+
+ @Override
+ public float[] getPathData(int instanceId) {
+ return mRemoteComposeState.getPathData(instanceId);
}
static class VarName {
@@ -162,7 +165,7 @@
* @param type the type of the data 0 = RGBA 8888, 1 = 888, 2 = 8 gray
* @param width with of image to be loaded largest dimension is 32767
* @param height height of image to be loaded
- * @param bitmap a byte array containing the image information
+ * @param data a byte array containing the image information
* @oaram imageId the id of the image
*/
@Override
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
index 8f55f8a..ecfd13a 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
@@ -349,6 +349,27 @@
private int mCount;
private long mTime = System.nanoTime();
+ private long mDuration;
+ private boolean mEvalTime = false;
+
+ /**
+ * This returns the amount of time in ms the player used to evalueate a pass it is averaged over
+ * a number of evaluations.
+ *
+ * @return time in ms
+ */
+ public float getEvalTime() {
+ if (!mEvalTime) {
+ mEvalTime = true;
+ return 0.0f;
+ }
+ double avg = mDuration / (double) mCount;
+ if (mCount > 100) {
+ mDuration /= 2;
+ mCount /= 2;
+ }
+ return (float) (avg * 1E-6); // ms
+ }
@Override
protected void onDraw(Canvas canvas) {
@@ -356,6 +377,7 @@
if (mDocument == null) {
return;
}
+ long start = mEvalTime ? System.nanoTime() : 0;
mARContext.setAnimationEnabled(true);
mARContext.currentTime = System.currentTimeMillis();
mARContext.setDebug(mDebug);
@@ -376,5 +398,9 @@
if (mDocument.needsRepaint() > 0) {
invalidate();
}
+ if (mEvalTime) {
+ mDuration += System.nanoTime() - start;
+ mCount++;
+ }
}
}
diff --git a/core/java/com/android/server/pm/pkg/AndroidPackage.java b/core/java/com/android/server/pm/pkg/AndroidPackage.java
index d05f5e3..70dd10f 100644
--- a/core/java/com/android/server/pm/pkg/AndroidPackage.java
+++ b/core/java/com/android/server/pm/pkg/AndroidPackage.java
@@ -875,6 +875,14 @@
int getMemtagMode();
/**
+ * @see ApplicationInfo#getPageSizeAppCompatFlags()
+ * @see R.styleable#AndroidManifestApplication_pageSizeCompat
+ * @hide
+ */
+ @ApplicationInfo.PageSizeAppCompatFlags
+ int getPageSizeAppCompatFlags();
+
+ /**
* TODO(b/135203078): Make all the Bundles immutable (and non-null by shared empty reference?)
* @see R.styleable#AndroidManifestMetaData
* @hide
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 5c03c5c..e22d958 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -214,6 +214,7 @@
"android_media_ToneGenerator.cpp",
"android_hardware_Camera.cpp",
"android_hardware_camera2_CameraMetadata.cpp",
+ "android_hardware_camera2_CameraDevice.cpp",
"android_hardware_camera2_DngCreator.cpp",
"android_hardware_camera2_impl_CameraExtensionJpegProcessor.cpp",
"android_hardware_camera2_utils_SurfaceUtils.cpp",
@@ -304,7 +305,12 @@
"av-types-aidl-cpp",
"android.hardware.camera.device@3.2",
"camera_platform_flags_c_lib",
+ "android.hardware.common.fmq-V1-cpp",
+ "android.hardware.common-V2-cpp",
+ "android.hardware.common.fmq-V1-ndk",
+ "android.hardware.common-V2-ndk",
"libandroid_net",
+ "libfmq",
"libbattery",
"libnetdutils",
"libmemtrack",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 00a6297..ac187b0 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -77,6 +77,7 @@
extern int register_android_hardware_Camera(JNIEnv *env);
extern int register_android_hardware_camera2_CameraMetadata(JNIEnv *env);
+extern int register_android_hardware_camera2_CameraDevice(JNIEnv *env);
extern int register_android_hardware_camera2_DngCreator(JNIEnv *env);
extern int register_android_hardware_camera2_impl_CameraExtensionJpegProcessor(JNIEnv* env);
extern int register_android_hardware_camera2_utils_SurfaceUtils(JNIEnv* env);
@@ -1623,6 +1624,7 @@
REG_JNI(register_com_android_internal_util_VirtualRefBasePtr),
REG_JNI(register_android_hardware_Camera),
REG_JNI(register_android_hardware_camera2_CameraMetadata),
+ REG_JNI(register_android_hardware_camera2_CameraDevice),
REG_JNI(register_android_hardware_camera2_DngCreator),
REG_JNI(register_android_hardware_camera2_impl_CameraExtensionJpegProcessor),
REG_JNI(register_android_hardware_camera2_utils_SurfaceUtils),
diff --git a/core/jni/android_hardware_camera2_CameraDevice.cpp b/core/jni/android_hardware_camera2_CameraDevice.cpp
new file mode 100644
index 0000000..493c707
--- /dev/null
+++ b/core/jni/android_hardware_camera2_CameraDevice.cpp
@@ -0,0 +1,148 @@
+/*
+**
+** Copyright 2024, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+// #define LOG_NDEBUG 0
+
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+
+#include <memory>
+#define LOG_TAG "CameraDevice-JNI"
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <utils/Trace.h>
+#include <vector>
+
+#include "jni.h"
+#include <nativehelper/JNIHelp.h>
+#include "android_os_Parcel.h"
+#include "core_jni_helpers.h"
+#include <android/binder_parcel_jni.h>
+#include <android/hardware/camera2/ICameraDeviceUser.h>
+#include <aidl/android/hardware/common/fmq/MQDescriptor.h>
+#include <aidl/android/hardware/common/fmq/SynchronizedReadWrite.h>
+#include <fmq/AidlMessageQueue.h>
+#include <camera/CameraMetadata.h>
+
+using namespace android;
+
+using ::android::AidlMessageQueue;
+using ResultMetadataQueue = AidlMessageQueue<int8_t, SynchronizedReadWrite>;
+
+class FMQReader {
+ public:
+ FMQReader(MQDescriptor<int8_t, SynchronizedReadWrite> &resultMQ) {
+ mCaptureResultMetadataQueue = std::make_shared<ResultMetadataQueue>(resultMQ);
+ }
+ std::shared_ptr<CameraMetadata> readOneResultMetadata(long metadataSize);
+ private:
+ std::shared_ptr<ResultMetadataQueue> mCaptureResultMetadataQueue = nullptr;
+};
+
+std::shared_ptr<CameraMetadata> FMQReader::readOneResultMetadata(long metadataSize) {
+ ATRACE_CALL();
+ if (metadataSize == 0) {
+ return nullptr;
+ }
+ auto metadataVec = std::make_unique<int8_t []>(metadataSize);
+ bool read = mCaptureResultMetadataQueue->read(metadataVec.get(), metadataSize);
+ if (!read) {
+ ALOGE("%s capture metadata could't be read from fmq", __FUNCTION__);
+ return nullptr;
+ }
+
+ // Takes ownership of metadataVec, this doesn't copy
+ std::shared_ptr<CameraMetadata> retVal =
+ std::make_shared<CameraMetadata>(
+ reinterpret_cast<camera_metadata_t *>(metadataVec.release()));
+ return retVal;
+}
+
+extern "C" {
+
+static jlong CameraDevice_createFMQReader(JNIEnv *env, jclass thiz,
+ jobject resultParcel) {
+ AParcel *resultAParcel = AParcel_fromJavaParcel(env, resultParcel);
+ if (resultAParcel == nullptr) {
+ ALOGE("%s: Error creating result parcel", __FUNCTION__);
+ return 0;
+ }
+ AParcel_setDataPosition(resultAParcel, 0);
+
+ MQDescriptor<int8_t, SynchronizedReadWrite> resultMQ;
+ if (resultMQ.readFromParcel(resultAParcel) != OK) {
+ ALOGE("%s: read from result parcel failed", __FUNCTION__);
+ return 0;
+ }
+ return reinterpret_cast<jlong>(new std::shared_ptr<FMQReader>(
+ new FMQReader(resultMQ)));
+}
+
+static std::shared_ptr<FMQReader>* FMQReader_getSharedPtr(jlong fmqReaderLongPtr) {
+ return reinterpret_cast<std::shared_ptr<FMQReader>* >(fmqReaderLongPtr);
+}
+
+static jlong CameraDevice_readResultMetadata(JNIEnv *env, jclass thiz, jlong ptr,
+ jlong metadataSize) {
+ ALOGV("%s", __FUNCTION__);
+
+ FMQReader *fmqReader = FMQReader_getSharedPtr(ptr)->get();
+ auto metadataSp = fmqReader->readOneResultMetadata(metadataSize);
+ auto retVal = new std::shared_ptr<CameraMetadata>(metadataSp);
+ return reinterpret_cast<jlong>(retVal);
+}
+
+static void CameraDevice_close(JNIEnv *env, jclass thiz, jlong ptr) {
+ ALOGV("%s", __FUNCTION__);
+
+ auto fmqPtr = FMQReader_getSharedPtr(ptr);
+ if (fmqPtr != nullptr) {
+ delete fmqPtr;
+ }
+}
+
+}
+
+//-------------------------------------------------
+#define CAMERA_DEVICE_CLASS_NAME "android/hardware/camera2/impl/CameraDeviceImpl"
+static const JNINativeMethod gCameraDeviceMethods[] = {
+// static methods
+ { "nativeCreateFMQReader",
+ "(Landroid/os/Parcel;)J",
+ (void *)CameraDevice_createFMQReader},
+ { "nativeReadResultMetadata",
+ "(JJ)J",
+ (void *)CameraDevice_readResultMetadata},
+ { "nativeClose",
+ "(J)V",
+ (void*)CameraDevice_close},
+// instance methods
+};
+
+
+// Get all the required offsets in java class and register native functions
+int register_android_hardware_camera2_CameraDevice(JNIEnv *env)
+{
+ // Register native functions
+ return RegisterMethodsOrDie(env,
+ CAMERA_DEVICE_CLASS_NAME,
+ gCameraDeviceMethods,
+ NELEM(gCameraDeviceMethods));
+}
+
+extern "C" {
+
+} // extern "C"
\ No newline at end of file
diff --git a/core/proto/android/content/package_item_info.proto b/core/proto/android/content/package_item_info.proto
index b7408a4..facadee 100644
--- a/core/proto/android/content/package_item_info.proto
+++ b/core/proto/android/content/package_item_info.proto
@@ -114,6 +114,7 @@
optional int32 enable_memtag = 20;
optional bool native_heap_zero_init = 21;
optional bool allow_cross_uid_activity_switch_from_below = 22;
+ optional int32 enable_page_size_app_compat = 23;
}
optional Detail detail = 17;
repeated string overlay_paths = 18;
diff --git a/core/res/res/layout/notification_2025_conversation_face_pile_layout.xml b/core/res/res/layout/notification_2025_conversation_face_pile_layout.xml
new file mode 100644
index 0000000..b25adaa
--- /dev/null
+++ b/core/res/res/layout/notification_2025_conversation_face_pile_layout.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations underthe License
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/conversation_face_pile"
+ android:layout_width="@dimen/conversation_avatar_size"
+ android:layout_height="@dimen/conversation_avatar_size"
+ android:forceHasOverlappingRendering="false"
+ >
+ <ImageView
+ android:id="@+id/conversation_face_pile_top"
+ android:layout_width="@dimen/messaging_avatar_size"
+ android:layout_height="@dimen/messaging_avatar_size"
+ android:scaleType="centerCrop"
+ android:layout_gravity="end|top"
+ android:background="@drawable/notification_icon_circle"
+ android:clipToOutline="true"
+ />
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start|bottom">
+ <ImageView
+ android:id="@+id/conversation_face_pile_bottom_background"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/conversation_badge_background"
+ />
+ <ImageView
+ android:id="@+id/conversation_face_pile_bottom"
+ android:layout_width="@dimen/messaging_avatar_size"
+ android:layout_height="@dimen/messaging_avatar_size"
+ android:scaleType="centerCrop"
+ android:layout_gravity="center"
+ android:background="@drawable/notification_icon_circle"
+ android:clipToOutline="true"
+ />
+ </FrameLayout>
+</FrameLayout>
diff --git a/core/res/res/layout/notification_2025_conversation_icon_container.xml b/core/res/res/layout/notification_2025_conversation_icon_container.xml
new file mode 100644
index 0000000..90befd9
--- /dev/null
+++ b/core/res/res/layout/notification_2025_conversation_icon_container.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/conversation_icon_container"
+ android:layout_width="@dimen/conversation_content_start"
+ android:layout_height="wrap_content"
+ android:gravity="start|top"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:paddingTop="20dp"
+ android:paddingBottom="16dp"
+ android:importantForAccessibility="no"
+ >
+
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:layout_gravity="top|center_horizontal"
+ >
+
+ <!-- Big icon: 48x48, 12dp padding top, 16dp padding sides -->
+ <com.android.internal.widget.CachingIconView
+ android:id="@+id/conversation_icon"
+ android:layout_width="@dimen/conversation_avatar_size"
+ android:layout_height="@dimen/conversation_avatar_size"
+ android:layout_marginLeft="@dimen/conversation_badge_protrusion"
+ android:layout_marginRight="@dimen/conversation_badge_protrusion"
+ android:layout_marginBottom="@dimen/conversation_badge_protrusion"
+ android:background="@drawable/notification_icon_circle"
+ android:clipToOutline="true"
+ android:scaleType="centerCrop"
+ android:importantForAccessibility="no"
+ />
+
+ <ViewStub
+ android:layout="@layout/notification_2025_conversation_face_pile_layout"
+ android:layout_width="@dimen/conversation_avatar_size"
+ android:layout_height="@dimen/conversation_avatar_size"
+ android:layout_marginLeft="@dimen/conversation_badge_protrusion"
+ android:layout_marginRight="@dimen/conversation_badge_protrusion"
+ android:layout_marginBottom="@dimen/conversation_badge_protrusion"
+ android:id="@+id/conversation_face_pile"
+ />
+
+ <FrameLayout
+ android:id="@+id/conversation_icon_badge"
+ android:layout_width="@dimen/conversation_icon_size_badged"
+ android:layout_height="@dimen/conversation_icon_size_badged"
+ android:layout_gravity="end|bottom"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ >
+
+ <com.android.internal.widget.CachingIconView
+ android:id="@+id/conversation_icon_badge_bg"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:src="@drawable/conversation_badge_background"
+ android:forceHasOverlappingRendering="false"
+ android:scaleType="center"
+ />
+
+ <com.android.internal.widget.NotificationRowIconView
+ android:id="@+id/icon"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_margin="4dp"
+ android:layout_gravity="center"
+ android:forceHasOverlappingRendering="false"
+ />
+
+ <com.android.internal.widget.CachingIconView
+ android:id="@+id/conversation_icon_badge_ring"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:src="@drawable/conversation_badge_ring"
+ android:visibility="gone"
+ android:forceHasOverlappingRendering="false"
+ android:clipToPadding="false"
+ android:scaleType="center"
+ />
+ </FrameLayout>
+ </FrameLayout>
+</FrameLayout>
diff --git a/core/res/res/layout/notification_2025_messaging_group.xml b/core/res/res/layout/notification_2025_messaging_group.xml
new file mode 100644
index 0000000..c1b491f
--- /dev/null
+++ b/core/res/res/layout/notification_2025_messaging_group.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<!-- extends LinearLayout -->
+<com.android.internal.widget.MessagingGroup
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+ <FrameLayout
+ android:id="@+id/message_icon_container"
+ android:layout_width="@dimen/conversation_content_start"
+ android:layout_height="wrap_content">
+ <ImageView
+ android:layout_gravity="top|center_horizontal"
+ android:id="@+id/message_icon"
+ android:layout_width="@dimen/messaging_avatar_size"
+ android:layout_height="@dimen/messaging_avatar_size"
+ android:background="@drawable/notification_icon_circle"
+ android:clipToOutline="true"
+ android:scaleType="centerCrop"
+ android:importantForAccessibility="no" />
+ </FrameLayout>
+ <com.android.internal.widget.RemeasuringLinearLayout
+ android:id="@+id/messaging_group_content_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:baselineAligned="true"
+ android:orientation="vertical">
+ <com.android.internal.widget.ImageFloatingTextView
+ android:id="@+id/message_name"
+ style="@style/Widget.DeviceDefault.Notification.MessagingName"
+ android:layout_width="wrap_content"
+ android:textAlignment="viewStart"
+ />
+ <com.android.internal.widget.MessagingLinearLayout
+ android:id="@+id/group_message_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_text_margin_top"
+ android:spacing="2dp" />
+ </com.android.internal.widget.RemeasuringLinearLayout>
+ <FrameLayout
+ android:id="@+id/messaging_group_icon_container"
+ android:layout_width="@dimen/messaging_avatar_size"
+ android:layout_height="@dimen/messaging_avatar_size"
+ android:layout_marginStart="12dp"
+ android:visibility="gone"/>
+ <FrameLayout
+ android:id="@+id/messaging_group_sending_progress_container"
+ android:layout_width="@dimen/messaging_group_sending_progress_size"
+ android:layout_height="@dimen/messaging_avatar_size"
+ android:layout_marginStart="12dp"
+ android:layout_gravity="top"
+ android:visibility="gone">
+ <ProgressBar
+ android:id="@+id/messaging_group_sending_progress"
+ android:layout_height="@dimen/messaging_group_sending_progress_size"
+ android:layout_width="@dimen/messaging_group_sending_progress_size"
+ android:layout_gravity="center"
+ android:indeterminate="true"
+ style="?android:attr/progressBarStyleSmall" />
+ </FrameLayout>
+</com.android.internal.widget.MessagingGroup>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_base.xml b/core/res/res/layout/notification_2025_template_collapsed_base.xml
index 4eac6d9..a790e5d 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_base.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_base.xml
@@ -123,7 +123,7 @@
<!-- This is the simplest way to keep this text vertically centered without
gravity="center_vertical" which causes jumpiness in expansion animations. -->
<include
- layout="@layout/notification_2025_template_text"
+ layout="@layout/notification_2025_text"
android:layout_width="match_parent"
android:layout_height="@dimen/notification_text_height"
android:layout_gravity="center_vertical"
diff --git a/core/res/res/layout/notification_2025_template_collapsed_call.xml b/core/res/res/layout/notification_2025_template_collapsed_call.xml
new file mode 100644
index 0000000..06f5f06
--- /dev/null
+++ b/core/res/res/layout/notification_2025_template_collapsed_call.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<!-- Extends FrameLayout -->
+<com.android.internal.widget.CallLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:tag="call"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ >
+
+ <!-- CallLayout shares visual appearance with ConversationLayout, so shares layouts -->
+ <include layout="@layout/notification_2025_conversation_icon_container" />
+
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="88dp"
+ android:orientation="horizontal"
+ >
+
+ <LinearLayout
+ android:id="@+id/notification_main_column"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginStart="@dimen/conversation_content_start"
+ android:orientation="vertical"
+ android:minHeight="68dp"
+ android:paddingBottom="@dimen/notification_headerless_margin_twoline"
+ >
+
+ <include
+ layout="@layout/notification_template_conversation_header"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+
+ <include layout="@layout/notification_template_text"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_text_height"
+ />
+
+ </LinearLayout>
+
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/notification_content_margin_end"
+ >
+
+ <include
+ layout="@layout/notification_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ />
+
+ </FrameLayout>
+
+ </LinearLayout>
+
+</com.android.internal.widget.CallLayout>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_media.xml b/core/res/res/layout/notification_2025_template_collapsed_media.xml
new file mode 100644
index 0000000..427c4e4
--- /dev/null
+++ b/core/res/res/layout/notification_2025_template_collapsed_media.xml
@@ -0,0 +1,197 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<!-- Note: This is the old media style notification (different from UMO). -->
+
+<!-- extends FrameLayout -->
+<com.android.internal.widget.MediaNotificationView
+ android:id="@+id/status_bar_latest_event_content"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_min_height"
+ android:tag="media"
+ >
+
+
+ <ImageView
+ android:id="@+id/left_icon"
+ android:layout_width="@dimen/notification_2025_left_icon_size"
+ android:layout_height="@dimen/notification_2025_left_icon_size"
+ android:layout_gravity="center_vertical|start"
+ android:layout_marginStart="@dimen/notification_left_icon_start"
+ android:background="@drawable/notification_large_icon_outline"
+ android:clipToOutline="true"
+ android:importantForAccessibility="no"
+ android:scaleType="centerCrop"
+ android:visibility="gone"
+ />
+
+ <com.android.internal.widget.NotificationRowIconView
+ android:id="@+id/icon"
+ android:layout_width="@dimen/notification_2025_icon_circle_size"
+ android:layout_height="@dimen/notification_2025_icon_circle_size"
+ android:layout_gravity="center_vertical|start"
+ android:layout_marginStart="@dimen/notification_icon_circle_start"
+ android:background="@drawable/notification_icon_circle"
+ android:padding="@dimen/notification_2025_icon_circle_padding"
+ />
+
+ <FrameLayout
+ android:id="@+id/alternate_expand_target"
+ android:layout_width="@dimen/notification_2025_content_margin_start"
+ android:layout_height="match_parent"
+ android:layout_gravity="start"
+ android:importantForAccessibility="no"
+ android:focusable="false"
+ />
+
+ <LinearLayout
+ android:id="@+id/notification_headerless_view_row"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:orientation="horizontal"
+ >
+
+ <LinearLayout
+ android:id="@+id/notification_headerless_view_column"
+ android:layout_width="0px"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_weight="1"
+ android:layout_marginBottom="@dimen/notification_headerless_margin_twoline"
+ android:layout_marginTop="@dimen/notification_headerless_margin_twoline"
+ android:orientation="vertical"
+ >
+
+ <NotificationTopLineView
+ android:id="@+id/notification_top_line"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_headerless_line_height"
+ android:clipChildren="false"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ >
+
+ <!--
+ NOTE: The notification_top_line_views layout contains the app_name_text.
+ In order to include the title view at the beginning, the Notification.Builder
+ has logic to hide that view whenever this title view is to be visible.
+ -->
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:ellipsize="end"
+ android:fadingEdge="horizontal"
+ android:singleLine="true"
+ android:textAlignment="viewStart"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
+ />
+
+ <include layout="@layout/notification_top_line_views" />
+
+ </NotificationTopLineView>
+
+ <LinearLayout
+ android:id="@+id/notification_main_column"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ >
+
+ <com.android.internal.widget.NotificationVanishingFrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_headerless_line_height"
+ >
+ <!-- This is the simplest way to keep this text vertically centered without
+ gravity="center_vertical" which causes jumpiness in expansion animations. -->
+ <include
+ layout="@layout/notification_template_text"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_text_height"
+ android:layout_gravity="center_vertical"
+ android:layout_marginTop="0dp"
+ />
+ </com.android.internal.widget.NotificationVanishingFrameLayout>
+
+ <include
+ layout="@layout/notification_template_progress"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_headerless_line_height"
+ />
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/right_icon"
+ android:layout_width="@dimen/notification_right_icon_size"
+ android:layout_height="@dimen/notification_right_icon_size"
+ android:layout_gravity="center_vertical|end"
+ android:layout_marginTop="@dimen/notification_right_icon_headerless_margin"
+ android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin"
+ android:layout_marginStart="@dimen/notification_right_icon_content_margin"
+ android:background="@drawable/notification_large_icon_outline"
+ android:clipToOutline="true"
+ android:importantForAccessibility="no"
+ android:scaleType="centerCrop"
+ />
+
+ <LinearLayout
+ android:id="@+id/media_actions"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layoutDirection="ltr"
+ android:orientation="horizontal"
+ >
+ <include
+ layout="@layout/notification_material_media_action"
+ android:id="@+id/action0"
+ />
+ <include
+ layout="@layout/notification_material_media_action"
+ android:id="@+id/action1"
+ />
+ <include
+ layout="@layout/notification_material_media_action"
+ android:id="@+id/action2"
+ />
+ </LinearLayout>
+
+ <FrameLayout
+ android:id="@+id/expand_button_touch_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/notification_content_margin_end"
+ >
+
+ <include layout="@layout/notification_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|end"
+ />
+
+ </FrameLayout>
+
+ </LinearLayout>
+</com.android.internal.widget.MediaNotificationView>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
new file mode 100644
index 0000000..f0e4c0f
--- /dev/null
+++ b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
@@ -0,0 +1,220 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2016 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<!-- Note: This is the old "Messaging Style" notification (not a conversation). -->
+
+<!-- extends FrameLayout -->
+<com.android.internal.widget.MessagingLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:tag="messaging"
+ >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:orientation="vertical"
+ >
+
+
+ <com.android.internal.widget.NotificationMaxHeightFrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_min_height"
+ android:clipChildren="false"
+ >
+
+ <ImageView
+ android:id="@+id/left_icon"
+ android:layout_width="@dimen/notification_2025_left_icon_size"
+ android:layout_height="@dimen/notification_2025_left_icon_size"
+ android:layout_gravity="center_vertical|start"
+ android:layout_marginStart="@dimen/notification_left_icon_start"
+ android:background="@drawable/notification_large_icon_outline"
+ android:clipToOutline="true"
+ android:importantForAccessibility="no"
+ android:scaleType="centerCrop"
+ android:visibility="gone"
+ />
+
+ <com.android.internal.widget.NotificationRowIconView
+ android:id="@+id/icon"
+ android:layout_width="@dimen/notification_2025_icon_circle_size"
+ android:layout_height="@dimen/notification_2025_icon_circle_size"
+ android:layout_gravity="center_vertical|start"
+ android:layout_marginStart="@dimen/notification_icon_circle_start"
+ android:background="@drawable/notification_icon_circle"
+ android:padding="@dimen/notification_2025_icon_circle_padding"
+ />
+
+ <FrameLayout
+ android:id="@+id/alternate_expand_target"
+ android:layout_width="@dimen/notification_2025_content_margin_start"
+ android:layout_height="match_parent"
+ android:layout_gravity="start"
+ android:importantForAccessibility="no"
+ android:focusable="false"
+ />
+
+ <!--
+ NOTE: to make the expansion animation of id/notification_messaging happen vertically,
+ its X positioning must be the left edge of the notification, so instead of putting the
+ layout_marginStart on the id/notification_headerless_view_row, we put it on
+ id/notification_top_line, making the layout here just a bit different from the base.
+ -->
+ <LinearLayout
+ android:id="@+id/notification_headerless_view_row"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:clipChildren="false"
+ >
+
+ <!--
+ NOTE: because messaging will always have 2 lines, this LinearLayout should NOT
+ have the id/notification_headerless_view_column, as that is used for modifying
+ vertical margins to accommodate the single-line state that base supports
+ -->
+ <LinearLayout
+ android:layout_width="0px"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_weight="1"
+ android:layout_marginBottom="@dimen/notification_headerless_margin_twoline"
+ android:layout_marginTop="@dimen/notification_headerless_margin_twoline"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:clipChildren="false"
+ android:orientation="vertical"
+ >
+
+ <NotificationTopLineView
+ android:id="@+id/notification_top_line"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_headerless_line_height"
+ android:clipChildren="false"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ >
+
+ <!--
+ NOTE: The notification_top_line_views layout contains the app_name_text.
+ In order to include the title view at the beginning, the Notification.Builder
+ has logic to hide that view whenever this title view is to be visible.
+ -->
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:ellipsize="end"
+ android:fadingEdge="horizontal"
+ android:singleLine="true"
+ android:textAlignment="viewStart"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
+ />
+
+ <include layout="@layout/notification_top_line_views" />
+
+ </NotificationTopLineView>
+
+ <LinearLayout
+ android:id="@+id/notification_main_column"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:clipChildren="false"
+ >
+ <com.android.internal.widget.MessagingLinearLayout
+ android:id="@+id/notification_messaging"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:spacing="@dimen/notification_messaging_spacing" />
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <!-- Images -->
+ <com.android.internal.widget.MessagingLinearLayout
+ android:id="@+id/conversation_image_message_container"
+ android:layout_width="@dimen/notification_right_icon_size"
+ android:layout_height="@dimen/notification_right_icon_size"
+ android:layout_gravity="center_vertical|end"
+ android:layout_marginTop="@dimen/notification_right_icon_headerless_margin"
+ android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin"
+ android:layout_marginStart="@dimen/notification_right_icon_content_margin"
+ android:forceHasOverlappingRendering="false"
+ android:spacing="0dp"
+ android:clipChildren="false"
+ android:visibility="gone"
+ />
+
+ <ImageView
+ android:id="@+id/right_icon"
+ android:layout_width="@dimen/notification_right_icon_size"
+ android:layout_height="@dimen/notification_right_icon_size"
+ android:layout_gravity="center_vertical|end"
+ android:layout_marginTop="@dimen/notification_right_icon_headerless_margin"
+ android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin"
+ android:layout_marginStart="@dimen/notification_right_icon_content_margin"
+ android:background="@drawable/notification_large_icon_outline"
+ android:clipToOutline="true"
+ android:importantForAccessibility="no"
+ android:scaleType="centerCrop"
+ />
+
+ <FrameLayout
+ android:id="@+id/expand_button_touch_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/notification_content_margin_end"
+ >
+
+ <include layout="@layout/notification_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|end"
+ />
+
+ </FrameLayout>
+
+ </LinearLayout>
+
+ </com.android.internal.widget.NotificationMaxHeightFrameLayout>
+
+ <LinearLayout
+ android:id="@+id/notification_action_list_margin_target"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="-20dp"
+ android:clipChildren="false"
+ android:orientation="vertical">
+ <include layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_content_margin"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end" />
+ <include layout="@layout/notification_material_action_list" />
+ </LinearLayout>
+</LinearLayout>
+</com.android.internal.widget.MessagingLayout>
diff --git a/core/res/res/layout/notification_2025_template_conversation.xml b/core/res/res/layout/notification_2025_template_conversation.xml
new file mode 100644
index 0000000..0c4c7fb
--- /dev/null
+++ b/core/res/res/layout/notification_2025_template_conversation.xml
@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<!-- extends FrameLayout -->
+<com.android.internal.widget.ConversationLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:tag="conversation"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ >
+
+ <include layout="@layout/notification_2025_conversation_icon_container" />
+
+ <!-- Wraps entire "expandable" notification -->
+ <com.android.internal.widget.RemeasuringLinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:clipToPadding="false"
+ android:clipChildren="false"
+ android:orientation="vertical"
+ >
+ <!-- LinearLayout for Expand Button-->
+ <com.android.internal.widget.RemeasuringLinearLayout
+ android:id="@+id/expand_button_and_content_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="start|top"
+ android:orientation="horizontal"
+ android:clipChildren="false"
+ android:clipToPadding="false">
+ <!--TODO: move this into a separate layout and share logic with the header to bring back app opps etc-->
+ <com.android.internal.widget.RemeasuringLinearLayout
+ android:id="@+id/notification_action_list_margin_target"
+ android:layout_width="0dp"
+ android:orientation="vertical"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+
+ <!-- Header -->
+
+ <!-- Use layout_marginStart instead of paddingStart to work around strange
+ measurement behavior on lower display densities. -->
+ <include
+ layout="@layout/notification_template_conversation_header"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="2dp"
+ android:layout_marginStart="@dimen/conversation_content_start"
+ />
+
+ <!-- Messages -->
+ <com.android.internal.widget.MessagingLinearLayout
+ android:id="@+id/notification_messaging"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_text_size"
+ android:spacing="@dimen/notification_messaging_spacing"
+ android:clipToPadding="false"
+ android:clipChildren="false"
+ />
+ </com.android.internal.widget.RemeasuringLinearLayout>
+
+ <!-- This is where the expand button container will be placed when collapsed-->
+ </com.android.internal.widget.RemeasuringLinearLayout>
+
+ <include layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_content_margin"
+ android:layout_marginStart="@dimen/conversation_content_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end" />
+ <include layout="@layout/notification_material_action_list" />
+ </com.android.internal.widget.RemeasuringLinearLayout>
+
+ <!--expand_button_a11y_container ensures talkback focus order is correct when view is expanded.
+ The -1px of marginTop and 1px of paddingTop make sure expand_button_a11y_container is prior to
+ its sibling view in accessibility focus order.
+ {see android.view.ViewGroup.addChildrenForAccessibility()}
+ expand_button_container will be moved under expand_button_and_content_container when collapsed,
+ this dynamic movement ensures message can flow under expand button when expanded-->
+ <FrameLayout
+ android:id="@+id/expand_button_a11y_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="end|top"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:layout_marginTop="-1px"
+ android:paddingTop="1px"
+ >
+ <!--expand_button_container is dynamically placed between here and at the end of the
+ layout. It starts here since only FrameLayout layout params have gravity-->
+ <LinearLayout
+ android:id="@+id/expand_button_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="end|top"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:orientation="vertical">
+ <include layout="@layout/notification_close_button"
+ android:layout_width="@dimen/notification_close_button_size"
+ android:layout_height="@dimen/notification_close_button_size"
+ android:layout_gravity="end"
+ android:layout_marginEnd="20dp"
+ />
+ <!--expand_button_touch_container makes sure that we can nicely center the expand
+ content in the collapsed layout while the parent makes sure that we're never laid out
+ bigger than the messaging content.-->
+ <LinearLayout
+ android:id="@+id/expand_button_touch_container"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/conversation_expand_button_height"
+ android:orientation="horizontal"
+ android:layout_gravity="end|top"
+ android:paddingEnd="0dp"
+ android:clipToPadding="false"
+ android:clipChildren="false"
+ >
+ <!-- Images -->
+ <com.android.internal.widget.MessagingLinearLayout
+ android:id="@+id/conversation_image_message_container"
+ android:forceHasOverlappingRendering="false"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:layout_marginStart="@dimen/conversation_image_start_margin"
+ android:spacing="0dp"
+ android:layout_gravity="center"
+ android:clipToPadding="false"
+ android:clipChildren="false"
+ />
+ <include layout="@layout/notification_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ />
+ </LinearLayout>
+ </LinearLayout>
+ </FrameLayout>
+</com.android.internal.widget.ConversationLayout>
diff --git a/core/res/res/layout/notification_2025_template_expanded_call.xml b/core/res/res/layout/notification_2025_template_expanded_call.xml
new file mode 100644
index 0000000..3ff71b7
--- /dev/null
+++ b/core/res/res/layout/notification_2025_template_expanded_call.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<!-- Extends FrameLayout -->
+<com.android.internal.widget.CallLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:tag="call"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ >
+
+ <!-- CallLayout shares visual appearance with ConversationLayout, so shares layouts -->
+ <include layout="@layout/notification_2025_conversation_icon_container" />
+
+ <LinearLayout
+ android:id="@+id/notification_action_list_margin_target"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/notification_content_margin"
+ android:orientation="vertical"
+ >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ >
+
+ <LinearLayout
+ android:id="@+id/notification_main_column"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginStart="@dimen/conversation_content_start"
+ android:orientation="vertical"
+ android:minHeight="68dp"
+ >
+
+ <include
+ layout="@layout/notification_template_conversation_header"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+
+ <include layout="@layout/notification_template_text_multiline" />
+
+ <include
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_progress_bar_height"
+ android:layout_marginTop="@dimen/notification_progress_margin_top"
+ layout="@layout/notification_template_progress"
+ />
+ </LinearLayout>
+
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/notification_content_margin_end"
+ >
+
+ <include
+ layout="@layout/notification_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+
+ </FrameLayout>
+
+ </LinearLayout>
+
+ <ViewStub
+ android:layout="@layout/notification_material_reply_text"
+ android:id="@+id/notification_material_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
+
+ <include
+ layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginTop="@dimen/notification_content_margin"
+ />
+
+ <include layout="@layout/notification_material_action_list" />
+
+ </LinearLayout>
+
+</com.android.internal.widget.CallLayout>
diff --git a/core/res/res/layout/notification_2025_template_text.xml b/core/res/res/layout/notification_2025_text.xml
similarity index 100%
rename from core/res/res/layout/notification_2025_template_text.xml
rename to core/res/res/layout/notification_2025_text.xml
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 7ef5394..08cb4de 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1867,8 +1867,12 @@
16 KB device. 4 KB natives libs will be loaded app-compat mode if they are eligible.
@FlaggedApi(android.content.pm.Flags.FLAG_APP_COMPAT_OPTION_16KB) -->
<attr name="pageSizeCompat">
- <enum name="enabled" value="5" />
- <enum name="disabled" value="6" />
+ <!-- value for enabled must match with
+ ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_ENABLED -->
+ <enum name="enabled" value="32" />
+ <!-- value for disabled must match with
+ ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_DISABLED -->
+ <enum name="disabled" value="64" />
</attr>
@@ -2568,6 +2572,8 @@
against a development branch, in which case it will only work against
the development builds. -->
<attr name="minSdkVersion" format="integer|string" />
+ <!-- @FlaggedApi(android.content.pm.Flags.FLAG_SUPPORT_MINOR_VERSIONS_IN_MINSDKVERSION) -->
+ <attr name="minSdkVersionFull" format="string" />
<!-- This is the SDK version number that the application is targeting.
It is able to run on older versions (down to minSdkVersion), but
was explicitly tested to work with the version specified here.
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 38ebda7..0d13ca8 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2779,6 +2779,9 @@
If empty, logs "other" for all. -->
<string-array name="config_loggable_dream_prefixes"></string-array>
+ <!-- Whether to enable glanceable hub features on this device. -->
+ <bool name="config_glanceableHubEnabled">false</bool>
+
<!-- ComponentName of a dream to show whenever the system would otherwise have
gone to sleep. When the PowerManager is asked to go to sleep, it will instead
try to start this dream if possible. The dream should typically call startDozing()
@@ -6003,6 +6006,12 @@
<!-- If true, show multiuser switcher by default unless the user specifically disables it. -->
<bool name="config_showUserSwitcherByDefault">false</bool>
+ <!-- If true, user can change state of multiuser switcher. -->
+ <bool name="config_allowChangeUserSwitcherEnabled">true</bool>
+
+ <!-- If true, multiuser switcher would be automatically enabled when second user is created on the device. -->
+ <bool name="config_enableUserSwitcherUponUserCreation">true</bool>
+
<!-- Set to true to make assistant show in front of the dream/screensaver. -->
<bool name="config_assistantOnTopOfDream">false</bool>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index e75371d..76ff565 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -135,6 +135,8 @@
<public name="pageSizeCompat" />
<!-- @FlaggedApi(android.nfc.Flags.FLAG_NFC_ASSOCIATED_ROLE_SERVICES) -->
<public name="shareRolePriority"/>
+ <!-- @FlaggedApi(android.content.pm.Flags.FLAG_SUPPORT_MINOR_VERSIONS_IN_MINSDKVERSION) -->
+ <public name="minSdkVersionFull"/>
</staging-public-group>
<staging-public-group type="id" first-id="0x01b60000">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 413f0c3..d498b91 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -199,6 +199,21 @@
<!-- Displayed to confirm to the user that caller ID will not be restricted on the next call or in general. -->
<string name="CLIRDefaultOffNextCallOff">Caller ID defaults to not restricted. Next call: Not restricted</string>
+ <!-- Message displayed in dialog when APK is not 16 KB aligned. [CHAR LIMIT=NONE] -->
+ <string name="page_size_compat_apk_warning">This app isn’t 16 KB compatible. APK alignment check failed.
+ This app will be run using page size compatible mode. For best compatibility, please recompile the application with 16 KB support.
+ For more information, see <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> </string>
+
+ <!-- Message displayed in dialog when ELF is not 16 KB aligned. [CHAR LIMIT=NONE] -->
+ <string name="page_size_compat_elf_warning">This app isn’t 16 KB compatible. ELF alignment check failed.
+ This app will be run using page size compatible mode. For best compatibility, please recompile the application with 16 KB support.
+ For more information, see <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a></string>
+
+ <!-- Message displayed in dialog when APK and ELF are not 16 KB aligned. [CHAR LIMIT=NONE] -->
+ <string name="page_size_compat_apk_and_elf_warning">This app isn’t 16 KB compatible. APK and ELF alignment checks failed.
+ This app will be run using page size compatible mode. For best compatibility, please recompile the application with 16 KB support.
+ For more information, see <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a></string>
+
<!-- Displayed to tell the user that caller ID is not provisioned for their SIM. -->
<string name="serviceNotProvisioned">Service not provisioned.</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index fc24f45..4f029cd 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2054,6 +2054,7 @@
<java-symbol type="bool" name="config_allowTheaterModeWakeFromDock" />
<java-symbol type="bool" name="config_allowTheaterModeWakeFromWindowLayout" />
<java-symbol type="bool" name="config_keepDreamingWhenUnplugging" />
+ <java-symbol type="bool" name="config_glanceableHubEnabled" />
<java-symbol type="integer" name="config_keyguardDrawnTimeout" />
<java-symbol type="bool" name="config_goToSleepOnButtonPressTheaterMode" />
<java-symbol type="bool" name="config_supportLongPressPowerWhenNonInteractive" />
@@ -2393,6 +2394,8 @@
<java-symbol type="layout" name="notification_2025_template_expanded_base" />
<java-symbol type="layout" name="notification_2025_template_heads_up_base" />
<java-symbol type="layout" name="notification_2025_template_header" />
+ <java-symbol type="layout" name="notification_2025_template_collapsed_messaging" />
+ <java-symbol type="layout" name="notification_2025_template_collapsed_media" />
<java-symbol type="layout" name="notification_template_material_base" />
<java-symbol type="layout" name="notification_template_material_heads_up_base" />
<java-symbol type="layout" name="notification_template_material_compact_heads_up_base" />
@@ -3310,6 +3313,11 @@
<java-symbol type="string" name="language_selection_title" />
<java-symbol type="string" name="search_language_hint" />
+ <!-- Strings for page size app compat dialog -->
+ <java-symbol type="string" name="page_size_compat_apk_warning" />
+ <java-symbol type="string" name="page_size_compat_elf_warning" />
+ <java-symbol type="string" name="page_size_compat_apk_and_elf_warning" />
+
<!-- Work profile unlaunchable app alert dialog-->
<java-symbol type="style" name="AlertDialogWithEmergencyButton"/>
<java-symbol type="string" name="work_mode_emergency_call_button" />
@@ -3441,6 +3449,8 @@
<!-- Notifications: CallStyle -->
<java-symbol type="layout" name="notification_template_material_call" />
<java-symbol type="layout" name="notification_template_material_big_call" />
+ <java-symbol type="layout" name="notification_2025_template_collapsed_call" />
+ <java-symbol type="layout" name="notification_2025_template_expanded_call" />
<java-symbol type="string" name="call_notification_answer_action" />
<java-symbol type="string" name="call_notification_answer_video_action" />
<java-symbol type="string" name="call_notification_decline_action" />
@@ -4092,6 +4102,7 @@
<java-symbol type="layout" name="notification_template_messaging_text_message" />
<java-symbol type="layout" name="notification_template_messaging_image_message" />
<java-symbol type="layout" name="notification_template_messaging_group" />
+ <java-symbol type="layout" name="notification_2025_messaging_group" />
<java-symbol type="id" name="message_text" />
<java-symbol type="id" name="message_name" />
<java-symbol type="id" name="message_icon" />
@@ -4622,9 +4633,11 @@
<java-symbol type="dimen" name="conversation_icon_container_top_padding" />
<java-symbol type="dimen" name="conversation_icon_container_top_padding_small_avatar" />
<java-symbol type="layout" name="notification_template_material_conversation" />
+ <java-symbol type="layout" name="notification_2025_template_conversation" />
<java-symbol type="dimen" name="button_padding_horizontal_material" />
<java-symbol type="dimen" name="button_inset_horizontal_material" />
<java-symbol type="layout" name="conversation_face_pile_layout" />
+ <java-symbol type="layout" name="notification_2025_conversation_face_pile_layout" />
<java-symbol type="string" name="unread_convo_overflow" />
<java-symbol type="drawable" name="conversation_badge_background" />
<java-symbol type="drawable" name="conversation_badge_ring" />
@@ -4695,6 +4708,8 @@
<!-- If true, show multiuser switcher by default unless the user specifically disables it. -->
<java-symbol type="bool" name="config_showUserSwitcherByDefault" />
+ <java-symbol type="bool" name="config_allowChangeUserSwitcherEnabled" />
+ <java-symbol type="bool" name="config_enableUserSwitcherUponUserCreation" />
<!-- Set to true to make assistant show in front of the dream/screensaver. -->
<java-symbol type="bool" name="config_assistantOnTopOfDream"/>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable-mdpi/border_ltr.9.png b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable-mdpi/border_ltr.9.png
new file mode 100644
index 0000000..c7e937c
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable-mdpi/border_ltr.9.png
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable-mdpi/border_tr.9.png b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable-mdpi/border_tr.9.png
new file mode 100644
index 0000000..a3cff98
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable-mdpi/border_tr.9.png
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/power_other_24.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/power_other_24.xml
new file mode 100644
index 0000000..c9fb53e
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/power_other_24.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="?attr/colorControlNormal"
+ android:viewportHeight="960"
+ android:viewportWidth="960">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M720,600L720,520L800,520Q817,520 828.5,531.5Q840,543 840,560Q840,577 828.5,588.5Q817,600 800,600L720,600ZM720,760L720,680L800,680Q817,680 828.5,691.5Q840,703 840,720Q840,737 828.5,748.5Q817,760 800,760L720,760ZM560,800Q527,800 503.5,776.5Q480,753 480,720L400,720L400,560L480,560Q480,527 503.5,503.5Q527,480 560,480L680,480L680,800L560,800ZM280,680Q214,680 167,633Q120,586 120,520Q120,454 167,407Q214,360 280,360L340,360Q365,360 382.5,342.5Q400,325 400,300Q400,275 382.5,257.5Q365,240 340,240L200,240Q183,240 171.5,228.5Q160,217 160,200Q160,183 171.5,171.5Q183,160 200,160L340,160Q398,160 439,201Q480,242 480,300Q480,358 439,399Q398,440 340,440L280,440Q247,440 223.5,463.5Q200,487 200,520Q200,553 223.5,576.5Q247,600 280,600L360,600L360,680L280,680Z" />
+</vector>
\ No newline at end of file
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/screen_off_24.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/screen_off_24.xml
new file mode 100644
index 0000000..ca94825
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/screen_off_24.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="?attr/colorControlNormal"
+ android:viewportHeight="960"
+ android:viewportWidth="960">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M280,920Q247,920 223.5,896.5Q200,873 200,840L200,120Q200,87 223.5,63.5Q247,40 280,40L680,40Q713,40 736.5,63.5Q760,87 760,120L760,840Q760,873 736.5,896.5Q713,920 680,920L280,920ZM280,800L280,840Q280,840 280,840Q280,840 280,840L680,840Q680,840 680,840Q680,840 680,840L680,800L280,800ZM280,720L680,240L280,720ZM280,160L680,160L680,120Q680,120 680,120Q680,120 680,120L280,120Q280,120 280,120Q280,120 280,120L280,160ZM280,160L280,120Q280,120 280,120Q280,120 280,120L280,120Q280,120 280,120Q280,120 280,120L280,160ZM280,800L280,800L280,840Q280,840 280,840Q280,840 280,840L280,840Q280,840 280,840Q280,840 280,840L280,800Z" />
+</vector>
\ No newline at end of file
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/screen_on_24.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/screen_on_24.xml
new file mode 100644
index 0000000..48f990c
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/screen_on_24.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="?attr/colorControlNormal"
+ android:viewportHeight="960"
+ android:viewportWidth="960">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M280,920Q247,920 223.5,896.5Q200,873 200,840L200,120Q200,87 223.5,63.5Q247,40 280,40L680,40Q713,40 736.5,63.5Q760,87 760,120L760,840Q760,873 736.5,896.5Q713,920 680,920L280,920ZM280,800L280,840Q280,840 280,840Q280,840 280,840L680,840Q680,840 680,840Q680,840 680,840L680,800L280,800ZM280,720L680,720L680,240L280,240L280,720ZM280,160L680,160L680,120Q680,120 680,120Q680,120 680,120L280,120Q280,120 280,120Q280,120 280,120L280,160ZM280,160L280,120Q280,120 280,120Q280,120 280,120L280,120Q280,120 280,120Q280,120 280,120L280,160ZM280,800L280,800L280,840Q280,840 280,840Q280,840 280,840L280,840Q280,840 280,840Q280,840 280,840L280,800Z" />
+</vector>
\ No newline at end of file
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_entry_layout.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_entry_layout.xml
index be0e135..e1f4623 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_entry_layout.xml
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_entry_layout.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -14,46 +13,127 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:minHeight="?android:attr/listPreferredItemHeightSmall"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:paddingTop="8dp"
- android:paddingBottom="8dp">
+ android:orientation="vertical">
- <ImageView
- android:id="@+id/icon"
- android:layout_width="wrap_content"
+ <LinearLayout
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginEnd="8dp"
- android:paddingBottom="8dp"/>
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:orientation="horizontal"
+ android:paddingBottom="8dp"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingTop="8dp">
- <TextView
- android:id="@+id/title"
- android:layout_width="0dp"
- android:layout_weight="1"
- android:layout_height="wrap_content"
- android:textAppearance="@style/TextAppearanceBody"/>
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginEnd="8dp"
+ android:paddingBottom="8dp" />
- <TextView
- android:id="@+id/value1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="8dp"
- android:gravity="right"
- android:maxLines="1"
- android:textAppearance="@style/TextAppearanceBody"/>
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="@style/TextAppearanceBody" />
- <TextView
- android:id="@+id/value2"
- android:layout_width="76dp"
+ <TextView
+ android:id="@+id/value1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="8dp"
+ android:gravity="right"
+ android:maxLines="1"
+ android:textAppearance="@style/TextAppearanceBody" />
+
+ <TextView
+ android:id="@+id/value2"
+ android:layout_width="76dp"
+ android:layout_height="wrap_content"
+ android:gravity="right"
+ android:maxLines="1"
+ android:textAppearance="@style/TextAppearanceBody" />
+ </LinearLayout>
+
+ <TableLayout
+ android:id="@+id/table"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:gravity="right"
- android:maxLines="1"
- android:textAppearance="@style/TextAppearanceBody"/>
+ android:layout_marginEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:layout_marginStart="50dp"
+ android:stretchColumns="1,2,3,4">
+
+ <TableRow android:background="#EEFFEE">
+ <LinearLayout
+ style="@style/TableCell.Start"
+ android:layout_width="65dp">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="3dip"
+ android:text="State"
+ android:textStyle="bold" />
+ </LinearLayout>
+
+ <RelativeLayout style="@style/TableCell.Inner">
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:src="@drawable/screen_on_24"
+ android:tint="@color/battery_consumer_slice_icon" />
+ </RelativeLayout>
+
+ <RelativeLayout style="@style/TableCell.Inner">
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:src="@drawable/screen_off_24"
+ android:tint="@color/battery_consumer_slice_icon" />
+ </RelativeLayout>
+
+ <RelativeLayout style="@style/TableCell.Inner">
+ <ImageView
+ android:id="@+id/screen_on_24_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:src="@drawable/screen_on_24"
+ android:tint="@color/battery_consumer_slice_icon" />
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/screen_on_24_icon"
+ android:src="@drawable/power_other_24"
+ android:tint="@color/battery_consumer_slice_icon" />
+ </RelativeLayout>
+
+ <RelativeLayout style="@style/TableCell.End">
+ <ImageView
+ android:id="@+id/screen_off_24_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:src="@drawable/screen_off_24"
+ android:tint="@color/battery_consumer_slice_icon" />
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/screen_off_24_icon"
+ android:src="@drawable/power_other_24"
+ android:tint="@color/battery_consumer_slice_icon" />
+ </RelativeLayout>
+ </TableRow>
+
+ <View
+ android:layout_height="1dip"
+ android:background="#000000" />
+ </TableLayout>
</LinearLayout>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_picker_layout.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_picker_layout.xml
index 987de6b..b88425a 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_picker_layout.xml
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_picker_layout.xml
@@ -14,16 +14,30 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/swipe_refresh"
- android:paddingTop="?attr/actionBarSize"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:fitsSystemWindows="true">
- <androidx.recyclerview.widget.RecyclerView
- android:id="@+id/list_view"
+ <androidx.appcompat.widget.Toolbar
+ android:id="@+id/toolbar"
android:layout_width="match_parent"
- android:layout_height="match_parent"/>
+ android:layout_height="?attr/actionBarSize"
+ android:background="?attr/colorPrimary"
+ android:elevation="4dp"
+ android:theme="@style/ThemeOverlay.AppCompat.ActionBar" />
-</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
+ <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
+ android:id="@+id/swipe_refresh"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1">
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/list_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
+</LinearLayout>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_slices_layout.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_slices_layout.xml
new file mode 100644
index 0000000..642c0de
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_slices_layout.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<TableRow xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <LinearLayout style="@style/TableCell.Start">
+ <TextView
+ android:id="@+id/procState"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+
+ <LinearLayout
+ style="@style/TableCell.Inner"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/power_b_on"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="right" />
+ <TextView
+ android:id="@+id/duration_b_on"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="right" />
+ </LinearLayout>
+
+ <LinearLayout
+ style="@style/TableCell.Inner"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/power_b_off"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="right" />
+ <TextView
+ android:id="@+id/duration_b_off"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="right" />
+ </LinearLayout>
+
+ <LinearLayout
+ style="@style/TableCell.Inner"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/power_c_on"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="right" />
+ <TextView
+ android:id="@+id/duration_c_on"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="right" />
+ </LinearLayout>
+
+ <LinearLayout
+ style="@style/TableCell.End"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/power_c_off"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="right" />
+ <TextView
+ android:id="@+id/duration_c_off"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="right" />
+ </LinearLayout>
+</TableRow>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml
index 2d276a5..46d8f04 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml
@@ -17,13 +17,12 @@
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/swipe_refresh"
- android:paddingTop="?attr/actionBarSize"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:fitsSystemWindows="true">
<LinearLayout
android:orientation="vertical"
- android:paddingTop="?attr/actionBarSize"
android:layout_width="match_parent"
android:layout_height="match_parent">
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/values/colors.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/values/colors.xml
index 6cc70bd..1dc288a 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/res/values/colors.xml
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/values/colors.xml
@@ -18,4 +18,5 @@
<resources>
<color name="battery_consumer_bg_power_profile">#ffffff</color>
<color name="battery_consumer_bg_energy_consumption">#fff5eb</color>
+ <color name="battery_consumer_slice_icon">#aaaaaa</color>
</resources>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/values/styles.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/values/styles.xml
index fa30b2c..a298cc9 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/res/values/styles.xml
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/values/styles.xml
@@ -17,11 +17,9 @@
-->
<resources>
- <style name="Theme" parent="Theme.MaterialComponents.Light">
+ <style name="Theme" parent="Theme.MaterialComponents.Light.NoActionBar">
<item name="colorPrimary">#34a853</item>
- <item name="android:windowActionBar">true</item>
- <item name="android:windowNoTitle">false</item>
- <item name="android:windowDrawsSystemBarBackgrounds">false</item>
+ <item name="toolbarStyle">@style/Widget.AppCompat.Toolbar</item>
</style>
<style name="LoadTestCardView" parent="Widget.MaterialComponents.CardView">
@@ -32,4 +30,25 @@
<item name="android:textColor">#000000</item>
<item name="android:textSize">18sp</item>
</style>
-</resources>
\ No newline at end of file
+
+ <style name="TableCell">
+ <item name="android:layout_height">match_parent</item>
+ <item name="android:padding">4dp</item>
+ </style>
+
+ <style name="TableCell.Start" parent="TableCell">
+ <item name="android:background">@drawable/border_ltr</item>
+ </style>
+
+ <style name="TableCell.Inner" parent="TableCell">
+ <item name="android:background">@drawable/border_tr</item>
+ <item name="android:layout_width">0dp</item>
+ <item name="android:layout_weight">1</item>
+ </style>
+
+ <style name="TableCell.End" parent="TableCell">
+ <item name="android:background">@drawable/border_tr</item>
+ <item name="android:layout_width">0dp</item>
+ <item name="android:layout_weight">1</item>
+ </style>
+</resources>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java
index f691a1b..35175a7 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java
+++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java
@@ -31,22 +31,16 @@
public static final String UID_BATTERY_CONSUMER_ID_PREFIX = "APP|";
public static final String AGGREGATE_BATTERY_CONSUMER_ID = "SYS|";
- enum EntryType {
- UID_TOTAL_POWER,
- UID_POWER_PROFILE,
- UID_POWER_PROFILE_PROCESS_STATE,
- UID_POWER_ENERGY_CONSUMPTION,
- UID_POWER_ENERGY_PROCESS_STATE,
- UID_POWER_CUSTOM,
- UID_DURATION,
+ public enum EntryType {
DEVICE_TOTAL_POWER,
- DEVICE_POWER_MODELED,
+ DEVICE_POWER,
DEVICE_POWER_ENERGY_CONSUMPTION,
DEVICE_POWER_CUSTOM,
DEVICE_DURATION,
+ UID,
}
- enum ConsumerType {
+ public enum ConsumerType {
UID_BATTERY_CONSUMER,
DEVICE_POWER_COMPONENT,
}
@@ -56,34 +50,38 @@
public String title;
public double value1;
public double value2;
+ public List<Slice> slices;
+ }
+
+ public static class Slice {
+ public int powerState;
+ public int screenState;
+ public int processState;
+ public double powerMah;
+ public long durationMs;
}
private BatteryConsumerInfoHelper.BatteryConsumerInfo mBatteryConsumerInfo;
private final List<Entry> mEntries = new ArrayList<>();
public BatteryConsumerData(Context context,
- List<BatteryUsageStats> batteryUsageStatsList, String batteryConsumerId) {
+ BatteryUsageStats batteryUsageStats, String batteryConsumerId) {
switch (getConsumerType(batteryConsumerId)) {
case UID_BATTERY_CONSUMER:
- populateForUidBatteryConsumer(context, batteryUsageStatsList, batteryConsumerId);
+ populateForUidBatteryConsumer(context, batteryUsageStats, batteryConsumerId);
break;
case DEVICE_POWER_COMPONENT:
- populateForAggregateBatteryConsumer(context, batteryUsageStatsList);
+ populateForAggregateBatteryConsumer(context, batteryUsageStats);
break;
}
}
- private void populateForUidBatteryConsumer(
- Context context, List<BatteryUsageStats> batteryUsageStatsList,
+ private void populateForUidBatteryConsumer(Context context, BatteryUsageStats batteryUsageStats,
String batteryConsumerId) {
- BatteryUsageStats batteryUsageStats = batteryUsageStatsList.get(0);
- BatteryUsageStats modeledBatteryUsageStats = batteryUsageStatsList.get(1);
BatteryConsumer requestedBatteryConsumer = getRequestedBatteryConsumer(batteryUsageStats,
batteryConsumerId);
- BatteryConsumer requestedModeledBatteryConsumer = getRequestedBatteryConsumer(
- modeledBatteryUsageStats, batteryConsumerId);
- if (requestedBatteryConsumer == null || requestedModeledBatteryConsumer == null) {
+ if (requestedBatteryConsumer == null) {
mBatteryConsumerInfo = null;
return;
}
@@ -92,118 +90,95 @@
batteryUsageStats, batteryConsumerId, context.getPackageManager());
double[] totalPowerByComponentMah = new double[BatteryConsumer.POWER_COMPONENT_COUNT];
- double[] totalModeledPowerByComponentMah =
- new double[BatteryConsumer.POWER_COMPONENT_COUNT];
long[] totalDurationByComponentMs = new long[BatteryConsumer.POWER_COMPONENT_COUNT];
- final int customComponentCount =
- requestedBatteryConsumer.getCustomPowerComponentCount();
+ final int customComponentCount = requestedBatteryConsumer.getCustomPowerComponentCount();
double[] totalCustomPowerByComponentMah = new double[customComponentCount];
computeTotalPower(batteryUsageStats, totalPowerByComponentMah);
- computeTotalPower(modeledBatteryUsageStats, totalModeledPowerByComponentMah);
computeTotalPowerForCustomComponent(batteryUsageStats, totalCustomPowerByComponentMah);
computeTotalDuration(batteryUsageStats, totalDurationByComponentMs);
- if (isPowerProfileModelsOnly(requestedBatteryConsumer)) {
- addEntry("Consumed", EntryType.UID_TOTAL_POWER,
- requestedBatteryConsumer.getConsumedPower(),
- batteryUsageStats.getAggregateBatteryConsumer(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
- .getConsumedPower());
- } else {
- addEntry("Consumed (PowerStats)", EntryType.UID_TOTAL_POWER,
- requestedBatteryConsumer.getConsumedPower(),
- batteryUsageStats.getAggregateBatteryConsumer(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
- .getConsumedPower());
- addEntry("Consumed (PowerProfile)", EntryType.UID_TOTAL_POWER,
- requestedModeledBatteryConsumer.getConsumedPower(),
- modeledBatteryUsageStats.getAggregateBatteryConsumer(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
- .getConsumedPower());
+ Entry totalsEntry = addEntry("Consumed", EntryType.UID,
+ requestedBatteryConsumer.getConsumedPower(),
+ batteryUsageStats.getAggregateBatteryConsumer(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
+ .getConsumedPower());
+ addSlices(totalsEntry, requestedBatteryConsumer, BatteryConsumer.POWER_COMPONENT_BASE);
+ for (Slice slice : totalsEntry.slices) {
+ slice.powerMah = requestedBatteryConsumer.getConsumedPower(
+ new BatteryConsumer.Dimensions(BatteryConsumer.POWER_COMPONENT_ANY,
+ slice.processState, slice.screenState, slice.powerState));
}
for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) {
+ if (component == BatteryConsumer.POWER_COMPONENT_BASE) {
+ continue;
+ }
final String metricTitle = getPowerMetricTitle(component);
- final int powerModel = requestedBatteryConsumer.getPowerModel(component);
- if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE
- || powerModel == BatteryConsumer.POWER_MODEL_UNDEFINED) {
- addEntry(metricTitle, EntryType.UID_POWER_PROFILE,
- requestedBatteryConsumer.getConsumedPower(component),
+ double consumedPower = requestedBatteryConsumer.getConsumedPower(component);
+ if (consumedPower != 0) {
+ Entry entry = addEntry(metricTitle, EntryType.UID, consumedPower,
totalPowerByComponentMah[component]);
- addProcessStateEntries(metricTitle, EntryType.UID_POWER_PROFILE_PROCESS_STATE,
- requestedBatteryConsumer, component);
- } else {
- addEntry(metricTitle + " (PowerStats)", EntryType.UID_POWER_ENERGY_CONSUMPTION,
- requestedBatteryConsumer.getConsumedPower(component),
- totalPowerByComponentMah[component]);
- addProcessStateEntries(metricTitle, EntryType.UID_POWER_ENERGY_PROCESS_STATE,
- requestedBatteryConsumer, component);
- addEntry(metricTitle + " (PowerProfile)", EntryType.UID_POWER_PROFILE,
- requestedModeledBatteryConsumer.getConsumedPower(component),
- totalModeledPowerByComponentMah[component]);
- addProcessStateEntries(metricTitle, EntryType.UID_POWER_PROFILE_PROCESS_STATE,
- requestedModeledBatteryConsumer, component);
+ addSlices(entry, requestedBatteryConsumer, component);
}
}
for (int component = 0; component < customComponentCount; component++) {
- final String name = requestedBatteryConsumer.getCustomPowerComponentName(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component);
- addEntry(name + " (PowerStats)", EntryType.UID_POWER_CUSTOM,
- requestedBatteryConsumer.getConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component),
- totalCustomPowerByComponentMah[component]
- );
- }
-
- for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) {
- final String metricTitle = getTimeMetricTitle(component);
- addEntry(metricTitle, EntryType.UID_DURATION,
- requestedBatteryConsumer.getUsageDurationMillis(component),
- totalDurationByComponentMs[component]
- );
+ int componentId = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component;
+ final String name = requestedBatteryConsumer.getCustomPowerComponentName(componentId);
+ double consumedPower = requestedBatteryConsumer.getConsumedPower(componentId);
+ if (consumedPower != 0) {
+ Entry entry = addEntry(name, EntryType.UID, consumedPower,
+ totalCustomPowerByComponentMah[component]);
+ addSlices(entry, requestedBatteryConsumer, componentId);
+ }
}
mBatteryConsumerInfo = BatteryConsumerInfoHelper.makeBatteryConsumerInfo(batteryUsageStats,
batteryConsumerId, context.getPackageManager());
}
- private void addProcessStateEntries(String metricTitle, EntryType entryType,
- BatteryConsumer batteryConsumer, int component) {
+ private void addSlices(Entry entry, BatteryConsumer batteryConsumer, int component) {
final BatteryConsumer.Key[] keys = batteryConsumer.getKeys(component);
if (keys == null || keys.length <= 1) {
return;
}
+ boolean hasProcStateData = false;
for (BatteryConsumer.Key key : keys) {
- String label;
- switch (key.processState) {
- case BatteryConsumer.PROCESS_STATE_FOREGROUND:
- label = "foreground";
- break;
- case BatteryConsumer.PROCESS_STATE_BACKGROUND:
- label = "background";
- break;
- case BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE:
- label = "FGS";
- break;
- case BatteryConsumer.PROCESS_STATE_CACHED:
- label = "cached";
- break;
- default:
- continue;
+ if (key.processState != BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
+ hasProcStateData = true;
+ break;
}
- addEntry(metricTitle + " \u2022 " + label, entryType,
- batteryConsumer.getConsumedPower(key), 0);
}
+
+ ArrayList<Slice> slices = new ArrayList<>();
+ for (BatteryConsumer.Key key : keys) {
+ if (hasProcStateData && key.processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
+ continue;
+ }
+
+ double powerMah = batteryConsumer.getConsumedPower(key);
+ long durationMs = batteryConsumer.getUsageDurationMillis(key);
+
+ if (powerMah == 0 && durationMs == 0) {
+ continue;
+ }
+
+ Slice slice = new Slice();
+ slice.powerState = key.powerState;
+ slice.screenState = key.screenState;
+ slice.processState = key.processState;
+ slice.powerMah = powerMah;
+ slice.durationMs = durationMs;
+
+ slices.add(slice);
+ }
+ entry.slices = slices;
}
private void populateForAggregateBatteryConsumer(Context context,
- List<BatteryUsageStats> batteryUsageStatsList) {
- BatteryUsageStats batteryUsageStats = batteryUsageStatsList.get(0);
- BatteryUsageStats modeledBatteryUsageStats = batteryUsageStatsList.get(1);
-
+ BatteryUsageStats batteryUsageStats) {
final BatteryConsumer deviceBatteryConsumer =
batteryUsageStats.getAggregateBatteryConsumer(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
@@ -211,46 +186,18 @@
batteryUsageStats.getAggregateBatteryConsumer(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
- BatteryConsumer modeledDeviceBatteryConsumer =
- modeledBatteryUsageStats.getAggregateBatteryConsumer(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
- BatteryConsumer modeledAppsBatteryConsumer =
- modeledBatteryUsageStats.getAggregateBatteryConsumer(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
-
- if (isPowerProfileModelsOnly(deviceBatteryConsumer)) {
- addEntry("Consumed", EntryType.DEVICE_TOTAL_POWER,
- deviceBatteryConsumer.getConsumedPower(),
- appsBatteryConsumer.getConsumedPower());
- } else {
- addEntry("Consumed (PowerStats)", EntryType.DEVICE_TOTAL_POWER,
- deviceBatteryConsumer.getConsumedPower(),
- appsBatteryConsumer.getConsumedPower());
- addEntry("Consumed (PowerProfile)", EntryType.DEVICE_TOTAL_POWER,
- modeledDeviceBatteryConsumer.getConsumedPower(),
- modeledAppsBatteryConsumer.getConsumedPower());
- }
+ addEntry("Consumed", EntryType.DEVICE_TOTAL_POWER,
+ deviceBatteryConsumer.getConsumedPower(),
+ appsBatteryConsumer.getConsumedPower());
mBatteryConsumerInfo = BatteryConsumerInfoHelper.makeBatteryConsumerInfo(batteryUsageStats,
AGGREGATE_BATTERY_CONSUMER_ID, context.getPackageManager());
-
for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) {
final String metricTitle = getPowerMetricTitle(component);
- final int powerModel = deviceBatteryConsumer.getPowerModel(component);
- if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE
- || powerModel == BatteryConsumer.POWER_MODEL_UNDEFINED) {
- addEntry(metricTitle, EntryType.DEVICE_POWER_MODELED,
- deviceBatteryConsumer.getConsumedPower(component),
- appsBatteryConsumer.getConsumedPower(component));
- } else {
- addEntry(metricTitle + " (PowerStats)", EntryType.DEVICE_POWER_ENERGY_CONSUMPTION,
- deviceBatteryConsumer.getConsumedPower(component),
- appsBatteryConsumer.getConsumedPower(component));
- addEntry(metricTitle + " (PowerProfile)", EntryType.DEVICE_POWER_MODELED,
- modeledDeviceBatteryConsumer.getConsumedPower(component),
- modeledAppsBatteryConsumer.getConsumedPower(component));
- }
+ addEntry(metricTitle, EntryType.DEVICE_POWER,
+ deviceBatteryConsumer.getConsumedPower(component),
+ appsBatteryConsumer.getConsumedPower(component));
}
final int customComponentCount =
@@ -258,10 +205,10 @@
for (int component = 0; component < customComponentCount; component++) {
final String name = deviceBatteryConsumer.getCustomPowerComponentName(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component);
- addEntry(name + " (PowerStats)", EntryType.DEVICE_POWER_CUSTOM,
- deviceBatteryConsumer.getConsumedPowerForCustomComponent(
+ addEntry(name, EntryType.DEVICE_POWER_CUSTOM,
+ deviceBatteryConsumer.getConsumedPower(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component),
- appsBatteryConsumer.getConsumedPowerForCustomComponent(
+ appsBatteryConsumer.getConsumedPower(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component));
}
@@ -272,17 +219,6 @@
}
}
- private boolean isPowerProfileModelsOnly(BatteryConsumer batteryConsumer) {
- for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) {
- final int powerModel = batteryConsumer.getPowerModel(component);
- if (powerModel != BatteryConsumer.POWER_MODEL_POWER_PROFILE
- && powerModel != BatteryConsumer.POWER_MODEL_UNDEFINED) {
- return false;
- }
- }
- return true;
- }
-
private BatteryConsumer getRequestedBatteryConsumer(BatteryUsageStats batteryUsageStats,
String batteryConsumerId) {
for (UidBatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) {
@@ -352,13 +288,14 @@
}
}
- private void addEntry(String title, EntryType entryType, double value1, double value2) {
+ private Entry addEntry(String title, EntryType entryType, double value1, double value2) {
Entry entry = new Entry();
entry.title = title;
entry.entryType = entryType;
entry.value1 = value1;
entry.value2 = value2;
mEntries.add(entry);
+ return entry;
}
public BatteryConsumerInfoHelper.BatteryConsumerInfo getBatteryConsumerInfo() {
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerInfoHelper.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerInfoHelper.java
index c6d71c3..37d6b17 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerInfoHelper.java
+++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerInfoHelper.java
@@ -24,6 +24,8 @@
import androidx.annotation.NonNull;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
import java.util.List;
class BatteryConsumerInfoHelper {
@@ -76,6 +78,8 @@
String packageWithHighestDrain = uidBatteryConsumer.getPackageWithHighestDrain();
if (uid == Process.ROOT_UID) {
info.label = "<root>";
+ } else if (uid < Process.FIRST_APPLICATION_UID) {
+ info.label = makeSystemUidLabel(uid);
} else {
String[] packages = packageManager.getPackagesForUid(uid);
String primaryPackageName = null;
@@ -134,6 +138,23 @@
return info;
}
+ private static CharSequence makeSystemUidLabel(int uid) {
+ for (Field field : Process.class.getDeclaredFields()) {
+ final int modifiers = field.getModifiers();
+ if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)
+ && field.getType().equals(int.class) && field.getName().endsWith("_UID")) {
+ try {
+ if (uid == field.getInt(null)) {
+ String label = field.getName();
+ return label.substring(0, label.lastIndexOf("_UID"));
+ }
+ } catch (IllegalAccessException ignored) {
+ }
+ }
+ }
+ return null;
+ }
+
private static BatteryConsumerInfo makeAggregateBatteryConsumerInfo(
BatteryUsageStats batteryUsageStats) {
BatteryConsumerInfo info = new BatteryConsumerInfo();
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerActivity.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerActivity.java
index 4469168..3699690 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerActivity.java
+++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerActivity.java
@@ -30,8 +30,8 @@
import android.widget.ImageView;
import android.widget.TextView;
-import androidx.activity.ComponentActivity;
import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.recyclerview.widget.LinearLayoutManager;
@@ -50,7 +50,7 @@
* Picker, showing a sorted lists of applications and other types of entities consuming power.
* Opens BatteryStatsViewerActivity upon item selection.
*/
-public class BatteryConsumerPickerActivity extends ComponentActivity {
+public class BatteryConsumerPickerActivity extends AppCompatActivity {
private static final String PREF_SELECTED_BATTERY_CONSUMER = "batteryConsumerId";
private static final int BATTERY_STATS_REFRESH_RATE_MILLIS = 60 * 1000;
private static final String FORCE_FRESH_STATS = "force_fresh_stats";
@@ -68,6 +68,7 @@
super.onCreate(icicle);
setContentView(R.layout.battery_consumer_picker_layout);
+ setSupportActionBar(findViewById(R.id.toolbar));
mSwipeRefreshLayout = findViewById(R.id.swipe_refresh);
mSwipeRefreshLayout.setColorSchemeResources(android.R.color.holo_green_light);
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java
index e165c49..35021316 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java
+++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java
@@ -17,14 +17,18 @@
package com.android.frameworks.core.batterystatsviewer;
import android.content.Context;
+import android.os.BatteryConsumer;
import android.os.BatteryStatsManager;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.Bundle;
+import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
+import android.widget.TableLayout;
+import android.widget.TableRow;
import android.widget.TextView;
import android.widget.Toast;
@@ -40,6 +44,7 @@
import com.android.settingslib.utils.AsyncLoaderCompat;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
@@ -63,7 +68,16 @@
private SwipeRefreshLayout mSwipeRefreshLayout;
private View mCardView;
private View mEmptyView;
- private List<BatteryUsageStats> mBatteryUsageStats;
+ private BatteryUsageStats mBatteryUsageStats;
+
+ private static SparseArray<String> sProcStateNames = new SparseArray<>();
+ static {
+ sProcStateNames.put(BatteryConsumer.PROCESS_STATE_UNSPECIFIED, "-");
+ sProcStateNames.put(BatteryConsumer.PROCESS_STATE_FOREGROUND, "FG");
+ sProcStateNames.put(BatteryConsumer.PROCESS_STATE_BACKGROUND, "BG");
+ sProcStateNames.put(BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, "FGS");
+ sProcStateNames.put(BatteryConsumer.PROCESS_STATE_CACHED, "Cached");
+ }
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -122,7 +136,7 @@
}
private static class BatteryUsageStatsLoader extends
- AsyncLoaderCompat<List<BatteryUsageStats>> {
+ AsyncLoaderCompat<BatteryUsageStats> {
private final BatteryStatsManager mBatteryStatsManager;
private final boolean mForceFreshStats;
@@ -133,51 +147,44 @@
}
@Override
- public List<BatteryUsageStats> loadInBackground() {
+ public BatteryUsageStats loadInBackground() {
final int maxStatsAgeMs = mForceFreshStats ? 0 : BATTERY_STATS_REFRESH_RATE_MILLIS;
final BatteryUsageStatsQuery queryDefault =
new BatteryUsageStatsQuery.Builder()
- .includePowerModels()
.includeProcessStateData()
+ .includeScreenStateData()
+ .includePowerStateData()
.setMaxStatsAgeMs(maxStatsAgeMs)
.build();
- final BatteryUsageStatsQuery queryPowerProfileModeledOnly =
- new BatteryUsageStatsQuery.Builder()
- .powerProfileModeledOnly()
- .includePowerModels()
- .includeProcessStateData()
- .setMaxStatsAgeMs(maxStatsAgeMs)
- .build();
- return mBatteryStatsManager.getBatteryUsageStats(
- List.of(queryDefault, queryPowerProfileModeledOnly));
+ return mBatteryStatsManager.getBatteryUsageStats(queryDefault);
}
@Override
- protected void onDiscardResult(List<BatteryUsageStats> result) {
+ protected void onDiscardResult(BatteryUsageStats result) {
}
}
private class BatteryUsageStatsLoaderCallbacks
- implements LoaderCallbacks<List<BatteryUsageStats>> {
+ implements LoaderCallbacks<BatteryUsageStats> {
@NonNull
@Override
- public Loader<List<BatteryUsageStats>> onCreateLoader(int id, Bundle args) {
+ public Loader<BatteryUsageStats> onCreateLoader(int id, Bundle args) {
return new BatteryUsageStatsLoader(BatteryStatsViewerActivity.this,
args.getBoolean(FORCE_FRESH_STATS));
}
@Override
- public void onLoadFinished(@NonNull Loader<List<BatteryUsageStats>> loader,
- List<BatteryUsageStats> batteryUsageStats) {
+ public void onLoadFinished(@NonNull Loader<BatteryUsageStats> loader,
+ BatteryUsageStats batteryUsageStats) {
onBatteryUsageStatsLoaded(batteryUsageStats);
}
@Override
- public void onLoaderReset(@NonNull Loader<List<BatteryUsageStats>> loader) {
+ public void onLoaderReset(@NonNull Loader<BatteryUsageStats> loader) {
}
}
- private void onBatteryUsageStatsLoaded(List<BatteryUsageStats> batteryUsageStats) {
+ private void onBatteryUsageStatsLoaded(BatteryUsageStats batteryUsageStats) {
mBatteryUsageStats = batteryUsageStats;
onBatteryStatsDataLoaded();
}
@@ -238,10 +245,21 @@
private static class BatteryStatsDataAdapter extends
RecyclerView.Adapter<BatteryStatsDataAdapter.ViewHolder> {
public static class ViewHolder extends RecyclerView.ViewHolder {
+ public static class SliceViewHolder {
+ public TableRow tableRow;
+ public int procState;
+ public int powerState;
+ public int screenState;
+ public TextView powerTextView;
+ public TextView durationTextView;
+ }
+
public ImageView iconImageView;
public TextView titleTextView;
public TextView value1TextView;
public TextView value2TextView;
+ public TableLayout table;
+ public List<SliceViewHolder> slices = new ArrayList<>();
ViewHolder(View itemView) {
super(itemView);
@@ -250,6 +268,40 @@
titleTextView = itemView.findViewById(R.id.title);
value1TextView = itemView.findViewById(R.id.value1);
value2TextView = itemView.findViewById(R.id.value2);
+ table = itemView.findViewById(R.id.table);
+
+ for (int i = 0; i < sProcStateNames.size(); i++) {
+ int procState = sProcStateNames.keyAt(i);
+ slices.add(createSliceViewHolder(procState,
+ BatteryConsumer.POWER_STATE_BATTERY,
+ BatteryConsumer.SCREEN_STATE_ON,
+ R.id.power_b_on, R.id.duration_b_on));
+ slices.add(createSliceViewHolder(procState,
+ BatteryConsumer.POWER_STATE_BATTERY,
+ BatteryConsumer.SCREEN_STATE_OTHER,
+ R.id.power_b_off, R.id.duration_b_off));
+ slices.add(createSliceViewHolder(procState,
+ BatteryConsumer.POWER_STATE_OTHER,
+ BatteryConsumer.SCREEN_STATE_ON,
+ R.id.power_c_on, R.id.duration_c_on));
+ slices.add(createSliceViewHolder(procState,
+ BatteryConsumer.POWER_STATE_OTHER,
+ BatteryConsumer.SCREEN_STATE_OTHER,
+ R.id.power_c_off, R.id.duration_c_off));
+ }
+ }
+
+ private SliceViewHolder createSliceViewHolder(int procState, int powerState,
+ int screenState, int powerTextViewResId, int durationTextViewResId) {
+ TableRow powerRow = table.findViewWithTag("procstate" + procState);
+ SliceViewHolder svh = new SliceViewHolder();
+ svh.tableRow = powerRow;
+ svh.procState = procState;
+ svh.powerState = powerState;
+ svh.screenState = screenState;
+ svh.powerTextView = powerRow.findViewById(powerTextViewResId);
+ svh.durationTextView = powerRow.findViewById(durationTextViewResId);
+ return svh;
}
}
@@ -269,62 +321,32 @@
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int position) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
- View itemView = layoutInflater.inflate(R.layout.battery_consumer_entry_layout, parent,
- false);
+ ViewGroup itemView = (ViewGroup) layoutInflater.inflate(
+ R.layout.battery_consumer_entry_layout, parent, false);
+ TableLayout table = itemView.findViewById(R.id.table);
+ int offset = 1; // Skip header
+ for (int i = 0; i < sProcStateNames.size(); i++) {
+ View powerRow = layoutInflater.inflate(R.layout.battery_consumer_slices_layout,
+ itemView, false);
+ ((TextView) powerRow.findViewById(R.id.procState))
+ .setText(sProcStateNames.valueAt(i));
+ powerRow.setTag("procstate" + sProcStateNames.keyAt(i));
+ table.addView(powerRow, offset++);
+ }
+
return new ViewHolder(itemView);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
BatteryConsumerData.Entry entry = mEntries.get(position);
-
switch (entry.entryType) {
- case UID_TOTAL_POWER:
+ case UID:
setTitleIconAndBackground(viewHolder, entry.title,
- R.drawable.gm_sum_24, 0);
+ R.drawable.gm_energy_24, 0);
setPowerText(viewHolder.value1TextView, entry.value1);
setProportionText(viewHolder.value2TextView, entry);
- break;
- case UID_POWER_PROFILE:
- setTitleIconAndBackground(viewHolder, entry.title,
- R.drawable.gm_calculate_24,
- R.color.battery_consumer_bg_power_profile);
- setPowerText(viewHolder.value1TextView, entry.value1);
- setProportionText(viewHolder.value2TextView, entry);
- break;
- case UID_POWER_PROFILE_PROCESS_STATE:
- setTitleIconAndBackground(viewHolder, " " + entry.title,
- R.drawable.gm_calculate_24,
- R.color.battery_consumer_bg_power_profile);
- setPowerText(viewHolder.value1TextView, entry.value1);
- viewHolder.value2TextView.setVisibility(View.INVISIBLE);
- break;
- case UID_POWER_ENERGY_CONSUMPTION:
- setTitleIconAndBackground(viewHolder, entry.title,
- R.drawable.gm_energy_24,
- R.color.battery_consumer_bg_energy_consumption);
- setPowerText(viewHolder.value1TextView, entry.value1);
- setProportionText(viewHolder.value2TextView, entry);
- break;
- case UID_POWER_ENERGY_PROCESS_STATE:
- setTitleIconAndBackground(viewHolder, " " + entry.title,
- R.drawable.gm_energy_24,
- R.color.battery_consumer_bg_energy_consumption);
- setPowerText(viewHolder.value1TextView, entry.value1);
- viewHolder.value2TextView.setVisibility(View.INVISIBLE);
- break;
- case UID_POWER_CUSTOM:
- setTitleIconAndBackground(viewHolder, entry.title,
- R.drawable.gm_energy_24,
- R.color.battery_consumer_bg_energy_consumption);
- setPowerText(viewHolder.value1TextView, entry.value1);
- setProportionText(viewHolder.value2TextView, entry);
- break;
- case UID_DURATION:
- setTitleIconAndBackground(viewHolder, entry.title,
- R.drawable.gm_timer_24, 0);
- setDurationText(viewHolder.value1TextView, (long) entry.value1);
- setProportionText(viewHolder.value2TextView, entry);
+ bindSlices(viewHolder, entry);
break;
case DEVICE_TOTAL_POWER:
setTitleIconAndBackground(viewHolder, entry.title,
@@ -332,27 +354,13 @@
setPowerText(viewHolder.value1TextView, entry.value1);
setPowerText(viewHolder.value2TextView, entry.value2);
break;
- case DEVICE_POWER_MODELED:
+ case DEVICE_POWER:
setTitleIconAndBackground(viewHolder, entry.title,
R.drawable.gm_calculate_24,
R.color.battery_consumer_bg_power_profile);
setPowerText(viewHolder.value1TextView, entry.value1);
setPowerText(viewHolder.value2TextView, entry.value2);
break;
- case DEVICE_POWER_ENERGY_CONSUMPTION:
- setTitleIconAndBackground(viewHolder, entry.title,
- R.drawable.gm_energy_24,
- R.color.battery_consumer_bg_energy_consumption);
- setPowerText(viewHolder.value1TextView, entry.value1);
- setPowerText(viewHolder.value2TextView, entry.value2);
- break;
- case DEVICE_POWER_CUSTOM:
- setTitleIconAndBackground(viewHolder, entry.title,
- R.drawable.gm_energy_24,
- R.color.battery_consumer_bg_energy_consumption);
- setPowerText(viewHolder.value1TextView, entry.value1);
- setPowerText(viewHolder.value2TextView, entry.value2);
- break;
case DEVICE_DURATION:
setTitleIconAndBackground(viewHolder, entry.title,
R.drawable.gm_timer_24, 0);
@@ -362,6 +370,65 @@
}
}
+ private void bindSlices(ViewHolder viewHolder, BatteryConsumerData.Entry entry) {
+ if (entry.slices == null || entry.slices.isEmpty()) {
+ viewHolder.table.setVisibility(View.GONE);
+ return;
+ }
+ viewHolder.table.setVisibility(View.VISIBLE);
+
+ boolean[] procStateRowPopulated =
+ new boolean[BatteryConsumer.PROCESS_STATE_COUNT];
+ for (BatteryConsumerData.Slice s : entry.slices) {
+ if (s.powerMah != 0 || s.durationMs != 0) {
+ procStateRowPopulated[s.processState] = true;
+ }
+ }
+
+ for (ViewHolder.SliceViewHolder sliceViewHolder : viewHolder.slices) {
+ BatteryConsumerData.Slice slice = null;
+ for (BatteryConsumerData.Slice s : entry.slices) {
+ if (s.powerState == sliceViewHolder.powerState
+ && s.screenState == sliceViewHolder.screenState
+ && s.processState == sliceViewHolder.procState) {
+ slice = s;
+ break;
+ }
+ }
+ if (!procStateRowPopulated[sliceViewHolder.procState]) {
+ sliceViewHolder.tableRow.setVisibility(View.GONE);
+ } else {
+ sliceViewHolder.tableRow.setVisibility(View.VISIBLE);
+
+ if (slice != null && (slice.powerMah != 0 || slice.durationMs != 0)) {
+ sliceViewHolder.powerTextView.setText(
+ String.format(Locale.getDefault(), "%.1f", slice.powerMah));
+ } else {
+ sliceViewHolder.powerTextView.setText(null);
+ }
+
+ if (slice != null && slice.durationMs != 0) {
+ sliceViewHolder.durationTextView.setVisibility(View.VISIBLE);
+ String timeString;
+ if (slice.durationMs < MILLIS_IN_MINUTE) {
+ timeString = String.format(Locale.getDefault(), "%ds",
+ slice.durationMs / 1000);
+ } else if (slice.durationMs < 60 * MILLIS_IN_MINUTE) {
+ timeString = String.format(Locale.getDefault(), "%dm %ds",
+ slice.durationMs / MILLIS_IN_MINUTE,
+ (slice.durationMs % MILLIS_IN_MINUTE) / 1000);
+ } else {
+ timeString = String.format(Locale.getDefault(), "%dm",
+ slice.durationMs / MILLIS_IN_MINUTE);
+ }
+ sliceViewHolder.durationTextView.setText(timeString);
+ } else {
+ sliceViewHolder.durationTextView.setVisibility(View.GONE);
+ }
+ }
+ }
+ }
+
private void setTitleIconAndBackground(ViewHolder viewHolder, String title, int icon,
int background) {
viewHolder.titleTextView.setText(title);
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/TrampolineActivity.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/TrampolineActivity.java
index b016488..412169e 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/TrampolineActivity.java
+++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/TrampolineActivity.java
@@ -42,5 +42,6 @@
private void launchMainActivity() {
startActivity(new Intent(this, BatteryConsumerPickerActivity.class));
+ finish();
}
}
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index 2fc72e1..177c7f0 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -26,6 +26,7 @@
import static com.android.internal.os.Flags.FLAG_APPLICATION_SHARED_MEMORY_ENABLED;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
@@ -34,6 +35,8 @@
import android.annotation.SuppressLint;
import android.app.PropertyInvalidatedCache.Args;
+import android.app.PropertyInvalidatedCache.NonceWatcher;
+import android.app.PropertyInvalidatedCache.NonceStore;
import android.os.Binder;
import com.android.internal.os.ApplicationSharedMemory;
@@ -45,11 +48,15 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.os.ApplicationSharedMemory;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import java.util.concurrent.TimeUnit;
+
/**
* Test for verifying the behavior of {@link PropertyInvalidatedCache}. This test does
* not use any actual binder calls - it is entirely self-contained. This test also relies
@@ -490,6 +497,62 @@
}
}
+ // Verify that NonceWatcher change reporting works properly
+ @Test
+ public void testNonceWatcherChanged() {
+ // Create a cache that will write a system nonce.
+ TestCache sysCache = new TestCache(MODULE_SYSTEM, "watcher1");
+ sysCache.testPropertyName();
+
+ try (NonceWatcher watcher1 = sysCache.getNonceWatcher()) {
+
+ // The property has never been invalidated so it is still unset.
+ assertFalse(watcher1.isChanged());
+
+ // Invalidate the cache. The first call to isChanged will return true but the second
+ // call will return false;
+ sysCache.invalidateCache();
+ assertTrue(watcher1.isChanged());
+ assertFalse(watcher1.isChanged());
+
+ // Invalidate the cache. The first call to isChanged will return true but the second
+ // call will return false;
+ sysCache.invalidateCache();
+ sysCache.invalidateCache();
+ assertTrue(watcher1.isChanged());
+ assertFalse(watcher1.isChanged());
+
+ NonceWatcher watcher2 = sysCache.getNonceWatcher();
+ // This watcher return isChanged() immediately because the nonce is not UNSET.
+ assertTrue(watcher2.isChanged());
+ }
+ }
+
+ // Verify that NonceWatcher wait-for-change works properly
+ @Test
+ public void testNonceWatcherWait() throws Exception {
+ // Create a cache that will write a system nonce.
+ TestCache sysCache = new TestCache(MODULE_TEST, "watcher1");
+
+ // Use the watcher outside a try-with-resources block.
+ NonceWatcher watcher1 = sysCache.getNonceWatcher();
+
+ // Invalidate the cache and then "wait".
+ sysCache.invalidateCache();
+ assertEquals(watcher1.waitForChange(), 1);
+
+ // Invalidate the cache three times and then "wait".
+ sysCache.invalidateCache();
+ sysCache.invalidateCache();
+ sysCache.invalidateCache();
+ assertEquals(watcher1.waitForChange(), 3);
+
+ // Wait for a change. It won't happen, but the code will time out after 10ms.
+ assertEquals(watcher1.waitForChange(10, TimeUnit.MILLISECONDS), 0);
+
+ watcher1.close();
+ }
+
// Verify the behavior of shared memory nonce storage. This does not directly test the cache
// storing nonces in shared memory.
@RequiresFlagsEnabled(FLAG_APPLICATION_SHARED_MEMORY_ENABLED)
@@ -502,10 +565,8 @@
// Create a server-side store and a client-side store. The server's store is mutable and
// the client's store is not mutable.
- PropertyInvalidatedCache.NonceStore server =
- new PropertyInvalidatedCache.NonceStore(shmem.getSystemNonceBlock(), true);
- PropertyInvalidatedCache.NonceStore client =
- new PropertyInvalidatedCache.NonceStore(shmem.getSystemNonceBlock(), false);
+ NonceStore server = new NonceStore(shmem.getSystemNonceBlock(), true);
+ NonceStore client = new NonceStore(shmem.getSystemNonceBlock(), false);
final String name1 = "name1";
assertEquals(server.getHandleForName(name1), INVALID_NONCE_INDEX);
diff --git a/core/tests/coretests/src/android/app/wallpaper/WallpaperDescriptionTest.java b/core/tests/coretests/src/android/app/wallpaper/WallpaperDescriptionTest.java
index 01c2abf..a22eae3 100644
--- a/core/tests/coretests/src/android/app/wallpaper/WallpaperDescriptionTest.java
+++ b/core/tests/coretests/src/android/app/wallpaper/WallpaperDescriptionTest.java
@@ -15,13 +15,20 @@
*/
package android.app.wallpaper;
+import static android.app.WallpaperManager.ORIENTATION_LANDSCAPE;
+import static android.app.WallpaperManager.ORIENTATION_PORTRAIT;
+import static android.app.WallpaperManager.ORIENTATION_SQUARE_LANDSCAPE;
+import static android.app.WallpaperManager.ORIENTATION_SQUARE_PORTRAIT;
+
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import android.content.ComponentName;
+import android.graphics.Rect;
import android.net.Uri;
import android.os.Parcel;
import android.os.PersistableBundle;
+import android.util.SparseArray;
import android.util.Xml;
import com.android.modules.utils.TypedXmlPullParser;
@@ -41,17 +48,20 @@
@RunWith(JUnit4.class)
public class WallpaperDescriptionTest {
- private static final String TAG = "WallpaperDescriptionTest";
+ private static final Rect DEFAULT_CROP_PORTRAIT = new Rect(1, 2, 3, 4);
+ private static final Rect DEFAULT_CROP_LANDSCAPE = new Rect(5, 6, 7, 8);
+ private static final Rect DEFAULT_CROP_SQUARE_PORTRAIT = new Rect(9, 10, 11, 12);
+ private static final Rect DEFAULT_CROP_SQUARE_LANDSCAPE = new Rect(13, 14, 15, 16);
private final ComponentName mTestComponent = new ComponentName("fakePackage", "fakeClass");
@Test
public void equals_ignoresIrrelevantFields() {
String id = "fakeId";
- WallpaperDescription desc1 = new WallpaperDescription.Builder().setComponent(
- mTestComponent).setId(id).setTitle("fake one").build();
- WallpaperDescription desc2 = new WallpaperDescription.Builder().setComponent(
- mTestComponent).setId(id).setTitle("fake different").build();
+ WallpaperDescription desc1 = new WallpaperDescription.Builder()
+ .setComponent(mTestComponent).setId(id).setTitle("fake one").build();
+ WallpaperDescription desc2 = new WallpaperDescription.Builder()
+ .setComponent(mTestComponent).setId(id).setTitle("fake different").build();
assertThat(desc1).isEqualTo(desc2);
}
@@ -72,13 +82,21 @@
final Uri thumbnail = Uri.parse("http://www.bogus.com/thumbnail");
final List<CharSequence> description = List.of("line1", "line2");
final Uri contextUri = Uri.parse("http://www.bogus.com/contextUri");
- final PersistableBundle content = new PersistableBundle();
- content.putString("ckey", "cvalue");
+ final PersistableBundle content = makeDefaultContent();
+ final SparseArray<Rect> cropHints = makeDefaultCropHints();
+ final float sampleSize = 0.9f;
WallpaperDescription source = new WallpaperDescription.Builder()
- .setComponent(mTestComponent).setId("fakeId").setThumbnail(thumbnail)
- .setTitle("Fake title").setDescription(description)
- .setContextUri(contextUri).setContextDescription("Context description")
- .setContent(content).build();
+ .setComponent(mTestComponent)
+ .setId("fakeId")
+ .setThumbnail(thumbnail)
+ .setTitle("Fake title")
+ .setDescription(description)
+ .setContextUri(contextUri)
+ .setContextDescription("Context description")
+ .setContent(content)
+ .setCropHints(cropHints)
+ .setSampleSize(sampleSize)
+ .build();
ByteArrayOutputStream ostream = new ByteArrayOutputStream();
TypedXmlSerializer serializer = Xml.newBinarySerializer();
@@ -122,6 +140,57 @@
assertThat(destination.getContent()).isNotNull();
assertThat(destination.getContent().getString("ckey")).isEqualTo(
source.getContent().getString("ckey"));
+ assertThat(destination.getCropHints()).isNotNull();
+ assertThat(destination.getCropHints().get(ORIENTATION_PORTRAIT)).isEqualTo(
+ DEFAULT_CROP_PORTRAIT);
+ assertThat(destination.getCropHints().get(ORIENTATION_LANDSCAPE)).isEqualTo(
+ DEFAULT_CROP_LANDSCAPE);
+ assertThat(destination.getCropHints().get(ORIENTATION_SQUARE_PORTRAIT)).isEqualTo(
+ DEFAULT_CROP_SQUARE_PORTRAIT);
+ assertThat(destination.getCropHints().get(ORIENTATION_SQUARE_LANDSCAPE)).isEqualTo(
+ DEFAULT_CROP_SQUARE_LANDSCAPE);
+ assertThat(destination.getSampleSize()).isEqualTo(sampleSize);
+ }
+
+ @Test
+ public void xml_roundTripSucceeds_withNulls() throws IOException, XmlPullParserException {
+ WallpaperDescription source = new WallpaperDescription.Builder().build();
+
+ ByteArrayOutputStream ostream = new ByteArrayOutputStream();
+ TypedXmlSerializer serializer = Xml.newBinarySerializer();
+ serializer.setOutput(ostream, StandardCharsets.UTF_8.name());
+ serializer.startDocument(null, true);
+ serializer.startTag(null, "test");
+ source.saveToXml(serializer);
+ serializer.endTag(null, "test");
+ serializer.endDocument();
+ ostream.close();
+
+ WallpaperDescription destination = null;
+ ByteArrayInputStream istream = new ByteArrayInputStream(ostream.toByteArray());
+ TypedXmlPullParser parser = Xml.newBinaryPullParser();
+ parser.setInput(istream, StandardCharsets.UTF_8.name());
+ int type;
+ do {
+ type = parser.next();
+ if (type == XmlPullParser.START_TAG && "test".equals(parser.getName())) {
+ destination = WallpaperDescription.restoreFromXml(parser);
+ }
+ } while (type != XmlPullParser.END_DOCUMENT);
+
+ assertThat(destination).isNotNull();
+ assertThat(destination.getComponent()).isEqualTo(source.getComponent());
+ assertThat(destination.getId()).isEqualTo(source.getId());
+ assertThat(destination.getThumbnail()).isEqualTo(source.getThumbnail());
+ assertThat(destination.getTitle()).isNull();
+ assertThat(destination.getDescription()).hasSize(0);
+ assertThat(destination.getContextUri()).isEqualTo(source.getContextUri());
+ assertThat(destination.getContextDescription()).isNull();
+ assertThat(destination.getContent()).isNotNull();
+ assertThat(destination.getContent().keySet()).isEmpty();
+ assertThat(destination.getCropHints()).isNotNull();
+ assertThat(destination.getCropHints().size()).isEqualTo(0);
+ assertThat(destination.getSampleSize()).isEqualTo(source.getSampleSize());
}
@Test
@@ -129,13 +198,21 @@
final Uri thumbnail = Uri.parse("http://www.bogus.com/thumbnail");
final List<CharSequence> description = List.of("line1", "line2");
final Uri contextUri = Uri.parse("http://www.bogus.com/contextUri");
- final PersistableBundle content = new PersistableBundle();
- content.putString("ckey", "cvalue");
- WallpaperDescription source = new WallpaperDescription.Builder().setComponent(
- mTestComponent).setId("fakeId").setThumbnail(thumbnail).setTitle(
- "Fake title").setDescription(description).setContextUri(
- contextUri).setContextDescription("Context description").setContent(
- content).build();
+ final PersistableBundle content = makeDefaultContent();
+ final SparseArray<Rect> cropHints = makeDefaultCropHints();
+ final float sampleSize = 0.9f;
+ WallpaperDescription source = new WallpaperDescription.Builder()
+ .setComponent(mTestComponent)
+ .setId("fakeId")
+ .setThumbnail(thumbnail)
+ .setTitle("Fake title")
+ .setDescription(description)
+ .setContextUri(contextUri)
+ .setContextDescription("Context description")
+ .setContent(content)
+ .setCropHints(cropHints)
+ .setSampleSize(sampleSize)
+ .build();
Parcel parcel = Parcel.obtain();
source.writeToParcel(parcel, 0);
@@ -162,6 +239,16 @@
assertThat(destination.getContent()).isNotNull();
assertThat(destination.getContent().getString("ckey")).isEqualTo(
source.getContent().getString("ckey"));
+ assertThat(destination.getCropHints()).isNotNull();
+ assertThat(destination.getCropHints().get(ORIENTATION_PORTRAIT)).isEqualTo(
+ DEFAULT_CROP_PORTRAIT);
+ assertThat(destination.getCropHints().get(ORIENTATION_LANDSCAPE)).isEqualTo(
+ DEFAULT_CROP_LANDSCAPE);
+ assertThat(destination.getCropHints().get(ORIENTATION_SQUARE_PORTRAIT)).isEqualTo(
+ DEFAULT_CROP_SQUARE_PORTRAIT);
+ assertThat(destination.getCropHints().get(ORIENTATION_SQUARE_LANDSCAPE)).isEqualTo(
+ DEFAULT_CROP_SQUARE_LANDSCAPE);
+ assertThat(destination.getSampleSize()).isEqualTo(sampleSize);
}
@Test
@@ -178,17 +265,14 @@
assertThat(destination.getId()).isEqualTo(source.getId());
assertThat(destination.getThumbnail()).isEqualTo(source.getThumbnail());
assertThat(destination.getTitle()).isNull();
- assertThat(destination.getDescription()).hasSize(source.getDescription().size());
- for (int i = 0; i < destination.getDescription().size(); i++) {
- CharSequence strDest = destination.getDescription().get(i);
- CharSequence strSrc = source.getDescription().get(i);
- assertWithMessage("description string mismatch")
- .that(CharSequence.compare(strDest, strSrc)).isEqualTo(0);
- }
+ assertThat(destination.getDescription()).hasSize(0);
assertThat(destination.getContextUri()).isEqualTo(source.getContextUri());
assertThat(destination.getContextDescription()).isNull();
assertThat(destination.getContent()).isNotNull();
assertThat(destination.getContent().keySet()).isEmpty();
+ assertThat(destination.getCropHints()).isNotNull();
+ assertThat(destination.getCropHints().size()).isEqualTo(0);
+ assertThat(destination.getSampleSize()).isEqualTo(source.getSampleSize());
}
@Test
@@ -197,14 +281,22 @@
final Uri thumbnail = Uri.parse("http://www.bogus.com/thumbnail");
final List<CharSequence> description = List.of("line1", "line2");
final Uri contextUri = Uri.parse("http://www.bogus.com/contextUri");
- final PersistableBundle content = new PersistableBundle();
- content.putString("ckey", "cvalue");
+ final PersistableBundle content = makeDefaultContent();
+ final SparseArray<Rect> cropHints = makeDefaultCropHints();
+ final float sampleSize = 1.1f;
final String destinationId = "destinationId";
- WallpaperDescription source = new WallpaperDescription.Builder().setComponent(
- mTestComponent).setId(sourceId).setThumbnail(thumbnail).setTitle(
- "Fake title").setDescription(description).setContextUri(
- contextUri).setContextDescription("Context description").setContent(
- content).build();
+ WallpaperDescription source = new WallpaperDescription.Builder()
+ .setComponent(mTestComponent)
+ .setId(sourceId)
+ .setThumbnail(thumbnail)
+ .setTitle("Fake title")
+ .setDescription(description)
+ .setContextUri(contextUri)
+ .setContextDescription("Context description")
+ .setContent(content)
+ .setCropHints(cropHints)
+ .setSampleSize(sampleSize)
+ .build();
WallpaperDescription destination = source.toBuilder().setId(destinationId).build();
@@ -227,5 +319,29 @@
assertThat(destination.getContent()).isNotNull();
assertThat(destination.getContent().getString("ckey")).isEqualTo(
source.getContent().getString("ckey"));
+ assertThat(destination.getCropHints().get(ORIENTATION_PORTRAIT)).isEqualTo(
+ DEFAULT_CROP_PORTRAIT);
+ assertThat(destination.getCropHints().get(ORIENTATION_LANDSCAPE)).isEqualTo(
+ DEFAULT_CROP_LANDSCAPE);
+ assertThat(destination.getCropHints().get(ORIENTATION_SQUARE_PORTRAIT)).isEqualTo(
+ DEFAULT_CROP_SQUARE_PORTRAIT);
+ assertThat(destination.getCropHints().get(ORIENTATION_SQUARE_LANDSCAPE)).isEqualTo(
+ DEFAULT_CROP_SQUARE_LANDSCAPE);
+ assertThat(destination.getSampleSize()).isEqualTo(sampleSize);
+ }
+
+ private static PersistableBundle makeDefaultContent() {
+ final PersistableBundle content = new PersistableBundle();
+ content.putString("ckey", "cvalue");
+ return content;
+ }
+
+ private static SparseArray<Rect> makeDefaultCropHints() {
+ final SparseArray<Rect> cropHints = new SparseArray<>();
+ cropHints.put(ORIENTATION_PORTRAIT, DEFAULT_CROP_PORTRAIT);
+ cropHints.put(ORIENTATION_LANDSCAPE, DEFAULT_CROP_LANDSCAPE);
+ cropHints.put(ORIENTATION_SQUARE_PORTRAIT, DEFAULT_CROP_SQUARE_PORTRAIT);
+ cropHints.put(ORIENTATION_SQUARE_LANDSCAPE, DEFAULT_CROP_SQUARE_LANDSCAPE);
+ return cropHints;
}
}
diff --git a/core/tests/coretests/src/android/os/BuildTest.java b/core/tests/coretests/src/android/os/BuildTest.java
index e8c701e..2a67716a 100644
--- a/core/tests/coretests/src/android/os/BuildTest.java
+++ b/core/tests/coretests/src/android/os/BuildTest.java
@@ -16,8 +16,10 @@
package android.os;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import android.platform.test.flag.junit.SetFlagsRule;
@@ -103,4 +105,99 @@
mSetFlagsRule.disableFlags(Flags.FLAG_ANDROID_OS_BUILD_VANILLA_ICE_CREAM);
assertFalse(Flags.androidOsBuildVanillaIceCream());
}
+
+ @Test
+ public void testParseFullVersionCorrectInputMajorDotMinor() throws Exception {
+ int version = Build.parseFullVersion("12.34");
+ assertEquals(12, Build.getMajorSdkVersion(version));
+ assertEquals(34, Build.getMinorSdkVersion(version));
+ }
+
+ @Test
+ public void testParseFullVersionCorrectInputOmitDotMinor() throws Exception {
+ int version = Build.parseFullVersion("1234");
+ assertEquals(1234, Build.getMajorSdkVersion(version));
+ assertEquals(0, Build.getMinorSdkVersion(version));
+ }
+
+ @Test
+ public void testParseFullVersionCorrectInputCurDevelopment() throws Exception {
+ int version = Build.parseFullVersion(Integer.toString(Build.VERSION_CODES.CUR_DEVELOPMENT));
+ assertEquals(Build.VERSION_CODES.CUR_DEVELOPMENT, Build.getMajorSdkVersion(version));
+ assertEquals(0, Build.getMinorSdkVersion(version));
+ }
+
+ @Test
+ public void testParseFullVersionIncorrectInputEmptyString() throws Exception {
+ assertThrows(IllegalArgumentException.class, () -> {
+ Build.parseFullVersion("");
+ });
+ }
+
+ @Test
+ public void testParseFullVersionIncorrectInputNoNumbersInString() throws Exception {
+ assertThrows(IllegalArgumentException.class, () -> {
+ Build.parseFullVersion("foobar");
+ });
+ }
+
+ @Test
+ public void testParseFullVersionIncorrectInputUnexpectedPatchVersion() throws Exception {
+ assertThrows(IllegalArgumentException.class, () -> {
+ Build.parseFullVersion("1.2.3");
+ });
+ }
+
+ @Test
+ public void testParseFullVersionIncorrectInputLeadingDotMissingMajorVersion() throws Exception {
+ assertThrows(IllegalArgumentException.class, () -> {
+ Build.parseFullVersion(".1234");
+ });
+ }
+
+ @Test
+ public void testParseFullVersionIncorrectInputTrailingDotMissingMinorVersion()
+ throws Exception {
+ assertThrows(IllegalArgumentException.class, () -> {
+ Build.parseFullVersion("1234.");
+ });
+ }
+
+ @Test
+ public void testParseFullVersionIncorrectInputNegativeMajorVersion() throws Exception {
+ assertThrows(IllegalArgumentException.class, () -> {
+ Build.parseFullVersion("-12.34");
+ });
+ }
+
+ @Test
+ public void testParseFullVersionIncorrectInputNegativeMinorVersion() throws Exception {
+ assertThrows(IllegalArgumentException.class, () -> {
+ Build.parseFullVersion("12.-34");
+ });
+ }
+
+ @Test
+ public void testFullVersionToStringCorrectInput() throws Exception {
+ assertEquals("0.0", Build.fullVersionToString(0));
+ assertEquals("1.0", Build.fullVersionToString(1 * 100000 + 0));
+ assertEquals("1.1", Build.fullVersionToString(1 * 100000 + 1));
+ assertEquals("12.34", Build.fullVersionToString(12 * 100000 + 34));
+ }
+
+ @Test
+ public void testFullVersionToStringSameStringAfterRoundTripViaParseFullVersion()
+ throws Exception {
+ String s = "12.34";
+ int major = Build.getMajorSdkVersion(Build.parseFullVersion(s));
+ int minor = Build.getMinorSdkVersion(Build.parseFullVersion(s));
+ assertEquals(s, Build.fullVersionToString(major * 100000 + minor));
+ }
+
+ @Test
+ public void testFullVersionToStringIncorrectInputNegativeVersion() throws Exception {
+ assertThrows(IllegalArgumentException.class, () -> {
+ Build.fullVersionToString(-1);
+ });
+ }
}
diff --git a/core/tests/coretests/src/android/os/OWNERS b/core/tests/coretests/src/android/os/OWNERS
index 4620cb8..c45080f 100644
--- a/core/tests/coretests/src/android/os/OWNERS
+++ b/core/tests/coretests/src/android/os/OWNERS
@@ -15,3 +15,6 @@
# RemoteCallbackList
per-file RemoteCallbackListTest.java = shayba@google.com
+
+# MessageQueue
+per-file MessageQueueTest.java = mfasheh@google.com, shayba@google.com
diff --git a/core/tests/coretests/src/android/window/OWNERS b/core/tests/coretests/src/android/window/OWNERS
index 6c80cf9..b3fcea2 100644
--- a/core/tests/coretests/src/android/window/OWNERS
+++ b/core/tests/coretests/src/android/window/OWNERS
@@ -1,2 +1,3 @@
include /services/core/java/com/android/server/wm/OWNERS
-charlesccchen@google.com
+
+# Bug component: 1519745 = per-file WindowContext*,WindowMetrics*,WindowProvider*,WindowTokenClient*
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/window/WindowContextTest.java b/core/tests/coretests/src/android/window/WindowContextTest.java
index f1fbd55..21930d1 100644
--- a/core/tests/coretests/src/android/window/WindowContextTest.java
+++ b/core/tests/coretests/src/android/window/WindowContextTest.java
@@ -29,6 +29,11 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import android.app.Activity;
import android.app.EmptyActivity;
@@ -60,6 +65,8 @@
import com.android.frameworks.coretests.R;
+import org.junit.After;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -88,9 +95,21 @@
private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
private final WindowContext mWindowContext = createWindowContext();
private final IWindowManager mWms = WindowManagerGlobal.getWindowManagerService();
+ private WindowTokenClientController mOriginalWindowTokenClientController;
private static final int TIMEOUT_IN_SECONDS = 4;
+ @Before
+ public void setUp() {
+ // Keeping the original to set it back after each test, in case they applied any override.
+ mOriginalWindowTokenClientController = WindowTokenClientController.getInstance();
+ }
+
+ @After
+ public void tearDown() {
+ WindowTokenClientController.overrideForTesting(mOriginalWindowTokenClientController);
+ }
+
@Test
public void testCreateWindowContextWindowManagerAttachClientToken() {
final WindowManager windowContextWm = WindowManagerImpl
@@ -320,6 +339,20 @@
}
}
+ @Test
+ public void updateDisplay_wasAttached_detachThenAttachedPropagatedToTokenController() {
+ final WindowTokenClientController mockWindowTokenClientController =
+ mock(WindowTokenClientController.class);
+ WindowTokenClientController.overrideForTesting(mockWindowTokenClientController);
+
+ mWindowContext.updateDisplay(DEFAULT_DISPLAY + 1);
+
+ verify(mockWindowTokenClientController).detachIfNeeded(any());
+ verify(mockWindowTokenClientController).attachToDisplayArea(any(),
+ anyInt(), /* displayId= */ eq(DEFAULT_DISPLAY + 1),
+ any());
+ }
+
private WindowContext createWindowContext() {
return createWindowContext(TYPE_APPLICATION_OVERLAY);
}
diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
index e640ce5..17fe15c 100644
--- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
+++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
@@ -16,13 +16,17 @@
package android.hardware.devicestate;
+import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
@@ -40,7 +44,6 @@
import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
-import com.android.internal.util.ConcurrentUtils;
import com.android.window.flags.Flags;
import org.junit.Before;
@@ -52,6 +55,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.Executor;
import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
import platform.test.runner.parameterized.Parameters;
@@ -103,7 +107,7 @@
permissionEnforcer, true /* simulatePostCallback */);
final DeviceStateManagerGlobal dsmGlobal = new DeviceStateManagerGlobal(service);
final DeviceStateCallback callback = mock(DeviceStateCallback.class);
- dsmGlobal.registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR);
+ dsmGlobal.registerDeviceStateCallback(callback, DIRECT_EXECUTOR);
verify(callback, never()).onDeviceStateChanged(any());
@@ -120,49 +124,68 @@
final IDeviceStateManager service = new TestDeviceStateManagerService(permissionEnforcer);
final DeviceStateManagerGlobal dsmGlobal = new DeviceStateManagerGlobal(service);
final DeviceStateCallback callback = mock(DeviceStateCallback.class);
- dsmGlobal.registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR);
+ dsmGlobal.registerDeviceStateCallback(callback, DIRECT_EXECUTOR);
verify(callback).onDeviceStateChanged(eq(DEFAULT_DEVICE_STATE));
}
@Test
- public void registerCallback() {
+ public void registerCallback_usesExecutorForCallbacks() {
+ final DeviceStateCallback callback = mock(DeviceStateCallback.class);
+ final Executor executor = mock(Executor.class);
+ doAnswer(invocation -> {
+ Runnable runnable = (Runnable) invocation.getArguments()[0];
+ runnable.run();
+ return null;
+ }).when(executor).execute(any(Runnable.class));
+
+ mDeviceStateManagerGlobal.registerDeviceStateCallback(callback, executor);
+ mService.setBaseState(OTHER_DEVICE_STATE);
+ mService.setSupportedStates(List.of(OTHER_DEVICE_STATE));
+
+ // Verify that the given executor is used for both initial and subsequent callbacks.
+ verify(executor, times(4)).execute(any(Runnable.class));
+ }
+
+ @Test
+ public void registerCallback_supportedStatesChanged() {
final DeviceStateCallback callback1 = mock(DeviceStateCallback.class);
final DeviceStateCallback callback2 = mock(DeviceStateCallback.class);
+ mDeviceStateManagerGlobal.registerDeviceStateCallback(callback1, DIRECT_EXECUTOR);
+ mDeviceStateManagerGlobal.registerDeviceStateCallback(callback2, DIRECT_EXECUTOR);
- mDeviceStateManagerGlobal.registerDeviceStateCallback(callback1,
- ConcurrentUtils.DIRECT_EXECUTOR);
- mDeviceStateManagerGlobal.registerDeviceStateCallback(callback2,
- ConcurrentUtils.DIRECT_EXECUTOR);
-
- // Verify initial callbacks
- verify(callback1).onSupportedStatesChanged(eq(mService.getSupportedDeviceStates()));
- verify(callback1).onDeviceStateChanged(eq(mService.getMergedState()));
- verify(callback2).onDeviceStateChanged(eq(mService.getMergedState()));
-
- reset(callback1);
- reset(callback2);
-
- // Change the supported states and verify callback
+ // Change the supported states and verify callback.
mService.setSupportedStates(List.of(DEFAULT_DEVICE_STATE));
+
verify(callback1).onSupportedStatesChanged(eq(mService.getSupportedDeviceStates()));
verify(callback2).onSupportedStatesChanged(eq(mService.getSupportedDeviceStates()));
+ }
+
+ @Test
+ public void registerCallback_baseStateChanged() {
+ final DeviceStateCallback callback1 = mock(DeviceStateCallback.class);
+ final DeviceStateCallback callback2 = mock(DeviceStateCallback.class);
+ mDeviceStateManagerGlobal.registerDeviceStateCallback(callback1, DIRECT_EXECUTOR);
+ mDeviceStateManagerGlobal.registerDeviceStateCallback(callback2, DIRECT_EXECUTOR);
mService.setSupportedStates(List.of(DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE));
- reset(callback1);
- reset(callback2);
-
- // Change the base state and verify callback
+ // Change the base state and verify callback.
mService.setBaseState(OTHER_DEVICE_STATE);
+
verify(callback1).onDeviceStateChanged(eq(mService.getMergedState()));
verify(callback2).onDeviceStateChanged(eq(mService.getMergedState()));
+ }
- reset(callback1);
- reset(callback2);
-
- // Change the requested state and verify callback
+ @Test
+ public void registerCallback_requestedStateChanged() {
+ final DeviceStateCallback callback1 = mock(DeviceStateCallback.class);
+ final DeviceStateCallback callback2 = mock(DeviceStateCallback.class);
final DeviceStateRequest request =
DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE.getIdentifier()).build();
+ mDeviceStateManagerGlobal.registerDeviceStateCallback(callback1, DIRECT_EXECUTOR);
+ mDeviceStateManagerGlobal.registerDeviceStateCallback(callback2, DIRECT_EXECUTOR);
+
+ // Change the requested state and verify callback.
mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */);
verify(callback1).onDeviceStateChanged(eq(mService.getMergedState()));
@@ -173,8 +196,7 @@
public void unregisterCallback() {
final DeviceStateCallback callback = mock(DeviceStateCallback.class);
- mDeviceStateManagerGlobal
- .registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR);
+ mDeviceStateManagerGlobal.registerDeviceStateCallback(callback, DIRECT_EXECUTOR);
// Verify initial callbacks
verify(callback).onSupportedStatesChanged(eq(mService.getSupportedDeviceStates()));
@@ -191,8 +213,7 @@
@Test
public void submitRequest() {
final DeviceStateCallback callback = mock(DeviceStateCallback.class);
- mDeviceStateManagerGlobal
- .registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR);
+ mDeviceStateManagerGlobal.registerDeviceStateCallback(callback, DIRECT_EXECUTOR);
verify(callback).onDeviceStateChanged(eq(mService.getBaseState()));
reset(callback);
@@ -212,8 +233,7 @@
@Test
public void submitBaseStateOverrideRequest() {
final DeviceStateCallback callback = mock(DeviceStateCallback.class);
- mDeviceStateManagerGlobal
- .registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR);
+ mDeviceStateManagerGlobal.registerDeviceStateCallback(callback, DIRECT_EXECUTOR);
verify(callback).onDeviceStateChanged(eq(mService.getBaseState()));
reset(callback);
@@ -234,8 +254,7 @@
@Test
public void submitBaseAndEmulatedStateOverride() {
final DeviceStateCallback callback = mock(DeviceStateCallback.class);
- mDeviceStateManagerGlobal
- .registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR);
+ mDeviceStateManagerGlobal.registerDeviceStateCallback(callback, DIRECT_EXECUTOR);
verify(callback).onDeviceStateChanged(eq(mService.getBaseState()));
reset(callback);
@@ -275,7 +294,7 @@
final DeviceStateRequest request =
DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE.getIdentifier()).build();
mDeviceStateManagerGlobal.requestState(request,
- ConcurrentUtils.DIRECT_EXECUTOR /* executor */,
+ DIRECT_EXECUTOR /* executor */,
callback /* callback */);
verify(callback).onRequestActivated(eq(request));
diff --git a/errorprone/Android.bp b/errorprone/Android.bp
index b559a15..1428b89 100644
--- a/errorprone/Android.bp
+++ b/errorprone/Android.bp
@@ -31,6 +31,14 @@
"//external/auto:auto_service_annotations",
],
+ javacflags: [
+ // These exports are needed because this errorprone plugin access some private classes
+ // of the java compiler.
+ "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
+ "--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
+ "--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
+ ],
+
plugins: [
"//external/auto:auto_service_plugin",
],
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
index 5ea3843..223f9f6 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
@@ -26,10 +26,12 @@
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
import android.hardware.devicestate.DeviceStateUtil;
+import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseIntArray;
+import androidx.annotation.BinderThread;
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
@@ -43,6 +45,7 @@
import java.util.List;
import java.util.Objects;
import java.util.Optional;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
@@ -69,38 +72,48 @@
private DeviceState mCurrentDeviceState = INVALID_DEVICE_STATE;
@NonNull
+ private final Context mContext;
+
+ @NonNull
private final RawFoldingFeatureProducer mRawFoldSupplier;
@NonNull
private final DeviceStateMapper mDeviceStateMapper;
- @VisibleForTesting
- final DeviceStateCallback mDeviceStateCallback = new DeviceStateCallback() {
- // The GuardedBy analysis is intra-procedural, meaning it doesn’t consider the getData()
- // implementation. See https://errorprone.info/bugpattern/GuardedBy for limitations.
- @SuppressWarnings("GuardedBy")
- @MainThread
+ private final DeviceStateCallback mDeviceStateCallback = new DeviceStateCallback() {
+ @BinderThread // Subsequent callback after registered.
+ @MainThread // Initial callback if registration is on the main thread.
@Override
public void onDeviceStateChanged(@NonNull DeviceState state) {
- mCurrentDeviceState = state;
- mRawFoldSupplier.getData(DeviceStateManagerFoldingFeatureProducer.this
- ::notifyFoldingFeatureChangeLocked);
+ final boolean isMainThread = Looper.myLooper() == Looper.getMainLooper();
+ final Executor executor = isMainThread ? Runnable::run : mContext.getMainExecutor();
+ executor.execute(() -> {
+ DeviceStateManagerFoldingFeatureProducer.this.onDeviceStateChanged(state);
+ });
}
};
public DeviceStateManagerFoldingFeatureProducer(@NonNull Context context,
@NonNull RawFoldingFeatureProducer rawFoldSupplier,
@NonNull DeviceStateManager deviceStateManager) {
+ mContext = context;
mRawFoldSupplier = rawFoldSupplier;
mDeviceStateMapper =
new DeviceStateMapper(context, deviceStateManager.getSupportedDeviceStates());
if (!mDeviceStateMapper.isDeviceStateToPostureMapEmpty()) {
Objects.requireNonNull(deviceStateManager)
- .registerCallback(context.getMainExecutor(), mDeviceStateCallback);
+ .registerCallback(Runnable::run, mDeviceStateCallback);
}
}
+ @MainThread
+ @VisibleForTesting
+ void onDeviceStateChanged(@NonNull DeviceState state) {
+ mCurrentDeviceState = state;
+ mRawFoldSupplier.getData(this::notifyFoldingFeatureChangeLocked);
+ }
+
/**
* Add a callback to mCallbacks if there is no device state. This callback will be run
* once a device state is set. Otherwise,run the callback immediately.
@@ -118,9 +131,6 @@
}
}
- // The GuardedBy analysis is intra-procedural, meaning it doesn’t consider the implementation of
- // addDataChangedCallback(). See https://errorprone.info/bugpattern/GuardedBy for limitations.
- @SuppressWarnings("GuardedBy")
@Override
protected void onListenersChanged() {
super.onListenersChanged();
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index 6928409..a5a84db 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -182,8 +182,15 @@
@NonNull
List<ParcelableSplitContainerData> getParcelableSplitContainerDataList() {
- final List<ParcelableSplitContainerData> data = new ArrayList<>(mSplitContainers.size());
+ final int size =
+ mSplitPinContainer != null ? mSplitContainers.size() - 1 : mSplitContainers.size();
+ final List<ParcelableSplitContainerData> data = new ArrayList<>(size);
for (SplitContainer splitContainer : mSplitContainers) {
+ if (splitContainer == mSplitPinContainer) {
+ // Skip SplitPinContainer as it cannot be restored because the SplitPinRule is
+ // set while pinning the container in runtime.
+ continue;
+ }
data.add(splitContainer.getParcelableData());
}
return data;
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducerTest.kt b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducerTest.kt
index fb01cd8..ad29bf6 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducerTest.kt
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducerTest.kt
@@ -21,6 +21,7 @@
import android.hardware.devicestate.DeviceState
import android.hardware.devicestate.DeviceStateManager
import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
import androidx.window.common.layout.CommonFoldingFeature
import androidx.window.common.layout.CommonFoldingFeature.COMMON_STATE_FLAT
import androidx.window.common.layout.CommonFoldingFeature.COMMON_STATE_HALF_OPENED
@@ -33,13 +34,14 @@
import com.android.internal.R
import com.google.common.truth.Truth.assertThat
import java.util.Optional
+import java.util.concurrent.Executor
import java.util.function.Consumer
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.doReturn
-import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.stub
@@ -69,14 +71,39 @@
}
@Test
- fun testRegisterCallback_usesMainExecutor() {
+ fun testRegisterCallback_initialCallbackOnMainThread_executesDirectly() {
DeviceStateManagerFoldingFeatureProducer(
mMockContext,
mRawFoldSupplier,
mMockDeviceStateManager,
)
+ val callbackCaptor = argumentCaptor<DeviceStateManager.DeviceStateCallback>()
+ verify(mMockDeviceStateManager).registerCallback(any(), callbackCaptor.capture())
- verify(mMockDeviceStateManager).registerCallback(eq(mMockContext.mainExecutor), any())
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ callbackCaptor.firstValue.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED)
+ }
+
+ verify(mMockContext, never()).getMainExecutor()
+ }
+
+ @Test
+ fun testRegisterCallback_subsequentCallbacks_postsToMainThread() {
+ val mockMainExecutor = mock<Executor>()
+ mMockContext.stub {
+ on { getMainExecutor() } doReturn mockMainExecutor
+ }
+ DeviceStateManagerFoldingFeatureProducer(
+ mMockContext,
+ mRawFoldSupplier,
+ mMockDeviceStateManager,
+ )
+ val callbackCaptor = argumentCaptor<DeviceStateManager.DeviceStateCallback>()
+ verify(mMockDeviceStateManager).registerCallback(any(), callbackCaptor.capture())
+
+ callbackCaptor.firstValue.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED)
+
+ verify(mockMainExecutor).execute(any())
}
@Test
@@ -86,7 +113,7 @@
mRawFoldSupplier,
mMockDeviceStateManager,
)
- ffp.mDeviceStateCallback.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED)
+ ffp.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED)
val currentData = ffp.getCurrentData()
@@ -209,7 +236,7 @@
mRawFoldSupplier,
mMockDeviceStateManager,
)
- ffp.mDeviceStateCallback.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED)
+ ffp.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED)
val storeFeaturesConsumer = mock<Consumer<List<CommonFoldingFeature>>>()
ffp.getData(storeFeaturesConsumer)
@@ -229,8 +256,8 @@
ffp.getData(storeFeaturesConsumer)
verify(storeFeaturesConsumer, never()).accept(any())
- ffp.mDeviceStateCallback.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED)
- ffp.mDeviceStateCallback.onDeviceStateChanged(DEVICE_STATE_OPENED)
+ ffp.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED)
+ ffp.onDeviceStateChanged(DEVICE_STATE_OPENED)
verify(storeFeaturesConsumer).accept(HALF_OPENED_FOLDING_FEATURES)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt
index 2b0724d..43544f6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt
@@ -72,35 +72,41 @@
KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW -> {
logV("Key gesture SNAP_LEFT_FREEFORM_WINDOW is handled")
getGloballyFocusedFreeformTask()?.let {
- desktopModeWindowDecorViewModel.get().onSnapResize(
- it.taskId,
- true,
- DesktopModeEventLogger.Companion.InputMethod.KEYBOARD,
- /* fromMenu= */ false
- )
+ mainExecutor.execute {
+ desktopModeWindowDecorViewModel.get().onSnapResize(
+ it.taskId,
+ true,
+ DesktopModeEventLogger.Companion.InputMethod.KEYBOARD,
+ /* fromMenu= */ false
+ )
+ }
}
return true
}
KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW -> {
logV("Key gesture SNAP_RIGHT_FREEFORM_WINDOW is handled")
getGloballyFocusedFreeformTask()?.let {
- desktopModeWindowDecorViewModel.get().onSnapResize(
- it.taskId,
- false,
- DesktopModeEventLogger.Companion.InputMethod.KEYBOARD,
- /* fromMenu= */ false
- )
+ mainExecutor.execute {
+ desktopModeWindowDecorViewModel.get().onSnapResize(
+ it.taskId,
+ false,
+ DesktopModeEventLogger.Companion.InputMethod.KEYBOARD,
+ /* fromMenu= */ false
+ )
+ }
}
return true
}
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW -> {
logV("Key gesture TOGGLE_MAXIMIZE_FREEFORM_WINDOW is handled")
getGloballyFocusedFreeformTask()?.let {
- desktopTasksController.get().toggleDesktopTaskSize(
- it,
- ResizeTrigger.MAXIMIZE_MENU,
- DesktopModeEventLogger.Companion.InputMethod.KEYBOARD,
- )
+ mainExecutor.execute {
+ desktopTasksController.get().toggleDesktopTaskSize(
+ it,
+ ResizeTrigger.MAXIMIZE_MENU,
+ DesktopModeEventLogger.Companion.InputMethod.KEYBOARD,
+ )
+ }
}
return true
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index ad28dcc..c479ab3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -797,6 +797,8 @@
taskInfo: RunningTaskInfo,
resizeTrigger: ResizeTrigger,
inputMethod: InputMethod,
+ maximizeCujRecorder: (() -> Unit)? = null,
+ unmaximizeCujRecorder: (() -> Unit)? = null,
) {
val currentTaskBounds = taskInfo.configuration.windowConfiguration.bounds
desktopModeEventLogger.logTaskResizingStarted(
@@ -818,6 +820,7 @@
// helpful to eliminate the current task from logic to calculate taskbar corner rounding.
val willMaximize = !isMaximized
if (isMaximized) {
+ unmaximizeCujRecorder?.invoke()
// The desktop task is at the maximized width and/or height of the stable bounds.
// If the task's pre-maximize stable bounds were saved, toggle the task to those bounds.
// Otherwise, toggle to the default bounds.
@@ -833,6 +836,7 @@
}
}
} else {
+ maximizeCujRecorder?.invoke()
// Save current bounds so that task can be restored back to original bounds if necessary
// and toggle to the stable bounds.
desktopTilingDecorViewModel.removeTaskIfTiled(taskInfo.displayId, taskInfo.taskId)
@@ -889,7 +893,7 @@
toggleDesktopTaskSize(
taskInfo,
ResizeTrigger.DRAG_TO_TOP_RESIZE_TRIGGER,
- DesktopModeEventLogger.getInputMethodFromMotionEvent(motionEvent)
+ DesktopModeEventLogger.getInputMethodFromMotionEvent(motionEvent),
)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt
index 9411150..6df3302 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt
@@ -119,6 +119,7 @@
initialBounds = null
boundsAnimator = null
interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW)
+ interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_UNMAXIMIZE_WINDOW)
interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE)
}
)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 8ee087b..8f02c1b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -35,6 +35,7 @@
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.PictureInPictureParams;
+import android.app.TaskInfo;
import android.content.Context;
import android.graphics.Point;
import android.graphics.PointF;
@@ -57,6 +58,7 @@
import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipMenuController;
import com.android.wm.shell.common.pip.PipUtils;
+import com.android.wm.shell.common.split.SplitScreenUtils;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip2.animation.PipAlphaAnimator;
import com.android.wm.shell.pip2.animation.PipEnterAnimator;
@@ -74,8 +76,8 @@
private static final String TAG = PipTransition.class.getSimpleName();
// Used when for ENTERING_PIP state update.
- private static final String PIP_TASK_TOKEN = "pip_task_token";
private static final String PIP_TASK_LEASH = "pip_task_leash";
+ private static final String PIP_TASK_INFO = "pip_task_info";
// Used for PiP CHANGING_BOUNDS state update.
static final String PIP_START_TX = "pip_start_tx";
@@ -245,8 +247,8 @@
// Update the PipTransitionState while supplying the PiP leash and token to be cached.
Bundle extra = new Bundle();
- extra.putParcelable(PIP_TASK_TOKEN, pipChange.getContainer());
extra.putParcelable(PIP_TASK_LEASH, pipChange.getLeash());
+ extra.putParcelable(PIP_TASK_INFO, pipChange.getTaskInfo());
mPipTransitionState.setState(PipTransitionState.ENTERING_PIP, extra);
if (isInSwipePipToHomeTransition()) {
@@ -899,10 +901,10 @@
Preconditions.checkState(extra != null,
"No extra bundle for " + mPipTransitionState);
- mPipTransitionState.setPipTaskToken(extra.getParcelable(
- PIP_TASK_TOKEN, WindowContainerToken.class));
mPipTransitionState.setPinnedTaskLeash(extra.getParcelable(
PIP_TASK_LEASH, SurfaceControl.class));
+ mPipTransitionState.setPipTaskInfo(extra.getParcelable(
+ PIP_TASK_INFO, TaskInfo.class));
boolean hasValidTokenAndLeash = mPipTransitionState.getPipTaskToken() != null
&& mPipTransitionState.getPinnedTaskLeash() != null;
@@ -915,16 +917,16 @@
mPipBoundsState.getBounds());
mPipBoundsState.saveReentryState(snapFraction);
- mPipTransitionState.setPipTaskToken(null);
mPipTransitionState.setPinnedTaskLeash(null);
+ mPipTransitionState.setPipTaskInfo(null);
break;
}
}
@Override
public boolean isPackageActiveInPip(@Nullable String packageName) {
- return packageName != null
- && mPipBoundsState.getLastPipComponentName() != null
- && packageName.equals(mPipBoundsState.getLastPipComponentName().getPackageName());
+ final TaskInfo inPipTask = mPipTransitionState.getPipTaskInfo();
+ return packageName != null && inPipTask != null && mPipTransitionState.isInPip()
+ && packageName.equals(SplitScreenUtils.getPackageName(inPipTask.baseIntent));
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
index 8e90bfe..6f9f40a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.pip2.phone;
import android.annotation.IntDef;
+import android.app.TaskInfo;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
@@ -133,17 +134,17 @@
private final Rect mSwipePipToHomeAppBounds = new Rect();
//
- // Tokens and leashes
+ // Task related caches
//
- // pinned PiP task's WC token
- @Nullable
- private WindowContainerToken mPipTaskToken;
-
// pinned PiP task's leash
@Nullable
private SurfaceControl mPinnedTaskLeash;
+ // pinned PiP task info
+ @Nullable
+ private TaskInfo mPipTaskInfo;
+
// Overlay leash potentially used during swipe PiP to home transition;
// if null while mInSwipePipToHomeTransition is true, then srcRectHint was invalid.
@Nullable
@@ -305,11 +306,7 @@
}
@Nullable WindowContainerToken getPipTaskToken() {
- return mPipTaskToken;
- }
-
- public void setPipTaskToken(@Nullable WindowContainerToken token) {
- mPipTaskToken = token;
+ return mPipTaskInfo != null ? mPipTaskInfo.getToken() : null;
}
@Nullable SurfaceControl getPinnedTaskLeash() {
@@ -320,6 +317,14 @@
mPinnedTaskLeash = leash;
}
+ @Nullable TaskInfo getPipTaskInfo() {
+ return mPipTaskInfo;
+ }
+
+ void setPipTaskInfo(@Nullable TaskInfo pipTaskInfo) {
+ mPipTaskInfo = pipTaskInfo;
+ }
+
/**
* @return true if either in swipe or button-nav fixed rotation.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 4f0f676..6e0e696 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -582,6 +582,7 @@
@Nullable WindowContainerToken hideTaskToken) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
"Legacy startTask does not support hide task token");
+ if (isTaskInSplitScreenForeground(taskId)) return;
final int[] result = new int[1];
IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 88a9566..265bb25 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -833,7 +833,14 @@
setSideStagePosition(splitPosition, wct);
options1 = options1 != null ? options1 : new Bundle();
- addActivityOptions(options1, mSideStage);
+ StageTaskListener stageForTask1;
+ if (enableFlexibleSplit()) {
+ stageForTask1 = mStageOrderOperator.getStageForLegacyPosition(splitPosition,
+ true /*checkAllStagesIfNotActive*/);
+ } else {
+ stageForTask1 = mSideStage;
+ }
+ addActivityOptions(options1, stageForTask1);
wct.sendPendingIntent(pendingIntent, fillInIntent, options1);
prepareTasksForSplitScreen(new int[] {taskId}, wct);
@@ -878,7 +885,14 @@
setSideStagePosition(splitPosition, wct);
options1 = options1 != null ? options1 : new Bundle();
- addActivityOptions(options1, mSideStage);
+ StageTaskListener stageForTask1;
+ if (enableFlexibleSplit()) {
+ stageForTask1 = mStageOrderOperator.getStageForLegacyPosition(splitPosition,
+ true /*checkAllStagesIfNotActive*/);
+ } else {
+ stageForTask1 = mSideStage;
+ }
+ addActivityOptions(options1, stageForTask1);
wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1);
prepareTasksForSplitScreen(new int[] {taskId}, wct);
@@ -1010,14 +1024,29 @@
setRootForceTranslucent(false, wct);
options1 = options1 != null ? options1 : new Bundle();
- addActivityOptions(options1, mSideStage);
+ StageTaskListener stageForTask1;
+ if (enableFlexibleSplit()) {
+ stageForTask1 = mStageOrderOperator.getStageForLegacyPosition(splitPosition,
+ true /*checkAllStagesIfNotActive*/);
+ } else {
+ stageForTask1 = mSideStage;
+ }
+ addActivityOptions(options1, stageForTask1);
if (shortcutInfo1 != null) {
wct.startShortcut(mContext.getPackageName(), shortcutInfo1, options1);
} else {
wct.sendPendingIntent(pendingIntent1, fillInIntent1, options1);
}
+
+ StageTaskListener stageForTask2;
+ if (enableFlexibleSplit()) {
+ stageForTask2 = mStageOrderOperator.getStageForLegacyPosition(
+ reverseSplitPosition(splitPosition), true /*checkAllStagesIfNotActive*/);
+ } else {
+ stageForTask2 = mMainStage;
+ }
options2 = options2 != null ? options2 : new Bundle();
- addActivityOptions(options2, mMainStage);
+ addActivityOptions(options2, stageForTask2);
if (shortcutInfo2 != null) {
wct.startShortcut(mContext.getPackageName(), shortcutInfo2, options2);
} else {
@@ -1246,6 +1275,9 @@
// Restore focus-ability to the windows and divider
wct.setFocusable(mRootTaskInfo.token, true);
+ if (enableFlexibleSplit()) {
+ mStageOrderOperator.onDoubleTappedDivider();
+ }
setSideStagePosition(reverseSplitPosition(mSideStagePosition), wct);
mSyncQueue.queue(wct);
mSyncQueue.runInSync(st -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageOrderOperator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageOrderOperator.kt
index b7b3c9b..3fa8df4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageOrderOperator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageOrderOperator.kt
@@ -38,6 +38,7 @@
import com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_C
import com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString
import com.android.wm.shell.windowdecor.WindowDecorViewModel
+import java.util.Collections
import java.util.Optional
/**
@@ -148,6 +149,24 @@
}
/**
+ * This will swap the stages for the two stages on either side of the given divider.
+ * Note: This will keep [activeStages] and [allStages] in sync by swapping both of them
+ * If there are no [activeStages] then this will be a no-op.
+ *
+ * TODO(b/379984874): Take in a divider identifier to determine which array indices to swap
+ */
+ fun onDoubleTappedDivider() {
+ if (activeStages.isEmpty()) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ "Stages not active, ignoring swap request")
+ return
+ }
+
+ Collections.swap(activeStages, 0, 1)
+ Collections.swap(allStages, 0, 1)
+ }
+
+ /**
* Returns a legacy split position for the given stage. If no stages are active then this will
* return [SPLIT_POSITION_UNDEFINED]
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index f8a4090..04ef7c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -586,11 +586,18 @@
if (decoration == null) {
return;
}
- mInteractionJankMonitor.begin(
- decoration.mTaskSurface, mContext, mMainHandler,
- Cuj.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW, source);
mDesktopTasksController.toggleDesktopTaskSize(decoration.mTaskInfo, resizeTrigger,
- DesktopModeEventLogger.getInputMethodFromMotionEvent(motionEvent));
+ DesktopModeEventLogger.getInputMethodFromMotionEvent(motionEvent), () -> {
+ mInteractionJankMonitor.begin(
+ decoration.mTaskSurface, mContext, mMainHandler,
+ Cuj.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW, source);
+ return null;
+ }, () -> {
+ mInteractionJankMonitor.begin(
+ decoration.mTaskSurface, mContext, mMainHandler,
+ Cuj.CUJ_DESKTOP_MODE_UNMAXIMIZE_WINDOW, source);
+ return null;
+ });
decoration.closeHandleMenu();
decoration.closeMaximizeMenu();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index bfea342..96cc559 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -1459,10 +1459,13 @@
@NonNull Function1<Integer, Unit> onIconClickListener
) {
if (mTaskInfo.isFreeform()) {
+ // The menu uses display-wide coordinates for positioning, so make position the sum
+ // of task position and caption position.
+ final Rect taskBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
mManageWindowsMenu = new DesktopHeaderManageWindowsMenu(
mTaskInfo,
- /* x= */ mResult.mCaptionX,
- /* y= */ mResult.mCaptionY + mResult.mCaptionTopPadding,
+ /* x= */ taskBounds.left + mResult.mCaptionX,
+ /* y= */ taskBounds.top + mResult.mCaptionY + mResult.mCaptionTopPadding,
mDisplayController,
mRootTaskDisplayAreaOrganizer,
mContext,
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BottomHalfPipLaunchingActivity.java b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/UnmaximizeAppWindowTest.kt
similarity index 60%
rename from tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BottomHalfPipLaunchingActivity.java
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/UnmaximizeAppWindowTest.kt
index d9d4361..7a71d4b 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BottomHalfPipLaunchingActivity.java
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/UnmaximizeAppWindowTest.kt
@@ -14,18 +14,14 @@
* limitations under the License.
*/
-package com.android.server.wm.flicker.testapp;
+package com.android.wm.shell.functional
-import android.content.Intent;
-import android.os.Bundle;
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.UnmaximizeAppWindow
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
-public class BottomHalfPipLaunchingActivity extends SimpleActivity {
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- final Intent intent = new Intent(this, BottomHalfPipActivity.class);
- startActivity(intent);
- }
-}
+/* Functional test for [UnmaximizeAppWindow]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class UnmaximizeAppWindowTest : UnmaximizeAppWindow()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/UnmaximizeAppWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/UnmaximizeAppWindow.kt
new file mode 100644
index 0000000..7411250
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/UnmaximizeAppWindow.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.flicker.rules.ChangeDisplayOrientationRule
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
+import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.window.flags.Flags
+import com.android.wm.shell.Utils
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+/**
+ * Testing "unmaximizing" a window e.g. making it get out of/exit a window that was already
+ * maximized.
+ */
+@Ignore("Test Base Class")
+abstract class UnmaximizeAppWindow
+constructor(private val rotation: Rotation = Rotation.ROTATION_0, isResizable: Boolean = true) {
+
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val testApp = if (isResizable) {
+ DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+ } else {
+ DesktopModeAppHelper(NonResizeableAppHelper(instrumentation))
+ }
+
+ @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
+
+ @Before
+ fun setup() {
+ Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+ ChangeDisplayOrientationRule.setRotation(rotation)
+ testApp.enterDesktopMode(wmHelper, device)
+ // Press the buttonn once to setup app window to be maximized already
+ testApp.maximiseDesktopApp(wmHelper, device)
+ }
+
+ @Test
+ open fun unmaximizeAppWindow() {
+ // Re-press button to exit maximized state
+ testApp.maximiseDesktopApp(wmHelper, device)
+ }
+
+ @After
+ fun teardown() {
+ testApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
index f40edae..ddbc681 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
@@ -266,26 +266,5 @@
test_suites: ["device-tests"],
}
-test_module_config {
- name: "WMShellFlickerTestsPip-nonMatchParent",
- base: "WMShellFlickerTestsPip",
- include_filters: ["com.android.wm.shell.flicker.pip.nonmatchparent.*"],
- test_suites: ["device-tests"],
-}
-
-test_module_config {
- name: "WMShellFlickerTestsPip-BottomHalfExitPipToAppViaExpandButtonTest",
- base: "WMShellFlickerTestsPip",
- include_filters: ["com.android.wm.shell.flicker.pip.nonmatchparent.BottomHalfExitPipToAppViaExpandButtonTest"],
- test_suites: ["device-tests"],
-}
-
-test_module_config {
- name: "WMShellFlickerTestsPip-BottomHalfExitPipToAppViaIntentTest",
- base: "WMShellFlickerTestsPip",
- include_filters: ["com.android.wm.shell.flicker.pip.nonmatchparent.BottomHalfExitPipToAppViaIntentTest"],
- test_suites: ["device-tests"],
-}
-
// End breakdowns for WMShellFlickerTestsPip module
////////////////////////////////////////////////////////////////////////////////
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt
index 7861d20..c37bf35 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt
@@ -40,6 +40,7 @@
@Rule
val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+ protected val pipApp = PipAppHelper(instrumentation)
protected val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.startRotation)
protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation)
@@ -62,11 +63,6 @@
}
}
- /**
- * Defines the test app to run PIP flicker test.
- */
- protected open val pipApp = PipAppHelper(instrumentation)
-
/** Defines the transition used to run the test */
protected open val thisTransition: FlickerBuilder.() -> Unit = {}
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppTransition.kt
deleted file mode 100644
index a6f29fc..0000000
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppTransition.kt
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.pip.nonmatchparent
-
-import android.platform.test.annotations.Presubmit
-import android.platform.test.annotations.RequiresFlagsEnabled
-import android.tools.flicker.legacy.LegacyFlickerTest
-import android.tools.traces.component.ComponentNameMatcher
-import com.android.server.wm.flicker.helpers.BottomHalfPipAppHelper
-import com.android.server.wm.flicker.helpers.PipAppHelper
-import com.android.window.flags.Flags
-import com.android.wm.shell.flicker.pip.common.ExitPipToAppTransition
-import org.junit.Test
-
-/**
- * Base test class to verify PIP exit animation with an activity layout to the bottom half of
- * the container.
- */
-@RequiresFlagsEnabled(Flags.FLAG_BETTER_SUPPORT_NON_MATCH_PARENT_ACTIVITY)
-abstract class BottomHalfExitPipToAppTransition(flicker: LegacyFlickerTest) :
- ExitPipToAppTransition(flicker) {
-
- override val pipApp: PipAppHelper = BottomHalfPipAppHelper(instrumentation)
-
- @Presubmit
- @Test
- override fun showBothAppLayersThenHidePip() {
- // Disabled since the BottomHalfPipActivity just covers half of the simple activity.
- }
-
- @Presubmit
- @Test
- override fun showBothAppWindowsThenHidePip() {
- // Disabled since the BottomHalfPipActivity just covers half of the simple activity.
- }
-
- @Presubmit
- @Test
- override fun pipAppCoversFullScreenAtEnd() {
- // Disabled since the BottomHalfPipActivity just covers half of the simple activity.
- }
-
- /**
- * Checks that the [testApp] and [pipApp] are always visible since the [pipApp] only covers
- * half of screen.
- */
- @Presubmit
- @Test
- fun showBothAppLayersDuringPipTransition() {
- flicker.assertLayers {
- isVisible(testApp)
- .isVisible(pipApp.or(ComponentNameMatcher.TRANSITION_SNAPSHOT))
- }
- }
-
- /**
- * Checks that the [testApp] and [pipApp] are always visible since the [pipApp] only covers
- * half of screen.
- */
- @Presubmit
- @Test
- fun showBothAppWindowsDuringPipTransition() {
- flicker.assertWm {
- isAppWindowVisible(testApp)
- .isAppWindowOnTop(pipApp)
- .isAppWindowVisible(pipApp)
- }
- }
-
- /**
- * Verify that the [testApp] and [pipApp] covers the entire screen at the end of PIP exit
- * animation since the [pipApp] will use a bottom half layout.
- */
- @Presubmit
- @Test
- fun testPlusPipAppCoversWindowFrameAtEnd() {
- flicker.assertLayersEnd {
- val pipRegion = visibleRegion(pipApp).region
- visibleRegion(testApp).plus(pipRegion).coversExactly(displayBounds)
- }
- }
-}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppViaExpandButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppViaExpandButtonTest.kt
deleted file mode 100644
index f492bd4..0000000
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppViaExpandButtonTest.kt
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-package com.android.wm.shell.flicker.pip.nonmatchparent
-
-import android.tools.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.flicker.legacy.FlickerBuilder
-import android.tools.flicker.legacy.LegacyFlickerTest
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test expanding a pip window back to bottom half layout via the expand button
- *
- * To run this test: `atest WMShellFlickerTestsPip:BottomHalfExitPipToAppViaExpandButtonTest`
- *
- * Actions:
- * ```
- * Launch an app in pip mode [bottomHalfPipApp],
- * Launch another full screen mode [testApp]
- * Expand [bottomHalfPipApp] app to bottom half layout by clicking on the pip window and
- * then on the expand button
- * ```
- *
- * Notes:
- * ```
- * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
- * are inherited [PipTransition]
- * 2. Part of the test setup occurs automatically via
- * [android.tools.flicker.legacy.runner.TransitionRunner],
- * including configuring navigation mode, initial orientation and ensuring no
- * apps are running before setup
- * ```
- */
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class BottomHalfExitPipToAppViaExpandButtonTest(flicker: LegacyFlickerTest) :
- BottomHalfExitPipToAppTransition(flicker)
-{
- override val thisTransition: FlickerBuilder.() -> Unit = {
- setup {
- // launch an app behind the pip one
- testApp.launchViaIntent(wmHelper)
- }
- transitions {
- // This will bring PipApp to fullscreen
- pipApp.expandPipWindowToApp(wmHelper)
- // Wait until the transition idle and test and pip app still shows.
- wmHelper.StateSyncBuilder().withLayerVisible(testApp).withLayerVisible(pipApp)
- .withAppTransitionIdle().waitForAndVerify()
- }
- }
-}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppViaIntentTest.kt
deleted file mode 100644
index a76a647..0000000
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppViaIntentTest.kt
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.pip.nonmatchparent
-
-import android.tools.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.flicker.legacy.FlickerBuilder
-import android.tools.flicker.legacy.LegacyFlickerTest
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test expanding a pip window back to bottom half layout via an intent
- *
- * To run this test: `atest WMShellFlickerTestsPip:BottomHalfExitPipToAppViaIntentTest`
- *
- * Actions:
- * ```
- * Launch an app in pip mode [bottomHalfPipApp],
- * Launch another full screen mode [testApp]
- * Expand [bottomHalfPipApp] app to bottom half layout via an intent
- * ```
- *
- * Notes:
- * ```
- * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
- * are inherited from [PipTransition]
- * 2. Part of the test setup occurs automatically via
- * [android.tools.flicker.legacy.runner.TransitionRunner],
- * including configuring navigation mode, initial orientation and ensuring no
- * apps are running before setup
- * ```
- */
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class BottomHalfExitPipToAppViaIntentTest(flicker: LegacyFlickerTest) :
- BottomHalfExitPipToAppTransition(flicker)
-{
- override val thisTransition: FlickerBuilder.() -> Unit = {
- setup {
- // launch an app behind the pip one
- testApp.launchViaIntent(wmHelper)
- }
- transitions {
- // This will bring PipApp to fullscreen
- pipApp.exitPipToFullScreenViaIntent(wmHelper)
- // Wait until the transition idle and test and pip app still shows.
- wmHelper.StateSyncBuilder().withLayerVisible(testApp).withLayerVisible(pipApp)
- .withAppTransitionIdle().waitForAndVerify()
- }
- }
-}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt
index 19397c2..6050695 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt
@@ -53,10 +53,9 @@
import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.dx.mockito.inline.extended.StaticMockitoSession
import com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS
+import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
-import com.android.wm.shell.common.ShellExecutor
-import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel
@@ -91,7 +90,7 @@
private val rootTaskDisplayAreaOrganizer = mock<RootTaskDisplayAreaOrganizer>()
private val shellTaskOrganizer = mock<ShellTaskOrganizer>()
private val focusTransitionObserver = mock<FocusTransitionObserver>()
- private val testExecutor = mock<ShellExecutor>()
+ private val testExecutor = TestShellExecutor()
private val inputManager = mock<InputManager>()
private val displayController = mock<DisplayController>()
private val displayLayout = mock<DisplayLayout>()
@@ -148,6 +147,7 @@
runningTasks.clear()
testScope.cancel()
+ testExecutor.flushAll()
}
@Test
@@ -201,12 +201,7 @@
val result = keyGestureEventHandler.handleKeyGestureEvent(event, null)
assertThat(result).isTrue()
- verify(desktopModeWindowDecorViewModel).onSnapResize(
- task.taskId,
- true,
- DesktopModeEventLogger.Companion.InputMethod.KEYBOARD,
- /* fromMenu= */ false
- )
+ assertThat(testExecutor.callbacks.size).isEqualTo(1)
}
@Test
@@ -228,12 +223,7 @@
val result = keyGestureEventHandler.handleKeyGestureEvent(event, null)
assertThat(result).isTrue()
- verify(desktopModeWindowDecorViewModel).onSnapResize(
- task.taskId,
- false,
- DesktopModeEventLogger.Companion.InputMethod.KEYBOARD,
- /* fromMenu= */ false
- )
+ assertThat(testExecutor.callbacks.size).isEqualTo(1)
}
@Test
@@ -255,11 +245,7 @@
val result = keyGestureEventHandler.handleKeyGestureEvent(event, null)
assertThat(result).isTrue()
- verify(desktopTasksController).toggleDesktopTaskSize(
- task,
- ResizeTrigger.MAXIMIZE_MENU,
- DesktopModeEventLogger.Companion.InputMethod.KEYBOARD
- )
+ assertThat(testExecutor.callbacks.size).isEqualTo(1)
}
@Test
@@ -281,6 +267,7 @@
val result = keyGestureEventHandler.handleKeyGestureEvent(event, null)
assertThat(result).isTrue()
+ assertThat(testExecutor.callbacks.size).isEqualTo(1)
}
private fun setUpFreeformTask(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index 6c910f5..153be07 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -398,9 +398,11 @@
maxOrRestoreListenerCaptor.value.invoke()
verify(mockDesktopTasksController).toggleDesktopTaskSize(
- decor.mTaskInfo,
- ResizeTrigger.MAXIMIZE_MENU,
- InputMethod.UNKNOWN_INPUT_METHOD
+ eq(decor.mTaskInfo),
+ eq(ResizeTrigger.MAXIMIZE_MENU),
+ eq(InputMethod.UNKNOWN_INPUT_METHOD),
+ any(),
+ any()
)
}
@@ -1059,9 +1061,11 @@
verify(mockDesktopTasksController)
.toggleDesktopTaskSize(
- decor.mTaskInfo,
- ResizeTrigger.MAXIMIZE_BUTTON,
- InputMethod.UNKNOWN_INPUT_METHOD
+ eq(decor.mTaskInfo),
+ eq(ResizeTrigger.MAXIMIZE_BUTTON),
+ eq(InputMethod.UNKNOWN_INPUT_METHOD),
+ any(),
+ any(),
)
}
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index 2d812d6..4dfe053 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -76,6 +76,7 @@
result->fBaseWeight = resolvedFace->fBaseWeight;
result->fAPIStyle = style;
result->fStyle = computeRelativeStyle(result->fBaseWeight, style);
+ result->fIsVariationInstance = resolvedFace->fIsVariationInstance;
}
return result;
}
@@ -88,6 +89,7 @@
result->fBaseWeight = resolvedFace->fBaseWeight;
result->fAPIStyle = computeAPIStyle(weight, italic);
result->fStyle = computeMinikinStyle(weight, italic);
+ result->fIsVariationInstance = resolvedFace->fIsVariationInstance;
}
return result;
}
@@ -109,6 +111,7 @@
result->fBaseWeight = resolvedFace->fBaseWeight;
result->fAPIStyle = resolvedFace->fAPIStyle;
result->fStyle = resolvedFace->fStyle;
+ result->fIsVariationInstance = true;
}
return result;
}
@@ -121,6 +124,7 @@
result->fBaseWeight = weight;
result->fAPIStyle = resolvedFace->fAPIStyle;
result->fStyle = computeRelativeStyle(weight, result->fAPIStyle);
+ result->fIsVariationInstance = resolvedFace->fIsVariationInstance;
}
return result;
}
@@ -170,6 +174,7 @@
result->fBaseWeight = weight;
result->fAPIStyle = computeAPIStyle(weight, italic);
result->fStyle = computeMinikinStyle(weight, italic);
+ result->fIsVariationInstance = false;
return result;
}
diff --git a/libs/hwui/hwui/Typeface.h b/libs/hwui/hwui/Typeface.h
index 2c96c1a..97d1bf4 100644
--- a/libs/hwui/hwui/Typeface.h
+++ b/libs/hwui/hwui/Typeface.h
@@ -44,6 +44,9 @@
// base weight in CSS-style units, 1..1000
int fBaseWeight;
+ // True if the Typeface is already created for variation settings.
+ bool fIsVariationInstance;
+
static const Typeface* resolveDefault(const Typeface* src);
// The following three functions create new Typeface from an existing Typeface with a different
diff --git a/libs/hwui/jni/text/TextShaper.cpp b/libs/hwui/jni/text/TextShaper.cpp
index 5f69346..c735989 100644
--- a/libs/hwui/jni/text/TextShaper.cpp
+++ b/libs/hwui/jni/text/TextShaper.cpp
@@ -68,7 +68,7 @@
static jlong shapeTextRun(const uint16_t* text, int textSize, int start, int count,
int contextStart, int contextCount, minikin::Bidi bidiFlags,
const Paint& paint, const Typeface* typeface) {
-
+ const Typeface* resolvedFace = Typeface::resolveDefault(typeface);
minikin::MinikinPaint minikinPaint = MinikinUtils::prepareMinikinPaint(&paint, typeface);
minikin::Layout layout = MinikinUtils::doLayout(&paint, bidiFlags, typeface,
@@ -103,8 +103,16 @@
fontId = it->second; // We've seen it.
} else {
fontId = fonts.size(); // This is new to us. Create new one.
- std::shared_ptr<minikin::Font> font = std::make_shared<minikin::Font>(
- fakedFont.font, fakedFont.fakery.variationSettings());
+ std::shared_ptr<minikin::Font> font;
+ if (resolvedFace->fIsVariationInstance) {
+ // The optimization for target SDK 35 or before because the variation instance
+ // is already created and no runtime variation resolution happens on such
+ // environment.
+ font = fakedFont.font;
+ } else {
+ font = std::make_shared<minikin::Font>(fakedFont.font,
+ fakedFont.fakery.variationSettings());
+ }
fonts.push_back(reinterpret_cast<jlong>(new FontWrapper(std::move(font))));
fakedToFontIds.insert(std::make_pair(fakedFont, fontId));
}
diff --git a/location/java/android/location/altitude/AltitudeConverter.java b/location/java/android/location/altitude/AltitudeConverter.java
index b9fe804..3a4e70d 100644
--- a/location/java/android/location/altitude/AltitudeConverter.java
+++ b/location/java/android/location/altitude/AltitudeConverter.java
@@ -26,8 +26,8 @@
import android.location.flags.Flags;
import com.android.internal.location.altitude.GeoidMap;
-import com.android.internal.location.altitude.S2CellIdUtils;
import com.android.internal.location.altitude.nano.MapParamsProto;
+import com.android.internal.location.geometry.S2CellIdUtils;
import com.android.internal.util.Preconditions;
import java.io.IOException;
diff --git a/location/java/com/android/internal/location/altitude/GeoidMap.java b/location/java/com/android/internal/location/altitude/GeoidMap.java
index df9ca97..d77fb9e 100644
--- a/location/java/com/android/internal/location/altitude/GeoidMap.java
+++ b/location/java/com/android/internal/location/altitude/GeoidMap.java
@@ -26,6 +26,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.location.altitude.nano.MapParamsProto;
import com.android.internal.location.altitude.nano.S2TileProto;
+import com.android.internal.location.geometry.S2CellIdUtils;
import com.android.internal.util.Preconditions;
import java.io.IOException;
diff --git a/location/java/com/android/internal/location/altitude/S2CellIdUtils.java b/location/java/com/android/internal/location/geometry/S2CellIdUtils.java
similarity index 80%
rename from location/java/com/android/internal/location/altitude/S2CellIdUtils.java
rename to location/java/com/android/internal/location/geometry/S2CellIdUtils.java
index 08bcda4..fbdaf49 100644
--- a/location/java/com/android/internal/location/altitude/S2CellIdUtils.java
+++ b/location/java/com/android/internal/location/geometry/S2CellIdUtils.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.location.altitude;
+package com.android.internal.location.geometry;
import android.annotation.NonNull;
@@ -48,12 +48,22 @@
private static final double UV_LIMIT = calculateUvLimit();
private static final UvTransform[] UV_TRANSFORMS = createUvTransforms();
private static final XyzTransform[] XYZ_TRANSFORMS = createXyzTransforms();
+ private static final long MAX_SI_TI = 1L << (MAX_LEVEL + 1);
// Used to encode (i, j, o) coordinates into primitive longs.
private static final int I_SHIFT = 33;
private static final int J_SHIFT = 2;
private static final long J_MASK = (1L << 31) - 1;
+ // Used to insert latitude and longitude values into arrays.
+ public static final int LAT_LNG_MIN_LENGTH = 2;
+ public static final int LAT_INDEX = 0;
+ public static final int LNG_INDEX = 1;
+
+ // Used to encode (si, ti) coordinates into primitive longs.
+ private static final int SI_SHIFT = 32;
+ private static final long TI_MASK = (1L << 32) - 1;
+
static {
initLookupCells();
}
@@ -63,6 +73,130 @@
}
/**
+ * Inserts into {@code latLngDegrees} the centroid latitude and longitude, in that order and
+ * both measured in degrees, for the specified S2 cell ID. This array must be non-null and of
+ * minimum length two. A reference to this array is returned.
+ *
+ * <p>Behavior is undefined for invalid S2 cell IDs.
+ */
+ public static double[] toLatLngDegrees(long s2CellId, double[] latLngDegrees) {
+ // Used latLngDegrees as scratchpad for toLatLngRadians(long, double[]).
+ final double[] latLngRadians = latLngDegrees;
+ toLatLngRadians(s2CellId, latLngRadians);
+ latLngDegrees[LAT_INDEX] = Math.toDegrees(latLngRadians[LAT_INDEX]);
+ latLngDegrees[LNG_INDEX] = Math.toDegrees(latLngRadians[LNG_INDEX]);
+ return latLngDegrees;
+ }
+
+
+ /**
+ * Inserts into {@code latLngRadians} the centroid latitude and longitude, in that order and
+ * both measured in radians, for the specified S2 cell ID. This array must be non-null and of
+ * minimum length two. A reference to this array is returned.
+ *
+ * <p>Behavior is undefined for invalid S2 cell IDs.
+ */
+ public static double[] toLatLngRadians(long s2CellId, double[] latLngRadians) {
+ checkNotNull(latLngRadians);
+ checkLengthGreaterThanOrEqualTo(LAT_LNG_MIN_LENGTH, latLngRadians.length);
+
+ final long siTi = toSiTi(s2CellId);
+ final double u = siTiToU(siTi);
+ final double v = siTiToV(siTi);
+
+ final int face = getFace(s2CellId);
+ final XyzTransform xyzTransform = faceToXyzTransform(face);
+ final double x = xyzTransform.uvToX(u, v);
+ final double y = xyzTransform.uvToY(u, v);
+ final double z = xyzTransform.uvToZ(u, v);
+
+ latLngRadians[LAT_INDEX] = xyzToLatRadians(x, y, z);
+ latLngRadians[LNG_INDEX] = xyzToLngRadians(x, y);
+ return latLngRadians;
+ }
+
+ private static long toSiTi(long s2CellId) {
+ final long ijo = toIjo(s2CellId);
+ final int i = ijoToI(ijo);
+ final int j = ijoToJ(ijo);
+ int delta = isLeaf(s2CellId) ? 1 : (((i ^ (((int) s2CellId) >>> 2)) & 1) != 0) ? 2 : 0;
+ return (((long) (2 * i + delta)) << SI_SHIFT) | ((2 * j + delta) & TI_MASK);
+ }
+
+ private static int siTiToSi(long siTi) {
+ return (int) (siTi >> SI_SHIFT);
+ }
+
+ private static int siTiToTi(long siTi) {
+ return (int) siTi;
+ }
+
+ private static double siTiToU(long siTi) {
+ final int si = siTiToSi(siTi);
+ return siToU(si);
+ }
+
+ private static double siTiToV(long siTi) {
+ final int ti = siTiToTi(siTi);
+ return tiToV(ti);
+ }
+
+ private static double siToU(long si) {
+ final double s = (1.0 / MAX_SI_TI) * si;
+ if (s >= 0.5) {
+ return (1 / 3.) * (4 * s * s - 1);
+ }
+ return (1 / 3.) * (1 - 4 * (1 - s) * (1 - s));
+ }
+
+ private static double tiToV(long ti) {
+ // Same calculation as siToU.
+ return siToU(ti);
+ }
+
+ private static XyzTransform faceToXyzTransform(int face) {
+ // We map illegal face indices to the largest face index to preserve legacy behavior, i.e.,
+ // we do not want to throw an index out of bounds exception. Note that getFace(s2CellId) is
+ // guaranteed to return a non-negative face index even for invalid S2 cells, so it is
+ // sufficient to just map all face indices greater than the largest face index to the
+ // largest face index.
+ return XYZ_TRANSFORMS[Math.min(NUM_FACES - 1, face)];
+ }
+
+ private static double xyzToLngRadians(double x, double y) {
+ return Math.atan2(y, x);
+ }
+
+ private static double xyzToLatRadians(double x, double y, double z) {
+ return Math.atan2(z, Math.sqrt(x * x + y * y));
+ }
+
+ private static void checkNotNull(Object object) {
+ if (object == null) {
+ throw new NullPointerException("Given array cannot be null.");
+ }
+ }
+
+ private static void checkLengthGreaterThanOrEqualTo(int minLength, int actualLength) {
+ if (actualLength < minLength) {
+ throw new IllegalArgumentException(
+ "Given array of length " + actualLength + " needs to be of minimum length "
+ + minLength);
+ }
+ }
+
+ /**
+ * Returns true if the provided S2 cell contains the provided latitude/longitude, both measured
+ * in degrees.
+ */
+ public static boolean containsLatLngDegrees(long s2CellId, double latDegrees,
+ double lngDegrees) {
+ int level = getLevel(s2CellId);
+ long leafCellId = fromLatLngDegrees(latDegrees, lngDegrees);
+ return (getParent(leafCellId, level) == s2CellId);
+ }
+
+ /**
* Returns the leaf S2 cell ID for the specified latitude and longitude, both measured in
* degrees.
*/
@@ -176,7 +310,7 @@
* Returns the level of the specified S2 cell. The returned level is in [0, 30] for valid
* S2 cell IDs. Behavior is undefined for invalid S2 cell IDs.
*/
- static int getLevel(long s2CellId) {
+ public static int getLevel(long s2CellId) {
if (isLeaf(s2CellId)) {
return MAX_LEVEL;
}
@@ -197,12 +331,12 @@
* Returns the ID of the first S2 cell in a traversal of the children S2 cells at the specified
* level, in Hilbert curve order.
*/
- static long getTraversalStart(long s2CellId, int level) {
+ public static long getTraversalStart(long s2CellId, int level) {
return s2CellId - getLowestOnBit(s2CellId) + getLowestOnBitForLevel(level);
}
/** Returns the ID of the next S2 cell at the same level along the Hilbert curve. */
- static long getTraversalNext(long s2CellId) {
+ public static long getTraversalNext(long s2CellId) {
return s2CellId + (getLowestOnBit(s2CellId) << 1);
}
@@ -211,7 +345,7 @@
* lower levels (i.e., larger cells) are encoded into fewer characters.
*/
@NonNull
- static String getToken(long s2CellId) {
+ public static String getToken(long s2CellId) {
if (s2CellId == 0) {
return "X";
}
diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java
index 66da031..dba9cc9 100644
--- a/media/java/android/media/AudioPlaybackConfiguration.java
+++ b/media/java/android/media/AudioPlaybackConfiguration.java
@@ -283,8 +283,19 @@
* Flag used when playback is muted by AppOpsManager#OP_PLAY_AUDIO.
*/
@SystemApi
+ @FlaggedApi(FLAG_MUTED_BY_PORT_VOLUME_API)
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- public static final int MUTED_BY_APP_OPS = (1 << 3);
+ public static final int MUTED_BY_OP_PLAY_AUDIO = (1 << 3);
+ /**
+ * @hide
+ * Flag used when playback is muted by AppOpsManager#OP_PLAY_AUDIO.
+ * @deprecated see {@link MUTED_BY_OP_PLAY_AUDIO}
+ */
+ @SystemApi
+ @Deprecated
+ @FlaggedApi(FLAG_MUTED_BY_PORT_VOLUME_API)
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public static final int MUTED_BY_APP_OPS = MUTED_BY_OP_PLAY_AUDIO;
/**
* @hide
* Flag used when muted by client volume.
@@ -324,7 +335,7 @@
@IntDef(
flag = true,
value = {MUTED_BY_MASTER, MUTED_BY_STREAM_VOLUME, MUTED_BY_STREAM_MUTED,
- MUTED_BY_APP_OPS, MUTED_BY_CLIENT_VOLUME, MUTED_BY_VOLUME_SHAPER,
+ MUTED_BY_OP_PLAY_AUDIO, MUTED_BY_CLIENT_VOLUME, MUTED_BY_VOLUME_SHAPER,
MUTED_BY_PORT_VOLUME, MUTED_BY_OP_CONTROL_AUDIO})
@Retention(RetentionPolicy.SOURCE)
public @interface PlayerMuteEvent {
@@ -770,7 +781,7 @@
private boolean isMuteAffectingActiveState() {
return (mMutedState & MUTED_BY_CLIENT_VOLUME) != 0
|| (mMutedState & MUTED_BY_VOLUME_SHAPER) != 0
- || (mMutedState & MUTED_BY_APP_OPS) != 0;
+ || (mMutedState & MUTED_BY_OP_PLAY_AUDIO) != 0;
}
/**
@@ -911,8 +922,8 @@
if ((mMutedState & MUTED_BY_STREAM_MUTED) != 0) {
apcToString.append("streamMute ");
}
- if ((mMutedState & MUTED_BY_APP_OPS) != 0) {
- apcToString.append("appOps ");
+ if ((mMutedState & MUTED_BY_OP_PLAY_AUDIO) != 0) {
+ apcToString.append("opPlayAudio ");
}
if ((mMutedState & MUTED_BY_CLIENT_VOLUME) != 0) {
apcToString.append("clientVolume ");
diff --git a/media/java/android/media/tv/flags/media_tv.aconfig b/media/java/android/media/tv/flags/media_tv.aconfig
index 4b832ae..572db97 100644
--- a/media/java/android/media/tv/flags/media_tv.aconfig
+++ b/media/java/android/media/tv/flags/media_tv.aconfig
@@ -112,3 +112,11 @@
description : "Feature flag to enable APIs for applying picture profiles"
bug: "337330263"
}
+
+flag {
+ name: "hdmi_control_collect_physical_address"
+ is_exported: true
+ namespace: "media_tv"
+ description: "Collect physical address from HDMI-CEC messages in metrics"
+ bug: "376001043"
+}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
index 88c1c43..ec336d5 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -27,6 +27,7 @@
import android.hardware.ICameraServiceListener;
import android.hardware.camera2.ICameraDeviceCallbacks;
import android.hardware.camera2.ICameraDeviceUser;
+import android.hardware.camera2.CameraMetadataInfo;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.CaptureResultExtras;
import android.hardware.camera2.impl.PhysicalCaptureResultInfo;
@@ -217,7 +218,7 @@
* android.hardware.camera2.CaptureResultExtras)
*/
@Override
- public void onResultReceived(CameraMetadataNative result, CaptureResultExtras resultExtras,
+ public void onResultReceived(CameraMetadataInfo result, CaptureResultExtras resultExtras,
PhysicalCaptureResultInfo physicalResults[]) throws RemoteException {
// TODO Auto-generated method stub
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index 3758c51..7d1e5f8 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -33,6 +33,7 @@
import android.hardware.ICameraService;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraMetadataInfo;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.ICameraDeviceCallbacks;
import android.hardware.camera2.ICameraDeviceUser;
@@ -141,7 +142,7 @@
* android.hardware.camera2.impl.CameraMetadataNative,
* android.hardware.camera2.CaptureResultExtras)
*/
- public void onResultReceived(CameraMetadataNative result, CaptureResultExtras resultExtras,
+ public void onResultReceived(CameraMetadataInfo result, CaptureResultExtras resultExtras,
PhysicalCaptureResultInfo physicalResults[]) throws RemoteException {
// TODO Auto-generated method stub
@@ -186,10 +187,14 @@
}
}
- class IsMetadataNotEmpty implements ArgumentMatcher<CameraMetadataNative> {
+ class IsMetadataNotEmpty implements ArgumentMatcher<CameraMetadataInfo> {
@Override
- public boolean matches(CameraMetadataNative obj) {
- return !obj.isEmpty();
+ public boolean matches(CameraMetadataInfo obj) {
+ if (obj.getTag() == CameraMetadataInfo.metadata) {
+ return !(obj.getMetadata().isEmpty());
+ } else {
+ return (obj.getFmqSize() != 0);
+ }
}
}
diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
index c5fb808..b006580 100644
--- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
+++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
@@ -73,13 +73,13 @@
MOCK_METHOD(ScopedAStatus, closeSessionChannel, (), (override));
MOCK_METHOD(ScopedAStatus, getCpuHeadroom,
(const ::aidl::android::os::CpuHeadroomParamsInternal& in_params,
- std::vector<float>* _aidl_return),
+ std::optional<hal::CpuHeadroomResult>* _aidl_return),
(override));
MOCK_METHOD(ScopedAStatus, getCpuHeadroomMinIntervalMillis, (int64_t* _aidl_return),
(override));
MOCK_METHOD(ScopedAStatus, getGpuHeadroom,
(const ::aidl::android::os::GpuHeadroomParamsInternal& in_params,
- float* _aidl_return),
+ std::optional<hal::GpuHeadroomResult>* _aidl_return),
(override));
MOCK_METHOD(ScopedAStatus, getGpuHeadroomMinIntervalMillis, (int64_t* _aidl_return),
(override));
diff --git a/nfc/tests/src/android/nfc/NdefMessageTest.java b/nfc/tests/src/android/nfc/NdefMessageTest.java
new file mode 100644
index 0000000..9ca295d
--- /dev/null
+++ b/nfc/tests/src/android/nfc/NdefMessageTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class NdefMessageTest {
+ private NdefMessage mNdefMessage;
+ private NdefRecord mNdefRecord;
+
+ @Before
+ public void setUp() {
+ mNdefRecord = NdefRecord.createUri("http://www.example.com");
+ mNdefMessage = new NdefMessage(mNdefRecord);
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+ @Test
+ public void testGetRecords() {
+ NdefRecord[] records = mNdefMessage.getRecords();
+ assertThat(records).isNotNull();
+ assertThat(records).hasLength(1);
+ assertThat(records[0]).isEqualTo(mNdefRecord);
+ }
+
+ @Test
+ public void testToByteArray() throws FormatException {
+ byte[] bytes = mNdefMessage.toByteArray();
+ assertThat(bytes).isNotNull();
+ assertThat(bytes.length).isGreaterThan(0);
+ NdefMessage ndefMessage = new NdefMessage(bytes);
+ assertThat(ndefMessage).isNotNull();
+ }
+}
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreAdapter.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreAdapter.kt
index c2728b4..7601b9a 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreAdapter.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreAdapter.kt
@@ -20,7 +20,7 @@
import com.android.settingslib.datastore.KeyValueStore
/** Adapter to translate [KeyValueStore] into [PreferenceDataStore]. */
-class PreferenceDataStoreAdapter(private val keyValueStore: KeyValueStore) : PreferenceDataStore() {
+class PreferenceDataStoreAdapter(val keyValueStore: KeyValueStore) : PreferenceDataStore() {
override fun getBoolean(key: String, defValue: Boolean): Boolean =
keyValueStore.getValue(key, Boolean::class.javaObjectType) ?: defValue
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
index 62ac3ad..7cec59c 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
@@ -74,16 +74,12 @@
private val preferences: ImmutableMap<String, PreferenceHierarchyNode>
private val dependencies: ImmutableMultimap<String, String>
private val lifecycleAwarePreferences: Array<PreferenceLifecycleProvider>
- private val storages = mutableSetOf<KeyedObservable<String>>()
+ private val storages = mutableMapOf<String, KeyedObservable<String>>()
private val preferenceObserver: KeyedObserver<String?>
private val storageObserver =
- KeyedObserver<String?> { key, _ ->
- if (key != null) {
- notifyChange(key, CHANGE_REASON_VALUE)
- }
- }
+ KeyedObserver<String> { key, _ -> notifyChange(key, CHANGE_REASON_VALUE) }
init {
val preferencesBuilder = ImmutableMap.builder<String, PreferenceHierarchyNode>()
@@ -98,7 +94,6 @@
preferencesBuilder.put(it.key, this)
it.dependencyOfEnabledState(context)?.addDependency(it)
if (it is PreferenceLifecycleProvider) lifecycleAwarePreferences.add(it)
- if (it is PersistentPreference<*>) storages.add(it.storage(context))
}
}
@@ -120,7 +115,16 @@
preferenceObserver = KeyedObserver { key, reason -> onPreferenceChange(key, reason) }
addObserver(preferenceObserver, mainExecutor)
- for (storage in storages) storage.addObserver(storageObserver, mainExecutor)
+
+ preferenceScreen.forEachRecursively {
+ val preferenceDataStore = it.preferenceDataStore
+ if (preferenceDataStore is PreferenceDataStoreAdapter) {
+ val key = it.key
+ val keyValueStore = preferenceDataStore.keyValueStore
+ storages[key] = keyValueStore
+ keyValueStore.addObserver(key, storageObserver, mainExecutor)
+ }
+ }
}
private fun onPreferenceChange(key: String?, reason: Int) {
@@ -181,7 +185,7 @@
fun onDestroy() {
removeObserver(preferenceObserver)
- for (storage in storages) storage.removeObserver(storageObserver)
+ for ((key, storage) in storages) storage.removeObserver(key, storageObserver)
for (preference in lifecycleAwarePreferences) {
preference.onDestroy(preferenceLifecycleContext)
}
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/Utils.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/Utils.kt
new file mode 100644
index 0000000..2e7221b
--- /dev/null
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/Utils.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.preference
+
+import androidx.preference.Preference
+import androidx.preference.PreferenceGroup
+
+/** Traversals preference hierarchy recursively and applies an action. */
+fun PreferenceGroup.forEachRecursively(action: (Preference) -> Unit) {
+ action.invoke(this)
+ for (index in 0 until preferenceCount) {
+ val preference = getPreference(index)
+ if (preference is PreferenceGroup) {
+ preference.forEachRecursively(action)
+ } else {
+ action.invoke(preference)
+ }
+ }
+}
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index 73d0bec..02e1904 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -29,7 +29,7 @@
allprojects {
extra["androidTop"] = androidTop
- extra["jetpackComposeVersion"] = "1.7.3"
+ extra["jetpackComposeVersion"] = "1.8.0-alpha06"
}
subprojects {
diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml
index 272dc2d..74811d3 100644
--- a/packages/SettingsLib/Spa/gradle/libs.versions.toml
+++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml
@@ -15,7 +15,7 @@
#
[versions]
-agp = "8.6.1"
+agp = "8.7.2"
compose-compiler = "1.5.11"
dexmaker-mockito = "2.28.3"
jvm = "17"
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.10.2-bin.zip b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.10.2-bin.zip
deleted file mode 100644
index 45f0424..0000000
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.10.2-bin.zip
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.11.1-bin.zip b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.11.1-bin.zip
new file mode 100644
index 0000000..f8c4ecb
--- /dev/null
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.11.1-bin.zip
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
index 1c25e974..ca510eb 100644
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
@@ -16,6 +16,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=gradle-8.10.2-bin.zip
+distributionUrl=gradle-8.11.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index 914f06c..1f32ad6 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -54,14 +54,14 @@
dependencies {
api(project(":SettingsLibColor"))
api("androidx.appcompat:appcompat:1.7.0")
- api("androidx.compose.material3:material3:1.4.0-alpha01")
- api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion")
+ api("androidx.compose.material3:material3:1.4.0-alpha04")
+ api("androidx.compose.material:material-icons-extended")
api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion")
api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
api("androidx.graphics:graphics-shapes-android:1.0.1")
api("androidx.lifecycle:lifecycle-livedata-ktx")
api("androidx.lifecycle:lifecycle-runtime-compose")
- api("androidx.navigation:navigation-compose:2.8.1")
+ api("androidx.navigation:navigation-compose:2.9.0-alpha03")
api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
api("com.google.android.material:material:1.12.0")
debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion")
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
index ea6a272..7466f95 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
@@ -41,8 +41,6 @@
import com.android.settingslib.spaprivileged.model.app.IPackageManagers
import com.android.settingslib.spaprivileged.model.app.PackageManagers
import com.android.settingslib.spaprivileged.model.app.toRoute
-import com.android.settingslib.spaprivileged.model.enterprise.EnhancedConfirmation
-import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderFactory
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl
import com.android.settingslib.spaprivileged.template.preference.RestrictedSwitchPreference
@@ -155,12 +153,12 @@
override val changeable = { isChangeable }
override val onCheckedChange: (Boolean) -> Unit = { setAllowed(record, it) }
}
- val restrictions = Restrictions(userId = userId,
- keys = switchRestrictionKeys,
- enhancedConfirmation = enhancedConfirmationKey?.let { EnhancedConfirmation(
- key = it,
- packageName = packageName) })
- RestrictedSwitchPreference(switchModel, restrictions, restrictionsProviderFactory)
+ RestrictedSwitchPreference(
+ model = switchModel,
+ restrictions = getRestrictions(userId, packageName),
+ ifBlockedByAdminOverrideCheckedValueTo = switchifBlockedByAdminOverrideCheckedValueTo,
+ restrictionsProviderFactory = restrictionsProviderFactory,
+ )
InfoPageAdditionalContent(record, isAllowed)
}
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
index 627b248..d2867af1 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
@@ -26,6 +26,8 @@
import com.android.settingslib.spa.framework.compose.rememberContext
import com.android.settingslib.spa.framework.util.asyncMapItem
import com.android.settingslib.spaprivileged.model.app.AppRecord
+import com.android.settingslib.spaprivileged.model.enterprise.EnhancedConfirmation
+import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
import kotlinx.coroutines.flow.Flow
/**
@@ -38,6 +40,16 @@
val switchRestrictionKeys: List<String>
get() = emptyList()
+ /**
+ * If this is not null, and on a switch UI restricted by admin, the switch's checked status will
+ * be overridden.
+ *
+ * And if there is an admin summary, such as "Enabled by admin" or "Disabled by admin", will
+ * also be overridden.
+ */
+ val switchifBlockedByAdminOverrideCheckedValueTo: Boolean?
+ get() = null
+
val enhancedConfirmationKey: String?
get() = null
@@ -101,6 +113,16 @@
record: T,
): Boolean = !record.isSystemOrRootUid() && isChangeable(record)
+fun <T : AppRecord> TogglePermissionAppListModel<T>.getRestrictions(
+ userId: Int,
+ packageName: String,
+) =
+ Restrictions(
+ userId = userId,
+ keys = switchRestrictionKeys,
+ enhancedConfirmation =
+ enhancedConfirmationKey?.let { key -> EnhancedConfirmation(key, packageName) },
+ )
interface TogglePermissionAppListProvider {
val permissionType: String
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
index 57102ba..ec44d2a 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
@@ -42,8 +42,6 @@
import com.android.settingslib.spaprivileged.model.app.AppListModel
import com.android.settingslib.spaprivileged.model.app.AppRecord
import com.android.settingslib.spaprivileged.model.app.userId
-import com.android.settingslib.spaprivileged.model.enterprise.EnhancedConfirmation
-import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderFactory
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl
import com.android.settingslib.spaprivileged.model.enterprise.rememberRestrictedMode
@@ -151,23 +149,19 @@
@Composable
fun getSummary(record: T): () -> String {
- val restrictions = remember(record.app.userId, record.app.packageName) {
- Restrictions(
+ val restrictions =
+ listModel.getRestrictions(
userId = record.app.userId,
- keys = listModel.switchRestrictionKeys,
- enhancedConfirmation = listModel.enhancedConfirmationKey?.let {
- EnhancedConfirmation(
- key = it,
- packageName = record.app.packageName)
- })
- }
+ packageName = record.app.packageName,
+ )
val restrictedMode by restrictionsProviderFactory.rememberRestrictedMode(restrictions)
val allowed = listModel.isAllowed(record)
return RestrictedSwitchPreferenceModel.getSummary(
context = context,
- restrictedModeSupplier = { restrictedMode },
summaryIfNoRestricted = { getSummaryIfNoRestricted(allowed()) },
- checked = allowed,
+ checkedIfNoRestricted = allowed,
+ checkedIfBlockedByAdmin = listModel.switchifBlockedByAdminOverrideCheckedValueTo,
+ restrictedModeSupplier = { restrictedMode },
)
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt
index cd72025..5fec110 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt
@@ -25,12 +25,25 @@
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl
import com.android.settingslib.spaprivileged.template.preference.RestrictedSwitchPreferenceModel.Companion.RestrictedSwitchWrapper
+/**
+ * @param ifBlockedByAdminOverrideCheckedValueTo if this is not null and there is an admin
+ * restriction, the switch's checked status will be overridden.
+ *
+ * And if there is an admin summary, such as "Enabled by admin" or "Disabled by admin", will also
+ * be overridden.
+ */
@Composable
fun RestrictedSwitchPreference(
model: SwitchPreferenceModel,
restrictions: Restrictions,
+ ifBlockedByAdminOverrideCheckedValueTo: Boolean? = null,
) {
- RestrictedSwitchPreference(model, restrictions, ::RestrictionsProviderImpl)
+ RestrictedSwitchPreference(
+ model = model,
+ restrictions = restrictions,
+ ifBlockedByAdminOverrideCheckedValueTo = ifBlockedByAdminOverrideCheckedValueTo,
+ restrictionsProviderFactory = ::RestrictionsProviderImpl,
+ )
}
@VisibleForTesting
@@ -38,13 +51,18 @@
internal fun RestrictedSwitchPreference(
model: SwitchPreferenceModel,
restrictions: Restrictions,
+ ifBlockedByAdminOverrideCheckedValueTo: Boolean? = null,
restrictionsProviderFactory: RestrictionsProviderFactory,
) {
if (restrictions.isEmpty()) {
SwitchPreference(model)
return
}
- restrictionsProviderFactory.RestrictedSwitchWrapper(model, restrictions) {
+ restrictionsProviderFactory.RestrictedSwitchWrapper(
+ model = model,
+ restrictions = restrictions,
+ ifBlockedByAdminOverrideCheckedValueTo = ifBlockedByAdminOverrideCheckedValueTo,
+ ) {
SwitchPreference(it)
}
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt
index fb23637..0bb92ce 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt
@@ -42,26 +42,29 @@
context: Context,
model: SwitchPreferenceModel,
private val restrictedMode: RestrictedMode?,
+ private val ifBlockedByAdminOverrideCheckedValueTo: Boolean?,
) : SwitchPreferenceModel {
override val title = model.title
- override val summary = getSummary(
- context = context,
- restrictedModeSupplier = { restrictedMode },
- summaryIfNoRestricted = model.summary,
- checked = model.checked,
- )
+ override val checked =
+ when (restrictedMode) {
+ null -> ({ null })
+ is NoRestricted -> model.checked
+ is BaseUserRestricted -> ({ false })
+ is BlockedByAdmin -> ({ ifBlockedByAdminOverrideCheckedValueTo ?: model.checked() })
+ is BlockedByEcm -> model.checked
+ }
+
+ override val summary =
+ getSummary(
+ context = context,
+ restrictedModeSupplier = { restrictedMode },
+ summaryIfNoRestricted = model.summary,
+ checkedIfNoRestricted = checked,
+ )
override val icon = model.icon
- override val checked = when (restrictedMode) {
- null -> ({ null })
- is NoRestricted -> model.checked
- is BaseUserRestricted -> ({ false })
- is BlockedByAdmin -> model.checked
- is BlockedByEcm -> model.checked
- }
-
override val changeable = restrictedMode.restrictEnabled(model.changeable)
override val onCheckedChange = restrictedMode.restrictOnClick(model.onCheckedChange)
@@ -112,12 +115,20 @@
fun RestrictedSwitchWrapper(
model: SwitchPreferenceModel,
restrictedMode: RestrictedMode?,
+ ifBlockedByAdminOverrideCheckedValueTo: Boolean? = null,
content: @Composable (SwitchPreferenceModel) -> Unit,
) {
val context = LocalContext.current
- val restrictedSwitchPreferenceModel = remember(restrictedMode, model) {
- RestrictedSwitchPreferenceModel(context, model, restrictedMode)
- }
+ val restrictedSwitchPreferenceModel =
+ remember(restrictedMode, model) {
+ RestrictedSwitchPreferenceModel(
+ context = context,
+ model = model,
+ restrictedMode = restrictedMode,
+ ifBlockedByAdminOverrideCheckedValueTo =
+ ifBlockedByAdminOverrideCheckedValueTo,
+ )
+ }
restrictedSwitchPreferenceModel.RestrictionWrapper {
content(restrictedSwitchPreferenceModel)
}
@@ -127,23 +138,31 @@
fun RestrictionsProviderFactory.RestrictedSwitchWrapper(
model: SwitchPreferenceModel,
restrictions: Restrictions,
+ ifBlockedByAdminOverrideCheckedValueTo: Boolean? = null,
content: @Composable (SwitchPreferenceModel) -> Unit,
) {
- RestrictedSwitchWrapper(model, rememberRestrictedMode(restrictions).value, content)
+ RestrictedSwitchWrapper(
+ model = model,
+ restrictedMode = rememberRestrictedMode(restrictions).value,
+ ifBlockedByAdminOverrideCheckedValueTo = ifBlockedByAdminOverrideCheckedValueTo,
+ content = content,
+ )
}
fun getSummary(
context: Context,
- restrictedModeSupplier: () -> RestrictedMode?,
summaryIfNoRestricted: () -> String,
- checked: () -> Boolean?,
+ checkedIfNoRestricted: () -> Boolean?,
+ checkedIfBlockedByAdmin: Boolean? = null,
+ restrictedModeSupplier: () -> RestrictedMode?,
): () -> String = {
when (val restrictedMode = restrictedModeSupplier()) {
is NoRestricted -> summaryIfNoRestricted()
is BaseUserRestricted ->
context.getString(com.android.settingslib.R.string.disabled)
- is BlockedByAdmin -> restrictedMode.getSummary(checked())
+ is BlockedByAdmin ->
+ restrictedMode.getSummary(checkedIfBlockedByAdmin ?: checkedIfNoRestricted())
is BlockedByEcm ->
context.getString(com.android.settingslib.R.string.disabled)
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPageTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPageTest.kt
index bf0ad0b..e736115 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPageTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPageTest.kt
@@ -26,9 +26,11 @@
import androidx.compose.ui.test.performClick
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.RestrictedLockUtils
import com.android.settingslib.spa.testutils.FakeNavControllerWrapper
import com.android.settingslib.spaprivileged.R
import com.android.settingslib.spaprivileged.framework.compose.getPlaceholder
+import com.android.settingslib.spaprivileged.model.enterprise.BlockedByAdminImpl
import com.android.settingslib.spaprivileged.model.enterprise.NoRestricted
import com.android.settingslib.spaprivileged.tests.testutils.FakeRestrictionsProvider
import com.android.settingslib.spaprivileged.tests.testutils.TestAppRecord
@@ -97,6 +99,26 @@
}
@Test
+ fun summary_whenAllowedButAdminOverrideToNotAllowed() {
+ fakeRestrictionsProvider.restrictedMode =
+ BlockedByAdminImpl(context = context, enforcedAdmin = ENFORCED_ADMIN)
+ val listModel =
+ TestTogglePermissionAppListModel(
+ isAllowed = true,
+ switchifBlockedByAdminOverrideCheckedValueTo = false,
+ )
+
+ val summary = getSummary(listModel)
+
+ assertThat(summary)
+ .isEqualTo(
+ context.getString(
+ com.android.settingslib.widget.restricted.R.string.disabled_by_admin
+ )
+ )
+ }
+
+ @Test
fun appListItem_onClick_navigate() {
val listModel = TestTogglePermissionAppListModel()
composeTestRule.setContent {
@@ -162,8 +184,9 @@
const val PACKAGE_NAME = "package.name"
const val LABEL = "Label"
const val SUMMARY = "Summary"
- val APP = ApplicationInfo().apply {
- packageName = PACKAGE_NAME
- }
+ val APP = ApplicationInfo().apply { packageName = PACKAGE_NAME }
+ const val RESTRICTION = "restriction"
+ val ENFORCED_ADMIN: RestrictedLockUtils.EnforcedAdmin =
+ RestrictedLockUtils.EnforcedAdmin.createDefaultEnforcedAdminWithRestriction(RESTRICTION)
}
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceTest.kt
index b88d1c5..1fd7ecf 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceTest.kt
@@ -121,7 +121,7 @@
}
@Test
- fun whenBlockedByAdmin_disabled() {
+ fun whenBlockedByAdmin_notOverrideChecked() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = fakeBlockedByAdmin
@@ -133,6 +133,18 @@
}
@Test
+ fun whenBlockedByAdmin_overrideCheckedToFalse() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = fakeBlockedByAdmin
+
+ setContent(restrictions, ifBlockedByAdminOverrideCheckedValueTo = false)
+
+ composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsEnabled()
+ composeTestRule.onNodeWithText(FakeBlockedByAdmin.SUMMARY).assertIsDisplayed()
+ composeTestRule.onNode(isOff()).assertIsDisplayed()
+ }
+
+ @Test
fun whenBlockedByAdmin_click() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = fakeBlockedByAdmin
@@ -166,9 +178,16 @@
assertThat(fakeBlockedByEcm.showRestrictedSettingsDetailsIsCalled).isTrue()
}
- private fun setContent(restrictions: Restrictions) {
+ private fun setContent(
+ restrictions: Restrictions,
+ ifBlockedByAdminOverrideCheckedValueTo: Boolean? = null,
+ ) {
composeTestRule.setContent {
- RestrictedSwitchPreference(switchPreferenceModel, restrictions) { _, _ ->
+ RestrictedSwitchPreference(
+ model = switchPreferenceModel,
+ restrictions = restrictions,
+ ifBlockedByAdminOverrideCheckedValueTo = ifBlockedByAdminOverrideCheckedValueTo,
+ ) { _, _ ->
fakeRestrictionsProvider
}
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/TestTogglePermissionAppListModel.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/TestTogglePermissionAppListModel.kt
index 1790313..000743e 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/TestTogglePermissionAppListModel.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/TestTogglePermissionAppListModel.kt
@@ -28,6 +28,7 @@
class TestTogglePermissionAppListModel(
isAllowed: Boolean? = null,
private val isChangeable: Boolean = false,
+ override val switchifBlockedByAdminOverrideCheckedValueTo: Boolean? = null,
) : TogglePermissionAppListModel<TestAppRecord> {
override val pageTitleResId = R.string.test_permission_title
override val switchTitleResId = R.string.test_permission_switch_title
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index 076f82a..89de995 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -178,3 +178,10 @@
description: "Enable the input routing control in device details and hearing devices dialog."
bug: "349255906"
}
+
+flag {
+ name: "hearing_device_set_connection_status_report"
+ namespace: "accessibility"
+ description: "Enable the connection status report for a set of hearing device."
+ bug: "357878944"
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 03fea37..6128d45 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -130,10 +130,12 @@
import libcore.util.HexEncoding;
+import java.io.BufferedReader;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
+import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -380,6 +382,8 @@
@GuardedBy("mLock")
private Handler mHandler;
+ private static final Set<String> sDeviceConfigAllowlistedNamespaces = new ArraySet<>();
+
// We have to call in the user manager with no lock held,
private volatile UserManager mUserManager;
@@ -2442,6 +2446,10 @@
if (!isRestrictedShell && hasWritePermission) {
assertCallingUserDenyList(flags);
} else if (hasAllowlistPermission) {
+ Set<String> allowlistedDeviceConfigNamespaces = null;
+ if (isRestrictedShell) {
+ allowlistedDeviceConfigNamespaces = getAllowlistedDeviceConfigNamespaces();
+ }
for (String flag : flags) {
boolean namespaceAllowed = false;
if (isRestrictedShell) {
@@ -2452,7 +2460,7 @@
} else {
flagNamespace = flag;
}
- if (WritableNamespaces.ALLOWLIST.contains(flagNamespace)) {
+ if (allowlistedDeviceConfigNamespaces.contains(flagNamespace)) {
namespaceAllowed = true;
}
} else {
@@ -2513,6 +2521,60 @@
}
}
+ /**
+ * Returns a Set of DeviceConfig allowlisted namespaces in which all flags can be modified
+ * by a caller with the {@code WRITE_ALLOWLISTED_DEVICE_CONFIG} permission.
+ * <p>
+ * This method also supports mainline modules that introduce their own allowlisted
+ * namespaces within the {@code etc/writable_namespaces} file under their directory.
+ */
+ private Set<String> getAllowlistedDeviceConfigNamespaces() {
+ synchronized (sDeviceConfigAllowlistedNamespaces) {
+ if (!sDeviceConfigAllowlistedNamespaces.isEmpty()) {
+ return sDeviceConfigAllowlistedNamespaces;
+ }
+ if (android.provider.flags.Flags.deviceConfigWritableNamespacesApi()) {
+ sDeviceConfigAllowlistedNamespaces.addAll(DeviceConfig.getAdbWritableNamespaces());
+ } else {
+ sDeviceConfigAllowlistedNamespaces.addAll(WritableNamespaces.ALLOWLIST);
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ List<String> apexDirectories;
+ try {
+ apexDirectories = mPackageManager.getAllApexDirectories();
+ } catch (RemoteException e) {
+ Slog.e(LOG_TAG, "Caught a RemoteException obtaining APEX directories: ", e);
+ return sDeviceConfigAllowlistedNamespaces;
+ }
+ for (int i = 0; i < apexDirectories.size(); i++) {
+ String apexDirectory = apexDirectories.get(i);
+ File namespaceFile = Environment.buildPath(new File(apexDirectory), "etc",
+ "writable_namespaces");
+ if (namespaceFile.exists() && namespaceFile.isFile()) {
+ try (BufferedReader reader = new BufferedReader(
+ new FileReader(namespaceFile))) {
+ String namespace;
+ while ((namespace = reader.readLine()) != null) {
+ namespace = namespace.trim();
+ // Support comments by ignoring any lines that start with '#'.
+ if (!namespace.isEmpty() && !namespace.startsWith("#")) {
+ sDeviceConfigAllowlistedNamespaces.add(namespace);
+ }
+ }
+ } catch (IOException e) {
+ Slog.e(LOG_TAG, "Caught an exception parsing file: " + namespaceFile,
+ e);
+ }
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ return sDeviceConfigAllowlistedNamespaces;
+ }
+ }
+
private static void warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk(
int targetSdkVersion, String name) {
// If the app targets Lollipop MR1 or older SDK we warn, otherwise crash.
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java b/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java
index b0409c0..5ce97eb 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java
@@ -33,6 +33,7 @@
final class WritableNamespaces {
public static final Set<String> ALLOWLIST =
new ArraySet<String>(Arrays.asList(
+ "adservices",
"captive_portal_login",
"connectivity",
"exo",
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index d1a22e8..3d250fd 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -81,33 +81,37 @@
visibility: ["//visibility:private"],
}
-// Tests where robolectric conversion caused errors in SystemUITests at runtime
-filegroup {
- name: "SystemUI-tests-broken-robofiles-sysui-run",
- srcs: [
- "tests/src/**/systemui/broadcast/BroadcastDispatcherTest.kt",
- "tests/src/**/systemui/broadcast/ActionReceiverTest.kt",
- "tests/src/**/systemui/globalactions/GlobalActionsDialogLiteTest.java",
- "tests/src/**/systemui/globalactions/GlobalActionsImeTest.java",
- "tests/src/**/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt",
- "tests/src/**/systemui/media/dialog/MediaOutputAdapterTest.java",
- "tests/src/**/systemui/media/dialog/MediaOutputBaseDialogTest.java",
- "tests/src/**/systemui/media/dialog/MediaOutputBroadcastDialogTest.java",
- "tests/src/**/systemui/media/dialog/MediaOutputDialogTest.java",
- "tests/src/**/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt",
- "tests/src/**/systemui/settings/brightness/BrightnessDialogTest.kt",
- ],
-}
-
// Tests where robolectric failed at runtime. (go/central-multivalent)
filegroup {
name: "SystemUI-tests-broken-robofiles-run",
srcs: [
- "tests/src/**/systemui/ExpandHelperTest.java",
+ "tests/src/**/systemui/shade/NotificationShadeWindowViewControllerTest.kt",
+ "tests/src/**/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt",
+ "tests/src/**/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModelParameterizedTest.kt",
+ "tests/src/**/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt",
+ "tests/src/**/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt",
+ "tests/src/**/systemui/animation/back/FlingOnBackAnimationCallbackTest.kt",
+ "tests/src/**/systemui/education/domain/ui/view/ContextualEduDialogTest.kt",
+ "tests/src/**/systemui/screenshot/ActionIntentCreatorTest.kt",
+ "tests/src/**/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt",
+ "tests/src/**/systemui/accessibility/WindowMagnificationControllerTest.java",
+ "tests/src/**/systemui/broadcast/BroadcastDispatcherTest.kt",
+ "tests/src/**/systemui/globalactions/GlobalActionsDialogLiteTest.java",
+ "tests/src/**/systemui/globalactions/GlobalActionsImeTest.java",
+ "tests/src/**/systemui/media/dialog/MediaOutputBaseDialogTest.java",
+ "tests/src/**/systemui/media/dialog/MediaOutputBroadcastDialogTest.java",
+ "tests/src/**/systemui/media/dialog/MediaOutputDialogTest.java",
+ "tests/src/**/systemui/settings/brightness/BrightnessDialogTest.kt",
+ "tests/src/**/systemui/shared/clocks/view/SimpleDigitalClockTextViewTest.kt",
+ "tests/src/**/systemui/statusbar/policy/SecurityControllerTest.java",
+ "tests/src/**/systemui/lifecycle/SysUiViewModelTest.kt",
+ "tests/src/**/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt",
+ "tests/src/**/systemui/graphics/ImageLoaderContentProviderTest.kt",
+ "tests/src/**/systemui/flags/FakeFeatureFlagsTest.kt",
+ "tests/src/**/systemui/communal/data/backup/CommunalBackupUtilsTest.kt",
"tests/src/**/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java",
"tests/src/**/systemui/accessibility/AccessibilityGestureTargetsObserverTest.java",
"tests/src/**/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java",
- "tests/src/**/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java",
"tests/src/**/systemui/screenshot/appclips/AppClipsActivityTest.java",
"tests/src/**/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java",
"tests/src/**/systemui/screenshot/appclips/AppClipsViewModelTest.java",
@@ -124,36 +128,27 @@
"tests/src/**/systemui/classifier/FalsingDataProviderTest.java",
"tests/src/**/systemui/screenshot/ImageExporterTest.java",
"tests/src/**/systemui/bouncer/data/repository/KeyguardBouncerRepositoryTest.kt",
- "tests/src/**/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt",
"tests/src/**/systemui/logcat/LogAccessDialogActivityTest.java",
"tests/src/**/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt",
"tests/src/**/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt",
"tests/src/**/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java",
"tests/src/**/systemui/accessibility/floatingmenu/MenuViewLayerTest.java",
- "tests/src/**/systemui/accessibility/floatingmenu/MenuViewTest.java",
"tests/src/**/systemui/classifier/PointerCountClassifierTest.java",
"tests/src/**/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java",
"tests/src/**/systemui/screenrecord/RecordingControllerTest.java",
"tests/src/**/systemui/screenshot/RequestProcessorTest.kt",
"tests/src/**/systemui/media/controls/domain/resume/ResumeMediaBrowserTest.kt",
- "tests/src/**/systemui/screenshot/SaveImageInBackgroundTaskTest.kt",
"tests/src/**/systemui/screenshot/scroll/ScrollCaptureClientTest.java",
"tests/src/**/systemui/accessibility/SecureSettingsContentObserverTest.java",
"tests/src/**/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt",
"tests/src/**/systemui/qs/external/TileServicesTest.java",
"tests/src/**/systemui/ambient/touch/TouchMonitorTest.java",
- "tests/src/**/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java",
"tests/src/**/systemui/accessibility/WindowMagnificationSettingsTest.java",
- "tests/src/androidx/core/animation/AnimatorTestRuleIsolationTest.kt",
"tests/src/**/systemui/CameraProtectionLoaderImplTest.kt",
- "tests/src/**/systemui/DependencyTest.java",
- "tests/src/**/systemui/InitControllerTest.java",
"tests/src/**/systemui/SliceBroadcastRelayHandlerTest.java",
"tests/src/**/systemui/SystemUIApplicationTest.kt",
"tests/src/**/systemui/SysUICutoutProviderTest.kt",
- "tests/src/**/keyguard/ActiveUnlockConfigTest.kt",
"tests/src/**/keyguard/AdminSecondaryLockScreenControllerTest.java",
- "tests/src/**/keyguard/KeyguardClockAccessibilityDelegateTest.java",
"tests/src/**/keyguard/KeyguardStatusViewControllerTest.java",
"tests/src/**/systemui/accessibility/AccessibilityButtonModeObserverTest.java",
"tests/src/**/systemui/accessibility/AccessibilityButtonTargetsObserverTest.java",
@@ -164,16 +159,12 @@
"tests/src/**/systemui/animation/TextAnimatorTest.kt",
"tests/src/**/systemui/animation/TextInterpolatorTest.kt",
"tests/src/**/systemui/animation/ActivityTransitionAnimatorTest.kt",
- "tests/src/**/systemui/animation/AnimatorTestRuleOrderTest.kt",
"tests/src/**/systemui/animation/DialogTransitionAnimatorTest.kt",
- "tests/src/**/systemui/broadcast/ActionReceiverTest.kt",
"tests/src/**/systemui/broadcast/BroadcastDispatcherTest.kt",
- "tests/src/**/systemui/compose/ComposeInitializerTest.kt",
"tests/src/**/systemui/controls/ui/ControlsActivityTest.kt",
"tests/src/**/systemui/controls/management/ControlsEditingActivityTest.kt",
"tests/src/**/systemui/controls/management/ControlsRequestDialogTest.kt",
"tests/src/**/systemui/controls/ui/DetailDialogTest.kt",
- "tests/src/**/systemui/fontscaling/FontScalingDialogDelegateTest.kt",
"tests/src/**/systemui/keyguard/CustomizationProviderTest.kt",
"tests/src/**/systemui/globalactions/GlobalActionsColumnLayoutTest.java",
"tests/src/**/systemui/globalactions/GlobalActionsDialogLiteTest.java",
@@ -182,10 +173,6 @@
"tests/src/**/systemui/keyguard/CustomizationProviderTest.kt",
"tests/src/**/systemui/keyguard/KeyguardViewMediatorTest.java",
"tests/src/**/systemui/keyguard/LifecycleTest.java",
- "tests/src/**/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt",
- "tests/src/**/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt",
- "tests/src/**/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt",
- "tests/src/**/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt",
"tests/src/**/systemui/lifecycle/RepeatWhenAttachedTest.kt",
"tests/src/**/systemui/log/LogBufferTest.kt",
"tests/src/**/systemui/media/dialog/MediaOutputBaseDialogTest.java",
@@ -193,52 +180,35 @@
"tests/src/**/systemui/media/dialog/MediaOutputDialogTest.java",
"tests/src/**/systemui/media/controls/domain/resume/MediaResumeListenerTest.kt",
"tests/src/**/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt",
- "tests/src/**/systemui/navigationbar/views/NavigationBarButtonTest.java",
"tests/src/**/systemui/people/PeopleProviderTest.java",
"tests/src/**/systemui/people/PeopleSpaceUtilsTest.java",
"tests/src/**/systemui/people/widget/PeopleSpaceWidgetManagerTest.java",
"tests/src/**/systemui/people/PeopleTileViewHelperTest.java",
"tests/src/**/systemui/power/data/repository/PowerRepositoryImplTest.kt",
- "tests/src/**/systemui/privacy/PrivacyConfigFlagsTest.kt",
- "tests/src/**/systemui/privacy/PrivacyDialogV2Test.kt",
- "tests/src/**/systemui/qs/external/TileRequestDialogEventLoggerTest.kt",
- "tests/src/**/systemui/qs/AutoAddTrackerTest.kt",
- "tests/src/**/systemui/qs/external/TileRequestDialogEventLoggerTest.kt",
"tests/src/**/systemui/qs/tiles/DndTileTest.kt",
"tests/src/**/systemui/qs/tiles/DreamTileTest.java",
- "tests/src/**/systemui/qs/FgsManagerControllerTest.java",
"tests/src/**/systemui/qs/QSPanelTest.kt",
"tests/src/**/systemui/reardisplay/RearDisplayCoreStartableTest.kt",
"tests/src/**/systemui/reardisplay/RearDisplayDialogControllerTest.java",
"tests/src/**/systemui/reardisplay/RearDisplayInnerDialogDelegateTest.kt",
"tests/src/**/systemui/statusbar/KeyboardShortcutListSearchTest.java",
"tests/src/**/systemui/statusbar/KeyboardShortcutsTest.java",
- "tests/src/**/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt",
- "tests/src/**/systemui/statusbar/notification/AssistantFeedbackControllerTest.java",
"tests/src/**/systemui/statusbar/notification/collection/NotificationEntryTest.java",
- "tests/src/**/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt",
"tests/src/**/systemui/statusbar/notification/collection/ShadeListBuilderTest.java",
- "tests/src/**/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java",
"tests/src/**/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java",
"tests/src/**/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt",
"tests/src/**/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt",
- "tests/src/**/systemui/statusbar/NotificationLockscreenUserManagerTest.java",
"tests/src/**/systemui/statusbar/notification/logging/NotificationLoggerTest.java",
"tests/src/**/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java",
- "tests/src/**/systemui/statusbar/notification/row/NotificationContentInflaterTest.java",
"tests/src/**/systemui/statusbar/notification/row/NotificationContentViewTest.kt",
"tests/src/**/systemui/statusbar/notification/row/NotificationConversationInfoTest.java",
- "tests/src/**/systemui/statusbar/notification/row/NotificationGutsManagerTest.java",
"tests/src/**/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt",
"tests/src/**/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt",
"tests/src/**/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java",
- "tests/src/**/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt",
- "tests/src/**/systemui/statusbar/phone/AutoTileManagerTest.java",
"tests/src/**/systemui/statusbar/phone/CentralSurfacesImplTest.java",
"tests/src/**/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java",
"tests/src/**/systemui/statusbar/phone/PhoneStatusBarTransitionsTest.kt",
"tests/src/**/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt",
- "tests/src/**/systemui/statusbar/phone/PhoneStatusBarView.java",
"tests/src/**/systemui/statusbar/phone/PhoneStatusBarViewTest.kt",
"tests/src/**/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt",
"tests/src/**/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt",
@@ -252,13 +222,9 @@
"tests/src/**/systemui/statusbar/policy/LocationControllerImplTest.java",
"tests/src/**/systemui/statusbar/policy/RemoteInputViewTest.java",
"tests/src/**/systemui/statusbar/policy/SmartReplyViewTest.java",
- "tests/src/**/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt",
- "tests/src/**/systemui/statusbar/StatusBarStateControllerImplTest.kt",
"tests/src/**/systemui/theme/ThemeOverlayApplierTest.java",
"tests/src/**/systemui/touch/TouchInsetManagerTest.java",
"tests/src/**/systemui/util/LifecycleFragmentTest.java",
- "tests/src/**/systemui/util/kotlin/PairwiseFlowTest",
- "tests/src/**/systemui/util/sensors/AsyncManagerTest.java",
"tests/src/**/systemui/util/sensors/ThresholdSensorImplTest.java",
"tests/src/**/systemui/volume/VolumeDialogImplTest.java",
"tests/src/**/systemui/wallet/controller/QuickAccessWalletControllerTest.java",
@@ -271,26 +237,17 @@
"tests/src/**/systemui/clipboardoverlay/ClipboardListenerTest.java",
"tests/src/**/systemui/communal/data/db/CommunalDatabaseMigrationsTest.kt",
"tests/src/**/systemui/communal/data/db/CommunalWidgetDaoTest.kt",
- "tests/src/**/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt",
"tests/src/**/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt",
"tests/src/**/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt",
"tests/src/**/systemui/lifecycle/ActivatableTest.kt",
- "tests/src/**/systemui/lifecycle/HydratorTest.kt",
"tests/src/**/systemui/media/dialog/MediaSwitchingControllerTest.java",
"tests/src/**/systemui/qs/QSImplTest.java",
"tests/src/**/systemui/qs/panels/ui/compose/DragAndDropTest.kt",
"tests/src/**/systemui/qs/panels/ui/compose/ResizingTest.kt",
"tests/src/**/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java",
- "tests/src/**/systemui/accessibility/floatingmenu/PositionTest.java",
"tests/src/**/systemui/animation/TransitionAnimatorTest.kt",
- "tests/src/**/systemui/screenshot/scroll/ScrollCaptureControllerTest.java",
- "tests/src/**/systemui/lifecycle/SysuiViewModelTest.kt",
- "tests/src/**/systemui/flags/FakeFeatureFlags.kt",
"tests/src/**/systemui/animation/TransitionAnimatorTest.kt",
- "tests/src/**/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java",
"tests/src/**/systemui/statusbar/connectivity/NetworkControllerSignalTest.java",
- "tests/src/**/systemui/statusbar/notification/row/BigPictureIconManagerTest.kt",
- "tests/src/**/systemui/statusbar/policy/RotationLockControllerImplTest.java",
"tests/src/**/systemui/statusbar/phone/ScrimControllerTest.java",
"tests/src/**/systemui/toast/ToastUITest.java",
"tests/src/**/systemui/statusbar/policy/FlashlightControllerImplTest.kt",
@@ -329,47 +286,48 @@
"tests/src/**/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java",
"tests/src/**/systemui/qs/external/TileLifecycleManagerTest.java",
"tests/src/**/systemui/ScreenDecorationsTest.java",
+ "tests/src/**/systemui/statusbar/policy/BatteryControllerStartableTest.java",
"tests/src/**/keyguard/CarrierTextManagerTest.java",
"tests/src/**/keyguard/KeyguardUpdateMonitorTest.java",
],
}
-// Tests where robolectric failed at compile time. (go/multivalent-tests)
+// Tests where compilation failed due to kotlin internal references.
filegroup {
- name: "SystemUI-tests-broken-robofiles-compile",
+ name: "SystemUI-tests-broken-robofiles-internal",
srcs: [
- "tests/src/**/systemui/statusbar/notification/icon/IconManagerTest.kt",
- "tests/src/**/systemui/statusbar/KeyguardIndicationControllerTest.java",
- "tests/src/**/systemui/doze/DozeScreenStateTest.java",
- "tests/src/**/systemui/notetask/NoteTaskInitializerTest.kt",
- "tests/src/**/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt",
- "tests/src/**/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt",
- "tests/src/**/systemui/controls/management/ControlsFavoritingActivityTest.kt",
- "tests/src/**/systemui/controls/management/ControlsProviderSelectorActivityTest.kt",
- "tests/src/**/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt",
- "tests/src/**/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt",
- "tests/src/**/systemui/qs/tileimpl/QSTileViewImplTest.kt",
+ "tests/src/**/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt",
+ "tests/src/**/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt",
+ "tests/src/**/android/systemui/statusbar/notification/icon/IconManagerTest.kt",
+ "tests/src/**/android/systemui/notetask/NoteTaskInitializerTest.kt",
+ "tests/src/**/systemui/statusbar/policy/VariableDateViewControllerTest.kt",
+ "tests/src/**/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt",
+ "tests/src/**/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt",
+ "tests/src/**/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModelTest.kt",
+ "tests/src/**/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt",
+ "tests/src/**/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt",
"tests/src/**/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt",
+ "tests/src/**/systemui/statusbar/policy/WalletControllerImplTest.kt",
"tests/src/**/keyguard/ClockEventControllerTest.kt",
- "tests/src/**/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt",
+ "tests/src/**/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt",
"tests/src/**/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt",
"tests/src/**/systemui/bluetooth/qsdialog/BluetoothTileDialogRepositoryTest.kt",
"tests/src/**/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt",
+ "tests/src/**/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt",
"tests/src/**/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt",
"tests/src/**/systemui/broadcast/UserBroadcastDispatcherTest.kt",
"tests/src/**/systemui/charging/WiredChargingRippleControllerTest.kt",
"tests/src/**/systemui/clipboardoverlay/ClipboardModelTest.kt",
"tests/src/**/systemui/controls/controller/AuxiliaryPersistenceWrapperTest.kt",
- "tests/src/**/systemui/controls/controller/ControlsBindingControllerImplTest.kt",
"tests/src/**/systemui/controls/controller/ControlsControllerImplTest.kt",
"tests/src/**/systemui/controls/controller/DeletionJobServiceTest.kt",
- "tests/src/**/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt",
+ "tests/src/**/systemui/controls/management/ControlsFavoritingActivityTest.kt",
"tests/src/**/systemui/controls/ui/ControlsUiControllerImplTest.kt",
- "tests/src/**/systemui/controls/ui/ControlViewHolderTest.kt",
"tests/src/**/systemui/controls/ui/SelectionItemTest.kt",
"tests/src/**/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt",
"tests/src/**/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt",
- "tests/src/**/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt",
+ "tests/src/**/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt",
+ "tests/src/**/systemui/media/controls/ui/MediaPlayerDataTest.kt",
"tests/src/**/systemui/media/controls/ui/animation/AnimationBindHandlerTest.kt",
"tests/src/**/systemui/media/controls/ui/animation/ColorSchemeTransitionTest.kt",
"tests/src/**/systemui/media/controls/ui/animation/MetadataAnimationHandlerTest.kt",
@@ -377,7 +335,6 @@
"tests/src/**/systemui/media/controls/ui/controller/MediaControlPanelTest.kt",
"tests/src/**/systemui/media/controls/ui/controller/MediaViewControllerTest.kt",
"tests/src/**/systemui/media/controls/ui/drawable/SquigglyProgressTest.kt",
- "tests/src/**/systemui/media/controls/ui/MediaPlayerDataTest.kt",
"tests/src/**/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt",
"tests/src/**/systemui/navigationbar/gestural/BackPanelControllerTest.kt",
"tests/src/**/systemui/notetask/NoteTaskControllerTest.kt",
@@ -386,61 +343,61 @@
"tests/src/**/systemui/qs/external/CustomTileStatePersisterTest.kt",
"tests/src/**/systemui/qs/external/TileRequestDialogTest.kt",
"tests/src/**/systemui/qs/external/TileServiceRequestControllerTest.kt",
- "tests/src/**/systemui/qs/tileimpl/TilesStatesTextTest.kt",
+ "tests/src/**/systemui/qs/tileimpl/QSTileViewImplTest.kt",
"tests/src/**/systemui/qs/tiles/AlarmTileTest.kt",
"tests/src/**/systemui/qs/tiles/BluetoothTileTest.kt",
- "tests/src/**/systemui/screenshot/ScreenshotPolicyImplTest.kt",
- "tests/src/**/systemui/settings/DisplayTrackerImplTest.kt",
+ "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt",
+ "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt",
+ "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt",
+ "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt",
+ "tests/src/**/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt",
+ "tests/src/**/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt",
+ "tests/src/**/systemui/statusbar/phone/FoldStateListenerTest.kt",
+ "tests/src/**/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerTest.kt",
+ "tests/src/**/systemui/statusbar/notification/row/TextPrecomputerTest.kt",
+ "tests/src/**/systemui/statusbar/notification/row/SingleLineConversationViewBinderTest.kt",
+ "tests/src/**/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt",
+ "tests/src/**/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt",
+ "tests/src/**/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt",
+ "tests/src/**/systemui/statusbar/notification/logging/NotificationMemoryLoggerTest.kt",
+ "tests/src/**/systemui/statusbar/notification/logging/NotificationMemoryMeterTest.kt",
+ "tests/src/**/systemui/statusbar/notification/logging/NotificationMemoryViewWalkerTest.kt",
+ "tests/src/**/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt",
+ "tests/src/**/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt",
+ "tests/src/**/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt",
+ "tests/src/**/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinatorTest.kt",
+ "tests/src/**/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt",
+ "tests/src/**/systemui/statusbar/notification/RoundableTest.kt",
+ "tests/src/**/systemui/stylus/StylusUsiPowerUiTest.kt",
+ "tests/src/**/systemui/statusbar/gesture/GenericGestureDetectorTest.kt",
+ "tests/src/**/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt",
+ "tests/src/**/systemui/statusbar/connectivity/MobileStateTest.kt",
+ "tests/src/**/systemui/statusbar/commandline/CommandParserTest.kt",
+ "tests/src/**/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt",
+ "tests/src/**/systemui/statusbar/LightRevealScrimTest.kt",
+ "tests/src/**/systemui/shade/transition/LargeScreenShadeInterpolatorImplTest.kt",
+ "tests/src/**/systemui/shade/ShadeExpansionStateManagerTest.kt",
+ "tests/src/**/systemui/shade/ShadeHeaderControllerTest.kt",
+ "tests/src/**/systemui/shade/NotificationsQSContainerControllerTest.kt",
"tests/src/**/systemui/settings/UserFileManagerImplTest.kt",
"tests/src/**/systemui/settings/UserTrackerImplReceiveTest.kt",
"tests/src/**/systemui/settings/UserTrackerImplTest.kt",
"tests/src/**/systemui/shade/GlanceableHubContainerControllerTest.kt",
"tests/src/**/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt",
- "tests/src/**/systemui/shade/NotificationsQSContainerControllerTest.kt",
- "tests/src/**/systemui/shade/ShadeExpansionStateManagerTest.kt",
- "tests/src/**/systemui/shade/ShadeHeaderControllerTest.kt",
- "tests/src/**/systemui/shade/transition/LargeScreenShadeInterpolatorImplTest.kt",
- "tests/src/**/systemui/statusbar/commandline/CommandParserTest.kt",
- "tests/src/**/systemui/statusbar/connectivity/MobileStateTest.kt",
- "tests/src/**/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt",
- "tests/src/**/systemui/statusbar/gesture/GenericGestureDetectorTest.kt",
- "tests/src/**/systemui/statusbar/LightRevealScrimTest.kt",
- "tests/src/**/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt",
- "tests/src/**/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinatorTest.kt",
- "tests/src/**/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt",
- "tests/src/**/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt",
- "tests/src/**/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt",
- "tests/src/**/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt",
- "tests/src/**/systemui/statusbar/notification/logging/NotificationMemoryLoggerTest.kt",
- "tests/src/**/systemui/statusbar/notification/logging/NotificationMemoryMeterTest.kt",
- "tests/src/**/systemui/statusbar/notification/logging/NotificationMemoryViewWalkerTest.kt",
- "tests/src/**/systemui/statusbar/notification/RoundableTest.kt",
- "tests/src/**/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt",
- "tests/src/**/systemui/statusbar/notification/row/SingleLineConversationViewBinderTest.kt",
- "tests/src/**/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt",
- "tests/src/**/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt",
- "tests/src/**/systemui/statusbar/notification/row/TextPrecomputerTest.kt",
- "tests/src/**/systemui/statusbar/phone/FoldStateListenerTest.kt",
- "tests/src/**/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt",
- "tests/src/**/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt",
- "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt",
- "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt",
- "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt",
- "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt",
- "tests/src/**/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt",
- "tests/src/**/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModelTest.kt",
- "tests/src/**/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt",
- "tests/src/**/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt",
- "tests/src/**/systemui/statusbar/policy/VariableDateViewControllerTest.kt",
- "tests/src/**/systemui/statusbar/policy/WalletControllerImplTest.kt",
- "tests/src/**/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt",
- "tests/src/**/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt",
- "tests/src/**/systemui/statusbar/policy/BatteryControllerStartableTest.java",
- "tests/src/**/systemui/shared/plugins/PluginActionManagerTest.java",
- "tests/src/**/systemui/statusbar/policy/SecurityControllerTest.java",
- "tests/src/**/systemui/shared/clocks/view/SimpleDigitalClockTextViewTest.kt",
+ "tests/src/**/systemui/screenshot/ScreenshotPolicyImplTest.kt",
+ "tests/src/**/systemui/qs/tileimpl/TilesStatesTextTest.kt",
+ "tests/src/**/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt",
+ "tests/src/**/systemui/controls/ui/ControlViewHolderTest.kt",
+ "tests/src/**/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt",
+ "tests/src/**/systemui/controls/controller/ControlsBindingControllerImplTest.kt",
+ "tests/src/**/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt",
+ "tests/src/**/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt",
+ "tests/src/**/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt",
+ "tests/src/**/systemui/controls/management/ControlsProviderSelectorActivityTest.kt",
+ "tests/src/**/systemui/settings/DisplayTrackerImplTest.kt",
+ "tests/src/**/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt",
+ "tests/src/**/systemui/wmshell/BubblesTest.java",
],
- visibility: ["//visibility:private"],
}
//Create a library to expose SystemUI's resources to other modules.
@@ -903,9 +860,8 @@
],
exclude_srcs: [
":SystemUI-tests-broken-robofiles-mockito-extended",
- ":SystemUI-tests-broken-robofiles-compile",
+ ":SystemUI-tests-broken-robofiles-internal",
":SystemUI-tests-broken-robofiles-run",
- ":SystemUI-tests-broken-robofiles-sysui-run",
],
static_libs: [
"RoboTestLibraries",
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index e47704eb..cc01071 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -55,14 +55,6 @@
"exclude-filter": "android.platform.tests.HomeTest#testAssistantWidget"
}
]
- },
- {
- "name": "AndroidAutomotiveNotificationsTests",
- "options" : [
- {
- "include-filter": "android.platform.tests.NotificationTest"
- }
- ]
}
],
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
index 3db61a5..6bc0f42 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
@@ -220,9 +220,6 @@
@SuppressLint("MissingPermission")
private boolean isShortcutRestricted(int shortcutId) {
- if (!Flags.hideRestrictedActions()) {
- return false;
- }
final UserManager userManager = mService.getSystemService(UserManager.class);
if (userManager == null) {
return false;
@@ -366,12 +363,11 @@
if (mLayout.getVisibility() == View.VISIBLE) {
mLayout.setVisibility(View.GONE);
} else {
- if (Flags.hideRestrictedActions()) {
- // Reconfigure the shortcut list in case the set of restricted actions has changed.
- mA11yMenuViewPager.configureViewPagerAndFooter(
- mLayout, createShortcutList(), getPageIndex());
- updateViewLayout();
- }
+ // Reconfigure the shortcut list in case the set of restricted actions has changed.
+ mA11yMenuViewPager.configureViewPagerAndFooter(
+ mLayout, createShortcutList(), getPageIndex());
+ updateViewLayout();
+
mLayout.setVisibility(View.VISIBLE);
}
}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
index 4ab771b..7172619 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
@@ -46,9 +46,6 @@
import android.media.AudioManager;
import android.os.PowerManager;
import android.os.UserManager;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.uiautomator_helpers.WaitUtils;
import android.provider.Settings;
import android.util.Log;
@@ -63,7 +60,6 @@
import androidx.test.uiautomator.UiDevice;
import com.android.compatibility.common.util.TestUtils;
-import com.android.systemui.accessibility.accessibilitymenu.Flags;
import com.android.systemui.accessibility.accessibilitymenu.model.A11yMenuShortcut.ShortcutId;
import org.junit.After;
@@ -71,7 +67,6 @@
import org.junit.Assume;
import org.junit.Before;
import org.junit.BeforeClass;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -82,9 +77,6 @@
@RunWith(AndroidJUnit4.class)
public class AccessibilityMenuServiceTest {
- @Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
private static final String TAG = "A11yMenuServiceTest";
private static final int CLICK_ID = AccessibilityNodeInfo.ACTION_CLICK;
@@ -499,7 +491,6 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_HIDE_RESTRICTED_ACTIONS)
public void testRestrictedActions_BrightnessNotAvailable() throws Throwable {
try {
setUserRestriction(UserManager.DISALLOW_CONFIG_BRIGHTNESS, true);
@@ -519,7 +510,6 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_HIDE_RESTRICTED_ACTIONS)
public void testRestrictedActions_VolumeNotAvailable() throws Throwable {
try {
setUserRestriction(UserManager.DISALLOW_ADJUST_VOLUME, true);
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
index f1cbba7..a1f0c14 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
@@ -702,6 +702,8 @@
object : Controller by controller {
override val isLaunching: Boolean = false
}
+ // Cross-task close transitions should not use this animation, so we only register it for
+ // when the opening window is Launcher.
val returnFilter =
TransitionFilter().apply {
mRequirements =
@@ -710,7 +712,11 @@
mActivityType = WindowConfiguration.ACTIVITY_TYPE_STANDARD
mModes = intArrayOf(TRANSIT_CLOSE, TRANSIT_TO_BACK)
mTopActivity = component
- }
+ },
+ TransitionFilter.Requirement().apply {
+ mActivityType = WindowConfiguration.ACTIVITY_TYPE_HOME
+ mModes = intArrayOf(TRANSIT_OPEN, TRANSIT_TO_FRONT)
+ },
)
}
val returnRemoteTransition =
@@ -861,6 +867,9 @@
) {
// Raise closing task to "above" layer so it isn't covered.
t.setLayer(target.leash, aboveLayers - i)
+ } else if (TransitionUtil.isOpeningType(change.mode)) {
+ // Put into the "below" layer space.
+ t.setLayer(target.leash, belowLayers - i)
}
} else if (TransitionInfo.isIndependent(change, info)) {
// Root tasks
@@ -1141,7 +1150,7 @@
// If a [controller.windowAnimatorState] exists, treat this like a takeover.
takeOverAnimationInternal(
window,
- startWindowStates = null,
+ startWindowState = null,
startTransaction = null,
callback,
)
@@ -1156,22 +1165,23 @@
callback: IRemoteAnimationFinishedCallback?,
) {
val window = setUpAnimation(apps, callback) ?: return
- takeOverAnimationInternal(window, startWindowStates, startTransaction, callback)
+ val startWindowState = startWindowStates[apps!!.indexOf(window)]
+ takeOverAnimationInternal(window, startWindowState, startTransaction, callback)
}
private fun takeOverAnimationInternal(
window: RemoteAnimationTarget,
- startWindowStates: Array<WindowAnimationState>?,
+ startWindowState: WindowAnimationState?,
startTransaction: SurfaceControl.Transaction?,
callback: IRemoteAnimationFinishedCallback?,
) {
val useSpring =
- !controller.isLaunching && startWindowStates != null && startTransaction != null
+ !controller.isLaunching && startWindowState != null && startTransaction != null
startAnimation(
window,
navigationBar = null,
useSpring,
- startWindowStates,
+ startWindowState,
startTransaction,
callback,
)
@@ -1281,7 +1291,7 @@
window: RemoteAnimationTarget,
navigationBar: RemoteAnimationTarget? = null,
useSpring: Boolean = false,
- startingWindowStates: Array<WindowAnimationState>? = null,
+ startingWindowState: WindowAnimationState? = null,
startTransaction: SurfaceControl.Transaction? = null,
iCallback: IRemoteAnimationFinishedCallback? = null,
) {
@@ -1327,6 +1337,7 @@
val isExpandingFullyAbove =
transitionAnimator.isExpandingFullyAbove(controller.transitionContainer, endState)
+ val windowState = startingWindowState ?: controller.windowAnimatorState
// We animate the opening window and delegate the view expansion to [this.controller].
val delegate = this.controller
@@ -1349,18 +1360,6 @@
}
}
- // The states are sorted matching the changes inside the transition info.
- // Using this info, the RemoteAnimationTargets are created, with their
- // prefixOrderIndex fields in reverse order to that of changes. To extract
- // the right state, we need to invert again.
- val windowState =
- if (startingWindowStates != null) {
- startingWindowStates[
- startingWindowStates.size - window.prefixOrderIndex]
- } else {
- controller.windowAnimatorState
- }
-
// TODO(b/323863002): use the timestamp and velocity to update the initial
// position.
val bounds = windowState?.bounds
@@ -1449,12 +1448,6 @@
delegate.onTransitionAnimationProgress(state, progress, linearProgress)
}
}
- val windowState =
- if (startingWindowStates != null) {
- startingWindowStates[startingWindowStates.size - window.prefixOrderIndex]
- } else {
- controller.windowAnimatorState
- }
val velocityPxPerS =
if (longLivedReturnAnimationsEnabled() && windowState?.velocityPxPerMs != null) {
val xVelocityPxPerS = windowState.velocityPxPerMs.x * 1000
@@ -1473,6 +1466,7 @@
fadeWindowBackgroundLayer = !controller.isBelowAnimatingWindow,
drawHole = !controller.isBelowAnimatingWindow,
startVelocity = velocityPxPerS,
+ startFrameTime = windowState?.timestamp ?: -1,
)
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
index e2bc409..4e889e9 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
@@ -27,6 +27,8 @@
import android.util.FloatProperty
import android.util.Log
import android.util.MathUtils
+import android.util.TimeUtils
+import android.view.Choreographer
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroupOverlay
@@ -366,6 +368,7 @@
@get:VisibleForTesting val springY: SpringAnimation,
@get:VisibleForTesting val springScale: SpringAnimation,
private val springState: SpringState,
+ private val startFrameTime: Long,
private val onAnimationStart: Runnable,
) : Animation {
@get:VisibleForTesting
@@ -374,6 +377,42 @@
override fun start() {
onAnimationStart.run()
+
+ // If no start frame time is provided, we start the springs normally.
+ if (startFrameTime < 0) {
+ startSprings()
+ return
+ }
+
+ // This function is not guaranteed to be called inside a frame. We try to access the
+ // frame time immediately, but if we're not inside a frame this will throw an exception.
+ // We must then post a callback to be run at the beginning of the next frame.
+ try {
+ initAndStartSprings(Choreographer.getInstance().frameTime)
+ } catch (_: IllegalStateException) {
+ Choreographer.getInstance().postFrameCallback { frameTimeNanos ->
+ initAndStartSprings(frameTimeNanos / TimeUtils.NANOS_PER_MS)
+ }
+ }
+ }
+
+ private fun initAndStartSprings(frameTime: Long) {
+ // Initialize the spring as if it had started at the time that its start state
+ // was created.
+ springX.doAnimationFrame(startFrameTime)
+ springY.doAnimationFrame(startFrameTime)
+ springScale.doAnimationFrame(startFrameTime)
+ // Move the spring time forward to the current frame, so it updates its internal state
+ // following the initial momentum over the elapsed time.
+ springX.doAnimationFrame(frameTime)
+ springY.doAnimationFrame(frameTime)
+ springScale.doAnimationFrame(frameTime)
+ // Actually start the spring. We do this after the previous calls because the framework
+ // doesn't like it when you call doAnimationFrame() after start() with an earlier time.
+ startSprings()
+ }
+
+ private fun startSprings() {
springX.start()
springY.start()
springScale.start()
@@ -471,7 +510,9 @@
* is true.
*
* If [startVelocity] (expressed in pixels per second) is not null, a multi-spring animation
- * using it for the initial momentum will be used instead of the default interpolators.
+ * using it for the initial momentum will be used instead of the default interpolators. In this
+ * case, [startFrameTime] (if non-negative) represents the frame time at which the springs
+ * should be started.
*/
fun startAnimation(
controller: Controller,
@@ -480,6 +521,7 @@
fadeWindowBackgroundLayer: Boolean = true,
drawHole: Boolean = false,
startVelocity: PointF? = null,
+ startFrameTime: Long = -1,
): Animation {
if (!controller.isLaunching) assertReturnAnimations()
if (startVelocity != null) assertLongLivedReturnAnimations()
@@ -502,6 +544,7 @@
fadeWindowBackgroundLayer,
drawHole,
startVelocity,
+ startFrameTime,
)
.apply { start() }
}
@@ -515,6 +558,7 @@
fadeWindowBackgroundLayer: Boolean = true,
drawHole: Boolean = false,
startVelocity: PointF? = null,
+ startFrameTime: Long = -1,
): Animation {
val transitionContainer = controller.transitionContainer
val transitionContainerOverlay = transitionContainer.overlay
@@ -537,6 +581,7 @@
startState,
endState,
startVelocity,
+ startFrameTime,
windowBackgroundLayer,
transitionContainer,
transitionContainerOverlay,
@@ -722,6 +767,7 @@
startState: State,
endState: State,
startVelocity: PointF,
+ startFrameTime: Long,
windowBackgroundLayer: GradientDrawable,
transitionContainer: View,
transitionContainerOverlay: ViewGroupOverlay,
@@ -912,7 +958,7 @@
}
}
- return MultiSpringAnimation(springX, springY, springScale, springState) {
+ return MultiSpringAnimation(springX, springY, springScale, springState, startFrameTime) {
onAnimationStart(
controller,
isExpandingFullyAbove,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalTouchableSurface.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalTouchableSurface.kt
index 3707a87..f2edec6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalTouchableSurface.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalTouchableSurface.kt
@@ -27,6 +27,8 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.key.onPreviewKeyEvent
import androidx.compose.ui.input.pointer.motionEventSpy
+import androidx.compose.ui.semantics.hideFromAccessibility
+import androidx.compose.ui.semantics.semantics
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
@OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class)
@@ -42,6 +44,9 @@
Box(
modifier =
modifier
+ // The touchable surface is hidden for accessibility because these actions are
+ // already provided through custom accessibility actions.
+ .semantics { hideFromAccessibility() }
.combinedClickable(
onLongClick = viewModel::onLongClick,
onClick = viewModel::onClick,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java
index 944066fa..d47ec8c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java
@@ -25,15 +25,12 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper;
import android.util.Size;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.util.FakeSharedPreferences;
@@ -59,18 +56,6 @@
mWindowMagnificationFrameSizePrefs = new WindowMagnificationFrameSizePrefs(mContext);
}
- @DisableFlags(Flags.FLAG_SAVE_AND_RESTORE_MAGNIFICATION_SETTINGS_BUTTONS)
- @Test
- public void saveSizeForCurrentDensity_getExpectedSize() {
- Size testSize = new Size(500, 500);
- mWindowMagnificationFrameSizePrefs
- .saveIndexAndSizeForCurrentDensity(MagnificationSize.CUSTOM, testSize);
-
- assertThat(mWindowMagnificationFrameSizePrefs.getSizeForCurrentDensity())
- .isEqualTo(testSize);
- }
-
- @EnableFlags(Flags.FLAG_SAVE_AND_RESTORE_MAGNIFICATION_SETTINGS_BUTTONS)
@Test
public void saveSizeForCurrentDensity_validPreference_getExpectedSize() {
int testIndex = MagnificationSize.MEDIUM;
@@ -81,7 +66,6 @@
.isEqualTo(testSize);
}
- @EnableFlags(Flags.FLAG_SAVE_AND_RESTORE_MAGNIFICATION_SETTINGS_BUTTONS)
@Test
public void saveSizeForCurrentDensity_validPreference_getExpectedIndex() {
int testIndex = MagnificationSize.MEDIUM;
@@ -92,7 +76,6 @@
.isEqualTo(testIndex);
}
- @EnableFlags(Flags.FLAG_SAVE_AND_RESTORE_MAGNIFICATION_SETTINGS_BUTTONS)
@Test
public void saveSizeForCurrentDensity_invalidPreference_getDefaultIndex() {
mSharedPreferences
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/fontscaling b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt
similarity index 96%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/fontscaling
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt
index 0bd00fb..f1401f1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/fontscaling
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt
@@ -50,8 +50,8 @@
import org.mockito.Mock
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
private const val ON: Int = 1
private const val OFF: Int = 0
@@ -103,7 +103,7 @@
systemClock,
userTracker,
mainHandler,
- backgroundDelayableExecutor
+ backgroundDelayableExecutor,
)
)
@@ -116,7 +116,7 @@
sysuiState,
fakeBroadcastDispatcher,
mDialogTransitionAnimator,
- fontScalingDialogDelegate
+ fontScalingDialogDelegate,
)
whenever(dialogFactory.create(any(), any())).thenReturn(dialog)
@@ -132,7 +132,7 @@
systemSettings.getFloatForUser(
Settings.System.FONT_SCALE,
/* def= */ 1.0f,
- userTracker.userId
+ userTracker.userId,
)
assertThat(currentScale).isEqualTo(fontSizeValueArray[progress].toFloat())
@@ -163,7 +163,7 @@
systemSettings.getFloatForUser(
Settings.System.FONT_SCALE,
/* def= */ 1.0f,
- userTracker.userId
+ userTracker.userId,
)
assertThat(seekBar.getProgress()).isEqualTo(1)
assertThat(currentScale).isEqualTo(fontSizeValueArray[1].toFloat())
@@ -194,7 +194,7 @@
systemSettings.getFloatForUser(
Settings.System.FONT_SCALE,
/* def= */ 1.0f,
- userTracker.userId
+ userTracker.userId,
)
assertThat(seekBar.getProgress()).isEqualTo(fontSizeValueArray.size - 2)
assertThat(currentScale)
@@ -211,7 +211,7 @@
secureSettings.putIntForUser(
Settings.Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED,
OFF,
- userTracker.userId
+ userTracker.userId,
)
// Default seekbar progress for font size is 1, click start icon to decrease the progress
@@ -224,7 +224,7 @@
secureSettings.getIntForUser(
Settings.Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED,
/* def = */ OFF,
- userTracker.userId
+ userTracker.userId,
)
assertThat(currentSettings).isEqualTo(ON)
@@ -256,7 +256,7 @@
systemSettings.getFloatForUser(
Settings.System.FONT_SCALE,
/* def= */ 1.0f,
- userTracker.userId
+ userTracker.userId,
)
assertThat(systemScale).isEqualTo(1.0f)
@@ -269,7 +269,7 @@
// SeekBar interaction is finalized.
changeListener.onUserInteractionFinalized(
seekBar,
- OnSeekBarWithIconButtonsChangeListener.ControlUnitType.SLIDER
+ OnSeekBarWithIconButtonsChangeListener.ControlUnitType.SLIDER,
)
backgroundDelayableExecutor.runAllReady()
backgroundDelayableExecutor.advanceClockToNext()
@@ -280,7 +280,7 @@
systemSettings.getFloatForUser(
Settings.System.FONT_SCALE,
/* def= */ 1.0f,
- userTracker.userId
+ userTracker.userId,
)
assertThat(systemScale).isEqualTo(fontSizeValueArray[0].toFloat())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/activity/data/repository/ActivityManagerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/activity/data/repository/ActivityManagerRepositoryTest.kt
new file mode 100644
index 0000000..d6ba98d
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/activity/data/repository/ActivityManagerRepositoryTest.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.activity.data.repository
+
+import android.app.ActivityManager
+import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
+import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE
+import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_TOP_SLEEPING
+import android.app.activityManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.logcatLogBuffer
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ActivityManagerRepositoryTest : SysuiTestCase() {
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+ private val logger = Logger(logcatLogBuffer("ActivityManagerRepositoryTest"), "tag")
+
+ private val Kosmos.underTest by Kosmos.Fixture { realActivityManagerRepository }
+
+ @Test
+ fun createIsAppVisibleFlow_fetchesInitialValue_true() =
+ kosmos.runTest {
+ whenever(activityManager.getUidImportance(THIS_UID)).thenReturn(IMPORTANCE_FOREGROUND)
+
+ val latest by
+ collectLastValue(underTest.createIsAppVisibleFlow(THIS_UID, logger, LOG_TAG))
+
+ assertThat(latest).isTrue()
+ }
+
+ @Test
+ fun createIsAppVisibleFlow_fetchesInitialValue_false() =
+ kosmos.runTest {
+ whenever(activityManager.getUidImportance(THIS_UID)).thenReturn(IMPORTANCE_GONE)
+
+ val latest by
+ collectLastValue(underTest.createIsAppVisibleFlow(THIS_UID, logger, LOG_TAG))
+
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ fun createIsAppVisibleFlow_getsImportanceUpdates() =
+ kosmos.runTest {
+ val latest by
+ collectLastValue(underTest.createIsAppVisibleFlow(THIS_UID, logger, LOG_TAG))
+
+ val listenerCaptor = argumentCaptor<ActivityManager.OnUidImportanceListener>()
+ verify(activityManager).addOnUidImportanceListener(listenerCaptor.capture(), any())
+ val listener = listenerCaptor.firstValue
+
+ listener.onUidImportance(THIS_UID, IMPORTANCE_GONE)
+ assertThat(latest).isFalse()
+
+ listener.onUidImportance(THIS_UID, IMPORTANCE_FOREGROUND)
+ assertThat(latest).isTrue()
+
+ listener.onUidImportance(THIS_UID, IMPORTANCE_TOP_SLEEPING)
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ fun createIsAppVisibleFlow_ignoresUpdatesForOtherUids() =
+ kosmos.runTest {
+ val latest by
+ collectLastValue(underTest.createIsAppVisibleFlow(THIS_UID, logger, LOG_TAG))
+
+ val listenerCaptor = argumentCaptor<ActivityManager.OnUidImportanceListener>()
+ verify(activityManager).addOnUidImportanceListener(listenerCaptor.capture(), any())
+ val listener = listenerCaptor.firstValue
+
+ listener.onUidImportance(THIS_UID, IMPORTANCE_GONE)
+ assertThat(latest).isFalse()
+
+ // WHEN another UID becomes foreground
+ listener.onUidImportance(THIS_UID + 2, IMPORTANCE_FOREGROUND)
+
+ // THEN this UID still stays not visible
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ fun createIsAppVisibleFlow_securityExceptionOnUidRegistration_ok() =
+ kosmos.runTest {
+ whenever(activityManager.getUidImportance(THIS_UID)).thenReturn(IMPORTANCE_GONE)
+ whenever(activityManager.addOnUidImportanceListener(any(), any()))
+ .thenThrow(SecurityException())
+
+ val latest by
+ collectLastValue(underTest.createIsAppVisibleFlow(THIS_UID, logger, LOG_TAG))
+
+ // Verify no crash, and we get a value emitted
+ assertThat(latest).isFalse()
+ }
+
+ /** Regression test for b/216248574. */
+ @Test
+ fun createIsAppVisibleFlow_getUidImportanceThrowsException_ok() =
+ kosmos.runTest {
+ whenever(activityManager.getUidImportance(any())).thenThrow(SecurityException())
+
+ val latest by
+ collectLastValue(underTest.createIsAppVisibleFlow(THIS_UID, logger, LOG_TAG))
+
+ // Verify no crash, and we get a value emitted
+ assertThat(latest).isFalse()
+ }
+
+ companion object {
+ private const val THIS_UID = 558
+ private const val LOG_TAG = "LogTag"
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt
similarity index 98%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt
index 97f2e56..af8c357 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt
@@ -59,6 +59,7 @@
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.motion.createSysUiComposeMotionTestRule
+import com.android.systemui.qs.ui.viewmodel.fakeQsSceneAdapter
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.domain.startable.sceneContainerStartable
import com.android.systemui.scene.sceneContainerViewModelFactory
@@ -183,6 +184,7 @@
initialSceneKey = Scenes.Bouncer,
overlayByKey = emptyMap(),
dataSourceDelegator = kosmos.sceneDataSourceDelegator,
+ qsSceneAdapter = { kosmos.fakeQsSceneAdapter },
)
}
},
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
index 06b710e..b66727e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
@@ -22,6 +22,7 @@
import android.app.admin.devicePolicyManager
import android.content.Intent
import android.content.pm.UserInfo
+import android.content.res.mainResources
import android.os.UserManager.USER_TYPE_PROFILE_MANAGED
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
@@ -29,6 +30,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
+import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.broadcastDispatcher
import com.android.systemui.communal.data.model.DisabledReason
@@ -53,7 +55,8 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class CommunalSettingsRepositoryImplTest : SysuiTestCase() {
- private val kosmos = testKosmos()
+ private val kosmos =
+ testKosmos().apply { mainResources = mContext.orCreateTestableResources.resources }
private val testScope = kosmos.testScope
private lateinit var underTest: CommunalSettingsRepository
@@ -67,6 +70,7 @@
}
@EnableFlags(FLAG_COMMUNAL_HUB)
+ @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
@Test
fun getFlagEnabled_bothEnabled() {
kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
@@ -74,7 +78,7 @@
assertThat(underTest.getFlagEnabled()).isTrue()
}
- @DisableFlags(FLAG_COMMUNAL_HUB)
+ @DisableFlags(FLAG_COMMUNAL_HUB, FLAG_GLANCEABLE_HUB_V2)
@Test
fun getFlagEnabled_bothDisabled() {
kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
@@ -82,7 +86,7 @@
assertThat(underTest.getFlagEnabled()).isFalse()
}
- @DisableFlags(FLAG_COMMUNAL_HUB)
+ @DisableFlags(FLAG_COMMUNAL_HUB, FLAG_GLANCEABLE_HUB_V2)
@Test
fun getFlagEnabled_onlyClassicFlagEnabled() {
kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
@@ -91,6 +95,7 @@
}
@EnableFlags(FLAG_COMMUNAL_HUB)
+ @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
@Test
fun getFlagEnabled_onlyTrunkFlagEnabled() {
kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
@@ -98,6 +103,57 @@
assertThat(underTest.getFlagEnabled()).isFalse()
}
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+ @DisableFlags(FLAG_COMMUNAL_HUB)
+ @Test
+ fun getFlagEnabled_mobileConfigEnabled() {
+ mContext.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_glanceableHubEnabled,
+ true,
+ )
+
+ assertThat(underTest.getFlagEnabled()).isTrue()
+ }
+
+ @DisableFlags(FLAG_GLANCEABLE_HUB_V2, FLAG_COMMUNAL_HUB)
+ @Test
+ fun getFlagEnabled_onlyMobileConfigEnabled() {
+ mContext.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_glanceableHubEnabled,
+ true,
+ )
+
+ assertThat(underTest.getFlagEnabled()).isFalse()
+ }
+
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+ @DisableFlags(FLAG_COMMUNAL_HUB)
+ @Test
+ fun getFlagEnabled_onlyMobileFlagEnabled() {
+ mContext.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_glanceableHubEnabled,
+ false,
+ )
+
+ assertThat(underTest.getFlagEnabled()).isFalse()
+ }
+
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+ @DisableFlags(FLAG_COMMUNAL_HUB)
+ @Test
+ fun getFlagEnabled_oldFlagIgnored() {
+ // New config flag enabled.
+ mContext.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_glanceableHubEnabled,
+ true,
+ )
+
+ // Old config flag disabled.
+ kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
+
+ assertThat(underTest.getFlagEnabled()).isTrue()
+ }
+
@EnableFlags(FLAG_COMMUNAL_HUB)
@Test
fun secondaryUserIsInvalid() =
@@ -134,7 +190,7 @@
kosmos.fakeSettings.putIntForUser(
Settings.Secure.GLANCEABLE_HUB_ENABLED,
0,
- PRIMARY_USER.id
+ PRIMARY_USER.id,
)
val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
assertThat(enabledState?.enabled).isFalse()
@@ -143,14 +199,14 @@
kosmos.fakeSettings.putIntForUser(
Settings.Secure.GLANCEABLE_HUB_ENABLED,
1,
- SECONDARY_USER.id
+ SECONDARY_USER.id,
)
assertThat(enabledState?.enabled).isFalse()
kosmos.fakeSettings.putIntForUser(
Settings.Secure.GLANCEABLE_HUB_ENABLED,
1,
- PRIMARY_USER.id
+ PRIMARY_USER.id,
)
assertThat(enabledState?.enabled).isTrue()
}
@@ -201,7 +257,7 @@
kosmos.fakeSettings.putIntForUser(
Settings.Secure.GLANCEABLE_HUB_ENABLED,
0,
- PRIMARY_USER.id
+ PRIMARY_USER.id,
)
setKeyguardFeaturesDisabled(PRIMARY_USER, KEYGUARD_DISABLE_WIDGETS_ALL)
@@ -228,7 +284,7 @@
kosmos.fakeSettings.putIntForUser(
GLANCEABLE_HUB_BACKGROUND_SETTING,
type.value,
- PRIMARY_USER.id
+ PRIMARY_USER.id,
)
assertWithMessage(
"Expected $type when $GLANCEABLE_HUB_BACKGROUND_SETTING is set to" +
@@ -253,12 +309,6 @@
UserInfo(/* id= */ 0, /* name= */ "primary user", /* flags= */ UserInfo.FLAG_MAIN)
val SECONDARY_USER = UserInfo(/* id= */ 1, /* name= */ "secondary user", /* flags= */ 0)
val WORK_PROFILE =
- UserInfo(
- 10,
- "work",
- /* iconPath= */ "",
- /* flags= */ 0,
- USER_TYPE_PROFILE_MANAGED,
- )
+ UserInfo(10, "work", /* iconPath= */ "", /* flags= */ 0, USER_TYPE_PROFILE_MANAGED)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractorImplTest.kt
new file mode 100644
index 0000000..e1344ca
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractorImplTest.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.display.domain.interactor
+
+import android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
+import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR
+import android.view.layoutInflater
+import android.view.windowManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.display.data.repository.fakeDisplayWindowPropertiesRepository
+import com.android.systemui.display.shared.model.DisplayWindowProperties
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class DisplayWindowPropertiesInteractorImplTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val repo = kosmos.fakeDisplayWindowPropertiesRepository
+
+ private val underTest = kosmos.displayWindowPropertiesInteractor
+
+ @Test
+ fun getForStatusBar_returnsPropertiesWithCorrectWindowType() {
+ val displayId = 123
+ val statusBarWindowProperties = createDisplayWindowProperties(displayId, TYPE_STATUS_BAR)
+ val navBarWindowProperties = createDisplayWindowProperties(displayId, TYPE_NAVIGATION_BAR)
+ repo.insert(statusBarWindowProperties)
+ repo.insert(navBarWindowProperties)
+
+ assertThat(underTest.getForStatusBar(displayId)).isEqualTo(statusBarWindowProperties)
+ }
+
+ private fun createDisplayWindowProperties(displayId: Int, windowType: Int) =
+ DisplayWindowProperties(
+ displayId,
+ windowType,
+ context,
+ kosmos.windowManager,
+ kosmos.layoutInflater,
+ )
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeScreenStateTest.java
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeScreenStateTest.java
index ba578a3..bbd78b3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeScreenStateTest.java
@@ -35,7 +35,7 @@
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Matchers.anyObject;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
@@ -315,6 +315,6 @@
public void authCallbackRemovedOnDestroy() {
mScreen.destroy();
- verify(mAuthController).removeCallback(anyObject());
+ verify(mAuthController).removeCallback(any());
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt
index dd83702..e288522 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt
@@ -25,6 +25,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.contextualeducation.GestureType
import com.android.systemui.contextualeducation.GestureType.BACK
+import com.android.systemui.contextualeducation.GestureType.HOME
import com.android.systemui.education.data.repository.fakeEduClock
import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduInteractor
import com.android.systemui.education.domain.interactor.contextualEducationInteractor
@@ -52,6 +53,7 @@
import org.mockito.Mock
import org.mockito.junit.MockitoJUnit
import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@@ -66,6 +68,7 @@
private val minDurationForNextEdu =
KeyboardTouchpadEduInteractor.minIntervalBetweenEdu + 1.seconds
private lateinit var underTest: ContextualEduUiCoordinator
+ private lateinit var previousDialog: Dialog
@Mock private lateinit var dialog: Dialog
@Mock private lateinit var notificationManager: NotificationManager
@Mock private lateinit var accessibilityManagerWrapper: AccessibilityManagerWrapper
@@ -95,9 +98,11 @@
kosmos.applicationCoroutineScope,
viewModel,
kosmos.applicationContext,
- notificationManager
+ notificationManager,
) { model ->
toastContent = model.message
+ previousDialog = dialog
+ dialog = mock<Dialog>()
dialog
}
underTest.start()
@@ -129,6 +134,14 @@
}
@Test
+ fun dismissPreviousDialogOnNewDialog() =
+ testScope.runTest {
+ triggerEducation(BACK)
+ triggerEducation(HOME)
+ verify(previousDialog).dismiss()
+ }
+
+ @Test
fun verifyBackEduToastContent() =
testScope.runTest {
triggerEducation(BACK)
@@ -149,14 +162,14 @@
verifyNotificationContent(
R.string.back_edu_notification_title,
R.string.back_edu_notification_content,
- notificationCaptor.value
+ notificationCaptor.value,
)
}
private fun verifyNotificationContent(
titleResId: Int,
contentResId: Int,
- notification: Notification
+ notification: Notification,
) {
val expectedContent = context.getString(contentResId)
val expectedTitle = context.getString(titleResId)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/InputShortcutsSourceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/InputShortcutsSourceTest.kt
index 715d907..c9f7035 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/InputShortcutsSourceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/InputShortcutsSourceTest.kt
@@ -17,21 +17,28 @@
package com.android.systemui.keyboard.shortcut.data.source
import android.content.res.mainResources
+import android.hardware.input.KeyGlyphMap
+import android.hardware.input.fakeInputManager
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.view.KeyEvent.KEYCODE_EMOJI_PICKER
import android.view.KeyboardShortcutGroup
import android.view.WindowManager.KeyboardShortcutsReceiver
import android.view.mockWindowManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_SHORTCUT_HELPER_KEY_GLYPH
import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -41,7 +48,8 @@
private val testScope = kosmos.testScope
private val mockWindowManager = kosmos.mockWindowManager
- private val source = InputShortcutsSource(kosmos.mainResources, mockWindowManager)
+ private val inputManager = kosmos.fakeInputManager.inputManager
+ private val source = InputShortcutsSource(kosmos.mainResources, mockWindowManager, inputManager)
private var wmImeShortcutGroups: List<KeyboardShortcutGroup>? = null
@@ -88,6 +96,36 @@
assertThat(groups).hasSize(4)
}
+ @Test
+ @EnableFlags(FLAG_SHORTCUT_HELPER_KEY_GLYPH)
+ fun shortcutGroups_flagEnabled_inputManagerReturnsKeyGlyph_returnsEmojiShortcut() =
+ testScope.runTest {
+ val mockKeyGlyph = mock(KeyGlyphMap::class.java)
+ whenever(mockKeyGlyph.functionRowKeys).thenReturn(intArrayOf(KEYCODE_EMOJI_PICKER))
+ whenever(inputManager.getKeyGlyphMap(TEST_DEVICE_ID)).thenReturn(mockKeyGlyph)
+ wmImeShortcutGroups = null
+
+ val groups = source.shortcutGroups(TEST_DEVICE_ID)
+
+ val keyCodes = groups[0].items.map { it.keycode }
+ assertThat(keyCodes).contains(KEYCODE_EMOJI_PICKER)
+ }
+
+ @Test
+ @DisableFlags(FLAG_SHORTCUT_HELPER_KEY_GLYPH)
+ fun shortcutGroups_flagDisabled_inputManagerReturnsKeyGlyph_returnsNoEmojiShortcut() =
+ testScope.runTest {
+ val mockKeyGlyph = mock(KeyGlyphMap::class.java)
+ whenever(mockKeyGlyph.functionRowKeys).thenReturn(intArrayOf(KEYCODE_EMOJI_PICKER))
+ whenever(inputManager.getKeyGlyphMap(TEST_DEVICE_ID)).thenReturn(mockKeyGlyph)
+ wmImeShortcutGroups = null
+
+ val groups = source.shortcutGroups(TEST_DEVICE_ID)
+
+ val keyCodes = groups[0].items.map { it.keycode }
+ assertThat(keyCodes).doesNotContain(KEYCODE_EMOJI_PICKER)
+ }
+
companion object {
private const val TEST_DEVICE_ID = 1234
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSourceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSourceTest.kt
new file mode 100644
index 0000000..495e98d
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSourceTest.kt
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.data.source
+
+import android.content.res.mainResources
+import android.hardware.input.KeyGlyphMap
+import android.hardware.input.KeyGlyphMap.KeyCombination
+import android.hardware.input.fakeInputManager
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.view.KeyEvent
+import android.view.KeyEvent.KEYCODE_BACK
+import android.view.KeyEvent.KEYCODE_HOME
+import android.view.KeyEvent.KEYCODE_RECENT_APPS
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_SHORTCUT_HELPER_KEY_GLYPH
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SystemShortcutsSourceTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+
+ private val inputManager = kosmos.fakeInputManager.inputManager
+ private val source = SystemShortcutsSource(kosmos.mainResources, inputManager)
+ private val mockKeyGlyphMap = mock(KeyGlyphMap::class.java)
+ private val functionRowKeyCodes = listOf(KEYCODE_HOME, KEYCODE_BACK, KEYCODE_RECENT_APPS)
+
+ @Before
+ fun setUp() {
+ whenever(mockKeyGlyphMap.functionRowKeys).thenReturn(intArrayOf())
+ whenever(inputManager.getKeyGlyphMap(TEST_DEVICE_ID)).thenReturn(mockKeyGlyphMap)
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHORTCUT_HELPER_KEY_GLYPH)
+ fun shortcutGroups_flagEnabled_inputManagerReturnsNoFunctionRowKeys_returnsNoFunctionRowShortcuts() =
+ testScope.runTest {
+ val groups = source.shortcutGroups(TEST_DEVICE_ID)
+
+ val keyCodes = groups[0].items.map { it.keycode }
+ assertThat(keyCodes).containsNoneIn(functionRowKeyCodes)
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHORTCUT_HELPER_KEY_GLYPH)
+ fun shortcutGroups_flagEnabled_inputManagerReturnsFunctionRowKeys_returnsFunctionRowShortcuts() =
+ testScope.runTest {
+ whenever(mockKeyGlyphMap.functionRowKeys)
+ .thenReturn(intArrayOf(KEYCODE_HOME, KEYCODE_BACK, KEYCODE_RECENT_APPS))
+
+ val groups = source.shortcutGroups(TEST_DEVICE_ID)
+
+ val keyCodes = groups[0].items.map { it.keycode }
+ assertThat(keyCodes).containsAtLeastElementsIn(functionRowKeyCodes)
+ }
+
+ @Test
+ @DisableFlags(FLAG_SHORTCUT_HELPER_KEY_GLYPH)
+ fun shortcutGroups_flagDisabled_inputManagerReturnsNoFunctionRowKeys_returnsDefaultFunctionRowShortcuts() =
+ testScope.runTest {
+ val groups = source.shortcutGroups(TEST_DEVICE_ID)
+
+ val keyCodes = groups[0].items.map { it.keycode }
+ assertThat(keyCodes).containsAtLeastElementsIn(functionRowKeyCodes)
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHORTCUT_HELPER_KEY_GLYPH)
+ fun shortcutGroups_flagEnabled_inputManagerReturnsHardwareShortcuts_returnsHardwareShortcuts() =
+ testScope.runTest {
+ whenever(mockKeyGlyphMap.functionRowKeys).thenReturn(intArrayOf())
+ val hardwareShortcutMap =
+ mapOf(Pair(KeyCombination(KeyEvent.META_META_ON, KeyEvent.KEYCODE_1), KEYCODE_BACK))
+ whenever(mockKeyGlyphMap.hardwareShortcuts).thenReturn(hardwareShortcutMap)
+
+ val groups = source.shortcutGroups(TEST_DEVICE_ID)
+
+ val shortcuts = groups[0].items.map { c -> Triple(c.label, c.modifiers, c.keycode) }
+ val hardwareShortcut =
+ Triple(
+ context.getString(R.string.group_system_go_back),
+ KeyEvent.META_META_ON,
+ KeyEvent.KEYCODE_1,
+ )
+ assertThat(shortcuts).contains(hardwareShortcut)
+ }
+
+ @Test
+ @DisableFlags(FLAG_SHORTCUT_HELPER_KEY_GLYPH)
+ fun shortcutGroups_flagDisabled_inputManagerReturnsHardwareShortcuts_returnsNoHardwareShortcuts() =
+ testScope.runTest {
+ val hardwareShortcutMap =
+ mapOf(Pair(KeyCombination(KeyEvent.META_META_ON, KeyEvent.KEYCODE_1), KEYCODE_BACK))
+ whenever(mockKeyGlyphMap.hardwareShortcuts).thenReturn(hardwareShortcutMap)
+
+ val groups = source.shortcutGroups(TEST_DEVICE_ID)
+
+ val shortcuts = groups[0].items.map { c -> Triple(c.label, c.modifiers, c.keycode) }
+ val hardwareShortcut =
+ Triple(
+ context.getString(R.string.group_system_go_back),
+ KeyEvent.META_META_ON,
+ KeyEvent.KEYCODE_1,
+ )
+ assertThat(shortcuts).doesNotContain(hardwareShortcut)
+ }
+
+ companion object {
+ private const val TEST_DEVICE_ID = 1234
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
index 8466eab..6d22b49 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
@@ -87,6 +87,10 @@
key("Shift")
key("M")
}
+ contentDescription {
+ "${shortcutInfoWithRepeatedLabel.label}, " +
+ "Press key Meta plus H, or Meta plus L, or Shift plus M"
+ }
}
private val goHomeShortcutInfo =
@@ -109,6 +113,7 @@
key(R.drawable.ic_ksh_key_meta)
key("N")
}
+ contentDescription { "${standardShortcutInfo1.label}, Press key Meta plus N" }
}
private val customGoHomeShortcut =
@@ -119,6 +124,7 @@
key("A")
isCustom(true)
}
+ contentDescription { "Go to home screen, Press key Ctrl plus Alt plus A" }
}
private val standardShortcutInfo2 =
@@ -135,6 +141,7 @@
key("Shift")
key("Z")
}
+ contentDescription { "${standardShortcutInfo2.label}, Press key Alt plus Shift plus Z" }
}
private val standardShortcutInfo3 =
@@ -150,6 +157,7 @@
key("Ctrl")
key("J")
}
+ contentDescription { "${standardShortcutInfo3.label}, Press key Ctrl plus J" }
}
private val shortcutInfoWithUnsupportedModifiers =
@@ -205,6 +213,7 @@
key("Ctrl")
key("Space")
}
+ contentDescription { "Switch to next language, Press key Ctrl plus Space" }
}
private val switchToPreviousLanguageShortcut =
@@ -214,6 +223,9 @@
key("Shift")
key("Space")
}
+ contentDescription {
+ "Switch to previous language, Press key Ctrl plus Shift plus Space"
+ }
}
private val subCategoryForInputLanguageSwitchShortcuts =
@@ -292,6 +304,10 @@
key("A")
isCustom(true)
}
+ contentDescription {
+ "Go to home screen, Press key Ctrl plus Alt plus B, " +
+ "or Ctrl plus Alt plus A"
+ }
}
),
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt
index f7c7701..c3cedba 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt
@@ -333,6 +333,42 @@
}
}
+ @Test
+ fun categories_addsSameContentDescriptionForShortcutsOfSameType() {
+ testScope.runTest {
+ setCustomInputGestures(listOf(customInputGestureTypeHome))
+ systemShortcutsSource.setGroups(groupWithGoHomeShortcutInfo)
+ helper.showFromActivity()
+
+ val categories by collectLastValue(interactor.shortcutCategories)
+ val contentDescriptions =
+ categories?.flatMap {
+ it.subCategories.flatMap { it.shortcuts.map { it.contentDescription } }
+ }
+
+ assertThat(contentDescriptions)
+ .containsExactly(
+ "Go to home screen, Press key Ctrl plus Alt plus B, or Ctrl plus Alt plus A",
+ "Standard shortcut 3, Press key Ctrl plus J",
+ "Standard shortcut 2, Press key Alt plus Shift plus Z",
+ "Standard shortcut 1, Press key Meta plus N",
+ "Standard shortcut 1, Press key Meta plus N",
+ "Standard shortcut 2, Press key Alt plus Shift plus Z",
+ "Standard shortcut 3, Press key Ctrl plus J",
+ "Switch to next language, Press key Ctrl plus Space",
+ "Switch to previous language, Press key Ctrl plus Shift plus Space",
+ "Standard shortcut 1, Press key Meta plus N",
+ "Standard shortcut 2, Press key Alt plus Shift plus Z",
+ "Standard shortcut 3, Press key Ctrl plus J",
+ "Standard shortcut 3, Press key Ctrl plus J",
+ "Standard shortcut 2, Press key Alt plus Shift plus Z",
+ "Standard shortcut 1, Press key Meta plus N",
+ "Standard shortcut 2, Press key Alt plus Shift plus Z",
+ "Standard shortcut 1, Press key Meta plus N",
+ )
+ }
+ }
+
private fun setCustomInputGestures(customInputGestures: List<InputGestureData>) {
whenever(fakeInputManager.inputManager.getCustomInputGestures(/* filter= */ anyOrNull()))
.thenReturn(customInputGestures)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorTest.kt
index bd26e42..bef995f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorTest.kt
@@ -18,6 +18,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.widget.lockPatternUtils
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
@@ -33,6 +34,8 @@
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -52,14 +55,21 @@
testScope.runTest {
val values by collectValues(underTest.lockWhileAwakeEvents)
- underTest.onKeyguardServiceDoKeyguardTimeout(options = null)
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(true)
+ runCurrent()
+
+ kosmos.keyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout(
+ options = null
+ )
runCurrent()
assertThat(values)
.containsExactly(LockWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON)
advanceTimeBy(1000)
- underTest.onKeyguardServiceDoKeyguardTimeout(options = null)
+ kosmos.keyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout(
+ options = null
+ )
runCurrent()
assertThat(values)
@@ -69,8 +79,15 @@
)
}
+ /**
+ * We re-show keyguard when it's re-enabled, but only if it was originally showing when we
+ * disabled it.
+ *
+ * If it wasn't showing when originally disabled it, re-enabling it should do nothing (the
+ * keyguard will re-show next time we're locked).
+ */
@Test
- fun emitsWhenKeyguardEnabled_onlyIfShowingWhenDisabled() =
+ fun emitsWhenKeyguardReenabled_onlyIfShowingWhenDisabled() =
testScope.runTest {
val values by collectValues(underTest.lockWhileAwakeEvents)
@@ -98,4 +115,49 @@
assertThat(values).containsExactly(LockWhileAwakeReason.KEYGUARD_REENABLED)
}
+
+ /**
+ * Un-suppressing keyguard should never cause us to re-show. We'll re-show when we're next
+ * locked, even if we were showing when originally suppressed.
+ */
+ @Test
+ fun doesNotEmit_keyguardNoLongerSuppressed() =
+ testScope.runTest {
+ val values by collectValues(underTest.lockWhileAwakeEvents)
+
+ // Enable keyguard and then suppress it.
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(true)
+ whenever(kosmos.lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(true)
+ runCurrent()
+
+ assertEquals(0, values.size)
+
+ // Un-suppress keyguard.
+ whenever(kosmos.lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false)
+ runCurrent()
+
+ assertEquals(0, values.size)
+ }
+
+ /**
+ * Lockdown and lockNow() should not cause us to lock while awake if we are suppressed via adb.
+ */
+ @Test
+ fun doesNotEmit_fromLockdown_orFromLockNow_ifEnabledButSuppressed() =
+ testScope.runTest {
+ val values by collectValues(underTest.lockWhileAwakeEvents)
+
+ // Set keyguard enabled, but then disable lockscreen (suppress it).
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(true)
+ whenever(kosmos.lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(true)
+ runCurrent()
+
+ kosmos.keyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout(null)
+ runCurrent()
+
+ kosmos.biometricSettingsRepository.setIsUserInLockdown(true)
+ runCurrent()
+
+ assertEquals(0, values.size)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt
index 7e249e8..ead151e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt
@@ -87,9 +87,9 @@
assertEquals(
listOf(
- false, // Defaults to false.
+ false // Defaults to false.
),
- canWake
+ canWake,
)
repository.setKeyguardEnabled(false)
@@ -100,33 +100,26 @@
false, // Default to false.
true, // True now that keyguard service is disabled
),
- canWake
+ canWake,
)
repository.setKeyguardEnabled(true)
runCurrent()
- assertEquals(
- listOf(
- false,
- true,
- false,
- ),
- canWake
- )
+ assertEquals(listOf(false, true, false), canWake)
}
@Test
@EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
- fun testCanWakeDirectlyToGone_lockscreenDisabledThenEnabled() =
+ fun testCanWakeDirectlyToGone_lockscreenDisabledThenEnabled_onlyAfterWakefulnessChange() =
testScope.runTest {
val canWake by collectValues(underTest.canWakeDirectlyToGone)
assertEquals(
listOf(
- false, // Defaults to false.
+ false // Defaults to false.
),
- canWake
+ canWake,
)
whenever(lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(true)
@@ -136,9 +129,9 @@
listOf(
// Still false - isLockScreenDisabled only causes canWakeDirectlyToGone to
// update on the next wake/sleep event.
- false,
+ false
),
- canWake
+ canWake,
)
kosmos.powerInteractor.setAsleepForTest()
@@ -150,7 +143,7 @@
// True since we slept after setting isLockScreenDisabled=true
true,
),
- canWake
+ canWake,
)
kosmos.powerInteractor.setAwakeForTest()
@@ -159,25 +152,75 @@
kosmos.powerInteractor.setAsleepForTest()
runCurrent()
- assertEquals(
- listOf(
- false,
- true,
- ),
- canWake
- )
+ assertEquals(listOf(false, true), canWake)
whenever(lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false)
kosmos.powerInteractor.setAwakeForTest()
runCurrent()
+ assertEquals(listOf(false, true, false), canWake)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testCanWakeDirectlyToGone_lockscreenDisabledThenEnabled_lockNowEvent() =
+ testScope.runTest {
+ val canWake by collectValues(underTest.canWakeDirectlyToGone)
+
+ assertEquals(
+ listOf(
+ false // Defaults to false.
+ ),
+ canWake,
+ )
+
+ whenever(lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(true)
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ // Still false - isLockScreenDisabled only causes canWakeDirectlyToGone to
+ // update on the next wakefulness or lockNow event.
+ false
+ ),
+ canWake,
+ )
+
+ kosmos.keyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout(null)
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false,
+ // True when lockNow() called after setting isLockScreenDisabled=true
+ true,
+ ),
+ canWake,
+ )
+
+ whenever(lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false)
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false,
+ // Still true since no lockNow() calls made.
+ true,
+ ),
+ canWake,
+ )
+
+ kosmos.keyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout(null)
+ runCurrent()
+
assertEquals(
listOf(
false,
true,
+ // False again after the lockNow() call.
false,
),
- canWake
+ canWake,
)
}
@@ -189,9 +232,9 @@
assertEquals(
listOf(
- false, // Defaults to false.
+ false // Defaults to false.
),
- canWake
+ canWake,
)
repository.setBiometricUnlockState(BiometricUnlockMode.WAKE_AND_UNLOCK)
@@ -213,9 +256,9 @@
assertEquals(
listOf(
- false, // Defaults to false.
+ false // Defaults to false.
),
- canWake
+ canWake,
)
repository.setCanIgnoreAuthAndReturnToGone(true)
@@ -237,9 +280,9 @@
assertEquals(
listOf(
- false, // Defaults to false.
+ false // Defaults to false.
),
- canWake
+ canWake,
)
whenever(kosmos.devicePolicyManager.getMaximumTimeToLock(eq(null), anyInt()))
@@ -257,13 +300,7 @@
)
runCurrent()
- assertEquals(
- listOf(
- false,
- true,
- ),
- canWake
- )
+ assertEquals(listOf(false, true), canWake)
verify(kosmos.alarmManager)
.setExactAndAllowWhileIdle(
@@ -281,9 +318,9 @@
assertEquals(
listOf(
- false, // Defaults to false.
+ false // Defaults to false.
),
- canWake
+ canWake,
)
whenever(kosmos.devicePolicyManager.getMaximumTimeToLock(eq(null), anyInt()))
@@ -312,7 +349,7 @@
// Timed out, so we can ignore auth/return to GONE.
true,
),
- canWake
+ canWake,
)
verify(kosmos.alarmManager)
@@ -338,7 +375,7 @@
// alarm in flight that should be canceled.
false,
),
- canWake
+ canWake,
)
kosmos.powerInteractor.setAsleepForTest(
@@ -354,25 +391,17 @@
// Back to sleep.
true,
),
- canWake
+ canWake,
)
// Simulate the first sleep's alarm coming in.
lastRegisteredBroadcastReceiver?.onReceive(
kosmos.mockedContext,
- Intent("com.android.internal.policy.impl.PhoneWindowManager.DELAYED_KEYGUARD")
+ Intent("com.android.internal.policy.impl.PhoneWindowManager.DELAYED_KEYGUARD"),
)
runCurrent()
// It should not have any effect.
- assertEquals(
- listOf(
- false,
- true,
- false,
- true,
- ),
- canWake
- )
+ assertEquals(listOf(false, true, false, true), canWake)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
index 2905a73..555ba56 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
@@ -90,6 +90,7 @@
import com.android.systemui.assist.AssistManager;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavBarHelper;
import com.android.systemui.navigationbar.NavigationBarController;
@@ -116,7 +117,7 @@
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.phone.AutoHideController;
+import com.android.systemui.statusbar.phone.AutoHideControllerStore;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.LightBarTransitionsController;
@@ -147,6 +148,7 @@
@SmallTest
public class NavigationBarTest extends SysuiTestCase {
private static final int EXTERNAL_DISPLAY_ID = 2;
+ private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
private NavigationBar mNavigationBar;
private NavigationBar mExternalDisplayNavigationBar;
@@ -210,10 +212,6 @@
@Mock
private LightBarController.Factory mLightBarcontrollerFactory;
@Mock
- private AutoHideController mAutoHideController;
- @Mock
- private AutoHideController.Factory mAutoHideControllerFactory;
- @Mock
private WindowManager mWindowManager;
@Mock
private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
@@ -247,6 +245,8 @@
private DeviceConfigProxyFake mDeviceConfigProxyFake = new DeviceConfigProxyFake();
private TaskStackChangeListeners mTaskStackChangeListeners =
TaskStackChangeListeners.getTestInstance();
+ private final AutoHideControllerStore mAutoHideControllerStore =
+ mKosmos.getAutoHideControllerStore();
@Rule
public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck();
@@ -258,7 +258,6 @@
MockitoAnnotations.initMocks(this);
when(mLightBarcontrollerFactory.create(any(Context.class))).thenReturn(mLightBarController);
- when(mAutoHideControllerFactory.create(any(Context.class))).thenReturn(mAutoHideController);
when(mNavigationBarView.getHomeButton()).thenReturn(mHomeButton);
when(mNavigationBarView.getRecentsButton()).thenReturn(mRecentsButton);
when(mNavigationBarView.getAccessibilityButton()).thenReturn(mAccessibilityButton);
@@ -651,8 +650,7 @@
mNavBarHelper,
mLightBarController,
mLightBarcontrollerFactory,
- mAutoHideController,
- mAutoHideControllerFactory,
+ mAutoHideControllerStore,
Optional.of(mTelecomManager),
mInputMethodManager,
mDeadZone,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index d720e1a..0d8d57e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -178,7 +178,7 @@
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
+import com.android.systemui.statusbar.phone.ShadeTouchableRegionManager;
import com.android.systemui.statusbar.phone.TapAgainViewController;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.policy.CastController;
@@ -245,7 +245,7 @@
@Mock protected NotificationPanelView mView;
@Mock protected LayoutInflater mLayoutInflater;
@Mock protected DynamicPrivacyController mDynamicPrivacyController;
- @Mock protected StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
+ @Mock protected ShadeTouchableRegionManager mShadeTouchableRegionManager;
@Mock protected KeyguardStateController mKeyguardStateController;
@Mock protected DozeLog mDozeLog;
private final ShadeLogger mShadeLog = new ShadeLogger(logcatLogBuffer());
@@ -703,7 +703,7 @@
mMetricsLogger,
mShadeLog,
mConfigurationController,
- () -> flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager,
+ () -> flingAnimationUtilsBuilder, mShadeTouchableRegionManager,
mConversationNotificationManager, mMediaHierarchyManager,
mStatusBarKeyguardViewManager,
mGutsManager,
@@ -819,7 +819,7 @@
mLockscreenShadeTransitionController,
mNotificationShadeDepthController,
mShadeHeaderController,
- mStatusBarTouchableRegionManager,
+ mShadeTouchableRegionManager,
() -> mStatusBarLongPressGestureDetector,
mKeyguardStateController,
mKeyguardBypassController,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
index 61d4c99..b58c13c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
@@ -71,8 +71,8 @@
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.ShadeTouchableRegionManager;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
@@ -125,7 +125,7 @@
@Mock protected LockscreenShadeTransitionController mLockscreenShadeTransitionController;
@Mock protected NotificationShadeDepthController mNotificationShadeDepthController;
@Mock protected ShadeHeaderController mShadeHeaderController;
- @Mock protected StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
+ @Mock protected ShadeTouchableRegionManager mShadeTouchableRegionManager;
@Mock protected StatusBarLongPressGestureDetector mStatusBarLongPressGestureDetector;
@Mock protected DozeParameters mDozeParameters;
@Mock protected KeyguardStateController mKeyguardStateController;
@@ -250,7 +250,7 @@
mLockscreenShadeTransitionController,
mNotificationShadeDepthController,
mShadeHeaderController,
- mStatusBarTouchableRegionManager,
+ mShadeTouchableRegionManager,
() -> mStatusBarLongPressGestureDetector,
mKeyguardStateController,
mKeyguardBypassController,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt
new file mode 100644
index 0000000..0966759
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.shade.display.ShadeDisplayPolicy
+import com.android.systemui.shade.display.SpecificDisplayIdPolicy
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ShadeDisplaysRepositoryTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val defaultPolicy = SpecificDisplayIdPolicy(0)
+
+ private val shadeDisplaysRepository =
+ ShadeDisplaysRepositoryImpl(defaultPolicy, testScope.backgroundScope)
+
+ @Test
+ fun policy_changing_propagatedFromTheLatestPolicy() =
+ testScope.runTest {
+ val displayIds by collectValues(shadeDisplaysRepository.displayId)
+ val policy1 = MutablePolicy()
+ val policy2 = MutablePolicy()
+
+ assertThat(displayIds).containsExactly(0)
+
+ shadeDisplaysRepository.policy.value = policy1
+
+ policy1.sendDisplayId(1)
+
+ assertThat(displayIds).containsExactly(0, 1)
+
+ policy1.sendDisplayId(2)
+
+ assertThat(displayIds).containsExactly(0, 1, 2)
+
+ shadeDisplaysRepository.policy.value = policy2
+
+ assertThat(displayIds).containsExactly(0, 1, 2, 0)
+
+ policy1.sendDisplayId(4)
+
+ // Changes to the first policy don't affect the output now
+ assertThat(displayIds).containsExactly(0, 1, 2, 0)
+
+ policy2.sendDisplayId(5)
+
+ assertThat(displayIds).containsExactly(0, 1, 2, 0, 5)
+ }
+
+ private class MutablePolicy : ShadeDisplayPolicy {
+ fun sendDisplayId(id: Int) {
+ _displayId.value = id
+ }
+
+ private val _displayId = MutableStateFlow(0)
+ override val name: String
+ get() = "mutable_policy"
+
+ override val displayId: StateFlow<Int>
+ get() = _displayId
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePrimaryDisplayCommandTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePrimaryDisplayCommandTest.kt
index af01547..d584dc9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePrimaryDisplayCommandTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePrimaryDisplayCommandTest.kt
@@ -23,12 +23,17 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.shade.ShadePrimaryDisplayCommand
+import com.android.systemui.shade.display.ShadeDisplayPolicy
import com.android.systemui.statusbar.commandline.commandRegistry
import com.android.systemui.testKosmos
+import com.google.common.truth.StringSubject
import com.google.common.truth.Truth.assertThat
import java.io.PrintWriter
import java.io.StringWriter
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -37,15 +42,26 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class ShadePrimaryDisplayCommandTest : SysuiTestCase() {
- private val kosmos = testKosmos()
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
private val testScope = kosmos.testScope
private val commandRegistry = kosmos.commandRegistry
private val displayRepository = kosmos.displayRepository
- private val shadeDisplaysRepository = ShadeDisplaysRepositoryImpl()
+ private val defaultPolicy = kosmos.defaultShadeDisplayPolicy
+ private val policy1 = makePolicy("policy_1")
+ private val shadeDisplaysRepository = kosmos.shadeDisplaysRepository
private val pw = PrintWriter(StringWriter())
+ private val policies =
+ setOf(defaultPolicy, policy1, makePolicy("policy_2"), makePolicy("policy_3"))
+
private val underTest =
- ShadePrimaryDisplayCommand(commandRegistry, displayRepository, shadeDisplaysRepository)
+ ShadePrimaryDisplayCommand(
+ commandRegistry,
+ displayRepository,
+ shadeDisplaysRepository,
+ policies,
+ defaultPolicy,
+ )
@Before
fun setUp() {
@@ -96,4 +112,41 @@
assertThat(displayId).isEqualTo(newDisplayId)
}
+
+ @Test
+ fun policies_listsAllPolicies() =
+ testScope.runTest {
+ val stringWriter = StringWriter()
+ commandRegistry.onShellCommand(
+ PrintWriter(stringWriter),
+ arrayOf("shade_display_override", "policies"),
+ )
+ val result = stringWriter.toString()
+
+ assertThat(result).containsAllIn(policies.map { it.name })
+ }
+
+ @Test
+ fun policies_setsSpecificPolicy() =
+ testScope.runTest {
+ val policy by collectLastValue(shadeDisplaysRepository.policy)
+
+ commandRegistry.onShellCommand(pw, arrayOf("shade_display_override", policy1.name))
+
+ assertThat(policy!!.name).isEqualTo(policy1.name)
+ }
+
+ private fun makePolicy(policyName: String): ShadeDisplayPolicy {
+ return object : ShadeDisplayPolicy {
+ override val name: String
+ get() = policyName
+
+ override val displayId: StateFlow<Int>
+ get() = MutableStateFlow(0)
+ }
+ }
+}
+
+private fun StringSubject.containsAllIn(strings: List<String>) {
+ strings.forEach { contains(it) }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/AnyExternalShadeDisplayPolicyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/AnyExternalShadeDisplayPolicyTest.kt
new file mode 100644
index 0000000..4d4efd1
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/AnyExternalShadeDisplayPolicyTest.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.display
+
+import android.view.Display
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.display.data.repository.display
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.test.runTest
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AnyExternalShadeDisplayPolicyTest : SysuiTestCase() {
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+ private val testScope = kosmos.testScope
+ private val displayRepository = kosmos.displayRepository
+ val underTest = AnyExternalShadeDisplayPolicy(displayRepository, testScope.backgroundScope)
+
+ @Test
+ fun displayId_ignoresUnwantedTypes() =
+ testScope.runTest {
+ val displayId by collectLastValue(underTest.displayId)
+
+ displayRepository.addDisplays(
+ display(id = 0, type = Display.TYPE_INTERNAL),
+ display(id = 1, type = Display.TYPE_UNKNOWN),
+ display(id = 2, type = Display.TYPE_VIRTUAL),
+ display(id = 3, type = Display.TYPE_EXTERNAL),
+ )
+
+ assertThat(displayId).isEqualTo(3)
+ }
+
+ @Test
+ fun displayId_onceRemoved_goesToNextDisplay() =
+ testScope.runTest {
+ val displayId by collectLastValue(underTest.displayId)
+
+ displayRepository.addDisplays(
+ display(id = 0, type = Display.TYPE_INTERNAL),
+ display(id = 2, type = Display.TYPE_EXTERNAL),
+ display(id = 3, type = Display.TYPE_EXTERNAL),
+ )
+
+ assertThat(displayId).isEqualTo(2)
+
+ displayRepository.removeDisplay(2)
+
+ assertThat(displayId).isEqualTo(3)
+ }
+
+ @Test
+ fun displayId_onlyDefaultDisplay_defaultDisplay() =
+ testScope.runTest {
+ val displayId by collectLastValue(underTest.displayId)
+
+ displayRepository.addDisplays(display(id = 0, type = Display.TYPE_INTERNAL))
+
+ assertThat(displayId).isEqualTo(0)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt
index 016a24a..982c51b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt
@@ -17,7 +17,6 @@
package com.android.systemui.shade.domain.interactor
import android.content.Context
-import android.content.MutableContextWrapper
import android.content.res.Configuration
import android.content.res.Resources
import android.view.Display
@@ -30,7 +29,6 @@
import com.android.systemui.display.shared.model.DisplayWindowProperties
import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.shade.data.repository.FakeShadeDisplayRepository
-import com.android.systemui.statusbar.phone.ConfigurationForwarder
import java.util.Optional
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
@@ -39,6 +37,7 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito.inOrder
import org.mockito.Mockito.verify
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
@@ -53,13 +52,10 @@
private val shadeRootview = mock<WindowRootView>()
private val positionRepository = FakeShadeDisplayRepository()
- private val defaultContext = mock<Context>()
- private val secondaryContext = mock<Context>()
+ private val shadeContext = mock<Context>()
private val contextStore = FakeDisplayWindowPropertiesRepository()
private val testScope = TestScope(UnconfinedTestDispatcher())
- private val configurationForwarder = mock<ConfigurationForwarder>()
- private val defaultWm = mock<WindowManager>()
- private val secondaryWm = mock<WindowManager>()
+ private val shadeWm = mock<WindowManager>()
private val resources = mock<Resources>()
private val configuration = mock<Configuration>()
private val display = mock<Display>()
@@ -68,11 +64,9 @@
ShadeDisplaysInteractor(
Optional.of(shadeRootview),
positionRepository,
- MutableContextWrapper(defaultContext),
- resources,
- contextStore,
+ shadeContext,
+ shadeWm,
testScope.backgroundScope,
- configurationForwarder,
testScope.backgroundScope.coroutineContext,
)
@@ -83,28 +77,15 @@
whenever(resources.configuration).thenReturn(configuration)
- whenever(defaultContext.displayId).thenReturn(0)
- whenever(defaultContext.getSystemService(any())).thenReturn(defaultWm)
- whenever(defaultContext.resources).thenReturn(resources)
+ whenever(shadeContext.displayId).thenReturn(0)
+ whenever(shadeContext.getSystemService(any())).thenReturn(shadeWm)
+ whenever(shadeContext.resources).thenReturn(resources)
contextStore.insert(
DisplayWindowProperties(
displayId = 0,
windowType = TYPE_NOTIFICATION_SHADE,
- context = defaultContext,
- windowManager = defaultWm,
- layoutInflater = mock(),
- )
- )
-
- whenever(secondaryContext.displayId).thenReturn(1)
- whenever(secondaryContext.getSystemService(any())).thenReturn(secondaryWm)
- whenever(secondaryContext.resources).thenReturn(resources)
- contextStore.insert(
- DisplayWindowProperties(
- displayId = 1,
- windowType = TYPE_NOTIFICATION_SHADE,
- context = secondaryContext,
- windowManager = secondaryWm,
+ context = shadeContext,
+ windowManager = shadeWm,
layoutInflater = mock(),
)
)
@@ -117,8 +98,7 @@
interactor.start()
testScope.advanceUntilIdle()
- verifyNoMoreInteractions(defaultWm)
- verifyNoMoreInteractions(secondaryWm)
+ verifyNoMoreInteractions(shadeWm)
}
@Test
@@ -127,8 +107,10 @@
positionRepository.setDisplayId(1)
interactor.start()
- verify(defaultWm).removeView(eq(shadeRootview))
- verify(secondaryWm).addView(eq(shadeRootview), any())
+ inOrder(shadeWm).apply {
+ verify(shadeWm).removeView(eq(shadeRootview))
+ verify(shadeWm).addView(eq(shadeRootview), any())
+ }
}
@Test
@@ -139,18 +121,9 @@
positionRepository.setDisplayId(1)
- verify(defaultWm).removeView(eq(shadeRootview))
- verify(secondaryWm).addView(eq(shadeRootview), any())
- }
-
- @Test
- fun start_shadePositionChanges_newConfigPropagated() {
- whenever(display.displayId).thenReturn(0)
- positionRepository.setDisplayId(0)
- interactor.start()
-
- positionRepository.setDisplayId(1)
-
- verify(configurationForwarder).onConfigurationChanged(eq(configuration))
+ inOrder(shadeWm).apply {
+ verify(shadeWm).removeView(eq(shadeRootview))
+ verify(shadeWm).addView(eq(shadeRootview), any())
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginActionManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/plugins/PluginActionManagerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginActionManagerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shared/plugins/PluginActionManagerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index da0029f..32a9f54 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -48,7 +48,6 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyObject;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
@@ -1633,7 +1632,7 @@
} else {
verify(mRotateTextViewController).hideIndication(type);
verify(mRotateTextViewController, never()).updateIndication(eq(type),
- anyObject(), anyBoolean());
+ any(), anyBoolean());
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt
new file mode 100644
index 0000000..7fed47a
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.notification.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.activity.data.repository.activityManagerRepository
+import com.android.systemui.activity.data.repository.fake
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SingleNotificationChipInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+ val factory = kosmos.singleNotificationChipInteractorFactory
+
+ @Test
+ fun notificationChip_startsWithStartingModel() =
+ kosmos.runTest {
+ val icon = mock<StatusBarIconView>()
+ val startingNotif = activeNotificationModel(key = "notif1", statusBarChipIcon = icon)
+
+ val underTest = factory.create(startingNotif)
+
+ val latest by collectLastValue(underTest.notificationChip)
+
+ assertThat(latest!!.key).isEqualTo("notif1")
+ assertThat(latest!!.statusBarChipIconView).isEqualTo(icon)
+ }
+
+ @Test
+ fun notificationChip_updatesAfterSet() =
+ kosmos.runTest {
+ val originalIconView = mock<StatusBarIconView>()
+ val underTest =
+ factory.create(
+ activeNotificationModel(key = "notif1", statusBarChipIcon = originalIconView)
+ )
+
+ val latest by collectLastValue(underTest.notificationChip)
+
+ val newIconView = mock<StatusBarIconView>()
+ underTest.setNotification(
+ activeNotificationModel(key = "notif1", statusBarChipIcon = newIconView)
+ )
+
+ assertThat(latest!!.key).isEqualTo("notif1")
+ assertThat(latest!!.statusBarChipIconView).isEqualTo(newIconView)
+ }
+
+ @Test
+ fun notificationChip_ignoresSetWithDifferentKey() =
+ kosmos.runTest {
+ val originalIconView = mock<StatusBarIconView>()
+ val underTest =
+ factory.create(
+ activeNotificationModel(key = "notif1", statusBarChipIcon = originalIconView)
+ )
+
+ val latest by collectLastValue(underTest.notificationChip)
+
+ val newIconView = mock<StatusBarIconView>()
+ underTest.setNotification(
+ activeNotificationModel(key = "other_notif", statusBarChipIcon = newIconView)
+ )
+
+ assertThat(latest!!.key).isEqualTo("notif1")
+ assertThat(latest!!.statusBarChipIconView).isEqualTo(originalIconView)
+ }
+
+ @Test
+ fun notificationChip_missingStatusBarIconChipView_inConstructor_emitsNull() =
+ kosmos.runTest {
+ val underTest =
+ factory.create(activeNotificationModel(key = "notif1", statusBarChipIcon = null))
+
+ val latest by collectLastValue(underTest.notificationChip)
+
+ assertThat(latest).isNull()
+ }
+
+ @Test
+ fun notificationChip_missingStatusBarIconChipView_inSet_emitsNull() =
+ kosmos.runTest {
+ val startingNotif = activeNotificationModel(key = "notif1", statusBarChipIcon = mock())
+ val underTest = factory.create(startingNotif)
+ val latest by collectLastValue(underTest.notificationChip)
+ assertThat(latest).isNotNull()
+
+ underTest.setNotification(
+ activeNotificationModel(key = "notif1", statusBarChipIcon = null)
+ )
+
+ assertThat(latest).isNull()
+ }
+
+ @Test
+ fun notificationChip_appIsVisibleOnCreation_emitsNull() =
+ kosmos.runTest {
+ activityManagerRepository.fake.startingIsAppVisibleValue = true
+
+ val underTest =
+ factory.create(
+ activeNotificationModel(key = "notif", uid = UID, statusBarChipIcon = mock())
+ )
+
+ val latest by collectLastValue(underTest.notificationChip)
+
+ assertThat(latest).isNull()
+ }
+
+ @Test
+ fun notificationChip_appNotVisibleOnCreation_emitsValue() =
+ kosmos.runTest {
+ activityManagerRepository.fake.startingIsAppVisibleValue = false
+
+ val underTest =
+ factory.create(
+ activeNotificationModel(key = "notif", uid = UID, statusBarChipIcon = mock())
+ )
+
+ val latest by collectLastValue(underTest.notificationChip)
+
+ assertThat(latest).isNotNull()
+ }
+
+ @Test
+ fun notificationChip_hidesWhenAppIsVisible() =
+ kosmos.runTest {
+ val underTest =
+ factory.create(
+ activeNotificationModel(key = "notif", uid = UID, statusBarChipIcon = mock())
+ )
+
+ val latest by collectLastValue(underTest.notificationChip)
+
+ activityManagerRepository.fake.setIsAppVisible(UID, false)
+ assertThat(latest).isNotNull()
+
+ activityManagerRepository.fake.setIsAppVisible(UID, true)
+ assertThat(latest).isNull()
+
+ activityManagerRepository.fake.setIsAppVisible(UID, false)
+ assertThat(latest).isNotNull()
+ }
+
+ // Note: This test is theoretically impossible because the notification key should contain the
+ // UID, so if the UID changes then the key would also change and a new interactor would be
+ // created. But, test it just in case.
+ @Test
+ fun notificationChip_updatedUid_rechecksAppVisibility_oldObserverUnregistered() =
+ kosmos.runTest {
+ activityManagerRepository.fake.startingIsAppVisibleValue = false
+
+ val hiddenUid = 100
+ val shownUid = 101
+
+ val underTest =
+ factory.create(
+ activeNotificationModel(
+ key = "notif",
+ uid = hiddenUid,
+ statusBarChipIcon = mock(),
+ )
+ )
+ val latest by collectLastValue(underTest.notificationChip)
+ assertThat(latest).isNotNull()
+
+ // WHEN the notif gets a new UID that starts as visible
+ activityManagerRepository.fake.startingIsAppVisibleValue = true
+ underTest.setNotification(
+ activeNotificationModel(key = "notif", uid = shownUid, statusBarChipIcon = mock())
+ )
+
+ // THEN we re-fetch the app visibility state with the new UID, and since that UID is
+ // visible, we hide the chip
+ assertThat(latest).isNull()
+ }
+
+ companion object {
+ private const val UID = 885
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt
index 19ed6a5..702e101 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt
@@ -16,30 +16,277 @@
package com.android.systemui.statusbar.chips.notification.domain.interactor
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
+import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
+import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.test.runTest
import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
@SmallTest
@RunWith(AndroidJUnit4::class)
-@EnableFlags(StatusBarNotifChips.FLAG_NAME)
class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
private val kosmos = testKosmos().useUnconfinedTestDispatcher()
private val testScope = kosmos.testScope
+ private val activeNotificationListRepository = kosmos.activeNotificationListRepository
- private val underTest = kosmos.statusBarNotificationChipsInteractor
+ private val underTest by lazy {
+ kosmos.statusBarNotificationChipsInteractor.also { it.start() }
+ }
@Test
+ @DisableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun notificationChips_flagOff_noNotifs() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.notificationChips)
+
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = mock<StatusBarIconView>(),
+ isPromoted = true,
+ )
+ )
+ )
+
+ assertThat(latest).isEmpty()
+ }
+
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun notificationChips_noNotifs_empty() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.notificationChips)
+
+ setNotifs(emptyList())
+
+ assertThat(latest).isEmpty()
+ }
+
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun notificationChips_notifMissingStatusBarChipIconView_empty() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.notificationChips)
+
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = null,
+ isPromoted = true,
+ )
+ )
+ )
+
+ assertThat(latest).isEmpty()
+ }
+
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun notificationChips_onePromotedNotif_statusBarIconViewMatches() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.notificationChips)
+
+ val icon = mock<StatusBarIconView>()
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = icon,
+ isPromoted = true,
+ )
+ )
+ )
+
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0].key).isEqualTo("notif")
+ assertThat(latest!![0].statusBarChipIconView).isEqualTo(icon)
+ }
+
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun notificationChips_onlyForPromotedNotifs() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.notificationChips)
+
+ val firstIcon = mock<StatusBarIconView>()
+ val secondIcon = mock<StatusBarIconView>()
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif1",
+ statusBarChipIcon = firstIcon,
+ isPromoted = true,
+ ),
+ activeNotificationModel(
+ key = "notif2",
+ statusBarChipIcon = secondIcon,
+ isPromoted = true,
+ ),
+ activeNotificationModel(
+ key = "notif3",
+ statusBarChipIcon = mock<StatusBarIconView>(),
+ isPromoted = false,
+ ),
+ )
+ )
+
+ assertThat(latest).hasSize(2)
+ assertThat(latest!![0].key).isEqualTo("notif1")
+ assertThat(latest!![0].statusBarChipIconView).isEqualTo(firstIcon)
+ assertThat(latest!![1].key).isEqualTo("notif2")
+ assertThat(latest!![1].statusBarChipIconView).isEqualTo(secondIcon)
+ }
+
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun notificationChips_notifUpdatesGoThrough() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.notificationChips)
+
+ val firstIcon = mock<StatusBarIconView>()
+ val secondIcon = mock<StatusBarIconView>()
+ val thirdIcon = mock<StatusBarIconView>()
+
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = firstIcon,
+ isPromoted = true,
+ )
+ )
+ )
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0].key).isEqualTo("notif")
+ assertThat(latest!![0].statusBarChipIconView).isEqualTo(firstIcon)
+
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = secondIcon,
+ isPromoted = true,
+ )
+ )
+ )
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0].key).isEqualTo("notif")
+ assertThat(latest!![0].statusBarChipIconView).isEqualTo(secondIcon)
+
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = thirdIcon,
+ isPromoted = true,
+ )
+ )
+ )
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0].key).isEqualTo("notif")
+ assertThat(latest!![0].statusBarChipIconView).isEqualTo(thirdIcon)
+ }
+
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun notificationChips_promotedNotifDisappearsThenReappears() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.notificationChips)
+
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = mock(),
+ isPromoted = true,
+ )
+ )
+ )
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0].key).isEqualTo("notif")
+
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = mock(),
+ isPromoted = false,
+ )
+ )
+ )
+ assertThat(latest).isEmpty()
+
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = mock(),
+ isPromoted = true,
+ )
+ )
+ )
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0].key).isEqualTo("notif")
+ }
+
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun notificationChips_notifChangesKey() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.notificationChips)
+
+ val firstIcon = mock<StatusBarIconView>()
+ val secondIcon = mock<StatusBarIconView>()
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif|uid1",
+ statusBarChipIcon = firstIcon,
+ isPromoted = true,
+ )
+ )
+ )
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0].key).isEqualTo("notif|uid1")
+ assertThat(latest!![0].statusBarChipIconView).isEqualTo(firstIcon)
+
+ // WHEN a notification changes UID, which is a key change
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif|uid2",
+ statusBarChipIcon = secondIcon,
+ isPromoted = true,
+ )
+ )
+ )
+
+ // THEN we correctly update
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0].key).isEqualTo("notif|uid2")
+ assertThat(latest!![0].statusBarChipIconView).isEqualTo(secondIcon)
+ }
+
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
fun onPromotedNotificationChipTapped_emitsKeys() =
testScope.runTest {
val latest by collectValues(underTest.promotedNotificationChipTapEvent)
@@ -56,6 +303,7 @@
}
@Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
fun onPromotedNotificationChipTapped_sameKeyTwice_emitsTwice() =
testScope.runTest {
val latest by collectValues(underTest.promotedNotificationChipTapEvent)
@@ -67,4 +315,11 @@
assertThat(latest[0]).isEqualTo("fakeKey")
assertThat(latest[1]).isEqualTo("fakeKey")
}
+
+ private fun setNotifs(notifs: List<ActiveNotificationModel>) {
+ activeNotificationListRepository.activeNotifications.value =
+ ActiveNotificationsStore.Builder()
+ .apply { notifs.forEach { addIndividualNotif(it) } }
+ .build()
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
index 1b41329..16376c5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
@@ -22,7 +22,9 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
@@ -34,26 +36,28 @@
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
+import org.junit.Before
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
@SmallTest
@RunWith(AndroidJUnit4::class)
-@OptIn(ExperimentalCoroutinesApi::class)
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
class NotifChipsViewModelTest : SysuiTestCase() {
- private val kosmos = testKosmos()
- private val testScope = kosmos.testScope
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
private val activeNotificationListRepository = kosmos.activeNotificationListRepository
- private val underTest = kosmos.notifChipsViewModel
+ private val underTest by lazy { kosmos.notifChipsViewModel }
+
+ @Before
+ fun setUp() {
+ kosmos.statusBarNotificationChipsInteractor.start()
+ }
@Test
fun chips_noNotifs_empty() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chips)
setNotifs(emptyList())
@@ -63,7 +67,7 @@
@Test
fun chips_notifMissingStatusBarChipIconView_empty() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chips)
setNotifs(
@@ -81,7 +85,7 @@
@Test
fun chips_onePromotedNotif_statusBarIconViewMatches() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chips)
val icon = mock<StatusBarIconView>()
@@ -103,7 +107,7 @@
@Test
fun chips_onlyForPromotedNotifs() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chips)
val firstIcon = mock<StatusBarIconView>()
@@ -135,7 +139,7 @@
@Test
fun chips_clickingChipNotifiesInteractor() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chips)
val latestChipTap by
collectLastValue(
@@ -163,7 +167,6 @@
ActiveNotificationsStore.Builder()
.apply { notifs.forEach { addIndividualNotif(it) } }
.build()
- testScope.runCurrent()
}
companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
index 25d5ce5..eb0978e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
@@ -27,6 +27,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
@@ -38,6 +39,7 @@
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection
import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.DemoNotifChipViewModelTest.Companion.addDemoNotifChip
import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.demoNotifChipViewModel
+import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.chips.notification.ui.viewmodel.NotifChipsViewModelTest.Companion.assertIsNotifChip
import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
@@ -67,6 +69,7 @@
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
@@ -79,7 +82,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
- private val kosmos = testKosmos()
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
private val testScope = kosmos.testScope
private val systemClock = kosmos.fakeSystemClock
private val commandRegistry = kosmos.commandRegistry
@@ -103,12 +106,13 @@
.thenReturn(chipBackgroundView)
}
- private val underTest = kosmos.ongoingActivityChipsViewModel
+ private val underTest by lazy { kosmos.ongoingActivityChipsViewModel }
@Before
fun setUp() {
setUpPackageManagerForMediaProjection(kosmos)
kosmos.demoNotifChipViewModel.start()
+ kosmos.statusBarNotificationChipsInteractor.start()
val icon =
BitmapDrawable(
context.resources,
@@ -616,6 +620,7 @@
}
@Test
+ @Ignore("b/364653005") // We'll need to re-do the animation story when we implement RON chips
fun primaryChip_screenRecordStoppedViaDialog_chipHiddenWithoutAnimation() =
testScope.runTest {
screenRecordState.value = ScreenRecordModel.Recording
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
index e5d2cf6..1d7f257 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
@@ -83,7 +83,9 @@
class HeadsUpCoordinatorTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val statusBarNotificationChipsInteractor = kosmos.statusBarNotificationChipsInteractor
+ private val statusBarNotificationChipsInteractor by lazy {
+ kosmos.statusBarNotificationChipsInteractor
+ }
private val notifCollection = kosmos.mockNotifCollection
private lateinit var coordinator: HeadsUpCoordinator
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ShadeTouchableRegionManagerTest.kt
similarity index 97%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ShadeTouchableRegionManagerTest.kt
index a008588..d82cb86 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ShadeTouchableRegionManagerTest.kt
@@ -41,12 +41,12 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
-class StatusBarTouchableRegionManagerTest : SysuiTestCase() {
+class ShadeTouchableRegionManagerTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val sceneRepository = kosmos.sceneContainerRepository
- private val underTest by Lazy { kosmos.statusBarTouchableRegionManager }
+ private val underTest by Lazy { kosmos.shadeTouchableRegionManager }
@Test
@EnableSceneContainer
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/data/repository/KeyguardBypassRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/data/repository/KeyguardBypassRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/data/repository/KeyguardBypassRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/data/repository/KeyguardBypassRepositoryTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt
index 7d3ed92..7aa389a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt
@@ -20,10 +20,12 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.testKosmos
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.Finished
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted
import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
+import com.android.systemui.touchpad.ui.gesture.fakeVelocityTracker
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -33,12 +35,24 @@
@RunWith(AndroidJUnit4::class)
class HomeGestureRecognizerTest : SysuiTestCase() {
+ companion object {
+ const val THRESHOLD_VELOCITY_PX_PER_MS = 1f
+ const val SLOW = THRESHOLD_VELOCITY_PX_PER_MS - 0.01f
+ const val FAST = THRESHOLD_VELOCITY_PX_PER_MS + 0.01f
+ }
+
private var gestureState: GestureState = GestureState.NotStarted
+ private var velocityTracker = testKosmos().fakeVelocityTracker
private val gestureRecognizer =
- HomeGestureRecognizer(gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt())
+ HomeGestureRecognizer(
+ gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt(),
+ velocityThresholdPxPerMs = THRESHOLD_VELOCITY_PX_PER_MS,
+ velocityTracker = velocityTracker,
+ )
@Before
fun before() {
+ velocityTracker.setVelocity(Velocity(FAST))
gestureRecognizer.addGestureStateCallback { gestureState = it }
}
@@ -48,6 +62,12 @@
}
@Test
+ fun doesntTriggerGestureFinished_onGestureSpeedTooSlow() {
+ velocityTracker.setVelocity(Velocity(SLOW))
+ assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = NotStarted)
+ }
+
+ @Test
fun triggersGestureProgressForThreeFingerGestureStarted() {
assertStateAfterEvents(
events = ThreeFingerGesture.startEvents(x = 0f, y = 0f),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt
index c5c0d59..cb74e65 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt
@@ -17,21 +17,19 @@
package com.android.systemui.touchpad.tutorial.ui.gesture
import android.view.MotionEvent
-import androidx.compose.ui.input.pointer.util.VelocityTracker1D
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.testKosmos
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.Finished
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted
import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
+import com.android.systemui.touchpad.ui.gesture.fakeVelocityTracker
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.kotlin.doReturn
-import org.mockito.kotlin.mock
-import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -39,26 +37,23 @@
companion object {
const val THRESHOLD_VELOCITY_PX_PER_MS = 0.1f
- // multiply by 1000 to get px/ms instead of px/s which is unit used by velocity tracker
- const val SLOW = THRESHOLD_VELOCITY_PX_PER_MS * 1000 - 1
- const val FAST = THRESHOLD_VELOCITY_PX_PER_MS * 1000 + 1
+ const val SLOW = THRESHOLD_VELOCITY_PX_PER_MS - 0.01f
+ const val FAST = THRESHOLD_VELOCITY_PX_PER_MS + 0.01f
}
+ private var velocityTracker = testKosmos().fakeVelocityTracker
+
private var gestureState: GestureState = GestureState.NotStarted
- private val velocityTracker1D =
- mock<VelocityTracker1D> {
- // by default return correct speed for the gesture - as if pointer is slowing down
- on { calculateVelocity() } doReturn SLOW
- }
private val gestureRecognizer =
RecentAppsGestureRecognizer(
gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt(),
velocityThresholdPxPerMs = THRESHOLD_VELOCITY_PX_PER_MS,
- velocityTracker = VerticalVelocityTracker(velocityTracker1D),
+ velocityTracker = velocityTracker,
)
@Before
fun before() {
+ velocityTracker.setVelocity(Velocity(SLOW))
gestureRecognizer.addGestureStateCallback { gestureState = it }
}
@@ -69,7 +64,7 @@
@Test
fun doesntTriggerGestureFinished_onGestureSpeedTooHigh() {
- whenever(velocityTracker1D.calculateVelocity()).thenReturn(FAST)
+ velocityTracker.setVelocity(Velocity(FAST))
assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = NotStarted)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLoggerTest.kt b/packages/SystemUI/multivalentTests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLoggerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLoggerTest.kt
rename to packages/SystemUI/multivalentTests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLoggerTest.kt
diff --git a/packages/SystemUI/res/layout/notification_2025_hybrid_conversation.xml b/packages/SystemUI/res/layout/notification_2025_hybrid_conversation.xml
index ff5d9d3..a338e4c 100644
--- a/packages/SystemUI/res/layout/notification_2025_hybrid_conversation.xml
+++ b/packages/SystemUI/res/layout/notification_2025_hybrid_conversation.xml
@@ -29,16 +29,18 @@
android:layout_height="@dimen/notification_2025_single_line_face_pile_size"
android:layout_marginEnd="8dp"
>
- <ImageView
+ <com.android.internal.widget.CachingIconView
android:id="@*android:id/conversation_icon"
android:layout_width="@dimen/notification_2025_single_line_avatar_size"
android:layout_height="@dimen/notification_2025_single_line_avatar_size"
android:layout_gravity="center_vertical|end"
+ android:background="@*android:drawable/notification_icon_circle"
+ android:clipToOutline="true"
/>
<ViewStub
android:id="@*android:id/conversation_face_pile"
- android:layout="@*android:layout/conversation_face_pile_layout"
+ android:layout="@*android:layout/notification_2025_conversation_face_pile_layout"
android:layout_width="@dimen/notification_2025_single_line_face_pile_size"
android:layout_height="@dimen/notification_2025_single_line_face_pile_size"
android:layout_gravity="center_vertical|end"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 411f36b..5270403 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1995,12 +1995,16 @@
<dimen name="backlight_indicator_step_large_radius">28dp</dimen>
<!-- Touchpad gestures tutorial-->
- <!-- This value is in unit of dp/ms
+ <!-- This value is in dp/ms.
TriggerSwipeUpTouchTracker (which is base for gesture tutorial implementation) uses value
of 0.5dp but from manual testing it's too high and doesn't really feel like it's forcing
slowing down. Also for tutorial it should be fine to lean to the side of being more strict
rather than not strict enough and not teaching user the proper gesture as a result.-->
<dimen name="touchpad_recent_apps_gesture_velocity_threshold">0.05dp</dimen>
+ <!-- This value is in dp/ms.
+ As above, it's not tied to system-wide value (defined in launcher's
+ quickstep_fling_threshold_speed) because for tutorial it's fine to be more strict. -->
+ <dimen name="touchpad_home_gesture_velocity_threshold">0.5dp</dimen>
<!-- Normal gesture threshold is system_gestures_distance_threshold but for tutorial we can
exaggerate gesture, which also works much better with live tracking -->
<dimen name="touchpad_tutorial_gestures_distance_threshold">48dp</dimen>
@@ -2086,7 +2090,7 @@
<dimen name="volume_dialog_floating_sliders_vertical_padding">10dp</dimen>
<dimen name="volume_dialog_floating_sliders_vertical_padding_negative">-10dp</dimen>
<dimen name="volume_dialog_floating_sliders_horizontal_padding">4dp</dimen>
- <dimen name="volume_dialog_button_size">48dp</dimen>
+ <dimen name="volume_dialog_button_size">40dp</dimen>
<dimen name="volume_dialog_slider_width">52dp</dimen>
<dimen name="volume_dialog_slider_height">254dp</dimen>
@@ -2094,7 +2098,7 @@
<dimen name="volume_dialog_background_square_corner_radius">12dp</dimen>
- <dimen name="volume_dialog_ringer_drawer_button_size">40dp</dimen>
+ <dimen name="volume_dialog_ringer_drawer_button_size">@dimen/volume_dialog_button_size</dimen>
<dimen name="volume_dialog_ringer_drawer_button_icon_radius">10dp</dimen>
<dimen name="volume_dialog_ringer_selected_button_background_radius">20dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 731c4ef..4bf67a1 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2287,6 +2287,14 @@
<string name="system_multitasking_replace">During split screen: replace an app from one to another</string>
<!-- User visible title for the keyboard shortcut that moves a focused task to a next display [CHAR LIMIT=70] -->
<string name="system_multitasking_move_to_next_display">Move active window between displays</string>
+ <!-- User visible title for the keyboard shortcut that snaps a task to the left in desktop mode [CHAR LIMIT=70] -->
+ <string name="system_desktop_mode_snap_left_window">Move window to the left</string>
+ <!-- User visible title for the keyboard shortcut that snaps a task to the right in desktop mode [CHAR LIMIT=70] -->
+ <string name="system_desktop_mode_snap_right_window">Move window to the right</string>
+ <!-- User visible title for the keyboard shortcut that toggles between maximizing and restoring a task's previous bounds in desktop mode [CHAR LIMIT=70] -->
+ <string name="system_desktop_mode_toggle_maximize_window">Maximize window</string>
+ <!-- User visible title for the keyboard shortcut that minimizes a task in desktop mode [CHAR LIMIT=70] -->
+ <string name="system_desktop_mode_minimize_window">Minimize window</string>
<!-- User visible title for the input keyboard shortcuts list. [CHAR LIMIT=70] -->
<string name="keyboard_shortcut_group_input">Input</string>
@@ -3816,6 +3824,14 @@
[CHAR LIMIT=NONE]
-->
<string name="shortcut_helper_key_combinations_or_separator">or</string>
+ <!-- Word that combines different possible key combinations of a shortcut. For example the
+ "Go to home screen" shortcut could be triggered using "ctrl" plus "h".
+ This is only used for accessibility content descriptions of the key combinations.
+ [CHAR LIMIT=NONE] -->
+ <string name="shortcut_helper_key_combinations_and_conjunction">plus</string>
+ <!-- Word that represents the forward slash character "/". To be used only in accessibility
+ content descriptions. [CHAR LIMIT=NONE] -->
+ <string name="shortcut_helper_key_combinations_forward_slash">forward slash</string>
<!-- Content description of the drag handle that allows to swipe to dismiss the shortcut helper.
The helper is a component that shows the user which keyboard shortcuts they can
use. The helper shows shortcuts in categories, which can be collapsed or expanded.
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 7d5cf23..3794e7b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -283,7 +283,7 @@
com.android.internal.R.integer.config_shortAnimTime));
updateDimensions();
- final Size windowFrameSize = restoreMagnificationWindowFrameSizeIfPossible();
+ final Size windowFrameSize = restoreMagnificationWindowFrameIndexAndSizeIfPossible();
setMagnificationFrame(windowFrameSize.getWidth(), windowFrameSize.getHeight(),
mWindowBounds.width() / 2, mWindowBounds.height() / 2);
computeBounceAnimationScale();
@@ -541,7 +541,7 @@
return false;
}
mWindowBounds.set(currentWindowBounds);
- final Size windowFrameSize = restoreMagnificationWindowFrameSizeIfPossible();
+ final Size windowFrameSize = restoreMagnificationWindowFrameIndexAndSizeIfPossible();
final float newCenterX = (getCenterX()) * mWindowBounds.width() / oldWindowBounds.width();
final float newCenterY = (getCenterY()) * mWindowBounds.height() / oldWindowBounds.height();
@@ -787,18 +787,6 @@
mMagnificationFrame.set(initX, initY, initX + width, initY + height);
}
- private Size restoreMagnificationWindowFrameSizeIfPossible() {
- if (Flags.saveAndRestoreMagnificationSettingsButtons()) {
- return restoreMagnificationWindowFrameIndexAndSizeIfPossible();
- }
-
- if (!mWindowMagnificationFrameSizePrefs.isPreferenceSavedForCurrentDensity()) {
- return getDefaultMagnificationWindowFrameSize();
- }
-
- return mWindowMagnificationFrameSizePrefs.getSizeForCurrentDensity();
- }
-
private Size restoreMagnificationWindowFrameIndexAndSizeIfPossible() {
if (!mWindowMagnificationFrameSizePrefs.isPreferenceSavedForCurrentDensity()) {
notifyWindowSizeRestored(MagnificationSize.DEFAULT);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefs.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefs.java
index ee36c6e..558c87c 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefs.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefs.java
@@ -22,8 +22,6 @@
import android.content.SharedPreferences;
import android.util.Size;
-import com.android.systemui.Flags;
-
/**
* Class to handle SharedPreference for window magnification size.
*/
@@ -52,14 +50,10 @@
* Saves the window frame size for current screen density.
*/
public void saveIndexAndSizeForCurrentDensity(int index, Size size) {
- if (Flags.saveAndRestoreMagnificationSettingsButtons()) {
- mWindowMagnificationSizePreferences.edit()
- .putString(getKey(),
- WindowMagnificationFrameSpec.serialize(index, size)).apply();
- } else {
- mWindowMagnificationSizePreferences.edit()
- .putString(getKey(), size.toString()).apply();
- }
+ mWindowMagnificationSizePreferences
+ .edit()
+ .putString(getKey(), WindowMagnificationFrameSpec.serialize(index, size))
+ .apply();
}
/**
@@ -91,13 +85,9 @@
* Gets the size preference for current screen density.
*/
public Size getSizeForCurrentDensity() {
- if (Flags.saveAndRestoreMagnificationSettingsButtons()) {
- return WindowMagnificationFrameSpec
- .deserialize(mWindowMagnificationSizePreferences.getString(getKey(), null))
- .getSize();
- } else {
- return Size.parseSize(mWindowMagnificationSizePreferences.getString(getKey(), null));
- }
+ return WindowMagnificationFrameSpec.deserialize(
+ mWindowMagnificationSizePreferences.getString(getKey(), null))
+ .getSize();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
index 2f0ca6e..9d9f569 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
@@ -59,7 +59,6 @@
import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
-import com.android.systemui.Flags;
import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView;
import com.android.systemui.res.R;
import com.android.systemui.util.settings.SecureSettings;
@@ -460,12 +459,8 @@
mAllowDiagonalScrollingView.setVisibility(View.VISIBLE);
mFullScreenButton.setVisibility(View.GONE);
if (selectedButtonIndex == MagnificationSize.FULLSCREEN) {
- if (Flags.saveAndRestoreMagnificationSettingsButtons()) {
- selectedButtonIndex =
- windowMagnificationFrameSizePrefs.getIndexForCurrentDensity();
- } else {
- selectedButtonIndex = MagnificationSize.CUSTOM;
- }
+ selectedButtonIndex =
+ windowMagnificationFrameSizePrefs.getIndexForCurrentDensity();
}
break;
@@ -482,10 +477,8 @@
} else { // mode = ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
mEditButton.setVisibility(View.VISIBLE);
mAllowDiagonalScrollingView.setVisibility(View.VISIBLE);
- if (Flags.saveAndRestoreMagnificationSettingsButtons()) {
- selectedButtonIndex =
- windowMagnificationFrameSizePrefs.getIndexForCurrentDensity();
- }
+ selectedButtonIndex =
+ windowMagnificationFrameSizePrefs.getIndexForCurrentDensity();
}
break;
@@ -581,13 +574,15 @@
|| (configDiff & ActivityInfo.CONFIG_ASSETS_PATHS) != 0
|| (configDiff & ActivityInfo.CONFIG_FONT_SCALE) != 0
|| (configDiff & ActivityInfo.CONFIG_LOCALE) != 0
- || (configDiff & ActivityInfo.CONFIG_DENSITY) != 0) {
+ || (configDiff & ActivityInfo.CONFIG_DENSITY) != 0
+ || (configDiff & ActivityInfo.CONFIG_FONT_WEIGHT_ADJUSTMENT) != 0) {
// We listen to following config changes to trigger layout inflation:
// CONFIG_UI_MODE: theme change
// CONFIG_ASSETS_PATHS: wallpaper change
// CONFIG_FONT_SCALE: font size change
// CONFIG_LOCALE: language change
// CONFIG_DENSITY: display size change
+ // CONFIG_FONT_WEIGHT_ADJUSTMENT: bold text setting change
mParams.width = getPanelWidth(mContext);
mParams.accessibilityTitle = getAccessibilityWindowTitle(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/activity/ActivityManagerModule.kt b/packages/SystemUI/src/com/android/systemui/activity/ActivityManagerModule.kt
new file mode 100644
index 0000000..db315e4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/activity/ActivityManagerModule.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.activity
+
+import com.android.systemui.activity.data.repository.ActivityManagerRepository
+import com.android.systemui.activity.data.repository.ActivityManagerRepositoryImpl
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface ActivityManagerModule {
+ @Binds
+ @SysUISingleton
+ fun activityManagerRepository(impl: ActivityManagerRepositoryImpl): ActivityManagerRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/activity/data/repository/ActivityManagerRepository.kt b/packages/SystemUI/src/com/android/systemui/activity/data/repository/ActivityManagerRepository.kt
new file mode 100644
index 0000000..94614b7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/activity/data/repository/ActivityManagerRepository.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.activity.data.repository
+
+import android.app.ActivityManager
+import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.log.core.Logger
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.onStart
+
+/** Repository for interfacing with [ActivityManager]. */
+interface ActivityManagerRepository {
+ /**
+ * Given a UID, creates a flow that emits true when the process with the given UID is visible to
+ * the user and false otherwise.
+ *
+ * @param identifyingLogTag a tag identifying who created this flow, used for logging.
+ */
+ fun createIsAppVisibleFlow(
+ creationUid: Int,
+ logger: Logger,
+ identifyingLogTag: String,
+ ): Flow<Boolean>
+}
+
+@SysUISingleton
+class ActivityManagerRepositoryImpl
+@Inject
+constructor(
+ @Background private val backgroundContext: CoroutineContext,
+ private val activityManager: ActivityManager,
+) : ActivityManagerRepository {
+ override fun createIsAppVisibleFlow(
+ creationUid: Int,
+ logger: Logger,
+ identifyingLogTag: String,
+ ): Flow<Boolean> {
+ return conflatedCallbackFlow {
+ val listener =
+ object : ActivityManager.OnUidImportanceListener {
+ override fun onUidImportance(uid: Int, importance: Int) {
+ if (uid != creationUid) {
+ return
+ }
+ val isAppVisible = isAppVisibleToUser(importance)
+ logger.d({
+ "$str1: #onUidImportance. importance=$int1, isAppVisible=$bool1"
+ }) {
+ str1 = identifyingLogTag
+ int1 = importance
+ bool1 = isAppVisible
+ }
+ trySend(isAppVisible)
+ }
+ }
+ try {
+ // TODO(b/286258140): Replace this with the #addOnUidImportanceListener
+ // overload that filters to certain UIDs.
+ activityManager.addOnUidImportanceListener(listener, IMPORTANCE_CUTPOINT)
+ } catch (e: SecurityException) {
+ logger.e({ "$str1: Security exception on #addOnUidImportanceListener" }, e) {
+ str1 = identifyingLogTag
+ }
+ }
+
+ awaitClose { activityManager.removeOnUidImportanceListener(listener) }
+ }
+ .distinctUntilChanged()
+ .onStart {
+ try {
+ val isVisibleOnStart =
+ isAppVisibleToUser(activityManager.getUidImportance(creationUid))
+ logger.d({ "$str1: Starting UID observation. isAppVisible=$bool1" }) {
+ str1 = identifyingLogTag
+ bool1 = isVisibleOnStart
+ }
+ emit(isVisibleOnStart)
+ } catch (e: SecurityException) {
+ logger.e({ "$str1: Security exception on #getUidImportance" }, e) {
+ str1 = identifyingLogTag
+ }
+ emit(false)
+ }
+ }
+ .flowOn(backgroundContext)
+ }
+
+ /** Returns true if the given [importance] represents an app that's visible to the user. */
+ private fun isAppVisibleToUser(importance: Int): Boolean {
+ return importance <= IMPORTANCE_CUTPOINT
+ }
+
+ companion object {
+ private const val IMPORTANCE_CUTPOINT = IMPORTANCE_FOREGROUND
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
index 5bad9fc..6f2a2c4 100644
--- a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
+++ b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
@@ -33,6 +33,7 @@
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
@@ -105,11 +106,11 @@
null
}
- val overriddenByAppState =
+ val overriddenByAppState by
if (Flags.showToastWhenAppControlBrightness()) {
- viewModel.brightnessOverriddenByWindow.collectAsStateWithLifecycle().value
+ viewModel.brightnessOverriddenByWindow.collectAsStateWithLifecycle()
} else {
- false
+ remember { mutableStateOf(false) }
}
PlatformSlider(
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
index 7b54815..26abb48 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
@@ -20,9 +20,11 @@
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL
import android.content.IntentFilter
import android.content.pm.UserInfo
+import android.content.res.Resources
import android.os.UserHandle
import android.provider.Settings
import com.android.systemui.Flags.communalHub
+import com.android.systemui.Flags.glanceableHubV2
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.communal.data.model.CommunalEnabledState
import com.android.systemui.communal.data.model.DisabledReason
@@ -33,6 +35,7 @@
import com.android.systemui.communal.shared.model.CommunalBackgroundType
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.util.kotlin.emitOnStart
@@ -53,13 +56,30 @@
fun getEnabledState(user: UserInfo): Flow<CommunalEnabledState>
/**
- * Returns true if both the communal trunk-stable flag and resource flag are enabled.
+ * Returns true if any glanceable hub functionality should be enabled via configs and flags.
*
- * The trunk-stable flag is controlled by server rollout and is on all devices. The resource
- * flag is enabled via resource overlay only on products we want the hub to be present on.
+ * This should be used for preventing basic glanceable hub functionality from running on devices
+ * that don't need it.
+ *
+ * If the glanceable_hub_v2 flag is enabled, checks the config_glanceableHubEnabled Android
+ * config boolean. Otherwise, checks the old config_communalServiceEnabled config and
+ * communal_hub flag.
*/
fun getFlagEnabled(): Boolean
+ /**
+ * Returns true if the Android config config_glanceableHubEnabled and the glanceable_hub_v2 flag
+ * are enabled.
+ *
+ * This should be used to flag off new glanceable hub or dream behavior that should launch
+ * together with the new hub experience that brings the hub to mobile.
+ *
+ * The trunk-stable flag is controlled by server rollout and is on all devices. The Android
+ * config flag is enabled via resource overlay only on products we want the hub to be present
+ * on.
+ */
+ fun getV2FlagEnabled(): Boolean
+
/** Keyguard widgets enabled state by Device Policy Manager for the specified user. */
fun getAllowedByDevicePolicy(user: UserInfo): Flow<Boolean>
@@ -72,6 +92,7 @@
@Inject
constructor(
@Background private val bgDispatcher: CoroutineDispatcher,
+ @Main private val resources: Resources,
private val featureFlagsClassic: FeatureFlagsClassic,
private val secureSettings: SecureSettings,
private val broadcastDispatcher: BroadcastDispatcher,
@@ -79,7 +100,18 @@
) : CommunalSettingsRepository {
override fun getFlagEnabled(): Boolean {
- return featureFlagsClassic.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED) && communalHub()
+ return if (getV2FlagEnabled()) {
+ true
+ } else {
+ // This config (exposed as a classic feature flag) is targeted only to tablet.
+ // TODO(b/379181581): clean up usages of communal_hub flag
+ featureFlagsClassic.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED) && communalHub()
+ }
+ }
+
+ override fun getV2FlagEnabled(): Boolean {
+ return resources.getBoolean(com.android.internal.R.bool.config_glanceableHubEnabled) &&
+ glanceableHubV2()
}
override fun getEnabledState(user: UserInfo): Flow<CommunalEnabledState> {
@@ -128,7 +160,7 @@
secureSettings.getIntForUser(
GLANCEABLE_HUB_BACKGROUND_SETTING,
CommunalBackgroundType.ANIMATED.value,
- user.id
+ user.id,
)
CommunalBackgroundType.entries.find { type -> type.value == intType }
?: CommunalBackgroundType.ANIMATED
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 6a777ee..d6f8957 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -32,6 +32,7 @@
import com.android.systemui.CameraProtectionModule;
import com.android.systemui.CoreStartable;
import com.android.systemui.SystemUISecondaryUserService;
+import com.android.systemui.activity.ActivityManagerModule;
import com.android.systemui.ambient.dagger.AmbientModule;
import com.android.systemui.appops.dagger.AppOpsModule;
import com.android.systemui.assist.AssistModule;
@@ -198,6 +199,7 @@
* may not appreciate that.
*/
@Module(includes = {
+ ActivityManagerModule.class,
AmbientModule.class,
AppOpsModule.class,
AssistModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt b/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
index e862525..9b181be 100644
--- a/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
@@ -30,6 +30,7 @@
import com.android.systemui.display.data.repository.FocusedDisplayRepositoryImpl
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractorImpl
+import com.android.systemui.display.domain.interactor.DisplayWindowPropertiesInteractorModule
import com.android.systemui.display.domain.interactor.RearDisplayStateInteractor
import com.android.systemui.display.domain.interactor.RearDisplayStateInteractorImpl
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
@@ -41,7 +42,7 @@
import dagger.multibindings.IntoMap
/** Module binding display related classes. */
-@Module
+@Module(includes = [DisplayWindowPropertiesInteractorModule::class])
interface DisplayModule {
@Binds
fun bindConnectedDisplayInteractor(
diff --git a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractor.kt b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractor.kt
new file mode 100644
index 0000000..22e467b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractor.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.display.domain.interactor
+
+import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository
+import com.android.systemui.display.shared.model.DisplayWindowProperties
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
+
+/** Provides per display instances of [DisplayWindowProperties]. */
+interface DisplayWindowPropertiesInteractor {
+
+ /**
+ * Returns a [DisplayWindowProperties] instance for a given display id, to be used for the
+ * status bar.
+ *
+ * @throws IllegalArgumentException if no display with the given display id exists.
+ */
+ fun getForStatusBar(displayId: Int): DisplayWindowProperties
+}
+
+@SysUISingleton
+class DisplayWindowPropertiesInteractorImpl
+@Inject
+constructor(private val repo: DisplayWindowPropertiesRepository) :
+ DisplayWindowPropertiesInteractor {
+
+ override fun getForStatusBar(displayId: Int): DisplayWindowProperties {
+ return repo.get(displayId, TYPE_STATUS_BAR)
+ }
+}
+
+@Module
+interface DisplayWindowPropertiesInteractorModule {
+
+ @Binds
+ fun interactor(impl: DisplayWindowPropertiesInteractorImpl): DisplayWindowPropertiesInteractor
+}
diff --git a/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt b/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt
index 2a3729b..c49ba80 100644
--- a/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt
@@ -27,6 +27,7 @@
import android.os.UserHandle
import android.view.accessibility.AccessibilityManager
import androidx.core.app.NotificationCompat
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -39,7 +40,6 @@
import com.android.systemui.res.R
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
/**
* A class to show contextual education on UI based on the edu produced from
@@ -107,6 +107,7 @@
}
private fun showDialog(model: ContextualEduToastViewModel) {
+ dialog?.dismiss()
dialog = createDialog(model)
dialog?.show()
}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialAnimation.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialAnimation.kt
index 2be619b..abd39cc 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialAnimation.kt
@@ -23,13 +23,16 @@
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
+import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.node.Ref
@@ -93,13 +96,19 @@
animationProperties: LottieDynamicProperties,
) {
val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(educationAnimationId))
+ var isPlaying by remember { mutableStateOf(true) }
val progress by
- animateLottieCompositionAsState(composition, iterations = LottieConstants.IterateForever)
+ animateLottieCompositionAsState(
+ composition,
+ iterations = LottieConstants.IterateForever,
+ isPlaying = isPlaying,
+ restartOnPlay = false,
+ )
LottieAnimation(
composition = composition,
progress = { progress },
dynamicProperties = animationProperties,
- modifier = Modifier.fillMaxSize(),
+ modifier = Modifier.fillMaxSize().clickable { isPlaying = !isPlaying },
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt
index a0897f2..27d1a30 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt
@@ -116,7 +116,10 @@
.filter {
// Allow KEYCODE_UNKNOWN (0) because shortcuts can have just modifiers and no
// keycode, or they could have a baseCharacter instead of a keycode.
- it.keycode == KeyEvent.KEYCODE_UNKNOWN || supportedKeyCodes.contains(it.keycode)
+ it.keycode == KeyEvent.KEYCODE_UNKNOWN ||
+ supportedKeyCodes.contains(it.keycode) ||
+ // Support keyboard function row key codes
+ keyGlyphMap?.functionRowKeys?.contains(it.keycode) ?: false
}
.mapNotNull { toShortcut(keyGlyphMap, keyCharacterMap, it, keepIcons) }
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperKeys.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperKeys.kt
index e47b33f..d91922d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperKeys.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperKeys.kt
@@ -99,6 +99,7 @@
import android.view.KeyEvent.KEYCODE_PAGE_UP
import android.view.KeyEvent.KEYCODE_PERIOD
import android.view.KeyEvent.KEYCODE_RECENT_APPS
+import android.view.KeyEvent.KEYCODE_SCREENSHOT
import android.view.KeyEvent.KEYCODE_SCROLL_LOCK
import android.view.KeyEvent.KEYCODE_SHIFT_LEFT
import android.view.KeyEvent.KEYCODE_SHIFT_RIGHT
@@ -125,6 +126,14 @@
KEYCODE_RECENT_APPS to R.drawable.ic_check_box_outline_blank,
)
+ val keyLabelResIds =
+ mapOf(
+ KEYCODE_BACK to R.string.group_system_go_back,
+ KEYCODE_HOME to R.string.group_system_access_home_screen,
+ KEYCODE_RECENT_APPS to R.string.group_system_overview_open_apps,
+ KEYCODE_SCREENSHOT to R.string.group_system_full_screenshot,
+ )
+
val modifierLabels =
mapOf<Int, (Context) -> String>(
// Modifiers
@@ -140,6 +149,7 @@
mapOf<Int, (Context) -> String>(
KEYCODE_HOME to { context -> context.getString(R.string.keyboard_key_home) },
KEYCODE_BACK to { context -> context.getString(R.string.keyboard_key_back) },
+ KEYCODE_RECENT_APPS to { context -> context.getString(R.string.accessibility_recent) },
KEYCODE_DPAD_UP to { context -> context.getString(R.string.keyboard_key_dpad_up) },
KEYCODE_DPAD_DOWN to { context -> context.getString(R.string.keyboard_key_dpad_down) },
KEYCODE_DPAD_LEFT to { context -> context.getString(R.string.keyboard_key_dpad_left) },
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/InputShortcutsSource.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/InputShortcutsSource.kt
index 1b20986..4787507 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/InputShortcutsSource.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/InputShortcutsSource.kt
@@ -17,12 +17,16 @@
package com.android.systemui.keyboard.shortcut.data.source
import android.content.res.Resources
+import android.hardware.input.InputManager
+import android.view.KeyEvent.KEYCODE_EMOJI_PICKER
import android.view.KeyEvent.KEYCODE_SPACE
import android.view.KeyEvent.META_CTRL_ON
import android.view.KeyEvent.META_SHIFT_ON
import android.view.KeyboardShortcutGroup
+import android.view.KeyboardShortcutInfo
import android.view.WindowManager
import android.view.WindowManager.KeyboardShortcutsReceiver
+import com.android.systemui.Flags.shortcutHelperKeyGlyph
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyboard.shortcut.data.model.shortcutInfo
import com.android.systemui.res.R
@@ -31,16 +35,19 @@
class InputShortcutsSource
@Inject
-constructor(@Main private val resources: Resources, private val windowManager: WindowManager) :
- KeyboardShortcutGroupsSource {
+constructor(
+ @Main private val resources: Resources,
+ private val windowManager: WindowManager,
+ private val inputManager: InputManager,
+) : KeyboardShortcutGroupsSource {
override suspend fun shortcutGroups(deviceId: Int): List<KeyboardShortcutGroup> =
- getInputLanguageShortcutGroup() + getImeShortcutGroup(deviceId)
+ getInputLanguageShortcutGroup(deviceId) + getImeShortcutGroup(deviceId)
- private fun getInputLanguageShortcutGroup() =
+ private fun getInputLanguageShortcutGroup(deviceId: Int) =
listOf(
KeyboardShortcutGroup(
resources.getString(R.string.shortcut_helper_category_input),
- inputLanguageShortcuts()
+ inputLanguageShortcuts() + hardwareShortcuts(deviceId),
)
)
@@ -53,9 +60,23 @@
/* Switch previous language (next language): Ctrl + Shift + Space */
shortcutInfo(resources.getString(R.string.input_switch_input_language_previous)) {
command(META_CTRL_ON or META_SHIFT_ON, KEYCODE_SPACE)
- }
+ },
)
+ private fun hardwareShortcuts(deviceId: Int): List<KeyboardShortcutInfo> {
+ if (shortcutHelperKeyGlyph()) {
+ val keyGlyphMap = inputManager.getKeyGlyphMap(deviceId)
+ if (keyGlyphMap != null && keyGlyphMap.functionRowKeys.contains(KEYCODE_EMOJI_PICKER)) {
+ return listOf(
+ shortcutInfo(resources.getString(R.string.input_access_emoji)) {
+ command(modifiers = 0, KEYCODE_EMOJI_PICKER)
+ }
+ )
+ }
+ }
+ return emptyList()
+ }
+
private suspend fun getImeShortcutGroup(deviceId: Int): List<KeyboardShortcutGroup> =
suspendCancellableCoroutine { continuation ->
val shortcutsReceiver = KeyboardShortcutsReceiver {
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt
index 0201f40..5ef869e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt
@@ -16,24 +16,34 @@
package com.android.systemui.keyboard.shortcut.data.source
+import android.content.Context
import android.content.res.Resources
import android.view.KeyEvent.KEYCODE_D
import android.view.KeyEvent.KEYCODE_DPAD_LEFT
import android.view.KeyEvent.KEYCODE_DPAD_RIGHT
import android.view.KeyEvent.KEYCODE_DPAD_UP
+import android.view.KeyEvent.KEYCODE_EQUALS
+import android.view.KeyEvent.KEYCODE_LEFT_BRACKET
+import android.view.KeyEvent.KEYCODE_MINUS
+import android.view.KeyEvent.KEYCODE_RIGHT_BRACKET
import android.view.KeyEvent.KEYCODE_TAB
import android.view.KeyEvent.META_ALT_ON
import android.view.KeyEvent.META_CTRL_ON
import android.view.KeyEvent.META_META_ON
import android.view.KeyEvent.META_SHIFT_ON
import android.view.KeyboardShortcutGroup
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyboard.shortcut.data.model.shortcutInfo
import com.android.systemui.res.R
import com.android.window.flags.Flags.enableMoveToNextDisplayShortcut
+import com.android.window.flags.Flags.enableTaskResizingKeyboardShortcuts
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import javax.inject.Inject
-class MultitaskingShortcutsSource @Inject constructor(@Main private val resources: Resources) :
+class MultitaskingShortcutsSource
+@Inject
+constructor(@Main private val resources: Resources, @Application private val context: Context) :
KeyboardShortcutGroupsSource {
override suspend fun shortcutGroups(deviceId: Int) =
@@ -95,6 +105,40 @@
}
)
}
+ if (
+ DesktopModeStatus.canEnterDesktopMode(context) && enableTaskResizingKeyboardShortcuts()
+ ) {
+ // Snap a freeform window to the left
+ // - Meta + Left bracket
+ add(
+ shortcutInfo(resources.getString(R.string.system_desktop_mode_snap_left_window)) {
+ command(META_META_ON, KEYCODE_LEFT_BRACKET)
+ }
+ )
+ // Snap a freeform window to the right
+ // - Meta + Right bracket
+ add(
+ shortcutInfo(resources.getString(R.string.system_desktop_mode_snap_right_window)) {
+ command(META_META_ON, KEYCODE_RIGHT_BRACKET)
+ }
+ )
+ // Toggle maximize a freeform window
+ // - Meta + Equals
+ add(
+ shortcutInfo(
+ resources.getString(R.string.system_desktop_mode_toggle_maximize_window)
+ ) {
+ command(META_META_ON, KEYCODE_EQUALS)
+ }
+ )
+ // Minimize a freeform window
+ // - Meta + Minus
+ add(
+ shortcutInfo(resources.getString(R.string.system_desktop_mode_minimize_window)) {
+ command(META_META_ON, KEYCODE_MINUS)
+ }
+ )
+ }
}
private fun recentsShortcuts() =
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt
index 7c0c75e..a650cd8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt
@@ -17,6 +17,8 @@
package com.android.systemui.keyboard.shortcut.data.source
import android.content.res.Resources
+import android.hardware.input.InputManager
+import android.hardware.input.KeyGlyphMap
import android.view.KeyEvent.KEYCODE_A
import android.view.KeyEvent.KEYCODE_BACK
import android.view.KeyEvent.KEYCODE_DEL
@@ -35,26 +37,87 @@
import android.view.KeyEvent.META_CTRL_ON
import android.view.KeyEvent.META_META_ON
import android.view.KeyboardShortcutGroup
+import android.view.KeyboardShortcutInfo
+import com.android.systemui.Flags.shortcutHelperKeyGlyph
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyboard.shortcut.data.model.shortcutInfo
+import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperKeys
import com.android.systemui.res.R
import javax.inject.Inject
-class SystemShortcutsSource @Inject constructor(@Main private val resources: Resources) :
+class SystemShortcutsSource
+@Inject
+constructor(@Main private val resources: Resources, private val inputManager: InputManager) :
KeyboardShortcutGroupsSource {
override suspend fun shortcutGroups(deviceId: Int) =
listOf(
KeyboardShortcutGroup(
resources.getString(R.string.shortcut_helper_category_system_controls),
- systemControlsShortcuts()
+ hardwareShortcuts(deviceId) + systemControlsShortcuts(),
),
KeyboardShortcutGroup(
resources.getString(R.string.shortcut_helper_category_system_apps),
- systemAppsShortcuts()
- )
+ systemAppsShortcuts(),
+ ),
)
+ private fun hardwareShortcuts(deviceId: Int): List<KeyboardShortcutInfo> =
+ if (shortcutHelperKeyGlyph()) {
+ val keyGlyphMap = inputManager.getKeyGlyphMap(deviceId)
+ if (keyGlyphMap != null) {
+ functionRowKeys(keyGlyphMap) + keyCombinationShortcuts(keyGlyphMap)
+ } else {
+ // Not add function row keys if it is not supported by keyboard
+ emptyList()
+ }
+ } else {
+ defaultFunctionRowKeys()
+ }
+
+ private fun defaultFunctionRowKeys(): List<KeyboardShortcutInfo> =
+ listOf(
+ shortcutInfo(resources.getString(R.string.group_system_access_home_screen)) {
+ command(modifiers = 0, KEYCODE_HOME)
+ },
+ shortcutInfo(resources.getString(R.string.group_system_go_back)) {
+ command(modifiers = 0, KEYCODE_BACK)
+ },
+ shortcutInfo(resources.getString(R.string.group_system_overview_open_apps)) {
+ command(modifiers = 0, KEYCODE_RECENT_APPS)
+ },
+ )
+
+ private fun functionRowKeys(keyGlyphMap: KeyGlyphMap): List<KeyboardShortcutInfo> {
+ val functionRowKeys = mutableListOf<KeyboardShortcutInfo>()
+ keyGlyphMap.functionRowKeys.forEach { keyCode ->
+ val labelResId = ShortcutHelperKeys.keyLabelResIds[keyCode]
+ if (labelResId != null) {
+ functionRowKeys.add(
+ shortcutInfo(resources.getString(labelResId)) {
+ command(modifiers = 0, keyCode)
+ }
+ )
+ }
+ }
+ return functionRowKeys
+ }
+
+ private fun keyCombinationShortcuts(keyGlyphMap: KeyGlyphMap): List<KeyboardShortcutInfo> {
+ val shortcuts = mutableListOf<KeyboardShortcutInfo>()
+ keyGlyphMap.hardwareShortcuts.forEach { (keyCombination, keyCode) ->
+ val labelResId = ShortcutHelperKeys.keyLabelResIds[keyCode]
+ if (labelResId != null) {
+ val info =
+ shortcutInfo(resources.getString(labelResId)) {
+ command(keyCombination.modifierState, keyCombination.keycode)
+ }
+ shortcuts.add(info)
+ }
+ }
+ return shortcuts
+ }
+
private fun systemControlsShortcuts() =
listOf(
// Access list of all apps and search (i.e. Search/Launcher):
@@ -63,36 +126,24 @@
command(META_META_ON)
},
// Access home screen:
- // - Home button
// - Meta + H
// - Meta + Enter
shortcutInfo(resources.getString(R.string.group_system_access_home_screen)) {
- command(modifiers = 0, KEYCODE_HOME)
- },
- shortcutInfo(resources.getString(R.string.group_system_access_home_screen)) {
command(META_META_ON, KEYCODE_H)
},
shortcutInfo(resources.getString(R.string.group_system_access_home_screen)) {
command(META_META_ON, KEYCODE_ENTER)
},
// Overview of open apps:
- // - Recent apps button
// - Meta + Tab
shortcutInfo(resources.getString(R.string.group_system_overview_open_apps)) {
- command(modifiers = 0, KEYCODE_RECENT_APPS)
- },
- shortcutInfo(resources.getString(R.string.group_system_overview_open_apps)) {
command(META_META_ON, KEYCODE_TAB)
},
// Back: go back to previous state (back button)
- // - Back button
// - Meta + Escape OR
// - Meta + Backspace OR
// - Meta + Left arrow
shortcutInfo(resources.getString(R.string.group_system_go_back)) {
- command(modifiers = 0, KEYCODE_BACK)
- },
- shortcutInfo(resources.getString(R.string.group_system_go_back)) {
command(META_META_ON, KEYCODE_ESCAPE)
},
shortcutInfo(resources.getString(R.string.group_system_go_back)) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt
index 0381eae..2385cc6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt
@@ -16,14 +16,22 @@
package com.android.systemui.keyboard.shortcut.domain.interactor
+import android.content.Context
+import android.view.KeyEvent.META_META_ON
import com.android.systemui.Flags.keyboardShortcutHelperShortcutCustomizer
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyboard.shortcut.data.repository.ShortcutCategoriesRepository
+import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperKeys
+import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperKeys.metaModifierIconResId
import com.android.systemui.keyboard.shortcut.qualifiers.CustomShortcutCategories
import com.android.systemui.keyboard.shortcut.qualifiers.DefaultShortcutCategories
import com.android.systemui.keyboard.shortcut.shared.model.Shortcut
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory
+import com.android.systemui.res.R
import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -34,6 +42,7 @@
class ShortcutHelperCategoriesInteractor
@Inject
constructor(
+ @Application private val context: Context,
@DefaultShortcutCategories defaultCategoriesRepository: ShortcutCategoriesRepository,
@CustomShortcutCategories customCategoriesRepositoryLazy: Lazy<ShortcutCategoriesRepository>,
) {
@@ -87,6 +96,54 @@
label = commonLabel,
icon = groupedShortcuts.firstOrNull()?.icon,
commands = groupedShortcuts.flatMap { it.commands },
+ contentDescription =
+ toContentDescription(commonLabel, groupedShortcuts.flatMap { it.commands }),
)
}
+
+ private fun toContentDescription(label: String, commands: List<ShortcutCommand>): String {
+ val pressKey = context.getString(R.string.shortcut_helper_add_shortcut_dialog_placeholder)
+ val andConjunction =
+ context.getString(R.string.shortcut_helper_key_combinations_and_conjunction)
+ val orConjunction =
+ context.getString(R.string.shortcut_helper_key_combinations_or_separator)
+ val forwardSlash =
+ context.getString(R.string.shortcut_helper_key_combinations_forward_slash)
+ return buildString {
+ append("$label, $pressKey")
+ commands.forEachIndexed { i, shortcutCommand ->
+ if (i > 0) {
+ append(", $orConjunction")
+ }
+ shortcutCommand.keys.forEachIndexed { j, shortcutKey ->
+ if (j > 0) {
+ append(" $andConjunction")
+ }
+ if (shortcutKey is ShortcutKey.Text) {
+ // Special handling for "/" as TalkBack will not read punctuation by
+ // default.
+ if (shortcutKey.value.equals("/")) {
+ append(" $forwardSlash")
+ } else {
+ append(" ${shortcutKey.value}")
+ }
+ } else if (shortcutKey is ShortcutKey.Icon.ResIdIcon) {
+ val keyLabel =
+ if (shortcutKey.drawableResId == metaModifierIconResId) {
+ ShortcutHelperKeys.modifierLabels[META_META_ON]
+ } else {
+ val keyCode =
+ ShortcutHelperKeys.keyIcons.entries
+ .firstOrNull { it.value == shortcutKey.drawableResId }
+ ?.key
+ ShortcutHelperKeys.specialKeyLabels[keyCode]
+ }
+ if (keyLabel != null) {
+ append(" ${keyLabel.invoke(context)}")
+ }
+ } // No-Op when shortcutKey is ShortcutKey.Icon.DrawableIcon
+ }
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt
index bf7df7e..9cc15ce 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt
@@ -20,18 +20,24 @@
val label: String,
val commands: List<ShortcutCommand>,
val icon: ShortcutIcon? = null,
+ val contentDescription: String = "",
) {
val containsCustomShortcutCommands: Boolean = commands.any { it.isCustom }
}
class ShortcutBuilder(private val label: String) {
val commands = mutableListOf<ShortcutCommand>()
+ var contentDescription = ""
fun command(builder: ShortcutCommandBuilder.() -> Unit) {
commands += ShortcutCommandBuilder().apply(builder).build()
}
- fun build() = Shortcut(label, commands)
+ fun contentDescription(string: () -> String) {
+ contentDescription = string.invoke()
+ }
+
+ fun build() = Shortcut(label, commands, contentDescription = contentDescription)
}
fun shortcut(label: String, block: ShortcutBuilder.() -> Unit): Shortcut =
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
index e3675de..1f37c7d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
@@ -96,6 +96,8 @@
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.hideFromAccessibility
import androidx.compose.ui.semantics.isTraversalGroup
import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics
@@ -111,6 +113,7 @@
import androidx.compose.ui.util.fastForEachIndexed
import com.android.compose.modifiers.thenIf
import com.android.compose.ui.graphics.painter.rememberDrawablePainter
+import com.android.systemui.keyboard.shortcut.shared.model.Shortcut as ShortcutModel
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo
@@ -121,7 +124,6 @@
import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCategoryUi
import com.android.systemui.keyboard.shortcut.ui.model.ShortcutsUiState
import com.android.systemui.res.R
-import com.android.systemui.keyboard.shortcut.shared.model.Shortcut as ShortcutModel
import kotlinx.coroutines.delay
@Composable
@@ -465,14 +467,10 @@
onCustomizationRequested = { requestInfo ->
when (requestInfo) {
is ShortcutCustomizationRequestInfo.Add ->
- onCustomizationRequested(
- requestInfo.copy(categoryType = category.type)
- )
+ onCustomizationRequested(requestInfo.copy(categoryType = category.type))
is ShortcutCustomizationRequestInfo.Delete ->
- onCustomizationRequested(
- requestInfo.copy(categoryType = category.type)
- )
+ onCustomizationRequested(requestInfo.copy(categoryType = category.type))
}
},
)
@@ -572,20 +570,31 @@
}
.focusable(interactionSource = interactionSource)
.padding(8.dp)
+ .semantics { contentDescription = shortcut.contentDescription }
) {
Row(
- modifier = Modifier.width(128.dp).align(Alignment.CenterVertically).weight(0.333f),
+ modifier =
+ Modifier.width(128.dp).align(Alignment.CenterVertically).weight(0.333f).semantics {
+ hideFromAccessibility()
+ },
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalAlignment = Alignment.CenterVertically,
) {
if (shortcut.icon != null) {
- ShortcutIcon(shortcut.icon, modifier = Modifier.size(24.dp))
+ ShortcutIcon(
+ shortcut.icon,
+ modifier = Modifier.size(24.dp).semantics { hideFromAccessibility() },
+ )
}
- ShortcutDescriptionText(searchQuery = searchQuery, shortcut = shortcut)
+ ShortcutDescriptionText(
+ searchQuery = searchQuery,
+ shortcut = shortcut,
+ modifier = Modifier.semantics { hideFromAccessibility() },
+ )
}
- Spacer(modifier = Modifier.width(24.dp))
+ Spacer(modifier = Modifier.width(24.dp).semantics { hideFromAccessibility() })
ShortcutKeyCombinations(
- modifier = Modifier.weight(.666f),
+ modifier = Modifier.weight(.666f).semantics { hideFromAccessibility() },
shortcut = shortcut,
isCustomizing = isCustomizing,
onAddShortcutRequested = {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 7097c1d..d40fe46 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -81,7 +81,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardLockWhileAwakeInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardServiceLockNowInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardStateCallbackInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardWakeDirectlyToGoneInteractor;
import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier;
@@ -330,8 +330,7 @@
return new FoldGracePeriodProvider();
}
};
- private final KeyguardLockWhileAwakeInteractor
- mKeyguardLockWhileAwakeInteractor;
+ private final KeyguardServiceLockNowInteractor mKeyguardServiceLockNowInteractor;
@Inject
public KeyguardService(
@@ -357,7 +356,7 @@
KeyguardDismissInteractor keyguardDismissInteractor,
Lazy<DeviceEntryInteractor> deviceEntryInteractorLazy,
KeyguardStateCallbackInteractor keyguardStateCallbackInteractor,
- KeyguardLockWhileAwakeInteractor keyguardLockWhileAwakeInteractor) {
+ KeyguardServiceLockNowInteractor keyguardServiceLockNowInteractor) {
super();
mKeyguardViewMediator = keyguardViewMediator;
mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
@@ -389,7 +388,7 @@
mKeyguardEnabledInteractor = keyguardEnabledInteractor;
mKeyguardWakeDirectlyToGoneInteractor = keyguardWakeDirectlyToGoneInteractor;
mKeyguardDismissInteractor = keyguardDismissInteractor;
- mKeyguardLockWhileAwakeInteractor = keyguardLockWhileAwakeInteractor;
+ mKeyguardServiceLockNowInteractor = keyguardServiceLockNowInteractor;
}
@Override
@@ -665,7 +664,7 @@
if (SceneContainerFlag.isEnabled()) {
mDeviceEntryInteractorLazy.get().lockNow();
} else if (KeyguardWmStateRefactor.isEnabled()) {
- mKeyguardLockWhileAwakeInteractor.onKeyguardServiceDoKeyguardTimeout(options);
+ mKeyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout(options);
}
mKeyguardViewMediator.doKeyguardTimeout(options);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 01ec4d0..9f13160 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -3943,7 +3943,7 @@
}
private void notifyDefaultDisplayCallbacks(boolean showing) {
- if (SceneContainerFlag.isEnabled()) {
+ if (SceneContainerFlag.isEnabled() || KeyguardWmStateRefactor.isEnabled()) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
index 631e44a..42cbd7d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
@@ -16,39 +16,52 @@
package com.android.systemui.keyguard.domain.interactor
+import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.internal.widget.LockPatternUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
-import com.android.app.tracing.coroutines.launchTraced as launch
+import kotlinx.coroutines.withContext
/**
- * Logic around the keyguard being enabled/disabled, per [KeyguardService]. If the keyguard is not
- * enabled, the lockscreen cannot be shown and the device will go from AOD/DOZING directly to GONE.
+ * Logic around the keyguard being enabled, disabled, or suppressed via adb. If the keyguard is
+ * disabled or suppressed, the lockscreen cannot be shown and the device will go from AOD/DOZING
+ * directly to GONE.
*
* Keyguard can be disabled by selecting Security: "None" in settings, or by apps that hold
* permission to do so (such as Phone). Some CTS tests also disable keyguard in onCreate or onStart
* rather than simply dismissing the keyguard or setting up the device to have Security: None, for
* reasons unknown.
+ *
+ * Keyguard can be suppressed by calling "adb shell locksettings set-disabled true", which is
+ * frequently done in tests. If keyguard is suppressed, it won't show even if the keyguard is
+ * enabled. If keyguard is not suppressed, then we defer to whether keyguard is enabled or disabled.
*/
@SysUISingleton
class KeyguardEnabledInteractor
@Inject
constructor(
- @Application scope: CoroutineScope,
+ @Application val scope: CoroutineScope,
+ @Background val backgroundDispatcher: CoroutineDispatcher,
val repository: KeyguardRepository,
val biometricSettingsRepository: BiometricSettingsRepository,
- keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor,
+ private val selectedUserInteractor: SelectedUserInteractor,
+ private val lockPatternUtils: LockPatternUtils,
+ keyguardDismissTransitionInteractor: dagger.Lazy<KeyguardDismissTransitionInteractor>,
internalTransitionInteractor: InternalKeyguardTransitionInteractor,
) {
@@ -62,6 +75,10 @@
* If the keyguard is disabled while we're locked, we will transition to GONE unless we're in
* lockdown mode. If the keyguard is re-enabled, we'll transition back to LOCKSCREEN if we were
* locked when it was disabled.
+ *
+ * Even if the keyguard is enabled, it's possible for it to be suppressed temporarily via adb.
+ * If you need to respect that adb command, you will need to use
+ * [isKeyguardEnabledAndNotSuppressed] instead of using this flow.
*/
val isKeyguardEnabled: StateFlow<Boolean> = repository.isKeyguardEnabled
@@ -96,9 +113,9 @@
val currentTransitionInfo =
internalTransitionInteractor.currentTransitionInfoInternal()
if (currentTransitionInfo.to != KeyguardState.GONE && !inLockdown) {
- keyguardDismissTransitionInteractor.startDismissKeyguardTransition(
- "keyguard disabled"
- )
+ keyguardDismissTransitionInteractor
+ .get()
+ .startDismissKeyguardTransition("keyguard disabled")
}
}
}
@@ -116,4 +133,37 @@
fun isShowKeyguardWhenReenabled(): Boolean {
return repository.isShowKeyguardWhenReenabled()
}
+
+ /**
+ * Whether the keyguard is enabled, and has not been suppressed via adb.
+ *
+ * There is unfortunately no callback for [isKeyguardSuppressed], which means this can't be a
+ * flow, since it's ambiguous when we would query the latest suppression value.
+ */
+ suspend fun isKeyguardEnabledAndNotSuppressed(): Boolean {
+ return isKeyguardEnabled.value && !isKeyguardSuppressed()
+ }
+
+ /**
+ * Returns whether the lockscreen has been disabled ("suppressed") via "adb shell locksettings
+ * set-disabled". If suppressed, we'll ignore all signals that would typically result in showing
+ * the keyguard, regardless of the value of [isKeyguardEnabled].
+ *
+ * It's extremely confusing to have [isKeyguardEnabled] not be the inverse of "is lockscreen
+ * disabled", so this method intentionally re-terms it as "suppressed".
+ *
+ * Note that if the lockscreen is currently showing when it's suppressed, it will remain visible
+ * until it's unlocked, at which point it will never re-appear until suppression is removed.
+ */
+ suspend fun isKeyguardSuppressed(
+ userId: Int = selectedUserInteractor.getSelectedUserId()
+ ): Boolean {
+ // isLockScreenDisabled returns true whenever keyguard is not enabled, even if the adb
+ // command was not used to disable/suppress the lockscreen. To make these booleans as clear
+ // as possible, only return true if keyguard is suppressed when it otherwise would have
+ // been enabled.
+ return withContext(backgroundDispatcher) {
+ isKeyguardEnabled.value && lockPatternUtils.isLockScreenDisabled(userId)
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractor.kt
index 0ab3e5c..ce84e71 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractor.kt
@@ -16,27 +16,16 @@
package com.android.systemui.keyguard.domain.interactor
-import android.os.Bundle
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
-/**
- * Emitted when we receive a [KeyguardLockWhileAwakeInteractor.onKeyguardServiceDoKeyguardTimeout]
- * call.
- *
- * Includes a timestamp so it's not conflated by the StateFlow.
- */
-data class KeyguardTimeoutWhileAwakeEvent(val timestamp: Long, val options: Bundle?)
-
/** The reason we're locking while awake, used for logging. */
enum class LockWhileAwakeReason(private val logReason: String) {
LOCKDOWN("Lockdown initiated."),
@@ -71,10 +60,8 @@
constructor(
biometricSettingsRepository: BiometricSettingsRepository,
keyguardEnabledInteractor: KeyguardEnabledInteractor,
+ keyguardServiceLockNowInteractor: KeyguardServiceLockNowInteractor,
) {
- /** Emits whenever a timeout event is received by [KeyguardService]. */
- private val timeoutEvents: MutableStateFlow<KeyguardTimeoutWhileAwakeEvent?> =
- MutableStateFlow(null)
/** Emits whenever the current user is in lockdown mode. */
private val inLockdown: Flow<LockWhileAwakeReason> =
@@ -97,25 +84,19 @@
/** Emits whenever we should lock while the screen is on, for any reason. */
val lockWhileAwakeEvents: Flow<LockWhileAwakeReason> =
merge(
- inLockdown,
- keyguardReenabled,
- timeoutEvents.filterNotNull().map {
- LockWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON
- },
+ // We're in lockdown, and the keyguard is enabled. If the keyguard is disabled, the
+ // lockdown button is hidden in the UI, but it's still possible to trigger lockdown in
+ // tests.
+ inLockdown
+ .filter { keyguardEnabledInteractor.isKeyguardEnabledAndNotSuppressed() }
+ .map { LockWhileAwakeReason.LOCKDOWN },
+ // The keyguard was re-enabled, and it was showing when it was originally disabled.
+ // Tests currently expect that if the keyguard is re-enabled, it will show even if it's
+ // suppressed, so we don't check for isKeyguardEnabledAndNotSuppressed() on this flow.
+ keyguardReenabled.map { LockWhileAwakeReason.KEYGUARD_REENABLED },
+ // KeyguardService says we need to lock now, and the lockscreen is enabled.
+ keyguardServiceLockNowInteractor.lockNowEvents
+ .filter { keyguardEnabledInteractor.isKeyguardEnabledAndNotSuppressed() }
+ .map { LockWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON },
)
-
- /**
- * Called by [KeyguardService] when it receives a doKeyguardTimeout() call. This indicates that
- * the device locked while the screen was on.
- *
- * [options] appears to be no longer used, but we'll keep it in this interactor in case that
- * turns out not to be true.
- */
- fun onKeyguardServiceDoKeyguardTimeout(options: Bundle?) {
- timeoutEvents.value =
- KeyguardTimeoutWhileAwakeEvent(
- timestamp = System.currentTimeMillis(),
- options = options,
- )
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceLockNowInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceLockNowInteractor.kt
new file mode 100644
index 0000000..9ed53ea
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceLockNowInteractor.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.annotation.SuppressLint
+import android.os.Bundle
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.launch
+
+/**
+ * Emitted when we receive a [KeyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout]
+ * call.
+ */
+data class KeyguardLockNowEvent(val options: Bundle?)
+
+/**
+ * Logic around requests by [KeyguardService] to lock the device right now, even though the device
+ * is awake and not going to sleep.
+ *
+ * This can happen if WM#lockNow() is called, or if the screen is forced to stay awake but the lock
+ * timeout elapses.
+ *
+ * This is not the only way for the device to lock while the screen is on. The other cases, which do
+ * not directly involve [KeyguardService], are handled in [KeyguardLockWhileAwakeInteractor].
+ */
+@SysUISingleton
+class KeyguardServiceLockNowInteractor
+@Inject
+constructor(@Background val backgroundScope: CoroutineScope) {
+
+ /**
+ * Emits whenever [KeyguardService] receives a call that indicates we should lock the device
+ * right now, even though the device is awake and not going to sleep.
+ *
+ * WARNING: This is only one of multiple reasons the device might need to lock while not going
+ * to sleep. Unless you're dealing with keyguard internals that specifically need to know that
+ * we're locking due to a call to doKeyguardTimeout, use
+ * [KeyguardLockWhileAwakeInteractor.lockWhileAwakeEvents].
+ *
+ * This is fundamentally an event flow, hence the SharedFlow.
+ */
+ @SuppressLint("SharedFlowCreation")
+ val lockNowEvents: MutableSharedFlow<KeyguardLockNowEvent> = MutableSharedFlow()
+
+ /**
+ * Called by [KeyguardService] when it receives a doKeyguardTimeout() call. This indicates that
+ * the device locked while the screen was on.
+ *
+ * [options] appears to be no longer used, but we'll keep it in this interactor in case that
+ * turns out not to be true.
+ */
+ fun onKeyguardServiceDoKeyguardTimeout(options: Bundle?) {
+ backgroundScope.launch { lockNowEvents.emit(KeyguardLockNowEvent(options = options)) }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt
index fbc7e2a..8641dfa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt
@@ -25,6 +25,7 @@
import android.content.IntentFilter
import android.provider.Settings
import android.provider.Settings.Secure
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -48,20 +49,21 @@
import kotlin.math.max
import kotlin.math.min
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.map
-import com.android.app.tracing.coroutines.launchTraced as launch
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onStart
/**
* Logic related to the ability to wake directly to GONE from asleep (AOD/DOZING), without going
* through LOCKSCREEN or a BOUNCER state.
*
* This is possible in the following scenarios:
- * - The lockscreen is disabled, either from an app request (SUW does this), or by the security
+ * - The keyguard is not enabled, either from an app request (SUW does this), or by the security
* "None" setting.
+ * - The keyguard was suppressed via adb.
* - A biometric authentication event occurred while we were asleep (fingerprint auth, etc). This
* specifically is referred to throughout the codebase as "wake and unlock".
* - The screen timed out, but the "lock after screen timeout" duration has not elapsed.
@@ -86,43 +88,44 @@
private val lockPatternUtils: LockPatternUtils,
private val systemSettings: SystemSettings,
private val selectedUserInteractor: SelectedUserInteractor,
+ keyguardEnabledInteractor: KeyguardEnabledInteractor,
+ keyguardServiceLockNowInteractor: KeyguardServiceLockNowInteractor,
) {
/**
- * Whether the lockscreen was disabled as of the last wake/sleep event, according to
- * LockPatternUtils.
- *
- * This will always be true if [repository.isKeyguardServiceEnabled]=false, but it can also be
- * true when the keyguard service is enabled if the lockscreen has been disabled via adb using
- * the `adb shell locksettings set-disabled true` command, which is often done in tests.
- *
- * Unlike keyguardServiceEnabled, changes to this value should *not* immediately show or hide
- * the keyguard. If the lockscreen is disabled in this way, it will just not show on the next
- * sleep/wake.
+ * Whether the keyguard was suppressed as of the most recent wakefulness event or lockNow
+ * command. Keyguard suppression can only be queried (there is no callback available), and
+ * legacy code only queried the value in onStartedGoingToSleep and doKeyguardTimeout. Tests now
+ * depend on that behavior, so for now, we'll replicate it here.
*/
- private val isLockscreenDisabled: Flow<Boolean> =
- powerInteractor.isAwake.map { isLockscreenDisabled() }
+ private val shouldSuppressKeyguard =
+ merge(powerInteractor.isAwake, keyguardServiceLockNowInteractor.lockNowEvents)
+ .map { keyguardEnabledInteractor.isKeyguardSuppressed() }
+ // Default to false, so that flows that combine this one emit prior to the first
+ // wakefulness emission.
+ .onStart { emit(false) }
/**
* Whether we can wake from AOD/DOZING directly to GONE, bypassing LOCKSCREEN/BOUNCER states.
*
* This is possible in the following cases:
* - Keyguard is disabled, either from an app request or from security being set to "None".
+ * - Keyguard is suppressed, via adb locksettings.
* - We're wake and unlocking (fingerprint auth occurred while asleep).
* - We're allowed to ignore auth and return to GONE, due to timeouts not elapsing.
*/
val canWakeDirectlyToGone =
combine(
repository.isKeyguardEnabled,
- isLockscreenDisabled,
+ shouldSuppressKeyguard,
repository.biometricUnlockState,
repository.canIgnoreAuthAndReturnToGone,
) {
keyguardEnabled,
- isLockscreenDisabled,
+ shouldSuppressKeyguard,
biometricUnlockState,
canIgnoreAuthAndReturnToGone ->
- (!keyguardEnabled || isLockscreenDisabled) ||
+ (!keyguardEnabled || shouldSuppressKeyguard) ||
BiometricUnlockMode.isWakeAndUnlock(biometricUnlockState.mode) ||
canIgnoreAuthAndReturnToGone
}
@@ -186,9 +189,9 @@
.sample(
transitionInteractor.isCurrentlyIn(
Scenes.Gone,
- stateWithoutSceneContainer = KeyguardState.GONE
+ stateWithoutSceneContainer = KeyguardState.GONE,
),
- ::Pair
+ ::Pair,
)
.collect { (wakefulness, finishedInGone) ->
// Save isAwake for use in onDreamingStarted/onDreamingStopped.
@@ -260,7 +263,7 @@
delayedActionFilter,
SYSTEMUI_PERMISSION,
null /* scheduler */,
- Context.RECEIVER_EXPORTED_UNAUDITED
+ Context.RECEIVER_EXPORTED_UNAUDITED,
)
}
@@ -282,7 +285,7 @@
context,
0,
intent,
- PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE,
)
val time = systemClock.elapsedRealtime() + getCanIgnoreAuthAndReturnToGoneDuration()
@@ -311,16 +314,6 @@
}
/**
- * Returns whether the lockscreen is disabled, either because the keyguard service is disabled
- * or because an adb command has disabled the lockscreen.
- */
- private fun isLockscreenDisabled(
- userId: Int = selectedUserInteractor.getSelectedUserId()
- ): Boolean {
- return lockPatternUtils.isLockScreenDisabled(userId)
- }
-
- /**
* Returns the duration within which we can return to GONE without auth after a screen timeout
* (or power button press, if lock instantly is disabled).
*
@@ -336,7 +329,7 @@
.getIntForUser(
Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
KEYGUARD_CAN_IGNORE_AUTH_DURATION,
- userId
+ userId,
)
.toLong()
@@ -352,7 +345,7 @@
.getIntForUser(
Settings.System.SCREEN_OFF_TIMEOUT,
KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT,
- userId
+ userId,
)
.toLong()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
index f0bccac..85ce5cd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
@@ -23,7 +23,9 @@
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.ui.composable.transitions.TO_BOUNCER_FADE_FRACTION
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -36,9 +38,7 @@
@SysUISingleton
class AlternateBouncerToPrimaryBouncerTransitionViewModel
@Inject
-constructor(
- animationFlow: KeyguardTransitionAnimationFlow,
-) : DeviceEntryIconTransition {
+constructor(animationFlow: KeyguardTransitionAnimationFlow) : DeviceEntryIconTransition {
private val transitionAnimation =
animationFlow
.setup(
@@ -46,9 +46,23 @@
edge = Edge.create(from = ALTERNATE_BOUNCER, to = Scenes.Bouncer),
)
.setupWithoutSceneContainer(
- edge = Edge.create(from = ALTERNATE_BOUNCER, to = PRIMARY_BOUNCER),
+ edge = Edge.create(from = ALTERNATE_BOUNCER, to = PRIMARY_BOUNCER)
)
+ private val alphaForAnimationStep: (Float) -> Float =
+ when {
+ SceneContainerFlag.isEnabled -> { step ->
+ 1f - Math.min((step / TO_BOUNCER_FADE_FRACTION), 1f)
+ }
+ else -> { step -> 1f - step }
+ }
+
+ val lockscreenAlpha: Flow<Float> =
+ transitionAnimation.sharedFlow(
+ duration = FromAlternateBouncerTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
+ onStep = alphaForAnimationStep,
+ )
+
override val deviceEntryParentViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0f)
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
index a3b7590..d2b1d54 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -56,7 +56,7 @@
import com.android.systemui.shared.statusbar.phone.BarTransitions.TransitionMode;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.phone.AutoHideController;
+import com.android.systemui.statusbar.phone.AutoHideControllerStore;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.Utils;
@@ -124,7 +124,7 @@
TaskbarDelegate taskbarDelegate,
NavigationBarComponent.Factory navigationBarComponentFactory,
DumpManager dumpManager,
- AutoHideController autoHideController,
+ AutoHideControllerStore autoHideControllerStore,
LightBarController lightBarController,
TaskStackChangeListeners taskStackChangeListeners,
Optional<Pip> pipOptional,
@@ -146,8 +146,9 @@
mTaskbarDelegate = taskbarDelegate;
mTaskbarDelegate.setDependencies(commandQueue, overviewProxyService,
navBarHelper, navigationModeController, sysUiFlagsContainer,
- dumpManager, autoHideController, lightBarController, pipOptional,
- backAnimation.orElse(null), taskStackChangeListeners);
+ dumpManager, autoHideControllerStore.forDisplay(mContext.getDisplayId()),
+ lightBarController, pipOptional, backAnimation.orElse(null),
+ taskStackChangeListeners);
mIsLargeScreen = isLargeScreen(mContext);
mIsPhone = determineIfPhone(mContext, deviceStateManager);
dumpManager.registerDumpable(this);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
index 96c0cac..c895732 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
@@ -151,6 +151,7 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.AutoHideController;
+import com.android.systemui.statusbar.phone.AutoHideControllerStore;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -261,8 +262,7 @@
private final LightBarController mMainLightBarController;
private final LightBarController.Factory mLightBarControllerFactory;
private AutoHideController mAutoHideController;
- private final AutoHideController mMainAutoHideController;
- private final AutoHideController.Factory mAutoHideControllerFactory;
+ private final AutoHideControllerStore mAutoHideControllerStore;
private final Optional<TelecomManager> mTelecomManagerOptional;
private final InputMethodManager mInputMethodManager;
private final TaskStackChangeListeners mTaskStackChangeListeners;
@@ -582,8 +582,7 @@
NavBarHelper navBarHelper,
LightBarController mainLightBarController,
LightBarController.Factory lightBarControllerFactory,
- AutoHideController mainAutoHideController,
- AutoHideController.Factory autoHideControllerFactory,
+ AutoHideControllerStore autoHideControllerStore,
Optional<TelecomManager> telecomManagerOptional,
InputMethodManager inputMethodManager,
DeadZone deadZone,
@@ -630,8 +629,7 @@
mNotificationShadeDepthController = notificationShadeDepthController;
mMainLightBarController = mainLightBarController;
mLightBarControllerFactory = lightBarControllerFactory;
- mMainAutoHideController = mainAutoHideController;
- mAutoHideControllerFactory = autoHideControllerFactory;
+ mAutoHideControllerStore = autoHideControllerStore;
mTelecomManagerOptional = telecomManagerOptional;
mInputMethodManager = inputMethodManager;
mUserContextProvider = userContextProvider;
@@ -846,13 +844,7 @@
? mMainLightBarController : mLightBarControllerFactory.create(mContext);
setLightBarController(lightBarController);
- // TODO(b/118592525): to support multi-display, we start to add something which is
- // per-display, while others may be global. I think it's time to
- // add a new class maybe named DisplayDependency to solve
- // per-display Dependency problem.
- // Alternative: this is a good case for a Dagger subcomponent. Same with LightBarController.
- AutoHideController autoHideController = mIsOnDefaultDisplay
- ? mMainAutoHideController : mAutoHideControllerFactory.create(mContext);
+ AutoHideController autoHideController = mAutoHideControllerStore.forDisplay(mDisplayId);
setAutoHideController(autoHideController);
restoreAppearanceAndTransientState();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
index 5167d17..9dc21fb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -629,11 +629,7 @@
id = R.string.accessibility_quick_settings_expand
)
)
- .padding(
- horizontal = {
- QuickSettingsShade.Dimensions.Padding.roundToPx()
- }
- )
+ .padding(horizontal = qsHorizontalMargin())
) {
QuickQuickSettingsLayout(
tiles = Tiles,
@@ -737,8 +733,8 @@
.sysuiResTag(ResIdTags.quickSettingsPanel)
.padding(
top = QuickSettingsShade.Dimensions.Padding,
- start = QuickSettingsShade.Dimensions.Padding,
- end = QuickSettingsShade.Dimensions.Padding,
+ start = qsHorizontalMargin(),
+ end = qsHorizontalMargin(),
)
) {
QuickSettingsLayout(
@@ -1127,6 +1123,8 @@
const val qsFooterActions = "qs_footer_actions"
}
+@Composable private fun qsHorizontalMargin() = dimensionResource(id = R.dimen.qs_horizontal_margin)
+
@Composable
private fun interactionsConfig() =
InteractionsConfig(
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
index b8ea8f9..c5c705c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
@@ -29,6 +29,7 @@
import android.view.ViewGroup
import android.view.WindowInsets
import android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL
+import android.view.accessibility.AccessibilityEvent
import android.widget.FrameLayout
import android.widget.ImageView
import com.android.systemui.res.R
@@ -83,6 +84,20 @@
})
gestureDetector.setIsLongpressEnabled(false)
+
+ // Extend the timeout on any accessibility event (e.g. voice access or explore-by-touch).
+ setAccessibilityDelegate(
+ object : AccessibilityDelegate() {
+ override fun onRequestSendAccessibilityEvent(
+ host: ViewGroup,
+ child: View,
+ event: AccessibilityEvent,
+ ): Boolean {
+ userInteractionCallback?.invoke()
+ return super.onRequestSendAccessibilityEvent(host, child, event)
+ }
+ }
+ )
}
override fun onFinishInflate() {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index 49ceba8..31780a5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -37,9 +37,11 @@
import androidx.lifecycle.LifecycleRegistry
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.theme.PlatformTheme
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.Flags
+import com.android.systemui.Flags.communalHubOnMobile
import com.android.systemui.ambient.touch.TouchMonitor
import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent
import com.android.systemui.communal.dagger.Communal
@@ -70,7 +72,6 @@
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
-import com.android.app.tracing.coroutines.launchTraced as launch
/**
* Controller that's responsible for the glanceable hub container view and its touch handling.
@@ -513,14 +514,19 @@
val touchOnUmo = keyguardMediaController.isWithinMediaViewBounds(ev.x.toInt(), ev.y.toInt())
val touchOnSmartspace =
lockscreenSmartspaceController.isWithinSmartspaceBounds(ev.x.toInt(), ev.y.toInt())
- if (!hubShowing && (touchOnNotifications || touchOnUmo || touchOnSmartspace)) {
+ val glanceableHubV2 = communalHubOnMobile()
+ if (
+ !hubShowing &&
+ (touchOnNotifications || touchOnUmo || touchOnSmartspace || glanceableHubV2)
+ ) {
logger.d({
"Lockscreen touch ignored: touchOnNotifications: $bool1, touchOnUmo: $bool2, " +
- "touchOnSmartspace: $bool3"
+ "touchOnSmartspace: $bool3, glanceableHubV2: $bool4"
}) {
bool1 = touchOnNotifications
bool2 = touchOnUmo
bool3 = touchOnSmartspace
+ bool4 = glanceableHubV2
}
return false
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 0c0add2..88522d5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -219,8 +219,8 @@
import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.ShadeTouchableRegionManager;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
import com.android.systemui.statusbar.phone.TapAgainViewController;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -298,7 +298,7 @@
* The minimum scale to "squish" the Shade and associated elements down to, for Back gesture
*/
public static final float SHADE_BACK_ANIM_MIN_SCALE = 0.9f;
- private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
+ private final ShadeTouchableRegionManager mShadeTouchableRegionManager;
private final Resources mResources;
private final KeyguardStateController mKeyguardStateController;
private final SysuiStatusBarStateController mStatusBarStateController;
@@ -695,7 +695,7 @@
ShadeLogger shadeLogger,
@ShadeDisplayAware ConfigurationController configurationController,
Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilder,
- StatusBarTouchableRegionManager statusBarTouchableRegionManager,
+ ShadeTouchableRegionManager shadeTouchableRegionManager,
ConversationNotificationManager conversationNotificationManager,
MediaHierarchyManager mediaHierarchyManager,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
@@ -840,7 +840,7 @@
mVibratorHelper = vibratorHelper;
mMSDLPlayer = msdlPlayer;
mVibrateOnOpening = mResources.getBoolean(R.bool.config_vibrateOnIconAnimation);
- mStatusBarTouchableRegionManager = statusBarTouchableRegionManager;
+ mShadeTouchableRegionManager = shadeTouchableRegionManager;
mSystemClock = systemClock;
mKeyguardMediaController = keyguardMediaController;
mMetricsLogger = metricsLogger;
@@ -1551,7 +1551,7 @@
private Rect calculateGestureExclusionRect() {
Rect exclusionRect = null;
- Region touchableRegion = mStatusBarTouchableRegionManager.calculateTouchableRegion();
+ Region touchableRegion = mShadeTouchableRegionManager.calculateTouchableRegion();
if (isFullyCollapsed() && touchableRegion != null) {
// Note: The manager also calculates the non-pinned touchable region
exclusionRect = touchableRegion.getBounds();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
index 04f89be..0df2299 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
@@ -98,7 +98,7 @@
import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
+import com.android.systemui.statusbar.phone.ShadeTouchableRegionManager;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.SplitShadeStateController;
@@ -141,7 +141,7 @@
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private final NotificationShadeDepthController mDepthController;
private final ShadeHeaderController mShadeHeaderController;
- private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
+ private final ShadeTouchableRegionManager mShadeTouchableRegionManager;
private final Provider<StatusBarLongPressGestureDetector> mStatusBarLongPressGestureDetector;
private final KeyguardStateController mKeyguardStateController;
private final KeyguardBypassController mKeyguardBypassController;
@@ -317,7 +317,7 @@
LockscreenShadeTransitionController lockscreenShadeTransitionController,
NotificationShadeDepthController notificationShadeDepthController,
ShadeHeaderController shadeHeaderController,
- StatusBarTouchableRegionManager statusBarTouchableRegionManager,
+ ShadeTouchableRegionManager shadeTouchableRegionManager,
Provider<StatusBarLongPressGestureDetector> statusBarLongPressGestureDetector,
KeyguardStateController keyguardStateController,
KeyguardBypassController keyguardBypassController,
@@ -366,7 +366,7 @@
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
mDepthController = notificationShadeDepthController;
mShadeHeaderController = shadeHeaderController;
- mStatusBarTouchableRegionManager = statusBarTouchableRegionManager;
+ mShadeTouchableRegionManager = shadeTouchableRegionManager;
mStatusBarLongPressGestureDetector = statusBarLongPressGestureDetector;
mKeyguardStateController = keyguardStateController;
mKeyguardBypassController = keyguardBypassController;
@@ -695,7 +695,7 @@
/* right= */ (int) mQsFrame.getX() + mQsFrame.getWidth(),
/* bottom= */ headerBottom + frameTop);
// Also allow QS to intercept if the touch is near the notch.
- mStatusBarTouchableRegionManager.updateRegionForNotch(mInterceptRegion);
+ mShadeTouchableRegionManager.updateRegionForNotch(mInterceptRegion);
final boolean onHeader = mInterceptRegion.contains((int) x, (int) y);
if (getExpanded()) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
index 0b36e68..91ca2ca 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
@@ -17,9 +17,10 @@
package com.android.systemui.shade
import android.content.Context
-import android.content.MutableContextWrapper
import android.content.res.Resources
import android.view.LayoutInflater
+import android.view.WindowManager
+import android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE
import com.android.systemui.CoreStartable
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.common.ui.ConfigurationStateImpl
@@ -29,9 +30,12 @@
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractorImpl
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.res.R
import com.android.systemui.scene.ui.view.WindowRootView
+import com.android.systemui.shade.data.repository.MutableShadeDisplaysRepository
import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
import com.android.systemui.shade.data.repository.ShadeDisplaysRepositoryImpl
+import com.android.systemui.shade.display.ShadeDisplayPolicyModule
import com.android.systemui.shade.domain.interactor.ShadeDisplaysInteractor
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl
@@ -56,7 +60,7 @@
* By using this dedicated module, we ensure the notification shade window always utilizes the
* correct display context and resources, regardless of the display it's on.
*/
-@Module(includes = [OptionalShadeDisplayAwareBindings::class])
+@Module(includes = [OptionalShadeDisplayAwareBindings::class, ShadeDisplayPolicyModule::class])
object ShadeDisplayAwareModule {
/** Creates a new context for the shade window. */
@@ -65,7 +69,9 @@
@SysUISingleton
fun provideShadeDisplayAwareContext(context: Context): Context {
return if (ShadeWindowGoesAround.isEnabled) {
- MutableContextWrapper(context)
+ context
+ .createWindowContext(context.display, TYPE_NOTIFICATION_SHADE, /* options= */ null)
+ .apply { setTheme(R.style.Theme_SystemUI) }
} else {
context
}
@@ -74,6 +80,20 @@
@Provides
@ShadeDisplayAware
@SysUISingleton
+ fun provideShadeWindowManager(
+ defaultWindowManager: WindowManager,
+ @ShadeDisplayAware context: Context,
+ ): WindowManager {
+ return if (ShadeWindowGoesAround.isEnabled) {
+ context.getSystemService(WindowManager::class.java) as WindowManager
+ } else {
+ defaultWindowManager
+ }
+ }
+
+ @Provides
+ @ShadeDisplayAware
+ @SysUISingleton
fun provideShadeDisplayAwareResources(@ShadeDisplayAware context: Context): Resources {
return context.resources
}
@@ -163,6 +183,15 @@
return impl
}
+ @SysUISingleton
+ @Provides
+ fun provideMutableShadePositionRepository(
+ impl: ShadeDisplaysRepositoryImpl
+ ): MutableShadeDisplaysRepository {
+ ShadeWindowGoesAround.isUnexpectedlyInLegacyMode()
+ return impl
+ }
+
@Provides
@IntoMap
@ClassKey(ShadePrimaryDisplayCommand::class)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt
index a5d9e96..a54f6b9 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt
@@ -20,11 +20,14 @@
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.display.data.repository.DisplayRepository
-import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
+import com.android.systemui.shade.data.repository.MutableShadeDisplaysRepository
+import com.android.systemui.shade.display.ShadeDisplayPolicy
+import com.android.systemui.shade.display.SpecificDisplayIdPolicy
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
import java.io.PrintWriter
import javax.inject.Inject
+import kotlin.text.toIntOrNull
@SysUISingleton
class ShadePrimaryDisplayCommand
@@ -32,7 +35,9 @@
constructor(
private val commandRegistry: CommandRegistry,
private val displaysRepository: DisplayRepository,
- private val positionRepository: ShadeDisplaysRepository,
+ private val positionRepository: MutableShadeDisplaysRepository,
+ private val policies: Set<@JvmSuppressWildcards ShadeDisplayPolicy>,
+ private val defaultPolicy: ShadeDisplayPolicy,
) : Command, CoreStartable {
override fun start() {
@@ -40,8 +45,11 @@
}
override fun help(pw: PrintWriter) {
- pw.println("shade_display_override <displayId> ")
- pw.println("Set the display which is holding the shade.")
+ pw.println("shade_display_override (<displayId>|<policyName>) ")
+ pw.println("Set the display which is holding the shade, or the policy that defines it.")
+ pw.println()
+ pw.println("shade_display_override policies")
+ pw.println("Lists available policies")
pw.println()
pw.println("shade_display_override reset ")
pw.println("Reset the display which is holding the shade.")
@@ -68,21 +76,27 @@
"reset" -> reset()
"list",
"status" -> printStatus()
+ "policies" -> printPolicies()
"any_external" -> anyExternal()
- else -> {
- val cmdAsInteger = command?.toIntOrNull()
- if (cmdAsInteger != null) {
- changeDisplay(displayId = cmdAsInteger)
- } else {
- help(pw)
- }
+ null -> help(pw)
+ else -> parsePolicy(command)
+ }
+ }
+
+ private fun parsePolicy(policyIdentifier: String) {
+ val displayId = policyIdentifier.toIntOrNull()
+ when {
+ displayId != null -> changeDisplay(displayId = displayId)
+ policies.any { it.name == policyIdentifier } -> {
+ positionRepository.policy.value = policies.first { it.name == policyIdentifier }
}
+ else -> help(pw)
}
}
private fun reset() {
- positionRepository.resetDisplayId()
- pw.println("Reset shade primary display id to ${Display.DEFAULT_DISPLAY}")
+ positionRepository.policy.value = defaultPolicy
+ pw.println("Reset shade display policy to default policy: ${defaultPolicy.name}")
}
private fun printStatus() {
@@ -95,6 +109,15 @@
}
}
+ private fun printPolicies() {
+ val currentPolicyName = positionRepository.policy.value.name
+ pw.println("Available policies: ")
+ policies.forEach {
+ pw.print(" - ${it.name}")
+ pw.println(if (currentPolicyName == it.name) " (Current policy)" else "")
+ }
+ }
+
private fun anyExternal() {
val anyExternalDisplay =
displaysRepository.displays.value.firstOrNull {
@@ -116,7 +139,7 @@
}
private fun setDisplay(id: Int) {
- positionRepository.setDisplayId(id)
+ positionRepository.policy.value = SpecificDisplayIdPolicy(id)
pw.println("New shade primary display id is $id")
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadeDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadeDisplayRepository.kt
index 71c5658..732d4d1 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadeDisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadeDisplayRepository.kt
@@ -23,14 +23,14 @@
class FakeShadeDisplayRepository : ShadeDisplaysRepository {
private val _displayId = MutableStateFlow(Display.DEFAULT_DISPLAY)
- override fun setDisplayId(displayId: Int) {
+ fun setDisplayId(displayId: Int) {
_displayId.value = displayId
}
override val displayId: StateFlow<Int>
get() = _displayId
- override fun resetDisplayId() {
+ fun resetDisplayId() {
_displayId.value = Display.DEFAULT_DISPLAY
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt
index 4a95e33..756241e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt
@@ -18,37 +18,40 @@
import android.view.Display
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.shade.display.ShadeDisplayPolicy
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.stateIn
+/** Source of truth for the display currently holding the shade. */
interface ShadeDisplaysRepository {
/** ID of the display which currently hosts the shade */
val displayId: StateFlow<Int>
-
- /**
- * Updates the value of the shade display id stored, emitting to the new display id to every
- * component dependent on the shade display id
- */
- fun setDisplayId(displayId: Int)
-
- /** Resets value of shade primary display to the default display */
- fun resetDisplayId()
}
-/** Source of truth for the display currently holding the shade. */
+/** Allows to change the policy that determines in which display the Shade window is visible. */
+interface MutableShadeDisplaysRepository : ShadeDisplaysRepository {
+ /** Updates the policy to select where the shade is visible. */
+ val policy: MutableStateFlow<ShadeDisplayPolicy>
+}
+
+/** Keeps the policy and propagates the display id for the shade from it. */
@SysUISingleton
-class ShadeDisplaysRepositoryImpl @Inject constructor() : ShadeDisplaysRepository {
- private val _displayId = MutableStateFlow(Display.DEFAULT_DISPLAY)
+@OptIn(ExperimentalCoroutinesApi::class)
+class ShadeDisplaysRepositoryImpl
+@Inject
+constructor(defaultPolicy: ShadeDisplayPolicy, @Background bgScope: CoroutineScope) :
+ MutableShadeDisplaysRepository {
+ override val policy = MutableStateFlow<ShadeDisplayPolicy>(defaultPolicy)
- override val displayId: StateFlow<Int>
- get() = _displayId
-
- override fun setDisplayId(displayId: Int) {
- _displayId.value = displayId
- }
-
- override fun resetDisplayId() {
- _displayId.value = Display.DEFAULT_DISPLAY
- }
+ override val displayId: StateFlow<Int> =
+ policy
+ .flatMapLatest { it.displayId }
+ .stateIn(bgScope, SharingStarted.WhileSubscribed(), Display.DEFAULT_DISPLAY)
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/AnyExternalShadeDisplayPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/AnyExternalShadeDisplayPolicy.kt
new file mode 100644
index 0000000..3f6c949
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/display/AnyExternalShadeDisplayPolicy.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.display
+
+import android.view.Display
+import android.view.Display.DEFAULT_DISPLAY
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.display.data.repository.DisplayRepository
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * Returns an external display if one exists, otherwise the default display.
+ *
+ * If there are multiple external displays, the one with minimum display ID is returned.
+ */
+@SysUISingleton
+class AnyExternalShadeDisplayPolicy
+@Inject
+constructor(displayRepository: DisplayRepository, @Background bgScope: CoroutineScope) :
+ ShadeDisplayPolicy {
+ override val name: String
+ get() = "any_external_display"
+
+ override val displayId: StateFlow<Int> =
+ displayRepository.displays
+ .map { displays ->
+ displays
+ .filter { it.displayId != DEFAULT_DISPLAY && it.type in ALLOWED_DISPLAY_TYPES }
+ .minOfOrNull { it.displayId } ?: DEFAULT_DISPLAY
+ }
+ .stateIn(bgScope, SharingStarted.WhileSubscribed(), DEFAULT_DISPLAY)
+
+ private companion object {
+ val ALLOWED_DISPLAY_TYPES =
+ setOf(Display.TYPE_EXTERNAL, Display.TYPE_OVERLAY, Display.TYPE_WIFI)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt
new file mode 100644
index 0000000..1b22ee4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.display
+
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+import kotlinx.coroutines.flow.StateFlow
+
+/** Describes the display the shade should be shown in. */
+interface ShadeDisplayPolicy {
+ val name: String
+
+ /** The display id the shade should be at, according to this policy. */
+ val displayId: StateFlow<Int>
+}
+
+@Module
+interface ShadeDisplayPolicyModule {
+ @IntoSet
+ @Binds
+ fun provideDefaultPolicyToSet(impl: DefaultShadeDisplayPolicy): ShadeDisplayPolicy
+
+ @IntoSet
+ @Binds
+ fun provideAnyExternalShadeDisplayPolicyToSet(
+ impl: AnyExternalShadeDisplayPolicy
+ ): ShadeDisplayPolicy
+
+ @Binds fun provideDefaultPolicy(impl: DefaultShadeDisplayPolicy): ShadeDisplayPolicy
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/SpecificDisplayIdPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/SpecificDisplayIdPolicy.kt
new file mode 100644
index 0000000..13e7664
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/display/SpecificDisplayIdPolicy.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.display
+
+import android.view.Display
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+/** Policy to specify a display id explicitly. */
+open class SpecificDisplayIdPolicy(displayId: Int) : ShadeDisplayPolicy {
+ override val name: String
+ get() = "display_${displayId}_policy"
+
+ override val displayId: StateFlow<Int> = MutableStateFlow(displayId)
+}
+
+class DefaultShadeDisplayPolicy @Inject constructor() :
+ SpecificDisplayIdPolicy(Display.DEFAULT_DISPLAY)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt
index 432d5f5..fb2cbec 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt
@@ -16,28 +16,21 @@
package com.android.systemui.shade.domain.interactor
-import android.content.ComponentCallbacks
import android.content.Context
-import android.content.MutableContextWrapper
-import android.content.res.Configuration
-import android.content.res.Resources
import android.util.Log
import android.view.WindowManager
-import android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE
+import androidx.annotation.UiThread
import com.android.app.tracing.coroutines.launchTraced
import com.android.app.tracing.traceSection
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository
-import com.android.systemui.display.shared.model.DisplayWindowProperties
import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.ShadeWindowLayoutParams
import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
-import com.android.systemui.statusbar.phone.ConfigurationForwarder
import com.android.systemui.util.kotlin.getOrNull
import java.util.Optional
import javax.inject.Inject
@@ -53,13 +46,14 @@
optionalShadeRootView: Optional<WindowRootView>,
private val shadePositionRepository: ShadeDisplaysRepository,
@ShadeDisplayAware private val shadeContext: Context,
- @ShadeDisplayAware private val shadeResources: Resources,
- private val displayWindowPropertiesRepository: DisplayWindowPropertiesRepository,
+ @ShadeDisplayAware private val wm: WindowManager,
@Background private val bgScope: CoroutineScope,
- @ShadeDisplayAware private val shadeConfigurationForwarder: ConfigurationForwarder,
@Main private val mainThreadContext: CoroutineContext,
) : CoreStartable {
+ private val shadeLayoutParams: WindowManager.LayoutParams =
+ ShadeWindowLayoutParams.create(shadeContext)
+
private val shadeRootView =
optionalShadeRootView.getOrNull()
?: error(
@@ -69,9 +63,6 @@
"""
.trimIndent()
)
- // TODO: b/362719719 - Get rid of this callback as the root view should automatically get the
- // correct configuration once it's moved to another window.
- private var unregisterConfigChangedCallbacks: (() -> Unit)? = null
override fun start() {
ShadeWindowGoesAround.isUnexpectedlyInLegacyMode()
@@ -94,7 +85,7 @@
return
}
try {
- moveShadeWindow(fromId = currentId, toId = destinationId)
+ withContext(mainThreadContext) { moveShadeWindow(toId = destinationId) }
} catch (e: IllegalStateException) {
Log.e(
TAG,
@@ -104,68 +95,26 @@
}
}
- private suspend fun moveShadeWindow(fromId: Int, toId: Int) {
- val (_, _, _, sourceWm) = getDisplayWindowProperties(fromId)
- val (_, _, destContext, destWm) = getDisplayWindowProperties(toId)
- withContext(mainThreadContext) {
- traceSection({ "MovingShadeWindow from $fromId to $toId" }) {
- removeShade(sourceWm)
- addShade(destWm)
- overrideContextAndResources(newContext = destContext)
- registerConfigurationChange(destContext)
- }
- traceSection("ShadeDisplaysInteractor#onConfigurationChanged") {
- dispatchConfigurationChanged(destContext.resources.configuration)
- }
+ @UiThread
+ private fun moveShadeWindow(toId: Int) {
+ traceSection({ "moveShadeWindow to $toId" }) {
+ removeShadeWindow()
+ updateContextDisplay(toId)
+ addShadeWindow()
}
}
- private fun removeShade(wm: WindowManager): Unit =
- traceSection("removeView") { wm.removeView(shadeRootView) }
+ @UiThread
+ private fun removeShadeWindow(): Unit =
+ traceSection("removeShadeWindow") { wm.removeView(shadeRootView) }
- private fun addShade(wm: WindowManager): Unit =
- traceSection("addView") {
- wm.addView(shadeRootView, ShadeWindowLayoutParams.create(shadeContext))
- }
+ @UiThread
+ private fun addShadeWindow(): Unit =
+ traceSection("addShadeWindow") { wm.addView(shadeRootView, shadeLayoutParams) }
- private fun overrideContextAndResources(newContext: Context) {
- val contextWrapper =
- shadeContext as? MutableContextWrapper
- ?: error("Shade context is not a MutableContextWrapper!")
- contextWrapper.baseContext = newContext
- // Override needed in case someone is keeping a reference to the resources from the old
- // context.
- // TODO: b/362719719 - This shouldn't be needed, as resources should be updated when the
- // window is moved to the new display automatically.
- shadeResources.impl = shadeContext.resources.impl
- }
-
- private fun dispatchConfigurationChanged(newConfig: Configuration) {
- shadeConfigurationForwarder.onConfigurationChanged(newConfig)
- shadeRootView.dispatchConfigurationChanged(newConfig)
- shadeRootView.requestLayout()
- }
-
- private fun registerConfigurationChange(context: Context) {
- // we should keep only one at the time.
- unregisterConfigChangedCallbacks?.invoke()
- val callback =
- object : ComponentCallbacks {
- override fun onConfigurationChanged(newConfig: Configuration) {
- dispatchConfigurationChanged(newConfig)
- }
-
- override fun onLowMemory() {}
- }
- context.registerComponentCallbacks(callback)
- unregisterConfigChangedCallbacks = {
- context.unregisterComponentCallbacks(callback)
- unregisterConfigChangedCallbacks = null
- }
- }
-
- private fun getDisplayWindowProperties(displayId: Int): DisplayWindowProperties {
- return displayWindowPropertiesRepository.get(displayId, TYPE_NOTIFICATION_SHADE)
+ @UiThread
+ private fun updateContextDisplay(newDisplayId: Int) {
+ traceSection("updateContextDisplay") { shadeContext.updateDisplay(newDisplayId) }
}
private companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt
index 8ce0dbf..6db610b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt
@@ -21,7 +21,10 @@
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogBufferFactory
import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.DemoNotifChipViewModel
+import com.android.systemui.statusbar.chips.notification.domain.interactor.StatusBarNotificationChipsInteractor
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import dagger.Binds
+import dagger.Lazy
import dagger.Module
import dagger.Provides
import dagger.multibindings.ClassKey
@@ -41,5 +44,19 @@
fun provideChipsLogBuffer(factory: LogBufferFactory): LogBuffer {
return factory.create("StatusBarChips", 200)
}
+
+ @Provides
+ @SysUISingleton
+ @IntoMap
+ @ClassKey(StatusBarNotificationChipsInteractor::class)
+ fun statusBarNotificationChipsInteractorAsCoreStartable(
+ interactorLazy: Lazy<StatusBarNotificationChipsInteractor>
+ ): CoreStartable {
+ return if (StatusBarNotifChips.isEnabled) {
+ interactorLazy.get()
+ } else {
+ CoreStartable.NOP
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt
new file mode 100644
index 0000000..087b510
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.notification.domain.interactor
+
+import com.android.systemui.activity.data.repository.ActivityManagerRepository
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.statusbar.chips.StatusBarChipLogTags.pad
+import com.android.systemui.statusbar.chips.StatusBarChipsLog
+import com.android.systemui.statusbar.chips.notification.domain.model.NotificationChipModel
+import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+
+/**
+ * Interactor representing a single notification's status bar chip.
+ *
+ * [startingModel.key] dictates which notification this interactor corresponds to - all updates sent
+ * to this interactor via [setNotification] should only be for the notification with the same key.
+ *
+ * [StatusBarNotificationChipsInteractor] will collect all the individual instances of this
+ * interactor and send all the necessary information to the UI layer.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+class SingleNotificationChipInteractor
+@AssistedInject
+constructor(
+ @Assisted startingModel: ActiveNotificationModel,
+ private val activityManagerRepository: ActivityManagerRepository,
+ @StatusBarChipsLog private val logBuffer: LogBuffer,
+) {
+ private val key = startingModel.key
+ private val logger = Logger(logBuffer, "Notif".pad())
+ // [StatusBarChipLogTag] recommends a max tag length of 20, so [extraLogTag] should NOT be the
+ // top-level tag. It should instead be provided as the first string in each log message.
+ private val extraLogTag = "SingleChipInteractor[key=$key]"
+
+ private val _notificationModel = MutableStateFlow(startingModel)
+
+ /**
+ * Sets the new notification info corresponding to this interactor. The key on [model] *must*
+ * match the key on the original [startingModel], otherwise the update won't be processed.
+ */
+ fun setNotification(model: ActiveNotificationModel) {
+ if (model.key != this.key) {
+ logger.w({ "$str1: received model for different key $str2" }) {
+ str1 = extraLogTag
+ str2 = model.key
+ }
+ return
+ }
+ _notificationModel.value = model
+ }
+
+ private val uid: Flow<Int> = _notificationModel.map { it.uid }
+
+ /** True if the application managing the notification is visible to the user. */
+ private val isAppVisible: Flow<Boolean> =
+ uid.flatMapLatest { currentUid ->
+ activityManagerRepository.createIsAppVisibleFlow(currentUid, logger, extraLogTag)
+ }
+
+ /**
+ * Emits this notification's status bar chip, or null if this notification shouldn't show a
+ * status bar chip.
+ */
+ val notificationChip: Flow<NotificationChipModel?> =
+ combine(_notificationModel, isAppVisible) { notif, isAppVisible ->
+ if (isAppVisible) {
+ // If the app that posted this notification is visible, we want to hide the chip
+ // because information between the status bar chip and the app itself could be
+ // out-of-sync (like a timer that's slightly off)
+ null
+ } else {
+ notif.toNotificationChipModel()
+ }
+ }
+
+ private fun ActiveNotificationModel.toNotificationChipModel(): NotificationChipModel? {
+ val statusBarChipIconView = this.statusBarChipIconView
+ if (statusBarChipIconView == null) {
+ logger.w({ "$str1: Can't show chip because status bar chip icon view is null" }) {
+ str1 = extraLogTag
+ }
+ return null
+ }
+ return NotificationChipModel(key, statusBarChipIconView)
+ }
+
+ @AssistedFactory
+ fun interface Factory {
+ fun create(startingModel: ActiveNotificationModel): SingleNotificationChipInteractor
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt
index 9e09671..e8cb35b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt
@@ -17,16 +17,42 @@
package com.android.systemui.statusbar.chips.notification.domain.interactor
import android.annotation.SuppressLint
+import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.statusbar.chips.StatusBarChipLogTags.pad
+import com.android.systemui.statusbar.chips.StatusBarChipsLog
+import com.android.systemui.statusbar.chips.notification.domain.model.NotificationChipModel
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
+import com.android.systemui.util.kotlin.pairwise
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
/** An interactor for the notification chips shown in the status bar. */
@SysUISingleton
-class StatusBarNotificationChipsInteractor @Inject constructor() {
+@OptIn(ExperimentalCoroutinesApi::class)
+class StatusBarNotificationChipsInteractor
+@Inject
+constructor(
+ @Background private val backgroundScope: CoroutineScope,
+ private val activeNotificationsInteractor: ActiveNotificationsInteractor,
+ private val singleNotificationChipInteractorFactory: SingleNotificationChipInteractor.Factory,
+ @StatusBarChipsLog private val logBuffer: LogBuffer,
+) : CoreStartable {
+ private val logger = Logger(logBuffer, "AllNotifs".pad())
// Each chip tap is an individual event, *not* a state, which is why we're using SharedFlow not
// StateFlow. There shouldn't be multiple updates per frame, which should avoid performance
@@ -45,4 +71,79 @@
StatusBarNotifChips.assertInNewMode()
_promotedNotificationChipTapEvent.emit(key)
}
+
+ /**
+ * A cache of interactors. Each currently-promoted notification should have a corresponding
+ * interactor in this map.
+ */
+ private val promotedNotificationInteractorMap =
+ mutableMapOf<String, SingleNotificationChipInteractor>()
+
+ /**
+ * A list of interactors. Each currently-promoted notification should have a corresponding
+ * interactor in this list.
+ */
+ private val promotedNotificationInteractors =
+ MutableStateFlow<List<SingleNotificationChipInteractor>>(emptyList())
+
+ override fun start() {
+ if (!StatusBarNotifChips.isEnabled) {
+ return
+ }
+
+ backgroundScope.launch("StatusBarNotificationChipsInteractor") {
+ activeNotificationsInteractor.promotedOngoingNotifications
+ .pairwise(initialValue = emptyList())
+ .collect { (oldNotifs, currentNotifs) ->
+ val removedNotifs = oldNotifs.minus(currentNotifs.toSet())
+ removedNotifs.forEach { removedNotif ->
+ val wasRemoved = promotedNotificationInteractorMap.remove(removedNotif.key)
+ if (wasRemoved == null) {
+ logger.w({
+ "Attempted to remove $str1 from interactor map but it wasn't present"
+ }) {
+ str1 = removedNotif.key
+ }
+ }
+ }
+ currentNotifs.forEach { notif ->
+ val interactor =
+ promotedNotificationInteractorMap.computeIfAbsent(notif.key) {
+ singleNotificationChipInteractorFactory.create(notif)
+ }
+ interactor.setNotification(notif)
+ }
+ logger.d({ "Interactors: $str1" }) {
+ str1 =
+ promotedNotificationInteractorMap.keys.joinToString(separator = " /// ")
+ }
+ promotedNotificationInteractors.value =
+ promotedNotificationInteractorMap.values.toList()
+ }
+ }
+ }
+
+ /**
+ * A flow modeling the notifications that should be shown as chips in the status bar. Emits an
+ * empty list if there are no notifications that should show a status bar chip.
+ */
+ val notificationChips: Flow<List<NotificationChipModel>> =
+ if (StatusBarNotifChips.isEnabled) {
+ // For all our current interactors...
+ promotedNotificationInteractors.flatMapLatest { interactors ->
+ if (interactors.isNotEmpty()) {
+ // Combine each interactor's [notificationChip] flow...
+ val allNotificationChips: List<Flow<NotificationChipModel?>> =
+ interactors.map { interactor -> interactor.notificationChip }
+ combine(allNotificationChips) {
+ // ... and emit just the non-null chips
+ it.filterNotNull()
+ }
+ } else {
+ flowOf(emptyList())
+ }
+ }
+ } else {
+ flowOf(emptyList())
+ }
}
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt
similarity index 66%
copy from ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java
copy to packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt
index 7d3d8b9..5698ee6 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.platform.test.ravenwood;
-/** Stub class. The actual implementation is in junit-impl-src. */
-public class RavenwoodConfigState {
- public RavenwoodConfigState(RavenwoodConfig config) {
- }
-}
+package com.android.systemui.statusbar.chips.notification.domain.model
+
+import com.android.systemui.statusbar.StatusBarIconView
+
+/** Modeling all the data needed to render a status bar notification chip. */
+data class NotificationChipModel(val key: String, val statusBarChipIconView: StatusBarIconView)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
index 7526748..9eff627 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
@@ -20,11 +20,10 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.chips.notification.domain.interactor.StatusBarNotificationChipsInteractor
+import com.android.systemui.statusbar.chips.notification.domain.model.NotificationChipModel
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
-import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
-import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
@@ -37,7 +36,6 @@
@Inject
constructor(
@Application private val applicationScope: CoroutineScope,
- activeNotificationsInteractor: ActiveNotificationsInteractor,
private val notifChipsInteractor: StatusBarNotificationChipsInteractor,
) {
/**
@@ -45,19 +43,14 @@
* no notifications that should show a status bar chip.
*/
val chips: Flow<List<OngoingActivityChipModel.Shown>> =
- activeNotificationsInteractor.promotedOngoingNotifications.map { notifications ->
- notifications.mapNotNull { it.toChipModel() }
+ notifChipsInteractor.notificationChips.map { notifications ->
+ notifications.map { it.toActivityChipModel() }
}
- /**
- * Converts the notification to the [OngoingActivityChipModel] object. Returns null if the
- * notification has invalid data such that it can't be displayed as a chip.
- */
- private fun ActiveNotificationModel.toChipModel(): OngoingActivityChipModel.Shown? {
+ /** Converts the notification to the [OngoingActivityChipModel] object. */
+ private fun NotificationChipModel.toActivityChipModel(): OngoingActivityChipModel.Shown {
StatusBarNotifChips.assertInNewMode()
- // TODO(b/364653005): Log error if there's no icon view.
- val rawIcon = this.statusBarChipIconView ?: return null
- val icon = OngoingActivityChipModel.ChipIcon.StatusBarView(rawIcon)
+ val icon = OngoingActivityChipModel.ChipIcon.StatusBarView(this.statusBarChipIconView)
// TODO(b/364653005): Use the notification color if applicable.
val colors = ColorsModel.Themed
val onClickListener =
@@ -65,7 +58,9 @@
// The notification pipeline needs everything to run on the main thread, so keep
// this event on the main thread.
applicationScope.launch {
- notifChipsInteractor.onPromotedNotificationChipTapped(this@toChipModel.key)
+ notifChipsInteractor.onPromotedNotificationChipTapped(
+ this@toActivityChipModel.key
+ )
}
}
return OngoingActivityChipModel.Shown.IconOnly(icon, colors, onClickListener)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt
index 84c7ab2..9e9a38e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt
@@ -26,6 +26,7 @@
import com.android.systemui.statusbar.data.repository.LightBarControllerStore
import com.android.systemui.statusbar.data.repository.PrivacyDotWindowControllerStore
import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
+import com.android.systemui.statusbar.phone.AutoHideControllerStore
import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore
import com.android.systemui.util.kotlin.pairwiseBy
@@ -50,6 +51,7 @@
private val initializerStore: StatusBarInitializerStore,
private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
private val statusBarInitializerStore: StatusBarInitializerStore,
+ private val autoHideControllerStore: AutoHideControllerStore,
private val privacyDotWindowControllerStore: PrivacyDotWindowControllerStore,
private val lightBarControllerStore: LightBarControllerStore,
) : CoreStartable {
@@ -95,6 +97,7 @@
statusBarModeRepositoryStore.forDisplay(displayId),
initializerStore.forDisplay(displayId),
statusBarWindowControllerStore.forDisplay(displayId),
+ autoHideControllerStore.forDisplay(displayId),
)
.start()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
index ff4760f..9f5a311 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
@@ -71,9 +71,9 @@
@Assisted private val statusBarInitializer: StatusBarInitializer,
@Assisted private val statusBarWindowController: StatusBarWindowController,
@Main private val mainContext: CoroutineContext,
+ @Assisted private val autoHideController: AutoHideController,
private val demoModeController: DemoModeController,
private val pluginDependencyProvider: PluginDependencyProvider,
- private val autoHideController: AutoHideController,
private val remoteInputManager: NotificationRemoteInputManager,
private val notificationShadeWindowViewControllerLazy:
Lazy<NotificationShadeWindowViewController>,
@@ -210,10 +210,6 @@
}
private fun setUpAutoHide() {
- if (displayId != Display.DEFAULT_DISPLAY) {
- return
- }
- // TODO(b/373309973): per display implementation of auto hide controller
autoHideController.setStatusBar(
object : AutoHideUiElement {
override fun synchronizeState() {}
@@ -241,10 +237,7 @@
if (!demoModeController.isInDemoMode) {
barTransitions.transitionTo(barMode.toTransitionModeInt(), animate)
}
- if (displayId == Display.DEFAULT_DISPLAY) {
- // TODO(b/373309973): per display implementation of auto hide controller
- autoHideController.touchAutoHide()
- }
+ autoHideController.touchAutoHide()
}
private fun updateBubblesVisibility(statusBarVisible: Boolean) {
@@ -288,6 +281,7 @@
statusBarModeRepository: StatusBarModePerDisplayRepository,
statusBarInitializer: StatusBarInitializer,
statusBarWindowController: StatusBarWindowController,
+ autoHideController: AutoHideController,
): StatusBarOrchestrator
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index a55a165..01efd5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -43,6 +43,7 @@
import com.android.systemui.keyguard.shared.model.StatusBarState.SHADE
import com.android.systemui.keyguard.shared.model.StatusBarState.SHADE_LOCKED
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToPrimaryBouncerTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.AodToGoneTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.AodToLockscreenTransitionViewModel
@@ -124,6 +125,8 @@
private val notificationStackAppearanceInteractor: NotificationStackAppearanceInteractor,
private val alternateBouncerToGoneTransitionViewModel:
AlternateBouncerToGoneTransitionViewModel,
+ private val alternateBouncerToPrimaryBouncerTransitionViewModel:
+ AlternateBouncerToPrimaryBouncerTransitionViewModel,
private val aodToGoneTransitionViewModel: AodToGoneTransitionViewModel,
private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
private val aodToOccludedTransitionViewModel: AodToOccludedTransitionViewModel,
@@ -560,6 +563,7 @@
lockscreenToGoneTransitionViewModel.notificationAlpha(viewState),
lockscreenToOccludedTransitionViewModel.lockscreenAlpha,
lockscreenToPrimaryBouncerTransitionViewModel.lockscreenAlpha,
+ alternateBouncerToPrimaryBouncerTransitionViewModel.lockscreenAlpha,
occludedToAodTransitionViewModel.lockscreenAlpha,
occludedToGoneTransitionViewModel.notificationAlpha(viewState),
occludedToLockscreenTransitionViewModel.lockscreenAlpha,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 7fa6707..c6af328 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -401,7 +401,7 @@
private final KeyguardBypassController mKeyguardBypassController;
private final KeyguardStateController mKeyguardStateController;
private final HeadsUpManager mHeadsUpManager;
- private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
+ private final ShadeTouchableRegionManager mShadeTouchableRegionManager;
private final FalsingCollector mFalsingCollector;
private final FalsingManager mFalsingManager;
private final BroadcastDispatcher mBroadcastDispatcher;
@@ -681,7 +681,7 @@
KeyguardIndicationController keyguardIndicationController,
DemoModeController demoModeController,
Lazy<NotificationShadeDepthController> notificationShadeDepthControllerLazy,
- StatusBarTouchableRegionManager statusBarTouchableRegionManager,
+ ShadeTouchableRegionManager shadeTouchableRegionManager,
BrightnessSliderController.Factory brightnessSliderFactory,
ScreenOffAnimationController screenOffAnimationController,
WallpaperController wallpaperController,
@@ -724,7 +724,7 @@
mHeadsUpManager = headsUpManager;
mBackActionInteractor = backActionInteractor;
mKeyguardIndicationController = keyguardIndicationController;
- mStatusBarTouchableRegionManager = statusBarTouchableRegionManager;
+ mShadeTouchableRegionManager = shadeTouchableRegionManager;
mFalsingCollector = falsingCollector;
mFalsingManager = falsingManager;
mBroadcastDispatcher = broadcastDispatcher;
@@ -1232,7 +1232,7 @@
mStatusBarInitializer.initializeStatusBar();
}
- mStatusBarTouchableRegionManager.setup(getNotificationShadeWindowView());
+ mShadeTouchableRegionManager.setup(getNotificationShadeWindowView());
if (!StatusBarConnectedDisplays.isEnabled()) {
createNavigationBar(result);
@@ -1856,10 +1856,10 @@
pw.println(" mHeadsUpManager: null");
}
- if (mStatusBarTouchableRegionManager != null) {
- mStatusBarTouchableRegionManager.dump(pw, args);
+ if (mShadeTouchableRegionManager != null) {
+ mShadeTouchableRegionManager.dump(pw, args);
} else {
- pw.println(" mStatusBarTouchableRegionManager: null");
+ pw.println(" mShadeTouchableRegionManager: null");
}
if (mLightBarController != null) {
@@ -2566,7 +2566,7 @@
dismissVolumeDialog();
mWakeUpCoordinator.setFullyAwake(false);
mKeyguardBypassController.onStartedGoingToSleep();
- mStatusBarTouchableRegionManager.updateTouchableRegion();
+ mShadeTouchableRegionManager.updateTouchableRegion();
// The unlocked screen off and fold to aod animations might use our LightRevealScrim -
// we need to be expanded for it to be visible.
@@ -2655,7 +2655,7 @@
// once we fully woke up.
updateRevealEffect(true /* wakingUp */);
updateNotificationPanelTouchState();
- mStatusBarTouchableRegionManager.updateTouchableRegion();
+ mShadeTouchableRegionManager.updateTouchableRegion();
// If we are waking up during the screen off animation, we should undo making the
// expanded visible (we did that so the LightRevealScrim would be visible).
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java
index 1a4f3ca..ea67f1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java
@@ -527,12 +527,16 @@
@Override
public LightBarController create(@NonNull Context context) {
// TODO: b/380394368 - Make sure correct per display instances are used.
- return mFactory.create(
+ LightBarControllerImpl lightBarController = mFactory.create(
context.getDisplayId(),
mApplicationScope,
mDarkIconDispatcherStore.getDefaultDisplay(),
mStatusBarModeRepositoryStore.getDefaultDisplay()
);
+ // Calling start() manually to keep the legacy behavior. Before, LightBarControllerImpl
+ // was doing work in the constructor, which moved to start().
+ lightBarController.start();
+ return lightBarController;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeTouchableRegionManager.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeTouchableRegionManager.java
index c09b9c5..bea8397 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeTouchableRegionManager.java
@@ -62,7 +62,7 @@
* of HeadsUpNotifications.
*/
@SysUISingleton
-public final class StatusBarTouchableRegionManager implements Dumpable {
+public final class ShadeTouchableRegionManager implements Dumpable {
private static final String TAG = "TouchableRegionManager";
private final Context mContext;
@@ -90,7 +90,7 @@
private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener;
@Inject
- public StatusBarTouchableRegionManager(
+ public ShadeTouchableRegionManager(
Context context,
NotificationShadeWindowController notificationShadeWindowController,
ConfigurationController configurationController,
@@ -165,7 +165,7 @@
@Override
public void dump(PrintWriter pw, String[] args) {
- pw.println("StatusBarTouchableRegionManager state:");
+ pw.println("ShadeTouchableRegionManager state:");
pw.print(" mTouchableRegion=");
pw.println(mTouchableRegion);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
index ee8c1ae..4f32aaa26 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
@@ -35,8 +35,6 @@
import com.android.systemui.statusbar.data.repository.PrivacyDotWindowControllerStoreModule
import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
import com.android.systemui.statusbar.events.PrivacyDotViewControllerModule
-import com.android.systemui.statusbar.phone.AutoHideController
-import com.android.systemui.statusbar.phone.AutoHideControllerImpl
import com.android.systemui.statusbar.phone.AutoHideControllerStore
import com.android.systemui.statusbar.phone.CentralSurfacesCommandQueueCallbacks
import com.android.systemui.statusbar.phone.MultiDisplayAutoHideControllerStore
@@ -80,9 +78,6 @@
@Binds fun statusBarInitializer(@Default impl: StatusBarInitializerImpl): StatusBarInitializer
- @Binds
- fun autoHideControllerFactory(impl: AutoHideControllerImpl.Factory): AutoHideController.Factory
-
companion object {
/** Binds {@link StatusBarInitializer} as a {@link CoreStartable}. */
@Provides
@@ -128,6 +123,7 @@
statusBarModeRepositoryStore: StatusBarModeRepositoryStore,
initializerStore: StatusBarInitializerStore,
statusBarWindowControllerStore: StatusBarWindowControllerStore,
+ autoHideControllerStore: AutoHideControllerStore,
statusBarOrchestratorFactory: StatusBarOrchestrator.Factory,
): StatusBarOrchestrator {
return statusBarOrchestratorFactory.create(
@@ -137,6 +133,7 @@
statusBarModeRepositoryStore.defaultDisplay,
initializerStore.defaultDisplay,
statusBarWindowControllerStore.defaultDisplay,
+ autoHideControllerStore.defaultDisplay,
)
}
@@ -207,5 +204,19 @@
singleDisplayLazy.get()
}
}
+
+ @Provides
+ @SysUISingleton
+ @IntoMap
+ @ClassKey(AutoHideControllerStore::class)
+ fun storeAsCoreStartable(
+ multiDisplayLazy: Lazy<MultiDisplayAutoHideControllerStore>
+ ): CoreStartable {
+ return if (StatusBarConnectedDisplays.isEnabled) {
+ multiDisplayLazy.get()
+ } else {
+ CoreStartable.NOP
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index aac2cd1..78926c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -25,6 +25,7 @@
import android.content.Context
import android.view.View
import androidx.annotation.VisibleForTesting
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.CoreStartable
import com.android.systemui.Dumpable
@@ -58,7 +59,6 @@
import java.util.concurrent.Executor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
/** A controller to handle the ongoing call chip in the collapsed status bar. */
@SysUISingleton
@@ -122,9 +122,9 @@
entry.sbn.uid,
entry.sbn.notification.extras.getInt(
Notification.EXTRA_CALL_TYPE,
- -1
+ -1,
) == CALL_TYPE_ONGOING,
- statusBarSwipedAway = callNotificationInfo?.statusBarSwipedAway ?: false
+ statusBarSwipedAway = callNotificationInfo?.statusBarSwipedAway ?: false,
)
if (newOngoingCallInfo == callNotificationInfo) {
return
@@ -236,7 +236,7 @@
bool1 = Flags.statusBarCallChipNotificationIcon()
bool2 = currentInfo.notificationIconView != null
},
- { "Creating OngoingCallModel.InCall. notifIconFlag=$bool1 hasIcon=$bool2" }
+ { "Creating OngoingCallModel.InCall. notifIconFlag=$bool1 hasIcon=$bool2" },
)
val icon =
if (Flags.statusBarCallChipNotificationIcon()) {
@@ -288,7 +288,7 @@
str1 = notifModel.callType.name
bool1 = notifModel.statusBarChipIconView != null
},
- { "NotifInteractorCallModel: key=$str1 when=$long1 callType=$str2 hasIcon=$bool1" }
+ { "NotifInteractorCallModel: key=$str1 when=$long1 callType=$str2 hasIcon=$bool1" },
)
val newOngoingCallInfo =
@@ -299,7 +299,7 @@
notifModel.contentIntent,
notifModel.uid,
isOngoing = true,
- statusBarSwipedAway = callNotificationInfo?.statusBarSwipedAway ?: false
+ statusBarSwipedAway = callNotificationInfo?.statusBarSwipedAway ?: false,
)
if (newOngoingCallInfo == callNotificationInfo) {
return
@@ -378,7 +378,7 @@
ActivityTransitionAnimator.Controller.fromView(
backgroundView,
InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
- )
+ ),
)
}
}
@@ -455,7 +455,7 @@
/** True if the call is currently ongoing (as opposed to incoming, screening, etc.). */
val isOngoing: Boolean,
/** True if the user has swiped away the status bar while in this phone call. */
- val statusBarSwipedAway: Boolean
+ val statusBarSwipedAway: Boolean,
) {
/**
* Returns true if the notification information has a valid call start time. See
@@ -472,6 +472,9 @@
/**
* Observer to tell us when the app that posted the ongoing call notification is visible so that
* we don't show the call chip at the same time (since the timers could be out-of-sync).
+ *
+ * For a more recommended architecture implementation, see
+ * [com.android.systemui.activity.data.repository.ActivityManagerRepository].
*/
inner class CallAppUidObserver : UidObserver() {
/** True if the application managing the call is visible to the user. */
@@ -512,7 +515,7 @@
uidObserver,
ActivityManager.UID_OBSERVER_PROCSTATE,
ActivityManager.PROCESS_STATE_UNKNOWN,
- context.opPackageName
+ context.opPackageName,
)
isRegistered = true
} catch (se: SecurityException) {
@@ -537,7 +540,7 @@
uid: Int,
procState: Int,
procStateSeq: Long,
- capability: Int
+ capability: Int,
) {
val currentCallAppUid = callAppUid ?: return
if (uid != currentCallAppUid) {
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt
index bcf4bad..45fdd21 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt
@@ -63,7 +63,8 @@
private fun rememberHomeGestureRecognizer(resources: Resources): GestureRecognizer {
val distance =
resources.getDimensionPixelSize(R.dimen.touchpad_tutorial_gestures_distance_threshold)
- return remember(distance) { HomeGestureRecognizer(distance) }
+ val velocity = resources.getDimension(R.dimen.touchpad_home_gesture_velocity_threshold)
+ return remember(distance) { HomeGestureRecognizer(distance, velocity) }
}
@Composable
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt
index e10b825..9801626 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt
@@ -19,9 +19,14 @@
import android.util.MathUtils
import android.view.MotionEvent
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress
+import kotlin.math.abs
/** Recognizes touchpad home gesture, that is - using three fingers on touchpad - swiping up. */
-class HomeGestureRecognizer(private val gestureDistanceThresholdPx: Int) : GestureRecognizer {
+class HomeGestureRecognizer(
+ private val gestureDistanceThresholdPx: Int,
+ private val velocityThresholdPxPerMs: Float,
+ private val velocityTracker: VelocityTracker = VerticalVelocityTracker(),
+) : GestureRecognizer {
private val distanceTracker = DistanceTracker()
private var gestureStateChangedCallback: (GestureState) -> Unit = {}
@@ -37,10 +42,14 @@
override fun accept(event: MotionEvent) {
if (!isThreeFingerTouchpadSwipe(event)) return
val gestureState = distanceTracker.processEvent(event)
+ velocityTracker.accept(event)
updateGestureState(
gestureStateChangedCallback,
gestureState,
- isFinished = { -it.deltaY >= gestureDistanceThresholdPx },
+ isFinished = {
+ -it.deltaY >= gestureDistanceThresholdPx &&
+ abs(velocityTracker.calculateVelocity().value) >= velocityThresholdPxPerMs
+ },
progress = { InProgress(MathUtils.saturate(-it.deltaY / gestureDistanceThresholdPx)) },
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt
index c478886..5ff583a 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt
@@ -28,10 +28,10 @@
class RecentAppsGestureRecognizer(
private val gestureDistanceThresholdPx: Int,
private val velocityThresholdPxPerMs: Float,
- private val distanceTracker: DistanceTracker = DistanceTracker(),
- private val velocityTracker: VerticalVelocityTracker = VerticalVelocityTracker(),
+ private val velocityTracker: VelocityTracker = VerticalVelocityTracker(),
) : GestureRecognizer {
+ private val distanceTracker = DistanceTracker()
private var gestureStateChangedCallback: (GestureState) -> Unit = {}
override fun addGestureStateCallback(callback: (GestureState) -> Unit) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
similarity index 96%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index f41d5c8..8552e48 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -63,8 +63,6 @@
import android.os.Handler;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
import android.provider.Settings;
import android.testing.TestableLooper;
import android.testing.TestableResources;
@@ -90,7 +88,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
-import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.AnimatorTestRule;
import com.android.systemui.kosmos.KosmosJavaAdapter;
@@ -612,46 +609,6 @@
.isEqualTo(expectedRatio);
}
- @DisableFlags(Flags.FLAG_SAVE_AND_RESTORE_MAGNIFICATION_SETTINGS_BUTTONS)
- @Test
- public void onScreenSizeAndDensityChanged_enabled_restoreSavedMagnifierWindow() {
- int newSmallestScreenWidthDp =
- mContext.getResources().getConfiguration().smallestScreenWidthDp * 2;
- int windowFrameSize = mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
- Size preferredWindowSize = new Size(windowFrameSize, windowFrameSize);
- mSharedPreferences
- .edit()
- .putString(String.valueOf(newSmallestScreenWidthDp),
- preferredWindowSize.toString())
- .commit();
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- });
-
- // Screen density and size change
- mContext.getResources().getConfiguration().smallestScreenWidthDp = newSmallestScreenWidthDp;
- final Rect testWindowBounds = new Rect(
- mWindowManager.getCurrentWindowMetrics().getBounds());
- testWindowBounds.set(testWindowBounds.left, testWindowBounds.top,
- testWindowBounds.right + 100, testWindowBounds.bottom + 100);
- mWindowManager.setWindowBounds(testWindowBounds);
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
- });
-
- // wait for rect update
- waitForIdleSync();
- ViewGroup.LayoutParams params = mSurfaceControlViewHost.getView().getLayoutParams();
- final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
- R.dimen.magnification_mirror_surface_margin);
- // The width and height of the view include the magnification frame and the margins.
- assertThat(params.width).isEqualTo(windowFrameSize + 2 * mirrorSurfaceMargin);
- assertThat(params.height).isEqualTo(windowFrameSize + 2 * mirrorSurfaceMargin);
- }
-
- @EnableFlags(Flags.FLAG_SAVE_AND_RESTORE_MAGNIFICATION_SETTINGS_BUTTONS)
@Test
public void onScreenSizeAndDensityChanged_enabled_restoreSavedMagnifierIndexAndWindow() {
int newSmallestScreenWidthDp =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/back/FlingOnBackAnimationCallbackTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/back/FlingOnBackAnimationCallbackTest.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/animation/back/FlingOnBackAnimationCallbackTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/animation/back/FlingOnBackAnimationCallbackTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
similarity index 99%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
index a4653e7..77db977 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
@@ -29,7 +29,6 @@
import org.junit.Test
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
-import platform.test.runner.parameterized.Parameter
import org.junit.runner.RunWith
@SmallTest
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/education/domain/ui/view/ContextualEduDialogTest.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduDialogTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/education/domain/ui/view/ContextualEduDialogTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
index feae901..3bd2496 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
@@ -413,6 +413,7 @@
key("Ctrl")
key("A")
}
+ contentDescription { "$label, Press key Ctrl plus A" }
}
companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
index bc3c0d9..d59a404 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
@@ -49,6 +49,7 @@
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.views.NavigationBar;
import com.android.systemui.recents.OverviewProxyService;
@@ -56,7 +57,7 @@
import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.phone.AutoHideController;
+import com.android.systemui.statusbar.phone.AutoHideControllerStore;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -89,6 +90,11 @@
private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+ private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
+
+ private final AutoHideControllerStore mAutoHideControllerStore =
+ mKosmos.getAutoHideControllerStore();
+
@Mock
private CommandQueue mCommandQueue;
@Mock
@@ -113,7 +119,7 @@
mTaskbarDelegate,
mNavigationBarFactory,
mock(DumpManager.class),
- mock(AutoHideController.class),
+ mAutoHideControllerStore,
mock(LightBarController.class),
TaskStackChangeListeners.getTestInstance(),
Optional.of(mock(Pip.class)),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index 859f84e..e7fb470c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -33,6 +33,7 @@
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.Flags
+import com.android.systemui.Flags.FLAG_COMMUNAL_HUB_ON_MOBILE
import com.android.systemui.Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX
import com.android.systemui.SysuiTestCase
import com.android.systemui.ambient.touch.TouchHandler
@@ -630,6 +631,7 @@
}
}
+ @DisableFlags(FLAG_COMMUNAL_HUB_ON_MOBILE)
@Test
fun onTouchEvent_shadeInteracting_movesNotDispatched() =
with(kosmos) {
@@ -686,6 +688,7 @@
}
}
+ @DisableFlags(FLAG_COMMUNAL_HUB_ON_MOBILE)
@Test
fun onTouchEvent_bouncerInteracting_movesNotDispatched() =
with(kosmos) {
@@ -718,6 +721,19 @@
}
}
+ @EnableFlags(FLAG_COMMUNAL_HUB_ON_MOBILE)
+ @Test
+ fun onTouchEvent_onLockscreenAndGlanceableHubV2_touchIgnored() =
+ with(kosmos) {
+ testScope.runTest {
+ // On lockscreen.
+ goToScene(CommunalScenes.Blank)
+
+ assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
+ verify(containerView, never()).onTouchEvent(DOWN_EVENT)
+ }
+ }
+
@Test
fun disposeView_destroysTouchMonitor() {
clearInvocations(touchMonitor)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 8b4c335..6912eda 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -334,7 +334,7 @@
@Mock private CentralSurfacesCommandQueueCallbacks mCentralSurfacesCommandQueueCallbacks;
@Mock private PluginManager mPluginManager;
@Mock private ViewMediatorCallback mViewMediatorCallback;
- @Mock private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
+ @Mock private ShadeTouchableRegionManager mShadeTouchableRegionManager;
@Mock private PluginDependencyProvider mPluginDependencyProvider;
@Mock private ExtensionController mExtensionController;
@Mock private UserInfoControllerImpl mUserInfoControllerImpl;
@@ -617,7 +617,7 @@
mKeyguardIndicationController,
mDemoModeController,
mNotificationShadeDepthControllerLazy,
- mStatusBarTouchableRegionManager,
+ mShadeTouchableRegionManager,
mBrightnessSliderFactory,
mScreenOffAnimationController,
mWallpaperController,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModelParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModelParameterizedTest.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModelParameterizedTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModelParameterizedTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
index 564dca0..3247a1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
@@ -354,8 +354,8 @@
}
private static DeviceState createDeviceState(int identifier, @NonNull String name,
- @NonNull Set<Integer> systemProperties,
- @NonNull Set<Integer> physicalProperties) {
+ @NonNull Set<@DeviceState.SystemDeviceStateProperties Integer> systemProperties,
+ @NonNull Set<@DeviceState.PhysicalDeviceStateProperties Integer> physicalProperties) {
DeviceState.Configuration deviceStateConfiguration = new DeviceState.Configuration.Builder(
identifier, name).setSystemProperties(systemProperties).setPhysicalProperties(
physicalProperties).build();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MotionEventHelper.java b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/MotionEventHelper.java
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MotionEventHelper.java
rename to packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/MotionEventHelper.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/TestableWindowManager.java b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/TestableWindowManager.java
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/TestableWindowManager.java
rename to packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/TestableWindowManager.java
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/activity/data/repository/ActivityManagerRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/activity/data/repository/ActivityManagerRepositoryKosmos.kt
new file mode 100644
index 0000000..a6e7133
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/activity/data/repository/ActivityManagerRepositoryKosmos.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.activity.data.repository
+
+import android.app.activityManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.log.core.Logger
+import kotlinx.coroutines.flow.MutableStateFlow
+
+val Kosmos.activityManagerRepository by Kosmos.Fixture { FakeActivityManagerRepository() }
+
+val Kosmos.realActivityManagerRepository by
+ Kosmos.Fixture { ActivityManagerRepositoryImpl(testDispatcher, activityManager) }
+
+class FakeActivityManagerRepository : ActivityManagerRepository {
+ private val uidFlows = mutableMapOf<Int, MutableList<MutableStateFlow<Boolean>>>()
+
+ var startingIsAppVisibleValue = false
+
+ override fun createIsAppVisibleFlow(
+ creationUid: Int,
+ logger: Logger,
+ identifyingLogTag: String,
+ ): MutableStateFlow<Boolean> {
+ val newFlow = MutableStateFlow(startingIsAppVisibleValue)
+ uidFlows.computeIfAbsent(creationUid) { mutableListOf() }.add(newFlow)
+ return newFlow
+ }
+
+ fun setIsAppVisible(uid: Int, isAppVisible: Boolean) {
+ uidFlows[uid]?.forEach { stateFlow -> stateFlow.value = isAppVisible }
+ }
+}
+
+val ActivityManagerRepository.fake
+ get() = this as FakeActivityManagerRepository
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryKosmos.kt
index 5485f79..5da6c7b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryKosmos.kt
@@ -17,6 +17,7 @@
package com.android.systemui.communal.data.repository
import android.app.admin.devicePolicyManager
+import android.content.res.mainResources
import com.android.systemui.broadcast.broadcastDispatcher
import com.android.systemui.flags.featureFlagsClassic
import com.android.systemui.kosmos.Kosmos
@@ -27,6 +28,7 @@
Kosmos.Fixture {
CommunalSettingsRepositoryImpl(
bgDispatcher = testDispatcher,
+ resources = mainResources,
featureFlagsClassic = featureFlagsClassic,
secureSettings = fakeSettings,
broadcastDispatcher = broadcastDispatcher,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
index 78ea700..ddcc926 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
@@ -57,6 +57,10 @@
addDisplay(display(type, id = displayId))
}
+ suspend fun addDisplays(vararg displays: Display) {
+ displays.forEach { addDisplay(it) }
+ }
+
suspend fun addDisplay(display: Display) {
flow.value += display
displayAdditionEventFlow.emit(display)
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java b/packages/SystemUI/tests/utils/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractorKosmos.kt
similarity index 64%
copy from ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java
copy to packages/SystemUI/tests/utils/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractorKosmos.kt
index 7d3d8b9..c2466bb 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractorKosmos.kt
@@ -13,10 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.platform.test.ravenwood;
-/** Stub class. The actual implementation is in junit-impl-src. */
-public class RavenwoodConfigState {
- public RavenwoodConfigState(RavenwoodConfig config) {
- }
-}
+package com.android.systemui.display.domain.interactor
+
+import com.android.systemui.display.data.repository.displayWindowPropertiesRepository
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.displayWindowPropertiesInteractor by
+ Kosmos.Fixture { DisplayWindowPropertiesInteractorImpl(displayWindowPropertiesRepository) }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeServiceFake.java b/packages/SystemUI/tests/utils/src/com/android/systemui/doze/DozeServiceFake.java
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeServiceFake.java
rename to packages/SystemUI/tests/utils/src/com/android/systemui/doze/DozeServiceFake.java
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
index 2c85816..e7672ff 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
@@ -55,10 +55,10 @@
Kosmos.Fixture { AppCategoriesShortcutsSource(windowManager, testDispatcher) }
var Kosmos.shortcutHelperSystemShortcutsSource: KeyboardShortcutGroupsSource by
- Kosmos.Fixture { SystemShortcutsSource(mainResources) }
+ Kosmos.Fixture { SystemShortcutsSource(mainResources, fakeInputManager.inputManager) }
var Kosmos.shortcutHelperMultiTaskingShortcutsSource: KeyboardShortcutGroupsSource by
- Kosmos.Fixture { MultitaskingShortcutsSource(mainResources) }
+ Kosmos.Fixture { MultitaskingShortcutsSource(mainResources, applicationContext) }
val Kosmos.shortcutHelperStateRepository by
Kosmos.Fixture {
@@ -72,7 +72,9 @@
}
var Kosmos.shortcutHelperInputShortcutsSource: KeyboardShortcutGroupsSource by
- Kosmos.Fixture { InputShortcutsSource(mainResources, windowManager) }
+ Kosmos.Fixture {
+ InputShortcutsSource(mainResources, windowManager, fakeInputManager.inputManager)
+ }
var Kosmos.shortcutHelperCurrentAppShortcutsSource: KeyboardShortcutGroupsSource by
Kosmos.Fixture { CurrentAppShortcutsSource(windowManager) }
@@ -141,7 +143,10 @@
val Kosmos.shortcutHelperCategoriesInteractor by
Kosmos.Fixture {
- ShortcutHelperCategoriesInteractor(defaultShortcutCategoriesRepository) {
+ ShortcutHelperCategoriesInteractor(
+ context = applicationContext,
+ defaultShortcutCategoriesRepository,
+ ) {
customShortcutCategoriesRepository
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt
index 007d229..f88ed07 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt
@@ -16,18 +16,24 @@
package com.android.systemui.keyguard.domain.interactor
+import com.android.internal.widget.lockPatternUtils
import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
import com.android.systemui.keyguard.data.repository.keyguardRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.user.domain.interactor.selectedUserInteractor
val Kosmos.keyguardEnabledInteractor by
Kosmos.Fixture {
KeyguardEnabledInteractor(
applicationCoroutineScope,
+ testDispatcher,
keyguardRepository,
biometricSettingsRepository,
- keyguardDismissTransitionInteractor,
+ selectedUserInteractor,
+ lockPatternUtils,
+ { keyguardDismissTransitionInteractor },
internalTransitionInteractor = internalKeyguardTransitionInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorKosmos.kt
index 39236c7..2423949 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorKosmos.kt
@@ -24,5 +24,6 @@
KeyguardLockWhileAwakeInteractor(
biometricSettingsRepository = biometricSettingsRepository,
keyguardEnabledInteractor = keyguardEnabledInteractor,
+ keyguardServiceLockNowInteractor = keyguardServiceLockNowInteractor,
)
}
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceLockNowInteractor.kt
similarity index 68%
copy from ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java
copy to packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceLockNowInteractor.kt
index 7d3d8b9..29335c5 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceLockNowInteractor.kt
@@ -13,10 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.platform.test.ravenwood;
-/** Stub class. The actual implementation is in junit-impl-src. */
-public class RavenwoodConfigState {
- public RavenwoodConfigState(RavenwoodConfig config) {
- }
-}
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+
+val Kosmos.keyguardServiceLockNowInteractor by
+ Kosmos.Fixture { KeyguardServiceLockNowInteractor(backgroundScope = testScope) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt
index 63e168d..4aa132c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt
@@ -41,5 +41,7 @@
lockPatternUtils,
fakeSettings,
selectedUserInteractor,
+ keyguardEnabledInteractor,
+ keyguardServiceLockNowInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index 3d60abf..5b2c8dd 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -78,6 +78,7 @@
import com.android.systemui.statusbar.notification.domain.interactor.seenNotificationsInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
+import com.android.systemui.statusbar.phone.fakeAutoHideControllerStore
import com.android.systemui.statusbar.phone.keyguardBypassController
import com.android.systemui.statusbar.phone.scrimController
import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepository
@@ -132,6 +133,7 @@
val simBouncerInteractor by lazy { kosmos.simBouncerInteractor }
val statusBarStateController by lazy { kosmos.statusBarStateController }
val statusBarModePerDisplayRepository by lazy { kosmos.fakeStatusBarModePerDisplayRepository }
+ val autoHideControllerStore by lazy { kosmos.fakeAutoHideControllerStore }
val interactionJankMonitor by lazy { kosmos.interactionJankMonitor }
val fakeSceneContainerConfig by lazy { kosmos.sceneContainerConfig }
val sceneInteractor by lazy { kosmos.sceneInteractor }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt
new file mode 100644
index 0000000..dbaa0b1
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.data.repository
+
+import android.view.Display
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.shade.display.ShadeDisplayPolicy
+import com.android.systemui.shade.display.SpecificDisplayIdPolicy
+
+val Kosmos.defaultShadeDisplayPolicy: ShadeDisplayPolicy by
+ Kosmos.Fixture { SpecificDisplayIdPolicy(Display.DEFAULT_DISPLAY) }
+
+val Kosmos.shadeDisplaysRepository: MutableShadeDisplaysRepository by
+ Kosmos.Fixture {
+ ShadeDisplaysRepositoryImpl(
+ defaultPolicy = defaultShadeDisplayPolicy,
+ bgScope = testScope.backgroundScope,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorFactoryKosmos.kt
new file mode 100644
index 0000000..1c095e1
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorFactoryKosmos.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.notification.domain.interactor
+
+import com.android.systemui.activity.data.repository.activityManagerRepository
+import com.android.systemui.activity.data.repository.fake
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.chips.statusBarChipsLogger
+
+val Kosmos.singleNotificationChipInteractorFactory: SingleNotificationChipInteractor.Factory by
+ Kosmos.Fixture {
+ SingleNotificationChipInteractor.Factory { startingModel ->
+ SingleNotificationChipInteractor(
+ startingModel,
+ activityManagerRepository.fake,
+ logBuffer = statusBarChipsLogger,
+ )
+ }
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorKosmos.kt
index 74c7611..03e9f3d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorKosmos.kt
@@ -17,6 +17,16 @@
package com.android.systemui.statusbar.chips.notification.domain.interactor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.chips.statusBarChipsLogger
+import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
val Kosmos.statusBarNotificationChipsInteractor: StatusBarNotificationChipsInteractor by
- Kosmos.Fixture { StatusBarNotificationChipsInteractor() }
+ Kosmos.Fixture {
+ StatusBarNotificationChipsInteractor(
+ testScope.backgroundScope,
+ activeNotificationsInteractor,
+ singleNotificationChipInteractorFactory,
+ logBuffer = statusBarChipsLogger,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt
index 68b28ad..4bcce86 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt
@@ -19,13 +19,8 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor
-import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
val Kosmos.notifChipsViewModel: NotifChipsViewModel by
Kosmos.Fixture {
- NotifChipsViewModel(
- applicationCoroutineScope,
- activeNotificationsInteractor,
- statusBarNotificationChipsInteractor,
- )
+ NotifChipsViewModel(applicationCoroutineScope, statusBarNotificationChipsInteractor)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarOrchestratorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarOrchestratorFactory.kt
index 9197dcd..2d88ae8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarOrchestratorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarOrchestratorFactory.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.core
import com.android.systemui.statusbar.data.repository.StatusBarModePerDisplayRepository
+import com.android.systemui.statusbar.phone.AutoHideController
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStatePerDisplayRepository
import kotlinx.coroutines.CoroutineScope
@@ -36,6 +37,7 @@
statusBarModeRepository: StatusBarModePerDisplayRepository,
statusBarInitializer: StatusBarInitializer,
statusBarWindowController: StatusBarWindowController,
+ autoHideController: AutoHideController,
): StatusBarOrchestrator =
mock<StatusBarOrchestrator>().also { createdOrchestrators[displayId] = it }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt
index 28edae7..8c37bd7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt
@@ -34,6 +34,7 @@
import com.android.systemui.statusbar.data.repository.statusBarModeRepository
import com.android.systemui.statusbar.mockNotificationRemoteInputManager
import com.android.systemui.statusbar.phone.mockAutoHideController
+import com.android.systemui.statusbar.phone.multiDisplayAutoHideControllerStore
import com.android.systemui.statusbar.window.data.repository.fakeStatusBarWindowStatePerDisplayRepository
import com.android.systemui.statusbar.window.data.repository.statusBarWindowStateRepositoryStore
import com.android.systemui.statusbar.window.fakeStatusBarWindowController
@@ -50,9 +51,9 @@
fakeStatusBarInitializer,
fakeStatusBarWindowController,
applicationCoroutineScope.coroutineContext,
+ mockAutoHideController,
mockDemoModeController,
mockPluginDependencyProvider,
- mockAutoHideController,
mockNotificationRemoteInputManager,
{ mockNotificationShadeWindowViewController },
mockShadeSurface,
@@ -80,6 +81,7 @@
statusBarInitializerStore,
statusBarWindowControllerStore,
statusBarInitializerStore,
+ multiDisplayAutoHideControllerStore,
privacyDotWindowControllerStore,
lightBarControllerStore,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
index 7fbf4e4..d1619b7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
@@ -23,6 +23,7 @@
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.ui.viewmodel.alternateBouncerToGoneTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.alternateBouncerToPrimaryBouncerTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.aodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.aodToGoneTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.aodToLockscreenTransitionViewModel
@@ -71,6 +72,8 @@
shadeInteractor = shadeInteractor,
notificationStackAppearanceInteractor = notificationStackAppearanceInteractor,
alternateBouncerToGoneTransitionViewModel = alternateBouncerToGoneTransitionViewModel,
+ alternateBouncerToPrimaryBouncerTransitionViewModel =
+ alternateBouncerToPrimaryBouncerTransitionViewModel,
aodToGoneTransitionViewModel = aodToGoneTransitionViewModel,
aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
aodToOccludedTransitionViewModel = aodToOccludedTransitionViewModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/AutoHideKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/AutoHideKosmos.kt
index 951ae59..b99e93a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/AutoHideKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/AutoHideKosmos.kt
@@ -18,6 +18,7 @@
import android.content.Context
import android.os.Handler
+import android.view.Display
import android.view.IWindowManager
import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.display.data.repository.fakeDisplayWindowPropertiesRepository
@@ -28,8 +29,6 @@
val Kosmos.mockAutoHideController: AutoHideController by
Kosmos.Fixture { mock(AutoHideController::class.java) }
-var Kosmos.autoHideController by Kosmos.Fixture { mockAutoHideController }
-
val Kosmos.fakeAutoHideControllerFactory by Kosmos.Fixture { FakeAutoHideControllerFactory() }
val Kosmos.multiDisplayAutoHideControllerStore by
@@ -42,6 +41,8 @@
)
}
+val Kosmos.fakeAutoHideControllerStore by Kosmos.Fixture { FakeAutoHideControllerStore() }
+
class FakeAutoHideControllerFactory :
AutoHideControllerImpl.Factory(mock(Handler::class.java), mock(IWindowManager::class.java)) {
@@ -49,3 +50,15 @@
return mock(AutoHideControllerImpl::class.java)
}
}
+
+class FakeAutoHideControllerStore : AutoHideControllerStore {
+
+ private val perDisplayMocks = mutableMapOf<Int, AutoHideController>()
+
+ override val defaultDisplay: AutoHideController
+ get() = forDisplay(Display.DEFAULT_DISPLAY)
+
+ override fun forDisplay(displayId: Int): AutoHideController {
+ return perDisplayMocks.computeIfAbsent(displayId) { mock(AutoHideController::class.java) }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ShadeTouchableRegionManagerKosmos.kt
similarity index 95%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ShadeTouchableRegionManagerKosmos.kt
index 6eb6fe8..5b7f23b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ShadeTouchableRegionManagerKosmos.kt
@@ -30,9 +30,9 @@
import com.android.systemui.util.kotlin.JavaAdapter
import com.android.systemui.util.mockito.mock
-var Kosmos.statusBarTouchableRegionManager by
+var Kosmos.shadeTouchableRegionManager by
Kosmos.Fixture {
- StatusBarTouchableRegionManager(
+ ShadeTouchableRegionManager(
applicationContext,
notificationShadeWindowController,
configurationController,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/ui/gesture/FakeVelocityTracker.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/ui/gesture/FakeVelocityTracker.kt
new file mode 100644
index 0000000..f12089a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/ui/gesture/FakeVelocityTracker.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touchpad.ui.gesture
+
+import android.view.MotionEvent
+import com.android.systemui.touchpad.tutorial.ui.gesture.Velocity
+import com.android.systemui.touchpad.tutorial.ui.gesture.VelocityTracker
+
+class FakeVelocityTracker : VelocityTracker {
+
+ private var fakeVelocity = Velocity(0f)
+
+ override fun calculateVelocity(): Velocity {
+ return fakeVelocity
+ }
+
+ override fun accept(event: MotionEvent) {}
+
+ fun setVelocity(velocity: Velocity) {
+ fakeVelocity = velocity
+ }
+}
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java b/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/ui/gesture/VelocityTrackerKosmos.kt
similarity index 73%
rename from ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java
rename to packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/ui/gesture/VelocityTrackerKosmos.kt
index 7d3d8b9..3b61e21 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/ui/gesture/VelocityTrackerKosmos.kt
@@ -13,10 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.platform.test.ravenwood;
-/** Stub class. The actual implementation is in junit-impl-src. */
-public class RavenwoodConfigState {
- public RavenwoodConfigState(RavenwoodConfig config) {
- }
-}
+package com.android.systemui.touchpad.ui.gesture
+
+import com.android.systemui.kosmos.Kosmos
+
+var Kosmos.fakeVelocityTracker: FakeVelocityTracker by Kosmos.Fixture { FakeVelocityTracker() }
diff --git a/packages/Vcn/service-b/Android.bp b/packages/Vcn/service-b/Android.bp
index a462297..03ef4e6 100644
--- a/packages/Vcn/service-b/Android.bp
+++ b/packages/Vcn/service-b/Android.bp
@@ -19,6 +19,19 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
+filegroup {
+ name: "vcn-location-sources",
+ srcs: select(release_flag("RELEASE_MOVE_VCN_TO_MAINLINE"), {
+ true: [
+ "vcn-location-flag/module/com/android/server/vcn/VcnLocation.java",
+ ],
+ default: [
+ "vcn-location-flag/platform/com/android/server/vcn/VcnLocation.java",
+ ],
+ }),
+ visibility: ["//frameworks/base/services/core"],
+}
+
java_library {
name: "service-connectivity-b-pre-jarjar",
sdk_version: "system_server_current",
diff --git a/packages/Vcn/service-b/vcn-location-flag/module/com/android/server/vcn/VcnLocation.java b/packages/Vcn/service-b/vcn-location-flag/module/com/android/server/vcn/VcnLocation.java
new file mode 100644
index 0000000..6c7d24d
--- /dev/null
+++ b/packages/Vcn/service-b/vcn-location-flag/module/com/android/server/vcn/VcnLocation.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn;
+
+/**
+ * Class to represent that VCN is in a mainline module
+ *
+ * <p>This class is used to check whether VCN is in the non-updatable platform or in a mainline
+ * module.
+ */
+// When VCN is in a mainline module, this class (module/com/android/server/vcn/VcnLocation.java)
+// will be built in to the vcn-location-sources filegroup. When VCN is in the non-updatable
+// platform, platform/com/android/server/vcn/VcnLocation.java will be built in to the filegroup
+public class VcnLocation {
+ /** Indicate that VCN is the platform */
+ public static final boolean IS_VCN_IN_MAINLINE = true;
+}
diff --git a/packages/Vcn/service-b/vcn-location-flag/platform/com/android/server/vcn/VcnLocation.java b/packages/Vcn/service-b/vcn-location-flag/platform/com/android/server/vcn/VcnLocation.java
new file mode 100644
index 0000000..c6c82a5
--- /dev/null
+++ b/packages/Vcn/service-b/vcn-location-flag/platform/com/android/server/vcn/VcnLocation.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn;
+
+/**
+ * Class to represent that VCN is in the platform
+ *
+ * <p>This class is used to check whether VCN is in the non-updatable platform or in a mainline
+ * module.
+ */
+// When VCN is in a mainline module, module/com/android/server/vcn/VcnLocation.java
+// will be built in to the vcn-location-sources filegroup. When VCN is in the non-updatable
+// platform, this class (platform/com/android/server/vcn/VcnLocation.java) will be built in to the
+// filegroup
+public class VcnLocation {
+ /** Indicate that VCN is the platform */
+ public static final boolean IS_VCN_IN_MAINLINE = false;
+}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
index 9b71f80..de3c5f2 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
@@ -133,9 +133,6 @@
Log.v(TAG, "RavenwoodAwareTestRunner starting for " + testClass.getCanonicalName());
- // This is needed to make AndroidJUnit4ClassRunner happy.
- InstrumentationRegistry.registerInstance(null, Bundle.EMPTY);
-
// Hook point to allow more customization.
runAnnotatedMethodsOnRavenwood(RavenwoodTestRunnerInitializing.class, null);
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigState.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigState.java
deleted file mode 100644
index 870a10a..0000000
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigState.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.platform.test.ravenwood;
-
-import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_EMPTY_RESOURCES_APK;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import android.annotation.Nullable;
-import android.app.ResourcesManager;
-import android.content.res.Resources;
-import android.view.DisplayAdjustments;
-
-import java.io.File;
-import java.util.HashMap;
-
-/**
- * Used to store various states associated with {@link RavenwoodConfig} that's inly needed
- * in junit-impl.
- *
- * We don't want to put it in junit-src to avoid having to recompile all the downstream
- * dependencies after changing this class.
- *
- * All members must be called from the runner's main thread.
- */
-public class RavenwoodConfigState {
- private static final String TAG = "RavenwoodConfigState";
-
- private final RavenwoodConfig mConfig;
-
- // TODO: Move the other contexts from RavenwoodConfig to here too? They're used by
- // RavenwoodRule too, but RavenwoodRule can probably use InstrumentationRegistry?
- RavenwoodContext mSystemServerContext;
-
- public RavenwoodConfigState(RavenwoodConfig config) {
- mConfig = config;
- }
-
- /** Map from path -> resources. */
- private final HashMap<File, Resources> mCachedResources = new HashMap<>();
-
- /**
- * Load {@link Resources} from an APK, with cache.
- */
- public Resources loadResources(@Nullable File apkPath) {
- var cached = mCachedResources.get(apkPath);
- if (cached != null) {
- return cached;
- }
-
- var fileToLoad = apkPath != null ? apkPath : new File(RAVENWOOD_EMPTY_RESOURCES_APK);
-
- assertTrue("File " + fileToLoad + " doesn't exist.", fileToLoad.isFile());
-
- final String path = fileToLoad.getAbsolutePath();
- final var emptyPaths = new String[0];
-
- ResourcesManager.getInstance().initializeApplicationPaths(path, emptyPaths);
-
- final var ret = ResourcesManager.getInstance().getResources(null, path,
- emptyPaths, emptyPaths, emptyPaths,
- emptyPaths, null, null,
- new DisplayAdjustments().getCompatibilityInfo(),
- RavenwoodRuntimeEnvironmentController.class.getClassLoader(), null);
-
- assertNotNull(ret);
-
- mCachedResources.put(apkPath, ret);
- return ret;
- }
-}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
index ec00e8f..6dfcf4ce 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
@@ -15,24 +15,23 @@
*/
package android.platform.test.ravenwood;
-import static com.android.ravenwood.common.RavenwoodCommonUtils.ensureIsPublicMember;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import android.annotation.Nullable;
import android.util.Log;
+import android.util.Pair;
-import com.android.ravenwood.common.RavenwoodRuntimeException;
+import com.android.ravenwood.RavenwoodRuntimeNative;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.rules.TestRule;
import org.junit.runner.Description;
-import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
/**
- * Used to store various states associated with the current test runner that's inly needed
+ * Used to store various states associated with the current test runner that's only needed
* in junit-impl.
*
* We don't want to put it in junit-src to avoid having to recompile all the downstream
@@ -42,6 +41,11 @@
*/
public final class RavenwoodRunnerState {
private static final String TAG = "RavenwoodRunnerState";
+ private static final String RAVENWOOD_RULE_ERROR =
+ "RavenwoodRule(s) are not executed in the correct order";
+
+ private static final List<Pair<RavenwoodRule, RavenwoodPropertyState>> sActiveProperties =
+ new ArrayList<>();
private final RavenwoodAwareTestRunner mRunner;
@@ -52,207 +56,95 @@
mRunner = runner;
}
- /**
- * The RavenwoodConfig used to configure the current Ravenwood environment.
- * This can either come from mConfig or mRule.
- */
- private RavenwoodConfig mCurrentConfig;
- /**
- * The RavenwoodConfig declared in the test class
- */
- private RavenwoodConfig mConfig;
- /**
- * The RavenwoodRule currently in effect, declared in the test class
- */
- private RavenwoodRule mRule;
- private boolean mHasRavenwoodRule;
private Description mMethodDescription;
- public RavenwoodConfig getConfig() {
- return mCurrentConfig;
- }
-
public void enterTestRunner() {
Log.i(TAG, "enterTestRunner: " + mRunner);
-
- mHasRavenwoodRule = hasRavenwoodRule(mRunner.mTestJavaClass);
- mConfig = extractConfiguration(mRunner.mTestJavaClass);
-
- if (mConfig != null) {
- if (mHasRavenwoodRule) {
- fail("RavenwoodConfig and RavenwoodRule cannot be used in the same class."
- + " Suggest migrating to RavenwoodConfig.");
- }
- mCurrentConfig = mConfig;
- } else if (!mHasRavenwoodRule) {
- // If no RavenwoodConfig and no RavenwoodRule, use a default config
- mCurrentConfig = new RavenwoodConfig.Builder().build();
- }
-
- if (mCurrentConfig != null) {
- RavenwoodRuntimeEnvironmentController.init(mRunner);
- }
+ RavenwoodRuntimeEnvironmentController.initForRunner();
}
public void enterTestClass() {
Log.i(TAG, "enterTestClass: " + mRunner.mTestJavaClass.getName());
-
- if (mCurrentConfig != null) {
- RavenwoodRuntimeEnvironmentController.init(mRunner);
- }
}
public void exitTestClass() {
Log.i(TAG, "exitTestClass: " + mRunner.mTestJavaClass.getName());
- try {
- if (mCurrentConfig != null) {
- RavenwoodRuntimeEnvironmentController.reset();
- }
- } finally {
- mConfig = null;
- mRule = null;
- }
+ assertTrue(RAVENWOOD_RULE_ERROR, sActiveProperties.isEmpty());
+ RavenwoodRuntimeEnvironmentController.exitTestClass();
}
public void enterTestMethod(Description description) {
mMethodDescription = description;
+ RavenwoodRuntimeEnvironmentController.initForMethod();
}
public void exitTestMethod() {
mMethodDescription = null;
- RavenwoodRuntimeEnvironmentController.reinit();
}
public void enterRavenwoodRule(RavenwoodRule rule) {
- if (!mHasRavenwoodRule) {
- fail("If you have a RavenwoodRule in your test, make sure the field type is"
- + " RavenwoodRule so Ravenwood can detect it.");
- }
- if (mRule != null) {
- fail("Multiple nesting RavenwoodRule's are detected in the same class,"
- + " which is not supported.");
- }
- mRule = rule;
- if (mCurrentConfig == null) {
- mCurrentConfig = rule.getConfiguration();
- }
- RavenwoodRuntimeEnvironmentController.init(mRunner);
+ pushTestProperties(rule);
}
public void exitRavenwoodRule(RavenwoodRule rule) {
- if (mRule != rule) {
- fail("RavenwoodRule did not take effect.");
- }
- mRule = null;
+ popTestProperties(rule);
}
- /**
- * @return a configuration from a test class, if any.
- */
- @Nullable
- private static RavenwoodConfig extractConfiguration(Class<?> testClass) {
- var field = findConfigurationField(testClass);
- if (field == null) {
- return null;
+ static class RavenwoodPropertyState {
+
+ final List<Pair<String, String>> mBackup;
+ final Set<String> mKeyReadable;
+ final Set<String> mKeyWritable;
+
+ RavenwoodPropertyState(RavenwoodTestProperties props) {
+ mBackup = props.mValues.keySet().stream()
+ .map(key -> Pair.create(key, RavenwoodRuntimeNative.getSystemProperty(key)))
+ .toList();
+ mKeyReadable = Set.copyOf(props.mKeyReadable);
+ mKeyWritable = Set.copyOf(props.mKeyWritable);
}
- try {
- return (RavenwoodConfig) field.get(null);
- } catch (IllegalAccessException e) {
- throw new RavenwoodRuntimeException("Failed to fetch from the configuration field", e);
+ boolean isKeyAccessible(String key, boolean write) {
+ return write ? mKeyWritable.contains(key) : mKeyReadable.contains(key);
}
- }
- /**
- * @return true if the current target class (or its super classes) has any @Rule / @ClassRule
- * fields of type RavenwoodRule.
- *
- * Note, this check won't detect cases where a Rule is of type
- * {@link TestRule} and still be a {@link RavenwoodRule}. But that'll be detected at runtime
- * as a failure, in {@link #enterRavenwoodRule}.
- */
- private static boolean hasRavenwoodRule(Class<?> testClass) {
- for (var field : testClass.getDeclaredFields()) {
- if (!field.isAnnotationPresent(Rule.class)
- && !field.isAnnotationPresent(ClassRule.class)) {
- continue;
- }
- if (field.getType().equals(RavenwoodRule.class)) {
- return true;
- }
- }
- // JUnit supports rules as methods, so we need to check them too.
- for (var method : testClass.getDeclaredMethods()) {
- if (!method.isAnnotationPresent(Rule.class)
- && !method.isAnnotationPresent(ClassRule.class)) {
- continue;
- }
- if (method.getReturnType().equals(RavenwoodRule.class)) {
- return true;
- }
- }
- // Look into the super class.
- if (!testClass.getSuperclass().equals(Object.class)) {
- return hasRavenwoodRule(testClass.getSuperclass());
- }
- return false;
- }
-
- /**
- * Find and return a field with @RavenwoodConfig.Config, which must be of type
- * RavenwoodConfig.
- */
- @Nullable
- private static Field findConfigurationField(Class<?> testClass) {
- Field foundField = null;
-
- for (var field : testClass.getDeclaredFields()) {
- final var hasAnot = field.isAnnotationPresent(RavenwoodConfig.Config.class);
- final var isType = field.getType().equals(RavenwoodConfig.class);
-
- if (hasAnot) {
- if (isType) {
- // Good, use this field.
- if (foundField != null) {
- fail(String.format(
- "Class %s has multiple fields with %s",
- testClass.getCanonicalName(),
- "@RavenwoodConfig.Config"));
- }
- // Make sure it's static public
- ensureIsPublicMember(field, true);
-
- foundField = field;
+ void restore() {
+ mBackup.forEach(pair -> {
+ if (pair.second == null) {
+ RavenwoodRuntimeNative.removeSystemProperty(pair.first);
} else {
- fail(String.format(
- "Field %s.%s has %s but type is not %s",
- testClass.getCanonicalName(),
- field.getName(),
- "@RavenwoodConfig.Config",
- "RavenwoodConfig"));
- return null; // unreachable
+ RavenwoodRuntimeNative.setSystemProperty(pair.first, pair.second);
}
- } else {
- if (isType) {
- fail(String.format(
- "Field %s.%s does not have %s but type is %s",
- testClass.getCanonicalName(),
- field.getName(),
- "@RavenwoodConfig.Config",
- "RavenwoodConfig"));
- return null; // unreachable
- } else {
- // Unrelated field, ignore.
- continue;
- }
- }
+ });
}
- if (foundField != null) {
- return foundField;
+ }
+
+ private static void pushTestProperties(RavenwoodRule rule) {
+ sActiveProperties.add(Pair.create(rule, new RavenwoodPropertyState(rule.mProperties)));
+ rule.mProperties.mValues.forEach(RavenwoodRuntimeNative::setSystemProperty);
+ }
+
+ private static void popTestProperties(RavenwoodRule rule) {
+ var pair = sActiveProperties.removeLast();
+ assertNotNull(RAVENWOOD_RULE_ERROR, pair);
+ assertEquals(RAVENWOOD_RULE_ERROR, rule, pair.first);
+ pair.second.restore();
+ }
+
+ @SuppressWarnings("unused") // Called from native code (ravenwood_sysprop.cpp)
+ private static void checkSystemPropertyAccess(String key, boolean write) {
+ if (write && RavenwoodSystemProperties.sDefaultValues.containsKey(key)) {
+ // The default core values should never be modified
+ throw new IllegalArgumentException(
+ "Setting core system property '" + key + "' is not allowed");
}
- if (!testClass.getSuperclass().equals(Object.class)) {
- return findConfigurationField(testClass.getSuperclass());
+
+ final boolean result = RavenwoodSystemProperties.isKeyAccessible(key, write)
+ || sActiveProperties.stream().anyMatch(p -> p.second.isKeyAccessible(key, write));
+
+ if (!result) {
+ throw new IllegalArgumentException((write ? "Write" : "Read")
+ + " access to system property '" + key + "' denied via RavenwoodRule");
}
- return null;
}
}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
index 979076e..e730a29 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
@@ -16,8 +16,11 @@
package android.platform.test.ravenwood;
+import static android.os.Process.FIRST_APPLICATION_UID;
+import static android.os.UserHandle.SYSTEM;
import static android.platform.test.ravenwood.RavenwoodSystemServer.ANDROID_PACKAGE_NAME;
+import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_EMPTY_RESOURCES_APK;
import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_INST_RESOURCE_APK;
import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_RESOURCE_APK;
import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING;
@@ -25,7 +28,9 @@
import static com.android.ravenwood.common.RavenwoodCommonUtils.parseNullableInt;
import static com.android.ravenwood.common.RavenwoodCommonUtils.withDefault;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
@@ -53,6 +58,7 @@
import android.system.ErrnoException;
import android.system.Os;
import android.util.Log;
+import android.view.DisplayAdjustments;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -62,7 +68,6 @@
import com.android.ravenwood.RavenwoodRuntimeNative;
import com.android.ravenwood.RavenwoodRuntimeState;
import com.android.ravenwood.common.RavenwoodCommonUtils;
-import com.android.ravenwood.common.RavenwoodRuntimeException;
import com.android.ravenwood.common.SneakyThrow;
import com.android.server.LocalServices;
import com.android.server.compat.PlatformCompat;
@@ -74,8 +79,10 @@
import java.io.IOException;
import java.io.PrintStream;
import java.util.Collections;
+import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
+import java.util.Random;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@@ -85,8 +92,7 @@
import java.util.function.Supplier;
/**
- * Responsible for initializing and de-initializing the environment, according to a
- * {@link RavenwoodConfig}.
+ * Responsible for initializing and the environment.
*/
public class RavenwoodRuntimeEnvironmentController {
private static final String TAG = "RavenwoodRuntimeEnvironmentController";
@@ -113,8 +119,6 @@
private static ScheduledFuture<?> sPendingTimeout;
- private static long sOriginalIdentityToken = -1;
-
/**
* When enabled, attempt to detect uncaught exceptions from background threads.
*/
@@ -147,6 +151,10 @@
return res;
}
+ /** Map from path -> resources. */
+ private static final HashMap<File, Resources> sCachedResources = new HashMap<>();
+ private static Set<String> sAdoptedPermissions = Collections.emptySet();
+
private static final Object sInitializationLock = new Object();
@GuardedBy("sInitializationLock")
@@ -155,15 +163,16 @@
@GuardedBy("sInitializationLock")
private static Throwable sExceptionFromGlobalInit;
- private static RavenwoodAwareTestRunner sRunner;
- private static RavenwoodSystemProperties sProps;
-
private static final int DEFAULT_TARGET_SDK_LEVEL = VERSION_CODES.CUR_DEVELOPMENT;
private static final String DEFAULT_PACKAGE_NAME = "com.android.ravenwoodtests.defaultname";
+ private static final int sMyPid = new Random().nextInt(100, 32768);
private static int sTargetSdkLevel;
private static String sTestPackageName;
private static String sTargetPackageName;
+ private static Instrumentation sInstrumentation;
+ private static final long sCallingIdentity =
+ packBinderIdentityToken(false, FIRST_APPLICATION_UID, sMyPid);
/**
* Initialize the global environment.
@@ -182,7 +191,7 @@
Log.e(TAG, "globalInit() failed", th);
sExceptionFromGlobalInit = th;
- throw th;
+ SneakyThrow.sneakyThrow(th);
}
} else {
// Subsequent calls. If the first call threw, just throw the same error, to prevent
@@ -197,10 +206,13 @@
}
}
- private static void globalInitInner() {
+ private static void globalInitInner() throws IOException {
if (RAVENWOOD_VERBOSE_LOGGING) {
Log.v(TAG, "globalInit() called here...", new RuntimeException("NOT A CRASH"));
}
+ if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) {
+ Thread.setDefaultUncaughtExceptionHandler(sUncaughtExceptionHandler);
+ }
// Some process-wide initialization. (maybe redirect stdout/stderr)
RavenwoodCommonUtils.loadJniLibrary(LIBRAVENWOOD_INITIALIZER_NAME);
@@ -220,7 +232,6 @@
// Do the basic set up for the android sysprops.
RavenwoodSystemProperties.initialize();
- setSystemProperties(null);
// Do this after loading RAVENWOOD_NATIVE_RUNTIME_NAME (which backs Os.setenv()),
// before loadFrameworkNativeCode() (which uses $ANDROID_LOG_TAGS).
@@ -251,6 +262,74 @@
loadRavenwoodProperties();
assertMockitoVersion();
+
+ Log.i(TAG, "TargetPackageName=" + sTargetPackageName);
+ Log.i(TAG, "TestPackageName=" + sTestPackageName);
+ Log.i(TAG, "TargetSdkLevel=" + sTargetSdkLevel);
+
+ RavenwoodRuntimeState.sUid = FIRST_APPLICATION_UID;
+ RavenwoodRuntimeState.sPid = sMyPid;
+ RavenwoodRuntimeState.sTargetSdkLevel = sTargetSdkLevel;
+
+ ServiceManager.init$ravenwood();
+ LocalServices.removeAllServicesForTest();
+
+ ActivityManager.init$ravenwood(SYSTEM.getIdentifier());
+
+ final var main = new HandlerThread(MAIN_THREAD_NAME);
+ main.start();
+ Looper.setMainLooperForTest(main.getLooper());
+
+ final boolean isSelfInstrumenting =
+ Objects.equals(sTestPackageName, sTargetPackageName);
+
+ // This will load the resources from the apk set to `resource_apk` in the build file.
+ // This is supposed to be the "target app"'s resources.
+ final Supplier<Resources> targetResourcesLoader = () -> {
+ var file = new File(RAVENWOOD_RESOURCE_APK);
+ return loadResources(file.exists() ? file : null);
+ };
+
+ // Set up test context's (== instrumentation context's) resources.
+ // If the target package name == test package name, then we use the main resources.
+ final Supplier<Resources> instResourcesLoader;
+ if (isSelfInstrumenting) {
+ instResourcesLoader = targetResourcesLoader;
+ } else {
+ instResourcesLoader = () -> {
+ var file = new File(RAVENWOOD_INST_RESOURCE_APK);
+ return loadResources(file.exists() ? file : null);
+ };
+ }
+
+ var instContext = new RavenwoodContext(
+ sTestPackageName, main, instResourcesLoader);
+ var targetContext = new RavenwoodContext(
+ sTargetPackageName, main, targetResourcesLoader);
+
+ // Set up app context.
+ var appContext = new RavenwoodContext(sTargetPackageName, main, targetResourcesLoader);
+ appContext.setApplicationContext(appContext);
+ if (isSelfInstrumenting) {
+ instContext.setApplicationContext(appContext);
+ targetContext.setApplicationContext(appContext);
+ } else {
+ // When instrumenting into another APK, the test context doesn't have an app context.
+ targetContext.setApplicationContext(appContext);
+ }
+
+ final Supplier<Resources> systemResourcesLoader = () -> loadResources(null);
+
+ var systemServerContext =
+ new RavenwoodContext(ANDROID_PACKAGE_NAME, main, systemResourcesLoader);
+
+ sInstrumentation = new Instrumentation();
+ sInstrumentation.basicInit(instContext, targetContext, null);
+ InstrumentationRegistry.registerInstance(sInstrumentation, Bundle.EMPTY);
+
+ RavenwoodSystemServer.init(systemServerContext);
+
+ initializeCompatIds();
}
private static void loadRavenwoodProperties() {
@@ -265,134 +344,41 @@
}
/**
- * Initialize the environment.
+ * Partially reset and initialize before each test class invocation
*/
- public static void init(RavenwoodAwareTestRunner runner) {
- if (RAVENWOOD_VERBOSE_LOGGING) {
- Log.v(TAG, "init() called here: " + runner, new RuntimeException("STACKTRACE"));
- }
- if (sRunner == runner) {
- return;
- }
- if (sRunner != null) {
- reset();
- }
- sRunner = runner;
- try {
- initInner(runner.mState.getConfig());
- } catch (Exception th) {
- Log.e(TAG, "init() failed", th);
+ public static void initForRunner() {
+ var targetContext = sInstrumentation.getTargetContext();
+ var instContext = sInstrumentation.getContext();
+ // We need to recreate the mock UiAutomation for each test class, because sometimes tests
+ // will call Mockito.framework().clearInlineMocks() after execution.
+ sInstrumentation.basicInit(instContext, targetContext, createMockUiAutomation());
- RavenwoodCommonUtils.runIgnoringException(()-> reset());
+ // Reset some global state
+ Process_ravenwood.reset();
+ DeviceConfig_host.reset();
+ Binder.restoreCallingIdentity(sCallingIdentity);
- SneakyThrow.sneakyThrow(th);
- }
- }
-
- private static void initInner(RavenwoodConfig config) throws IOException {
- if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) {
- maybeThrowPendingUncaughtException(false);
- Thread.setDefaultUncaughtExceptionHandler(sUncaughtExceptionHandler);
- }
-
- config.mTargetPackageName = sTargetPackageName;
- config.mTestPackageName = sTestPackageName;
- config.mTargetSdkLevel = sTargetSdkLevel;
-
- Log.i(TAG, "TargetPackageName=" + sTargetPackageName);
- Log.i(TAG, "TestPackageName=" + sTestPackageName);
- Log.i(TAG, "TargetSdkLevel=" + sTargetSdkLevel);
-
- RavenwoodRuntimeState.sUid = config.mUid;
- RavenwoodRuntimeState.sPid = config.mPid;
- RavenwoodRuntimeState.sTargetSdkLevel = config.mTargetSdkLevel;
- sOriginalIdentityToken = Binder.clearCallingIdentity();
- reinit();
- setSystemProperties(config.mSystemProperties);
-
- ServiceManager.init$ravenwood();
- LocalServices.removeAllServicesForTest();
-
- ActivityManager.init$ravenwood(config.mCurrentUser);
-
- final var main = new HandlerThread(MAIN_THREAD_NAME);
- main.start();
- Looper.setMainLooperForTest(main.getLooper());
-
- final boolean isSelfInstrumenting =
- Objects.equals(config.mTestPackageName, config.mTargetPackageName);
-
- // This will load the resources from the apk set to `resource_apk` in the build file.
- // This is supposed to be the "target app"'s resources.
- final Supplier<Resources> targetResourcesLoader = () -> {
- var file = new File(RAVENWOOD_RESOURCE_APK);
- return config.mState.loadResources(file.exists() ? file : null);
- };
-
- // Set up test context's (== instrumentation context's) resources.
- // If the target package name == test package name, then we use the main resources.
- final Supplier<Resources> instResourcesLoader;
- if (isSelfInstrumenting) {
- instResourcesLoader = targetResourcesLoader;
- } else {
- instResourcesLoader = () -> {
- var file = new File(RAVENWOOD_INST_RESOURCE_APK);
- return config.mState.loadResources(file.exists() ? file : null);
- };
- }
-
- var instContext = new RavenwoodContext(
- config.mTestPackageName, main, instResourcesLoader);
- var targetContext = new RavenwoodContext(
- config.mTargetPackageName, main, targetResourcesLoader);
-
- // Set up app context.
- var appContext = new RavenwoodContext(
- config.mTargetPackageName, main, targetResourcesLoader);
- appContext.setApplicationContext(appContext);
- if (isSelfInstrumenting) {
- instContext.setApplicationContext(appContext);
- targetContext.setApplicationContext(appContext);
- } else {
- // When instrumenting into another APK, the test context doesn't have an app context.
- targetContext.setApplicationContext(appContext);
- }
- config.mInstContext = instContext;
- config.mTargetContext = targetContext;
-
- final Supplier<Resources> systemResourcesLoader = () -> config.mState.loadResources(null);
-
- config.mState.mSystemServerContext =
- new RavenwoodContext(ANDROID_PACKAGE_NAME, main, systemResourcesLoader);
-
- // Prepare other fields.
- config.mInstrumentation = new Instrumentation();
- config.mInstrumentation.basicInit(instContext, targetContext, createMockUiAutomation());
- InstrumentationRegistry.registerInstance(config.mInstrumentation, Bundle.EMPTY);
-
- RavenwoodSystemServer.init(config);
-
- initializeCompatIds(config);
+ SystemProperties.clearChangeCallbacksForTest();
if (ENABLE_TIMEOUT_STACKS) {
sPendingTimeout = sTimeoutExecutor.schedule(
RavenwoodRuntimeEnvironmentController::dumpStacks,
TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
}
- }
-
- /**
- * Partially re-initialize after each test method invocation
- */
- public static void reinit() {
- // sRunner could be null, if there was a failure in the initialization.
- if (sRunner != null) {
- var config = sRunner.mState.getConfig();
- Binder.restoreCallingIdentity(packBinderIdentityToken(false, config.mUid, config.mPid));
+ if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) {
+ maybeThrowPendingUncaughtException(false);
}
}
- private static void initializeCompatIds(RavenwoodConfig config) {
+ /**
+ * Partially reset and initialize before each test method invocation
+ */
+ public static void initForMethod() {
+ // TODO(b/375272444): this is a hacky workaround to ensure binder identity
+ Binder.restoreCallingIdentity(sCallingIdentity);
+ }
+
+ private static void initializeCompatIds() {
// Set up compat-IDs for the app side.
// TODO: Inside the system server, all the compat-IDs should be enabled,
// Due to the `AppCompatCallbacks.install(new long[0], new long[0])` call in
@@ -400,8 +386,8 @@
// Compat framework only uses the package name and the target SDK level.
ApplicationInfo appInfo = new ApplicationInfo();
- appInfo.packageName = config.mTargetPackageName;
- appInfo.targetSdkVersion = config.mTargetSdkLevel;
+ appInfo.packageName = sTargetPackageName;
+ appInfo.targetSdkVersion = sTargetSdkLevel;
PlatformCompat platformCompat = null;
try {
@@ -418,65 +404,42 @@
}
/**
- * De-initialize.
- *
- * Note, we call this method when init() fails too, so this method should deal with
- * any partially-initialized states.
+ * Load {@link Resources} from an APK, with cache.
*/
- public static void reset() {
- if (RAVENWOOD_VERBOSE_LOGGING) {
- Log.v(TAG, "reset() called here", new RuntimeException("STACKTRACE"));
+ private static Resources loadResources(@Nullable File apkPath) {
+ var cached = sCachedResources.get(apkPath);
+ if (cached != null) {
+ return cached;
}
- if (sRunner == null) {
- throw new RavenwoodRuntimeException("Internal error: reset() already called");
- }
- var config = sRunner.mState.getConfig();
- sRunner = null;
+ var fileToLoad = apkPath != null ? apkPath : new File(RAVENWOOD_EMPTY_RESOURCES_APK);
+
+ assertTrue("File " + fileToLoad + " doesn't exist.", fileToLoad.isFile());
+
+ final String path = fileToLoad.getAbsolutePath();
+ final var emptyPaths = new String[0];
+
+ ResourcesManager.getInstance().initializeApplicationPaths(path, emptyPaths);
+
+ final var ret = ResourcesManager.getInstance().getResources(null, path,
+ emptyPaths, emptyPaths, emptyPaths,
+ emptyPaths, null, null,
+ new DisplayAdjustments().getCompatibilityInfo(),
+ RavenwoodRuntimeEnvironmentController.class.getClassLoader(), null);
+
+ assertNotNull(ret);
+
+ sCachedResources.put(apkPath, ret);
+ return ret;
+ }
+
+ /**
+ * A callback when a test class finishes its execution, mostly only for debugging.
+ */
+ public static void exitTestClass() {
if (ENABLE_TIMEOUT_STACKS) {
sPendingTimeout.cancel(false);
}
-
- RavenwoodSystemServer.reset(config);
-
- InstrumentationRegistry.registerInstance(null, Bundle.EMPTY);
- config.mInstrumentation = null;
- if (config.mInstContext != null) {
- ((RavenwoodContext) config.mInstContext).cleanUp();
- config.mInstContext = null;
- }
- if (config.mTargetContext != null) {
- ((RavenwoodContext) config.mTargetContext).cleanUp();
- config.mTargetContext = null;
- }
- if (config.mState.mSystemServerContext != null) {
- config.mState.mSystemServerContext.cleanUp();
- }
-
- if (Looper.getMainLooper() != null) {
- Looper.getMainLooper().quit();
- }
- Looper.clearMainLooperForTest();
-
- ActivityManager.reset$ravenwood();
-
- LocalServices.removeAllServicesForTest();
- ServiceManager.reset$ravenwood();
-
- setSystemProperties(null);
- if (sOriginalIdentityToken != -1) {
- Binder.restoreCallingIdentity(sOriginalIdentityToken);
- }
- RavenwoodRuntimeState.reset();
- Process_ravenwood.reset();
- DeviceConfig_host.reset();
-
- try {
- ResourcesManager.setInstance(null); // Better structure needed.
- } catch (Exception e) {
- // AOSP-CHANGE: AOSP doesn't support resources yet.
- }
-
if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) {
maybeThrowPendingUncaughtException(true);
}
@@ -521,19 +484,6 @@
}
}
- /**
- * Set the current configuration to the actual SystemProperties.
- */
- private static void setSystemProperties(@Nullable RavenwoodSystemProperties systemProperties) {
- SystemProperties.clearChangeCallbacksForTest();
- RavenwoodRuntimeNative.clearSystemProperties();
- if (systemProperties == null) systemProperties = new RavenwoodSystemProperties();
- sProps = new RavenwoodSystemProperties(systemProperties, true);
- for (var entry : systemProperties.getValues().entrySet()) {
- RavenwoodRuntimeNative.setSystemProperty(entry.getKey(), entry.getValue());
- }
- }
-
private static final String MOCKITO_ERROR = "FATAL: Unsupported Mockito detected!"
+ " Your test or its dependencies use one of the \"mockito-target-*\""
+ " modules as static library, which is unusable on host side."
@@ -558,40 +508,31 @@
// TODO: use the real UiAutomation class instead of a mock
private static UiAutomation createMockUiAutomation() {
- final Set[] adoptedPermission = { Collections.emptySet() };
+ sAdoptedPermissions = Collections.emptySet();
var mock = mock(UiAutomation.class, inv -> {
HostTestUtils.onThrowMethodCalled();
return null;
});
doAnswer(inv -> {
- adoptedPermission[0] = UiAutomation.ALL_PERMISSIONS;
+ sAdoptedPermissions = UiAutomation.ALL_PERMISSIONS;
return null;
}).when(mock).adoptShellPermissionIdentity();
doAnswer(inv -> {
if (inv.getArgument(0) == null) {
- adoptedPermission[0] = UiAutomation.ALL_PERMISSIONS;
+ sAdoptedPermissions = UiAutomation.ALL_PERMISSIONS;
} else {
- adoptedPermission[0] = Set.of(inv.getArguments());
+ sAdoptedPermissions = (Set) Set.of(inv.getArguments());
}
return null;
}).when(mock).adoptShellPermissionIdentity(any());
doAnswer(inv -> {
- adoptedPermission[0] = Collections.emptySet();
+ sAdoptedPermissions = Collections.emptySet();
return null;
}).when(mock).dropShellPermissionIdentity();
- doAnswer(inv -> adoptedPermission[0]).when(mock).getAdoptedShellPermissions();
+ doAnswer(inv -> sAdoptedPermissions).when(mock).getAdoptedShellPermissions();
return mock;
}
- @SuppressWarnings("unused") // Called from native code (ravenwood_sysprop.cpp)
- private static void checkSystemPropertyAccess(String key, boolean write) {
- boolean result = write ? sProps.isKeyWritable(key) : sProps.isKeyReadable(key);
- if (!result) {
- throw new IllegalArgumentException((write ? "Write" : "Read")
- + " access to system property '" + key + "' denied via RavenwoodConfig");
- }
- }
-
private static void dumpCommandLineArgs() {
Log.i(TAG, "JVM arguments:");
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
similarity index 74%
rename from ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
rename to ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
index 9bd376a..c545baa 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package android.platform.test.ravenwood;
import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING;
@@ -21,26 +20,30 @@
import android.util.Log;
+import com.android.ravenwood.RavenwoodRuntimeNative;
+
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
+/**
+ * A class to manage the core default system properties of the Ravenwood environment.
+ */
public class RavenwoodSystemProperties {
private static final String TAG = "RavenwoodSystemProperties";
- /** We pull in propeties from this file. */
+ /** We pull in properties from this file. */
private static final String RAVENWOOD_BUILD_PROP = "ravenwood-data/ravenwood-build.prop";
/** This is the actual build.prop we use to build the device (contents depends on lunch). */
private static final String DEVICE_BUILD_PROP = "ravenwood-data/build.prop";
/** The default values. */
- private static final Map<String, String> sDefaultValues = new HashMap<>();
+ static final Map<String, String> sDefaultValues = new HashMap<>();
private static final String[] PARTITIONS = {
"bootimage",
@@ -91,7 +94,7 @@
var deviceValue = deviceProps.get(deviceKey);
if (deviceValue == null) {
throw new RuntimeException("Failed to initialize system properties. Key '"
- + deviceKey + "' doesn't exist in the device side build.prop");
+ + deviceKey + "' doesn't exist in the device side build.prop");
}
value = deviceValue;
} else {
@@ -115,6 +118,7 @@
}
}
}
+
if (RAVENWOOD_VERBOSE_LOGGING) {
// Dump all properties for local debugging.
Log.v(TAG, "All system properties:");
@@ -122,35 +126,12 @@
Log.v(TAG, "" + key + "=" + sDefaultValues.get(key));
}
}
+
+ // Actually set the system properties
+ sDefaultValues.forEach(RavenwoodRuntimeNative::setSystemProperty);
}
- private volatile boolean mIsImmutable;
-
- private final Map<String, String> mValues = new HashMap<>();
-
- /** Set of additional keys that should be considered readable */
- private final Set<String> mKeyReadable = new HashSet<>();
-
- /** Set of additional keys that should be considered writable */
- private final Set<String> mKeyWritable = new HashSet<>();
-
- public RavenwoodSystemProperties() {
- mValues.putAll(sDefaultValues);
- }
-
- /** Copy constructor */
- public RavenwoodSystemProperties(RavenwoodSystemProperties source, boolean immutable) {
- mKeyReadable.addAll(source.mKeyReadable);
- mKeyWritable.addAll(source.mKeyWritable);
- mValues.putAll(source.mValues);
- mIsImmutable = immutable;
- }
-
- public Map<String, String> getValues() {
- return new HashMap<>(mValues);
- }
-
- public boolean isKeyReadable(String key) {
+ private static boolean isKeyReadable(String key) {
final String root = getKeyRoot(key);
if (root.startsWith("debug.")) return true;
@@ -183,10 +164,10 @@
return true;
}
- return mKeyReadable.contains(key);
+ return false;
}
- public boolean isKeyWritable(String key) {
+ private static boolean isKeyWritable(String key) {
final String root = getKeyRoot(key);
if (root.startsWith("debug.")) return true;
@@ -194,42 +175,11 @@
// For PropertyInvalidatedCache
if (root.startsWith("cache_key.")) return true;
- return mKeyWritable.contains(key);
+ return false;
}
- private void ensureNotImmutable() {
- if (mIsImmutable) {
- throw new RuntimeException("Unable to update immutable instance");
- }
- }
-
- public void setValue(String key, Object value) {
- ensureNotImmutable();
-
- final String valueString = (value == null) ? null : String.valueOf(value);
- if ((valueString == null) || valueString.isEmpty()) {
- mValues.remove(key);
- } else {
- mValues.put(key, valueString);
- }
- }
-
- public void setAccessNone(String key) {
- ensureNotImmutable();
- mKeyReadable.remove(key);
- mKeyWritable.remove(key);
- }
-
- public void setAccessReadOnly(String key) {
- ensureNotImmutable();
- mKeyReadable.add(key);
- mKeyWritable.remove(key);
- }
-
- public void setAccessReadWrite(String key) {
- ensureNotImmutable();
- mKeyReadable.add(key);
- mKeyWritable.add(key);
+ static boolean isKeyAccessible(String key, boolean write) {
+ return write ? isKeyWritable(key) : isKeyReadable(key);
}
/**
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
index 438a2bf..3346635 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
@@ -33,7 +33,7 @@
import com.android.server.compat.PlatformCompatNative;
import com.android.server.utils.TimingsTraceAndSlog;
-import java.util.List;
+import java.util.Collection;
import java.util.Set;
public class RavenwoodSystemServer {
@@ -68,27 +68,24 @@
private static TimingsTraceAndSlog sTimings;
private static SystemServiceManager sServiceManager;
- public static void init(RavenwoodConfig config) {
+ public static void init(Context systemServerContext) {
// Always start PlatformCompat, regardless of the requested services.
// PlatformCompat is not really a SystemService, so it won't receive boot phases / etc.
// This initialization code is copied from SystemServer.java.
- PlatformCompat platformCompat = new PlatformCompat(config.mState.mSystemServerContext);
+ PlatformCompat platformCompat = new PlatformCompat(systemServerContext);
ServiceManager.addService(Context.PLATFORM_COMPAT_SERVICE, platformCompat);
ServiceManager.addService(Context.PLATFORM_COMPAT_NATIVE_SERVICE,
new PlatformCompatNative(platformCompat));
- // Avoid overhead if no services required
- if (config.mServicesRequired.isEmpty()) return;
-
sStartedServices = new ArraySet<>();
sTimings = new TimingsTraceAndSlog();
- sServiceManager = new SystemServiceManager(config.mState.mSystemServerContext);
+ sServiceManager = new SystemServiceManager(systemServerContext);
sServiceManager.setStartInfo(false,
SystemClock.elapsedRealtime(),
SystemClock.uptimeMillis());
LocalServices.addService(SystemServiceManager.class, sServiceManager);
- startServices(config.mServicesRequired);
+ startServices(sKnownServices.keySet());
sServiceManager.sealStartedServices();
// TODO: expand to include additional boot phases when relevant
@@ -96,7 +93,7 @@
sServiceManager.startBootPhase(sTimings, SystemService.PHASE_BOOT_COMPLETED);
}
- public static void reset(RavenwoodConfig config) {
+ public static void reset() {
// TODO: consider introducing shutdown boot phases
LocalServices.removeServiceForTest(SystemServiceManager.class);
@@ -105,7 +102,7 @@
sStartedServices = null;
}
- private static void startServices(List<Class<?>> serviceClasses) {
+ private static void startServices(Collection<Class<?>> serviceClasses) {
for (Class<?> serviceClass : serviceClasses) {
// Quietly ignore duplicate requests if service already started
if (sStartedServices.contains(serviceClass)) continue;
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
index 7ca9239..3ed0f50 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
@@ -15,21 +15,13 @@
*/
package android.platform.test.ravenwood;
-import static android.os.Process.FIRST_APPLICATION_UID;
-import static android.os.UserHandle.SYSTEM;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.Instrumentation;
-import android.content.Context;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* @deprecated This class will be removed. Reach out to g/ravenwood if you need any features in it.
@@ -45,37 +37,10 @@
public @interface Config {
}
- private static final int NOBODY_UID = 9999;
-
- private static final AtomicInteger sNextPid = new AtomicInteger(100);
-
- int mCurrentUser = SYSTEM.getIdentifier();
-
- /**
- * Unless the test author requests differently, run as "nobody", and give each collection of
- * tests its own unique PID.
- */
- int mUid = FIRST_APPLICATION_UID;
- int mPid = sNextPid.getAndIncrement();
-
- String mTestPackageName;
- String mTargetPackageName;
-
- int mTargetSdkLevel;
-
- final RavenwoodSystemProperties mSystemProperties = new RavenwoodSystemProperties();
-
- final List<Class<?>> mServicesRequired = new ArrayList<>();
-
- volatile Context mInstContext;
- volatile Context mTargetContext;
- volatile Instrumentation mInstrumentation;
-
/**
* Stores internal states / methods associated with this config that's only needed in
* junit-impl.
*/
- final RavenwoodConfigState mState = new RavenwoodConfigState(this);
private RavenwoodConfig() {
}
@@ -159,34 +124,11 @@
return this;
}
- Builder setSystemPropertyImmutableReal(@NonNull String key,
- @Nullable Object value) {
- mConfig.mSystemProperties.setValue(key, value);
- mConfig.mSystemProperties.setAccessReadOnly(key);
- return this;
- }
-
- Builder setSystemPropertyMutableReal(@NonNull String key,
- @Nullable Object value) {
- mConfig.mSystemProperties.setValue(key, value);
- mConfig.mSystemProperties.setAccessReadWrite(key);
- return this;
- }
-
/**
- * Configure the set of system services that are required for this test to operate.
- *
- * For example, passing {@code android.hardware.SerialManager.class} as an argument will
- * ensure that the underlying service is created, initialized, and ready to use for the
- * duration of the test. The {@code SerialManager} instance can be obtained via
- * {@code RavenwoodRule.getContext()} and {@code Context.getSystemService()}, and
- * {@code SerialManagerInternal} can be obtained via {@code LocalServices.getService()}.
+ * @deprecated no longer used. All supported services are available.
*/
+ @Deprecated
public Builder setServicesRequired(@NonNull Class<?>... services) {
- mConfig.mServicesRequired.clear();
- for (Class<?> service : services) {
- mConfig.mServicesRequired.add(service);
- }
return this;
}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index 5681a90..e49d3d9 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -92,19 +92,11 @@
}
}
- private final RavenwoodConfig mConfiguration;
-
- public RavenwoodRule() {
- mConfiguration = new RavenwoodConfig.Builder().build();
- }
-
- private RavenwoodRule(RavenwoodConfig config) {
- mConfiguration = config;
- }
+ final RavenwoodTestProperties mProperties = new RavenwoodTestProperties();
public static class Builder {
- private final RavenwoodConfig.Builder mBuilder =
- new RavenwoodConfig.Builder();
+
+ private final RavenwoodRule mRule = new RavenwoodRule();
public Builder() {
}
@@ -152,7 +144,8 @@
* Has no effect on non-Ravenwood environments.
*/
public Builder setSystemPropertyImmutable(@NonNull String key, @Nullable Object value) {
- mBuilder.setSystemPropertyImmutableReal(key, value);
+ mRule.mProperties.setValue(key, value);
+ mRule.mProperties.setAccessReadOnly(key);
return this;
}
@@ -167,26 +160,21 @@
* Has no effect on non-Ravenwood environments.
*/
public Builder setSystemPropertyMutable(@NonNull String key, @Nullable Object value) {
- mBuilder.setSystemPropertyMutableReal(key, value);
+ mRule.mProperties.setValue(key, value);
+ mRule.mProperties.setAccessReadWrite(key);
return this;
}
/**
- * Configure the set of system services that are required for this test to operate.
- *
- * For example, passing {@code android.hardware.SerialManager.class} as an argument will
- * ensure that the underlying service is created, initialized, and ready to use for the
- * duration of the test. The {@code SerialManager} instance can be obtained via
- * {@code RavenwoodRule.getContext()} and {@code Context.getSystemService()}, and
- * {@code SerialManagerInternal} can be obtained via {@code LocalServices.getService()}.
+ * @deprecated no longer used. All supported services are available.
*/
+ @Deprecated
public Builder setServicesRequired(@NonNull Class<?>... services) {
- mBuilder.setServicesRequired(services);
return this;
}
public RavenwoodRule build() {
- return new RavenwoodRule(mBuilder.build());
+ return mRule;
}
}
@@ -227,7 +215,7 @@
@Override
public Statement apply(Statement base, Description description) {
- if (!RavenwoodConfig.isOnRavenwood()) {
+ if (!IS_ON_RAVENWOOD) {
return base;
}
return new Statement() {
@@ -296,8 +284,4 @@
public static RavenwoodPrivate private$ravenwood() {
return sRavenwoodPrivate;
}
-
- RavenwoodConfig getConfiguration() {
- return mConfiguration;
- }
}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodTestProperties.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodTestProperties.java
new file mode 100644
index 0000000..66a26b5
--- /dev/null
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodTestProperties.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.platform.test.ravenwood;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A class to store system properties defined by tests.
+ */
+public class RavenwoodTestProperties {
+ final Map<String, String> mValues = new HashMap<>();
+
+ /** Set of additional keys that should be considered readable */
+ final Set<String> mKeyReadable = new HashSet<>();
+
+ /** Set of additional keys that should be considered writable */
+ final Set<String> mKeyWritable = new HashSet<>();
+
+ public void setValue(String key, Object value) {
+ final String valueString = (value == null) ? null : String.valueOf(value);
+ if ((valueString == null) || valueString.isEmpty()) {
+ mValues.remove(key);
+ } else {
+ mValues.put(key, valueString);
+ }
+ }
+
+ public void setAccessNone(String key) {
+ mKeyReadable.remove(key);
+ mKeyWritable.remove(key);
+ }
+
+ public void setAccessReadOnly(String key) {
+ mKeyReadable.add(key);
+ mKeyWritable.remove(key);
+ }
+
+ public void setAccessReadWrite(String key) {
+ mKeyReadable.add(key);
+ mKeyWritable.add(key);
+ }
+}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java
index 7b940b4..9a78989 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java
@@ -56,7 +56,11 @@
public static native boolean setSystemProperty(String key, String value);
- public static native void clearSystemProperties();
+ public static native boolean removeSystemProperty(String key);
+
+ public static void clearSystemProperties() {
+ removeSystemProperty(null);
+ }
public static native int gettid();
diff --git a/ravenwood/runtime-jni/jni_helper.h b/ravenwood/runtime-jni/jni_helper.h
index 561fb3b..25d7519 100644
--- a/ravenwood/runtime-jni/jni_helper.h
+++ b/ravenwood/runtime-jni/jni_helper.h
@@ -26,6 +26,7 @@
constexpr const char* kCommonUtils = "com/android/ravenwood/common/RavenwoodCommonUtils";
constexpr const char* kRuntimeEnvController =
"android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController";
+constexpr const char* kRunnerState = "android/platform/test/ravenwood/RavenwoodRunnerState";
constexpr const char* kRuntimeNative = "com/android/ravenwood/RavenwoodRuntimeNative";
// We have to explicitly decode the string to real UTF-8, because when using GetStringUTFChars
diff --git a/ravenwood/runtime-jni/ravenwood_sysprop.cpp b/ravenwood/runtime-jni/ravenwood_sysprop.cpp
index aafc426..a78aa8d 100644
--- a/ravenwood/runtime-jni/ravenwood_sysprop.cpp
+++ b/ravenwood/runtime-jni/ravenwood_sysprop.cpp
@@ -117,7 +117,7 @@
// ---- JNI ----
static JavaVM* gVM = nullptr;
-static jclass gEnvController = nullptr;
+static jclass gRunnerState = nullptr;
static jmethodID gCheckSystemPropertyAccess;
static void reloadNativeLibrary(JNIEnv* env, jclass, jstring javaPath) {
@@ -128,11 +128,11 @@
// Call back into Java code to check property access
static void check_system_property_access(const char* key, bool write) {
- if (gVM != nullptr && gEnvController != nullptr) {
+ if (gVM != nullptr && gRunnerState != nullptr) {
JNIEnv* env;
if (gVM->GetEnv((void**)&env, JNI_VERSION_1_4) >= 0) {
ALOGI("%s access to system property '%s'", write ? "Write" : "Read", key);
- env->CallStaticVoidMethod(gEnvController, gCheckSystemPropertyAccess,
+ env->CallStaticVoidMethod(gRunnerState, gCheckSystemPropertyAccess,
env->NewStringUTF(key), write ? JNI_TRUE : JNI_FALSE);
return;
}
@@ -155,16 +155,29 @@
return property_set(key.c_str(), value.c_str()) ? JNI_TRUE : JNI_FALSE;
}
-static void clearSystemProperties(JNIEnv*, jclass) {
+static jboolean removeSystemProperty(JNIEnv* env, jclass, jstring javaKey) {
std::lock_guard lock(g_properties_lock);
- g_properties.clear();
+
+ if (javaKey == nullptr) {
+ g_properties.clear();
+ return JNI_TRUE;
+ } else {
+ ScopedUtfChars key(env, javaKey);
+ auto it = g_properties.find(key);
+ if (it != g_properties.end()) {
+ g_properties.erase(it);
+ return JNI_TRUE;
+ } else {
+ return JNI_FALSE;
+ }
+ }
}
static const JNINativeMethod sMethods[] = {
{"reloadNativeLibrary", "(Ljava/lang/String;)V", (void*)reloadNativeLibrary},
{"getSystemProperty", "(Ljava/lang/String;)Ljava/lang/String;", (void*)getSystemProperty},
{"setSystemProperty", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*)setSystemProperty},
- {"clearSystemProperties", "()V", (void*)clearSystemProperties},
+ {"removeSystemProperty", "(Ljava/lang/String;)Z", (void*)removeSystemProperty},
};
extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
@@ -174,9 +187,9 @@
gVM = vm;
// Fetch several references for future use
- gEnvController = FindGlobalClassOrDie(env, kRuntimeEnvController);
+ gRunnerState = FindGlobalClassOrDie(env, kRunnerState);
gCheckSystemPropertyAccess =
- GetStaticMethodIDOrDie(env, gEnvController, "checkSystemPropertyAccess",
+ GetStaticMethodIDOrDie(env, gRunnerState, "checkSystemPropertyAccess",
"(Ljava/lang/String;Z)V");
// Expose raw property methods as JNI methods
diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodMultipleRuleTest.java b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodMultipleRuleTest.java
deleted file mode 100644
index c25d2b4..0000000
--- a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodMultipleRuleTest.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.ravenwoodtest.bivalenttest;
-
-import android.platform.test.ravenwood.RavenwoodConfig;
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import org.junit.Assume;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.runner.RunWith;
-
-/**
- * Make sure having multiple RavenwoodRule's is detected.
- * (But only when running on ravenwod. Otherwise it'll be ignored.)
- */
-@RunWith(AndroidJUnit4.class)
-public class RavenwoodMultipleRuleTest {
-
- @Rule(order = Integer.MIN_VALUE)
- public final ExpectedException mExpectedException = ExpectedException.none();
-
- @Rule
- public final RavenwoodRule mRavenwood1 = new RavenwoodRule();
-
- @Rule
- public final RavenwoodRule mRavenwood2 = new RavenwoodRule();
-
- public RavenwoodMultipleRuleTest() {
- // We can't call it within the test method because the exception happens before
- // calling the method, so set it up here.
- if (RavenwoodConfig.isOnRavenwood()) {
- mExpectedException.expectMessage("Multiple nesting RavenwoodRule");
- }
- }
-
- @Test
- public void testMultipleRulesNotAllowed() {
- Assume.assumeTrue(RavenwoodConfig.isOnRavenwood());
- }
-}
diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRuleValidationTest.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRuleValidationTest.java
new file mode 100644
index 0000000..f9e73db
--- /dev/null
+++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRuleValidationTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ravenwoodtest.runnercallbacktests;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.SystemProperties;
+import android.platform.test.annotations.NoRavenizer;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+/**
+ * Test for RavenwoodRule.
+ */
+@NoRavenizer // This class shouldn't be executed with RavenwoodAwareTestRunner.
+public class RavenwoodRuleValidationTest extends RavenwoodRunnerTestBase {
+
+ public static class RuleInBaseClass {
+ static String PROPERTY_KEY = "debug.ravenwood.prop.in.base";
+ static String PROPERTY_VAL = "ravenwood";
+ @Rule
+ public final RavenwoodRule mRavenwood1 = new RavenwoodRule.Builder()
+ .setSystemPropertyImmutable(PROPERTY_KEY, PROPERTY_VAL).build();
+ }
+
+ /**
+ * Make sure that RavenwoodRule in a base class takes effect.
+ */
+ @RunWith(AndroidJUnit4.class)
+ // CHECKSTYLE:OFF
+ @Expected("""
+ testRunStarted: classes
+ testSuiteStarted: classes
+ testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleInBaseClassSuccessTest
+ testStarted: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleInBaseClassSuccessTest)
+ testFinished: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleInBaseClassSuccessTest)
+ testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleInBaseClassSuccessTest
+ testSuiteFinished: classes
+ testRunFinished: 1,0,0,0
+ """)
+ // CHECKSTYLE:ON
+ public static class RuleInBaseClassSuccessTest extends RuleInBaseClass {
+
+ @Test
+ public void testRuleInBaseClass() {
+ assertThat(SystemProperties.get(PROPERTY_KEY)).isEqualTo(PROPERTY_VAL);
+ }
+ }
+
+ /**
+ * Same as {@link RuleInBaseClass}, but the type of the rule field is not {@link RavenwoodRule}.
+ */
+ public abstract static class RuleWithDifferentTypeInBaseClass {
+ static String PROPERTY_KEY = "debug.ravenwood.prop.in.base.different.type";
+ static String PROPERTY_VAL = "ravenwood";
+ @Rule
+ public final TestRule mRavenwood1 = new RavenwoodRule.Builder()
+ .setSystemPropertyImmutable(PROPERTY_KEY, PROPERTY_VAL).build();
+ }
+
+ /**
+ * Make sure that RavenwoodRule in a base class takes effect, even if the field type is not
+ */
+ @RunWith(AndroidJUnit4.class)
+ // CHECKSTYLE:OFF
+ @Expected("""
+ testRunStarted: classes
+ testSuiteStarted: classes
+ testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest
+ testStarted: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest)
+ testFinished: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest)
+ testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest
+ testSuiteFinished: classes
+ testRunFinished: 1,0,0,0
+ """)
+ // CHECKSTYLE:ON
+ public static class RuleWithDifferentTypeInBaseClassSuccessTest extends RuleWithDifferentTypeInBaseClass {
+
+ @Test
+ public void testRuleInBaseClass() {
+ assertThat(SystemProperties.get(PROPERTY_KEY)).isEqualTo(PROPERTY_VAL);
+ }
+ }
+}
diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java
deleted file mode 100644
index f94b98b..0000000
--- a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java
+++ /dev/null
@@ -1,484 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.ravenwoodtest.runnercallbacktests;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.platform.test.annotations.NoRavenizer;
-import android.platform.test.ravenwood.RavenwoodConfig;
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import org.junit.Ignore;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestRule;
-import org.junit.runner.RunWith;
-
-
-/**
- * Test for @Config field extraction and validation.
- *
- * TODO(b/377765941) Most of the tests here will be obsolete and deleted with b/377765941, but
- * some of the tests may need to be re-implemented one way or another. (e.g. the package name
- * test.) Until that happens, we'll keep all tests here but add an {@code @Ignore} instead.
- */
-@NoRavenizer // This class shouldn't be executed with RavenwoodAwareTestRunner.
-public class RavenwoodRunnerConfigValidationTest extends RavenwoodRunnerTestBase {
- public abstract static class ConfigInBaseClass {
- static String PACKAGE_NAME = "com.ConfigInBaseClass";
-
- @RavenwoodConfig.Config
- public static RavenwoodConfig sConfig = new RavenwoodConfig.Builder()
- .setPackageName(PACKAGE_NAME).build();
- }
-
- /**
- * Make sure a config in the base class is detected.
- */
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigInBaseClassTest
- testStarted: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigInBaseClassTest)
- testFinished: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigInBaseClassTest)
- testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigInBaseClassTest
- testSuiteFinished: classes
- testRunFinished: 1,0,0,0
- """)
- // CHECKSTYLE:ON
- @Ignore // Package name is no longer set via config.
- public static class ConfigInBaseClassTest extends ConfigInBaseClass {
- @Test
- public void test() {
- assertThat(InstrumentationRegistry.getInstrumentation().getContext().getPackageName())
- .isEqualTo(PACKAGE_NAME);
- }
- }
-
- /**
- * Make sure a config in the base class is detected.
- */
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigOverridingTest
- testStarted: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigOverridingTest)
- testFinished: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigOverridingTest)
- testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigOverridingTest
- testSuiteFinished: classes
- testRunFinished: 1,0,0,0
- """)
- // CHECKSTYLE:ON
- @Ignore // Package name is no longer set via config.
- public static class ConfigOverridingTest extends ConfigInBaseClass {
- static String PACKAGE_NAME_OVERRIDE = "com.ConfigOverridingTest";
-
- @RavenwoodConfig.Config
- public static RavenwoodConfig sConfig = new RavenwoodConfig.Builder()
- .setPackageName(PACKAGE_NAME_OVERRIDE).build();
-
- @Test
- public void test() {
- assertThat(InstrumentationRegistry.getInstrumentation().getContext().getPackageName())
- .isEqualTo(PACKAGE_NAME_OVERRIDE);
- }
- }
-
- /**
- * Test to make sure that if a test has a config error, the failure would be reported from
- * each test method.
- */
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest)
- testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest.sConfig expected to be public static
- testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest)
- testSuiteFinished: classes
- testRunFinished: 1,1,0,0
- """)
- // CHECKSTYLE:ON
- public static class ErrorMustBeReportedFromEachTest {
- @RavenwoodConfig.Config
- private static RavenwoodConfig sConfig = // Invalid because it's private.
- new RavenwoodConfig.Builder().build();
-
- @Test
- public void testMethod1() {
- }
-
- @Test
- public void testMethod2() {
- }
-
- @Test
- public void testMethod3() {
- }
- }
-
- /**
- * Invalid because there are two @Config's.
- */
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateConfigTest)
- testFailure: Class com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest.DuplicateConfigTest has multiple fields with @RavenwoodConfig.Config
- testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateConfigTest)
- testSuiteFinished: classes
- testRunFinished: 1,1,0,0
- """)
- // CHECKSTYLE:ON
- public static class DuplicateConfigTest {
-
- @RavenwoodConfig.Config
- public static RavenwoodConfig sConfig1 =
- new RavenwoodConfig.Builder().build();
-
- @RavenwoodConfig.Config
- public static RavenwoodConfig sConfig2 =
- new RavenwoodConfig.Builder().build();
-
- @Test
- public void testConfig() {
- }
- }
-
- /**
- * @Config's must be static.
- */
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest)
- testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest.sConfig expected to be public static
- testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest)
- testSuiteFinished: classes
- testRunFinished: 1,1,0,0
- """)
- // CHECKSTYLE:ON
- public static class NonStaticConfigTest {
-
- @RavenwoodConfig.Config
- public RavenwoodConfig sConfig =
- new RavenwoodConfig.Builder().build();
-
- @Test
- public void testConfig() {
- }
- }
-
- /**
- * @Config's must be public.
- */
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest)
- testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest.sConfig expected to be public static
- testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest)
- testSuiteFinished: classes
- testRunFinished: 1,1,0,0
- """)
- // CHECKSTYLE:ON
- public static class NonPublicConfigTest {
-
- @RavenwoodConfig.Config
- RavenwoodConfig sConfig =
- new RavenwoodConfig.Builder().build();
-
- @Test
- public void testConfig() {
- }
- }
-
- /**
- * @Config's must be of type RavenwoodConfig.
- */
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeConfigTest)
- testFailure: Field com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest.WrongTypeConfigTest.sConfig has @RavenwoodConfig.Config but type is not RavenwoodConfig
- testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeConfigTest)
- testSuiteFinished: classes
- testRunFinished: 1,1,0,0
- """)
- // CHECKSTYLE:ON
- public static class WrongTypeConfigTest {
-
- @RavenwoodConfig.Config
- public static Object sConfig =
- new RavenwoodConfig.Builder().build();
-
- @Test
- public void testConfig() {
- }
-
- }
-
- /**
- * @Rule must be of type RavenwoodRule.
- */
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeRuleTest
- testStarted: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeRuleTest)
- testFailure: If you have a RavenwoodRule in your test, make sure the field type is RavenwoodRule so Ravenwood can detect it.
- testFinished: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeRuleTest)
- testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeRuleTest
- testSuiteFinished: classes
- testRunFinished: 1,1,0,0
- """)
- // CHECKSTYLE:ON
- public static class WrongTypeRuleTest {
-
- @Rule
- public TestRule mRule = new RavenwoodRule.Builder().build();
-
- @Test
- public void testConfig() {
- }
-
- }
-
- /**
- * Config can't be used with a (instance) Rule.
- */
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithInstanceRuleTest)
- testFailure: RavenwoodConfig and RavenwoodRule cannot be used in the same class. Suggest migrating to RavenwoodConfig.
- testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithInstanceRuleTest)
- testSuiteFinished: classes
- testRunFinished: 1,1,0,0
- """)
- // CHECKSTYLE:ON
- public static class WithInstanceRuleTest {
-
- @RavenwoodConfig.Config
- public static RavenwoodConfig sConfig =
- new RavenwoodConfig.Builder().build();
-
- @Rule
- public RavenwoodRule mRule = new RavenwoodRule.Builder().build();
-
- @Test
- public void testConfig() {
- }
- }
-
- /**
- * Config can't be used with a (static) Rule.
- */
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithStaticRuleTest)
- testFailure: Failed to instantiate class androidx.test.ext.junit.runners.AndroidJUnit4
- testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithStaticRuleTest)
- testSuiteFinished: classes
- testRunFinished: 1,1,0,0
- """)
- // CHECKSTYLE:ON
- public static class WithStaticRuleTest {
-
- @RavenwoodConfig.Config
- public static RavenwoodConfig sConfig =
- new RavenwoodConfig.Builder().build();
-
- @Rule
- public static RavenwoodRule sRule = new RavenwoodRule.Builder().build();
-
- @Test
- public void testConfig() {
- }
- }
-
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateRulesTest
- testStarted: testMultipleRulesNotAllowed(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateRulesTest)
- testFailure: Multiple nesting RavenwoodRule's are detected in the same class, which is not supported.
- testFinished: testMultipleRulesNotAllowed(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateRulesTest)
- testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateRulesTest
- testSuiteFinished: classes
- testRunFinished: 1,1,0,0
- """)
- // CHECKSTYLE:ON
- public static class DuplicateRulesTest {
-
- @Rule
- public final RavenwoodRule mRavenwood1 = new RavenwoodRule();
-
- @Rule
- public final RavenwoodRule mRavenwood2 = new RavenwoodRule();
-
- @Test
- public void testMultipleRulesNotAllowed() {
- }
- }
-
- public static class RuleInBaseClass {
- static String PACKAGE_NAME = "com.RuleInBaseClass";
- @Rule
- public final RavenwoodRule mRavenwood1 = new RavenwoodRule.Builder()
- .setPackageName(PACKAGE_NAME).build();
- }
-
- /**
- * Make sure that RavenwoodRule in a base class takes effect.
- */
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleInBaseClassSuccessTest
- testStarted: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleInBaseClassSuccessTest)
- testFinished: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleInBaseClassSuccessTest)
- testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleInBaseClassSuccessTest
- testSuiteFinished: classes
- testRunFinished: 1,0,0,0
- """)
- // CHECKSTYLE:ON
- @Ignore // Package name is no longer set via config.
- public static class RuleInBaseClassSuccessTest extends RuleInBaseClass {
-
- @Test
- public void testRuleInBaseClass() {
- assertThat(InstrumentationRegistry.getInstrumentation().getContext().getPackageName())
- .isEqualTo(PACKAGE_NAME);
- }
- }
-
- /**
- * Make sure that having a config and a rule in a base class should fail.
- * RavenwoodRule.
- */
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleInBaseClassTest)
- testFailure: RavenwoodConfig and RavenwoodRule cannot be used in the same class. Suggest migrating to RavenwoodConfig.
- testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleInBaseClassTest)
- testSuiteFinished: classes
- testRunFinished: 1,1,0,0
- """)
- // CHECKSTYLE:ON
- public static class ConfigWithRuleInBaseClassTest extends RuleInBaseClass {
- @RavenwoodConfig.Config
- public static RavenwoodConfig sConfig = new RavenwoodConfig.Builder().build();
-
- @Test
- public void test() {
- }
- }
-
- /**
- * Same as {@link RuleInBaseClass}, but the type of the rule field is not {@link RavenwoodRule}.
- */
- public abstract static class RuleWithDifferentTypeInBaseClass {
- static String PACKAGE_NAME = "com.RuleWithDifferentTypeInBaseClass";
- @Rule
- public final TestRule mRavenwood1 = new RavenwoodRule.Builder()
- .setPackageName(PACKAGE_NAME).build();
- }
-
- /**
- * Make sure that RavenwoodRule in a base class takes effect, even if the field type is not
- */
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest
- testStarted: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest)
- testFailure: If you have a RavenwoodRule in your test, make sure the field type is RavenwoodRule so Ravenwood can detect it.
- testFinished: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest)
- testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest
- testSuiteFinished: classes
- testRunFinished: 1,1,0,0
- """)
- // CHECKSTYLE:ON
- @Ignore // Package name is no longer set via config.
- public static class RuleWithDifferentTypeInBaseClassSuccessTest extends RuleWithDifferentTypeInBaseClass {
-
- @Test
- public void testRuleInBaseClass() {
- assertThat(InstrumentationRegistry.getInstrumentation().getContext().getPackageName())
- .isEqualTo(PACKAGE_NAME);
- }
- }
-
- /**
- * Make sure that having a config and a rule in a base class should fail, even if the field type is not
- * RavenwoodRule.
- */
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleWithDifferentTypeInBaseClassTest
- testStarted: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleWithDifferentTypeInBaseClassTest)
- testFailure: If you have a RavenwoodRule in your test, make sure the field type is RavenwoodRule so Ravenwood can detect it.
- testFinished: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleWithDifferentTypeInBaseClassTest)
- testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleWithDifferentTypeInBaseClassTest
- testSuiteFinished: classes
- testRunFinished: 1,1,0,0
- """)
- // CHECKSTYLE:ON
- public static class ConfigWithRuleWithDifferentTypeInBaseClassTest extends RuleWithDifferentTypeInBaseClass {
- @RavenwoodConfig.Config
- public static RavenwoodConfig sConfig = new RavenwoodConfig.Builder().build();
-
- @Test
- public void test() {
- }
- }
-}
diff --git a/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/IdentityTest.java b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/IdentityTest.java
index 8e04b69..271c27f 100644
--- a/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/IdentityTest.java
+++ b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/IdentityTest.java
@@ -15,7 +15,6 @@
*/
package com.android.ravenwoodtest.runtimetest;
-import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
import static android.os.Process.FIRST_APPLICATION_UID;
import static org.junit.Assert.assertEquals;
@@ -23,7 +22,6 @@
import android.os.Binder;
import android.os.Build;
import android.os.Process;
-import android.platform.test.ravenwood.RavenwoodConfig;
import android.system.Os;
import com.android.ravenwood.RavenwoodRuntimeState;
@@ -34,13 +32,6 @@
public class IdentityTest {
- @RavenwoodConfig.Config
- public static final RavenwoodConfig sConfig =
- new RavenwoodConfig.Builder()
- .setTargetSdkLevel(UPSIDE_DOWN_CAKE)
- .setProcessApp()
- .build();
-
@Test
public void testUid() {
assertEquals(FIRST_APPLICATION_UID, RavenwoodRuntimeState.sUid);
@@ -60,7 +51,7 @@
@Test
public void testTargetSdkLevel() {
assertEquals(Build.VERSION_CODES.CUR_DEVELOPMENT, RavenwoodRuntimeState.CUR_DEVELOPMENT);
- assertEquals(UPSIDE_DOWN_CAKE, RavenwoodRuntimeState.sTargetSdkLevel);
- assertEquals(UPSIDE_DOWN_CAKE, VMRuntime.getRuntime().getTargetSdkVersion());
+ assertEquals(RavenwoodRuntimeState.sTargetSdkLevel,
+ VMRuntime.getRuntime().getTargetSdkVersion());
}
}
diff --git a/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/SystemPropertyTest.java b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/SystemPropertyTest.java
new file mode 100644
index 0000000..70bf204
--- /dev/null
+++ b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/SystemPropertyTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ravenwoodtest.runtimetest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.os.SystemProperties;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runners.model.Statement;
+
+public class SystemPropertyTest {
+
+ private static final String PROP_KEY_1 = "debug.ravenwood.prop1";
+ private static final String PROP_VAL_1 = "ravenwood.1";
+ private static final String PROP_KEY_2 = "debug.ravenwood.prop2";
+ private static final String PROP_VAL_2 = "ravenwood.2";
+ private static final String PROP_KEY_3 = "debug.ravenwood.prop3";
+ private static final String PROP_VAL_3 = "ravenwood.3";
+ private static final String PROP_VAL_4 = "ravenwood.4";
+
+ @ClassRule(order = 0)
+ public static TestRule mCheckClassRule = (base, description) -> new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ assertTrue(SystemProperties.get(PROP_KEY_1).isEmpty());
+ assertTrue(SystemProperties.get(PROP_KEY_3).isEmpty());
+ try {
+ base.evaluate();
+ } finally {
+ assertTrue(SystemProperties.get(PROP_KEY_1).isEmpty());
+ assertTrue(SystemProperties.get(PROP_KEY_3).isEmpty());
+ }
+ }
+ };
+
+ @ClassRule(order = 1)
+ public static RavenwoodRule mClassRule = new RavenwoodRule.Builder()
+ .setSystemPropertyImmutable(PROP_KEY_1, PROP_VAL_1)
+ .setSystemPropertyImmutable(PROP_KEY_3, PROP_VAL_4)
+ .build();
+
+ @Rule(order = 0)
+ public TestRule mCheckRule = (base, description) -> new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ assertTrue(SystemProperties.get(PROP_KEY_2).isEmpty());
+ assertEquals(SystemProperties.get(PROP_KEY_3), PROP_VAL_4);
+ try {
+ base.evaluate();
+ } finally {
+ assertTrue(SystemProperties.get(PROP_KEY_2).isEmpty());
+ assertEquals(SystemProperties.get(PROP_KEY_3), PROP_VAL_4);
+ }
+ }
+ };
+
+ @Rule(order = 1)
+ public RavenwoodRule mRule = new RavenwoodRule.Builder()
+ .setSystemPropertyImmutable(PROP_KEY_2, PROP_VAL_2)
+ .setSystemPropertyImmutable(PROP_KEY_3, PROP_VAL_3)
+ .build();
+
+ @Test
+ public void testRavenwoodRuleSetProperty() {
+ assertEquals(SystemProperties.get(PROP_KEY_1), PROP_VAL_1);
+ assertEquals(SystemProperties.get(PROP_KEY_2), PROP_VAL_2);
+ assertEquals(SystemProperties.get(PROP_KEY_3), PROP_VAL_3);
+ }
+}
diff --git a/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java
index 4aae1e1..e83a247 100644
--- a/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java
+++ b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java
@@ -24,8 +24,6 @@
import android.content.Context;
import android.hardware.SerialManager;
import android.hardware.SerialManagerInternal;
-import android.platform.test.ravenwood.RavenwoodConfig;
-import android.platform.test.ravenwood.RavenwoodConfig.Config;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -42,12 +40,6 @@
public class RavenwoodServicesTest {
private static final String TEST_VIRTUAL_PORT = "virtual:example";
- @Config
- public static final RavenwoodConfig sRavenwood = new RavenwoodConfig.Builder()
- .setProcessSystem()
- .setServicesRequired(SerialManager.class)
- .build();
-
private Context mContext;
@Before
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index d6fc6e4..7ef6aac 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -1180,18 +1180,8 @@
}
private boolean anyServiceWantsGenericMotionEvent(MotionEvent event) {
- if (Flags.alwaysAllowObservingTouchEvents()) {
- final boolean isTouchEvent = event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN);
- if (isTouchEvent && !canShareGenericTouchEvent()) {
- return false;
- }
- final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK;
- return (mCombinedGenericMotionEventSources & eventSourceWithoutClass) != 0;
- }
- // Disable SOURCE_TOUCHSCREEN generic event interception if any service is performing
- // touch exploration.
- if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)
- && (mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) {
+ final boolean isTouchEvent = event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN);
+ if (isTouchEvent && !canShareGenericTouchEvent()) {
return false;
}
final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK;
@@ -1199,21 +1189,8 @@
}
private boolean anyServiceWantsToObserveMotionEvent(MotionEvent event) {
- if (Flags.alwaysAllowObservingTouchEvents()) {
- final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK;
- return (mCombinedMotionEventObservedSources & eventSourceWithoutClass) != 0;
- }
- // Disable SOURCE_TOUCHSCREEN generic event interception if any service is performing
- // touch exploration.
- if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)
- && (mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) {
- return false;
- }
final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK;
- return (mCombinedGenericMotionEventSources
- & mCombinedMotionEventObservedSources
- & eventSourceWithoutClass)
- != 0;
+ return (mCombinedMotionEventObservedSources & eventSourceWithoutClass) != 0;
}
private boolean canShareGenericTouchEvent() {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 47e0b0d..71a0fc4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -6541,8 +6541,7 @@
// Only continue setting up the packages if the service has been initialized.
// See: b/340927041
- if (Flags.skipPackageChangeBeforeUserSwitch()
- && !mManagerService.isServiceInitializedLocked()) {
+ if (!mManagerService.isServiceInitializedLocked()) {
Slog.w(LOG_TAG,
"onSomePackagesChanged: service not initialized, skip the callback.");
return;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index 5cbe0c4..8b870db 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -433,10 +433,24 @@
return Collections.emptyList();
}
- private void onWindowsForAccessibilityChanged(boolean forceSend, int topFocusedDisplayId,
- IBinder topFocusedWindowToken, @NonNull List<WindowInfo> windows) {
- // TODO(b/322444245): no longer need to get a lock.
+ /**
+ * Called when the windows for accessibility changed.
+ *
+ * @param forceSend Send the windows for accessibility even if they haven't
+ * changed.
+ * @param topFocusedDisplayId The display Id which has the top focused window.
+ * @param topFocusedWindowToken The window token of top focused window.
+ * @param screenSize The size of the display that the change happened.
+ * @param accessibilityWindows The windows for accessibility.
+ */
+ @Override
+ public void onAccessibilityWindowsChanged(boolean forceSend, int topFocusedDisplayId,
+ @NonNull IBinder topFocusedWindowToken, @NonNull Point screenSize,
+ @NonNull List<AccessibilityWindow> accessibilityWindows) {
synchronized (mLock) {
+ final List<WindowInfo> windows =
+ createWindowInfoListLocked(screenSize, accessibilityWindows);
+
if (DEBUG) {
Slogf.i(LOG_TAG, "mDisplayId=%d, topFocusedDisplayId=%d, currentUserId=%d, "
+ "visibleBgUsers=%s", mDisplayId, topFocusedDisplayId,
@@ -451,14 +465,15 @@
Slogf.i(LOG_TAG, "%d windows changed: %s", windows.size(), windowsInfo);
}
}
- if (shouldUpdateWindowsLocked(forceSend, windows)) {
+
+ if (forceSend || shouldUpdateWindowsLocked(windows)) {
mTopFocusedDisplayId = topFocusedDisplayId;
if (!isProxyed(topFocusedDisplayId)) {
mLastNonProxyTopFocusedDisplayId = topFocusedDisplayId;
}
mTopFocusedWindowToken = topFocusedWindowToken;
if (DEBUG) {
- Slogf.d(LOG_TAG, "onWindowsForAccessibilityChanged(): updating windows for "
+ Slogf.d(LOG_TAG, "onAccessibilityWindowsChanged(): updating windows for "
+ "display %d and token %s",
topFocusedDisplayId, topFocusedWindowToken);
}
@@ -468,37 +483,14 @@
windows);
// Someone may be waiting for the windows - advertise it.
mLock.notifyAll();
- }
- else if (DEBUG) {
- Slogf.d(LOG_TAG, "onWindowsForAccessibilityChanged(): NOT updating windows for "
+ } else if (DEBUG) {
+ Slogf.d(LOG_TAG, "onAccessibilityWindowsChanged(): NOT updating windows for "
+ "display %d and token %s",
topFocusedDisplayId, topFocusedWindowToken);
}
}
}
- /**
- * Called when the windows for accessibility changed.
- *
- * @param forceSend Send the windows for accessibility even if they haven't
- * changed.
- * @param topFocusedDisplayId The display Id which has the top focused window.
- * @param topFocusedWindowToken The window token of top focused window.
- * @param screenSize The size of the display that the change happened.
- * @param windows The windows for accessibility.
- */
- @Override
- public void onAccessibilityWindowsChanged(boolean forceSend, int topFocusedDisplayId,
- @NonNull IBinder topFocusedWindowToken, @NonNull Point screenSize,
- @NonNull List<AccessibilityWindow> windows) {
- synchronized (mLock) {
- final List<WindowInfo> windowInfoList =
- createWindowInfoListLocked(screenSize, windows);
- onWindowsForAccessibilityChanged(forceSend, topFocusedDisplayId,
- topFocusedWindowToken, windowInfoList);
- }
- }
-
private List<WindowInfo> createWindowInfoListLocked(@NonNull Point screenSize,
@NonNull List<AccessibilityWindow> visibleWindows) {
final Set<IBinder> addedWindows = new ArraySet<>();
@@ -650,12 +642,7 @@
windowInfo.locales = attributes.getLocales();
}
- private boolean shouldUpdateWindowsLocked(boolean forceSend,
- @NonNull List<WindowInfo> windows) {
- if (forceSend) {
- return true;
- }
-
+ private boolean shouldUpdateWindowsLocked(@NonNull List<WindowInfo> windows) {
final int windowCount = windows.size();
if (VERBOSE) {
Slogf.v(LOG_TAG,
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 3dcca14..4cf17ae 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -69,6 +69,7 @@
import android.sysprop.PowerProperties;
import android.util.EventLog;
import android.util.Slog;
+import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
@@ -303,6 +304,17 @@
*/
@VisibleForTesting
public long mLastBroadcastVoltageUpdateTime;
+ /**
+ * Time when the max charging current was updated last by HAL and we sent the
+ * {@link Intent#ACTION_BATTERY_CHANGED} broadcast.
+ * Note: This value is used to rate limit the {@link Intent#ACTION_BATTERY_CHANGED} broadcast
+ * so it is possible that max current was updated but we did not send the broadcast so in that
+ * case we do not update the time.
+ */
+ @VisibleForTesting
+ public long mLastBroadcastMaxChargingCurrentUpdateTime;
+
+ private boolean mIsFirstBatteryChangedUpdate = true;
private Led mLed;
@@ -350,16 +362,21 @@
private static final int ABSOLUTE_DECI_CELSIUS_DIFF_FOR_TEMP_UPDATE = 10;
/**
* This value is used to rate limit the {@link Intent#ACTION_BATTERY_CHANGED} broadcast. We
- * only send the broadcast if the last voltage was updated at least 20s seconds back and has a
+ * only send the broadcast if the last voltage was updated at least 20 seconds back and has a
* fluctuation of at least 1%.
*/
private static final int TIME_DIFF_FOR_VOLTAGE_UPDATE_MS = 20000;
/**
* The value is used to rate limit the {@link Intent#ACTION_BATTERY_CHANGED} broadcast. We
- * only send the broadcast if the last voltage was updated at least 20s seconds back and has a
+ * only send the broadcast if the last voltage was updated at least 20 seconds back and has a
* fluctuation of at least 1%.
*/
private static final float BASE_POINT_DIFF_FOR_VOLTAGE_UPDATE = 0.01f;
+ /**
+ * This value is used to rate limit the {@link Intent#ACTION_BATTERY_CHANGED} broadcast. We
+ * only send the broadcast if the last max charging current was updated at least 5 seconds back.
+ */
+ private static final int TIME_DIFF_FOR_MAX_CHARGING_CURRENT_UPDATE_MS = 5000;
private final Handler.Callback mLocalCallback = msg -> {
switch (msg.what) {
@@ -1252,8 +1269,10 @@
if (!com.android.server.flags.Flags.rateLimitBatteryChangedBroadcast()) {
return false;
}
- if (mLastBroadcastBatteryVoltage == 0 || mLastBroadcastBatteryTemperature == 0) {
+ if (mIsFirstBatteryChangedUpdate) {
mLastBroadcastVoltageUpdateTime = SystemClock.elapsedRealtime();
+ mLastBroadcastMaxChargingCurrentUpdateTime = SystemClock.elapsedRealtime();
+ mIsFirstBatteryChangedUpdate = false;
return false;
}
@@ -1261,13 +1280,14 @@
mLastBroadcastBatteryVoltage != mHealthInfo.batteryVoltageMillivolts;
final boolean temperatureUpdated =
mLastBroadcastBatteryTemperature != mHealthInfo.batteryTemperatureTenthsCelsius;
+ final boolean maxChargingCurrentUpdated =
+ mLastBroadcastMaxChargingCurrent != mHealthInfo.maxChargingCurrentMicroamps;
final boolean otherStatesUpdated = forceUpdate
|| mHealthInfo.batteryStatus != mLastBroadcastBatteryStatus
|| mHealthInfo.batteryHealth != mLastBroadcastBatteryHealth
|| mHealthInfo.batteryPresent != mLastBroadcastBatteryPresent
|| mHealthInfo.batteryLevel != mLastBroadcastBatteryLevel
|| mPlugType != mLastBroadcastPlugType
- || mHealthInfo.maxChargingCurrentMicroamps != mLastBroadcastMaxChargingCurrent
|| mHealthInfo.maxChargingVoltageMicrovolts != mLastBroadcastMaxChargingVoltage
|| mInvalidCharger != mLastBroadcastInvalidCharger
|| mHealthInfo.batteryCycleCount != mLastBroadcastBatteryCycleCount
@@ -1280,6 +1300,9 @@
if (voltageUpdated) {
mLastBroadcastVoltageUpdateTime = SystemClock.elapsedRealtime();
}
+ if (maxChargingCurrentUpdated) {
+ mLastBroadcastMaxChargingCurrentUpdateTime = SystemClock.elapsedRealtime();
+ }
return false;
}
@@ -1295,6 +1318,9 @@
>= TIME_DIFF_FOR_VOLTAGE_UPDATE_MS) {
mLastBroadcastVoltageUpdateTime = SystemClock.elapsedRealtime();
+ if (maxChargingCurrentUpdated) {
+ mLastBroadcastMaxChargingCurrentUpdateTime = SystemClock.elapsedRealtime();
+ }
return false;
}
@@ -1307,6 +1333,20 @@
if (voltageUpdated) {
mLastBroadcastVoltageUpdateTime = SystemClock.elapsedRealtime();
}
+ if (maxChargingCurrentUpdated) {
+ mLastBroadcastMaxChargingCurrentUpdateTime = SystemClock.elapsedRealtime();
+ }
+ return false;
+ }
+
+ if (maxChargingCurrentUpdated
+ && SystemClock.elapsedRealtime() - mLastBroadcastMaxChargingCurrentUpdateTime
+ >= TIME_DIFF_FOR_MAX_CHARGING_CURRENT_UPDATE_MS) {
+ mLastBroadcastMaxChargingCurrentUpdateTime = SystemClock.elapsedRealtime();
+
+ if (voltageUpdated) {
+ mLastBroadcastVoltageUpdateTime = SystemClock.elapsedRealtime();
+ }
return false;
}
@@ -1615,6 +1655,9 @@
pw.println(" Wireless powered: " + mHealthInfo.chargerWirelessOnline);
pw.println(" Dock powered: " + mHealthInfo.chargerDockOnline);
pw.println(" Max charging current: " + mHealthInfo.maxChargingCurrentMicroamps);
+ pw.println(" Time when the latest updated value of the Max charging current was"
+ + " sent via battery changed broadcast: "
+ + TimeUtils.formatDuration(mLastBroadcastMaxChargingCurrentUpdateTime));
pw.println(" Max charging voltage: " + mHealthInfo.maxChargingVoltageMicrovolts);
pw.println(" Charge counter: " + mHealthInfo.batteryChargeCounterUah);
pw.println(" status: " + mHealthInfo.batteryStatus);
@@ -1624,7 +1667,8 @@
pw.println(" scale: " + BATTERY_SCALE);
pw.println(" voltage: " + mHealthInfo.batteryVoltageMillivolts);
pw.println(" Time when the latest updated value of the voltage was sent via "
- + "battery changed broadcast: " + mLastBroadcastVoltageUpdateTime);
+ + "battery changed broadcast: "
+ + TimeUtils.formatDuration(mLastBroadcastVoltageUpdateTime));
pw.println(" The last voltage value sent via the battery changed broadcast: "
+ mLastBroadcastBatteryVoltage);
pw.println(" temperature: " + mHealthInfo.batteryTemperatureTenthsCelsius);
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index aabbd3b..0286f7b 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -1580,10 +1580,20 @@
}
private void registerBicCallback() {
+ if(!com.android.server.flags.Flags.optionalBackgroundInstallControl()) {
+ Slog.d(TAG, "BICS is disabled for this device, skipping registration.");
+ return;
+ }
IBackgroundInstallControlService iBics =
IBackgroundInstallControlService.Stub.asInterface(
ServiceManager.getService(
Context.BACKGROUND_INSTALL_CONTROL_SERVICE));
+ if(iBics == null) {
+ Slog.e(TAG, "Failed to register BackgroundInstallControl callback, either "
+ + "background install control service does not exist or disabled on this "
+ + "build.");
+ return;
+ }
try {
iBics.registerBackgroundInstallCallback(
new BicCallbackHandler(mServiceImpl));
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index ce66dc3..8da8358 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -176,6 +176,10 @@
"include-filter": "com.android.server.wm.BackgroundActivityStart*"
}
]
+ },
+ {
+ "name": "FrameworksMockingServicesTests_service_batteryServiceTest",
+ "file_patterns": ["BatteryService\\.java"]
}
]
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index d5bd057..400ebfd 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1126,20 +1126,8 @@
break;
}
case FrameworkStatsLog.BATTERY_USAGE_STATS_SINCE_RESET_USING_POWER_PROFILE_MODEL:
- if (Flags.disableCompositeBatteryUsageStatsAtoms()) {
- return StatsManager.PULL_SKIP;
- }
+ return StatsManager.PULL_SKIP;
- final BatteryUsageStatsQuery queryPowerProfile =
- new BatteryUsageStatsQuery.Builder()
- .setMaxStatsAgeMs(0)
- .includeProcessStateData()
- .includeVirtualUids()
- .powerProfileModeledOnly()
- .includePowerModels()
- .build();
- bus = getBatteryUsageStats(List.of(queryPowerProfile)).get(0);
- break;
case FrameworkStatsLog.BATTERY_USAGE_STATS_BEFORE_RESET: {
if (Flags.disableCompositeBatteryUsageStatsAtoms()) {
return StatsManager.PULL_SKIP;
@@ -3184,7 +3172,7 @@
}
}
- private void dumpUsageStats(FileDescriptor fd, PrintWriter pw, int model,
+ private void dumpUsageStats(FileDescriptor fd, PrintWriter pw,
boolean proto, boolean accumulated) {
awaitCompletion();
syncStats("dump", BatteryExternalStatsWorker.UPDATE_ALL);
@@ -3196,9 +3184,6 @@
if (Flags.batteryUsageStatsByPowerAndScreenState()) {
builder.includeScreenStateData().includePowerStateData();
}
- if (model == BatteryConsumer.POWER_MODEL_POWER_PROFILE) {
- builder.powerProfileModeledOnly();
- }
if (accumulated) {
builder.accumulated();
}
@@ -3393,7 +3378,6 @@
dumpPowerProfile(pw);
return;
} else if ("--usage".equals(arg)) {
- int model = BatteryConsumer.POWER_MODEL_UNDEFINED;
boolean proto = false;
boolean accumulated = false;
for (int j = i + 1; j < args.length; j++) {
@@ -3401,29 +3385,12 @@
case "--proto":
proto = true;
break;
- case "--model": {
- if (j + 1 < args.length) {
- j++;
- if ("power-profile".equals(args[j])) {
- model = BatteryConsumer.POWER_MODEL_POWER_PROFILE;
- } else {
- pw.println("Unknown power model: " + args[j]);
- dumpHelp(pw);
- return;
- }
- } else {
- pw.println("--model without a value");
- dumpHelp(pw);
- return;
- }
- break;
- }
case "--accumulated":
accumulated = true;
break;
}
}
- dumpUsageStats(fd, pw, model, proto, accumulated);
+ dumpUsageStats(fd, pw, proto, accumulated);
return;
} else if ("--wakeups".equals(arg)) {
mCpuWakeupStats.dump(new IndentingPrintWriter(pw, " "),
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index fd6586a..7660c15 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -140,6 +140,7 @@
// The list is sorted.
@VisibleForTesting
static final String[] sDeviceConfigAconfigScopes = new String[] {
+ "aaos_audio_triage",
"aaos_power_triage",
"aaos_sdv",
"accessibility",
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index a2200c9..1c01fb9 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -17,7 +17,7 @@
package com.android.server.audio;
import static android.media.AudioPlaybackConfiguration.EXTRA_PLAYER_EVENT_MUTE;
-import static android.media.AudioPlaybackConfiguration.MUTED_BY_APP_OPS;
+import static android.media.AudioPlaybackConfiguration.MUTED_BY_OP_PLAY_AUDIO;
import static android.media.AudioPlaybackConfiguration.MUTED_BY_CLIENT_VOLUME;
import static android.media.AudioPlaybackConfiguration.MUTED_BY_MASTER;
import static android.media.AudioPlaybackConfiguration.MUTED_BY_PORT_VOLUME;
@@ -1377,8 +1377,8 @@
if ((eventValue & MUTED_BY_STREAM_MUTED) != 0) {
builder.append("streamMute ");
}
- if ((eventValue & MUTED_BY_APP_OPS) != 0) {
- builder.append("appOps ");
+ if ((eventValue & MUTED_BY_OP_PLAY_AUDIO) != 0) {
+ builder.append("opPlayAudio ");
}
if ((eventValue & MUTED_BY_CLIENT_VOLUME) != 0) {
builder.append("clientVolume ");
diff --git a/services/core/java/com/android/server/biometrics/biometrics.aconfig b/services/core/java/com/android/server/biometrics/biometrics.aconfig
index d3da8dd..95ba695 100644
--- a/services/core/java/com/android/server/biometrics/biometrics.aconfig
+++ b/services/core/java/com/android/server/biometrics/biometrics.aconfig
@@ -34,3 +34,10 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "frr_dialog_improvement"
+ namespace: "biometrics_framework"
+ description: "This flag controls FRR dialog improvement"
+ bug: "380800403"
+}
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index e273c68..45106f5 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -257,6 +257,11 @@
Flags::displayListenerPerformanceImprovements
);
+ private final FlagState mSubscribeGranularDisplayEvents = new FlagState(
+ Flags.FLAG_SUBSCRIBE_GRANULAR_DISPLAY_EVENTS,
+ Flags::subscribeGranularDisplayEvents
+ );
+
/**
* @return {@code true} if 'port' is allowed in display layout configuration file.
*/
@@ -552,6 +557,13 @@
}
/**
+ * @return {@code true} if the flag for subscribing to granular display events is enabled
+ */
+ public boolean isSubscribeGranularDisplayEventsEnabled() {
+ return mSubscribeGranularDisplayEvents.isEnabled();
+ }
+
+ /**
* dumps all flagstates
* @param pw printWriter
*/
@@ -605,6 +617,7 @@
pw.println(" " + mGetSupportedRefreshRatesFlagState);
pw.println(" " + mEnablePluginManagerFlagState);
pw.println(" " + mDisplayListenerPerformanceImprovementsFlagState);
+ pw.println(" " + mSubscribeGranularDisplayEvents);
}
private static class FlagState {
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index e7ea868..3976d01 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -478,3 +478,14 @@
bug: "378385869"
is_fixed_read_only: true
}
+
+flag {
+ name: "subscribe_granular_display_events"
+ namespace: "display_manager"
+ description: "Enable subscription to granular display change events."
+ bug: "379250634"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java b/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java
index 53c0217..2e66fbc 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java
@@ -16,6 +16,8 @@
package com.android.server.hdmi;
+import static android.media.tv.flags.Flags.hdmiControlCollectPhysicalAddress;
+
import static com.android.server.hdmi.Constants.HDMI_EARC_STATUS_ARC_PENDING;
import static com.android.server.hdmi.Constants.HDMI_EARC_STATUS_EARC_CONNECTED;
import static com.android.server.hdmi.Constants.HDMI_EARC_STATUS_EARC_PENDING;
@@ -35,6 +37,8 @@
@VisibleForTesting
protected static final int FEATURE_ABORT_OPCODE_UNKNOWN = 0x100;
private static final int ERROR_CODE_UNKNOWN = -1;
+ @VisibleForTesting
+ protected static final int PHYSICAL_ADDRESS_INVALID = 0xFFFF;
/**
* Writes a HdmiCecMessageReported atom representing an HDMI CEC message.
@@ -95,6 +99,11 @@
return createUserControlPressedSpecialArgs(message);
case Constants.MESSAGE_FEATURE_ABORT:
return createFeatureAbortSpecialArgs(message);
+ case Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS:
+ if (hdmiControlCollectPhysicalAddress()) {
+ return createReportPhysicalAddressSpecialArgs(message);
+ }
+ return new MessageReportedSpecialArgs();
default:
return new MessageReportedSpecialArgs();
}
@@ -140,6 +149,23 @@
}
/**
+ * Constructs the special arguments for a <Report Physical Address> message.
+ *
+ * @param message The HDMI CEC message to log
+ */
+ private MessageReportedSpecialArgs createReportPhysicalAddressSpecialArgs(
+ HdmiCecMessage message) {
+ MessageReportedSpecialArgs specialArgs = new MessageReportedSpecialArgs();
+
+ if (message.getParams().length > 1) {
+ int physicalAddress = (message.getParams()[0] << 8) | message.getParams()[1];
+ specialArgs.mPhysicalAddress = physicalAddress;
+ }
+
+ return specialArgs;
+ }
+
+ /**
* Writes a HdmiCecMessageReported atom.
*
* @param genericArgs Generic arguments; shared by all HdmiCecMessageReported atoms
@@ -156,7 +182,8 @@
genericArgs.mSendMessageResult,
specialArgs.mUserControlPressedCommand,
specialArgs.mFeatureAbortOpcode,
- specialArgs.mFeatureAbortReason);
+ specialArgs.mFeatureAbortReason,
+ specialArgs.mPhysicalAddress);
}
/**
@@ -166,7 +193,7 @@
protected void writeHdmiCecMessageReportedAtom(int uid, int direction,
int initiatorLogicalAddress, int destinationLogicalAddress, int opcode,
int sendMessageResult, int userControlPressedCommand, int featureAbortOpcode,
- int featureAbortReason) {
+ int featureAbortReason, int physicalAddress) {
FrameworkStatsLog.write(
FrameworkStatsLog.HDMI_CEC_MESSAGE_REPORTED,
uid,
@@ -177,7 +204,8 @@
sendMessageResult,
userControlPressedCommand,
featureAbortOpcode,
- featureAbortReason);
+ featureAbortReason,
+ physicalAddress);
}
/**
@@ -284,5 +312,6 @@
int mUserControlPressedCommand = HdmiStatsEnums.USER_CONTROL_PRESSED_COMMAND_UNKNOWN;
int mFeatureAbortOpcode = FEATURE_ABORT_OPCODE_UNKNOWN;
int mFeatureAbortReason = HdmiStatsEnums.FEATURE_ABORT_REASON_UNKNOWN;
+ int mPhysicalAddress = PHYSICAL_ADDRESS_INVALID;
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index b7af9a4..02dd884 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -84,6 +84,7 @@
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.content.res.Resources;
+import android.graphics.Region;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.input.InputManager;
import android.inputmethodservice.InputMethodService;
@@ -6888,6 +6889,14 @@
@BinderThread
@Override
+ public void setHandwritingTouchableRegion(Region region) {
+ synchronized (ImfLock.class) {
+ mImms.mHwController.setHandwritingTouchableRegion(region);
+ }
+ }
+
+ @BinderThread
+ @Override
public void createInputContentUriToken(Uri contentUri, String packageName,
AndroidFuture future /* T=IBinder */) {
@SuppressWarnings("unchecked") final AndroidFuture<IBinder> typedFuture = future;
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 27bc1cf..e886f3b 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -2544,8 +2544,11 @@
mServiceRef = new WeakReference<>(service);
mUserRecord = userRecord;
mSystemProvider =
- new SystemMediaRoute2Provider(
- service.mContext, UserHandle.of(userRecord.mUserId), looper);
+ Flags.enableMirroringInMediaRouter2()
+ ? new SystemMediaRoute2Provider2(
+ service.mContext, UserHandle.of(userRecord.mUserId), looper)
+ : new SystemMediaRoute2Provider(
+ service.mContext, UserHandle.of(userRecord.mUserId), looper);
mRouteProviders.add(mSystemProvider);
mWatcher = new MediaRoute2ProviderWatcher(service.mContext, this,
this, mUserRecord.mUserId);
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java
new file mode 100644
index 0000000..a86e818
--- /dev/null
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import android.content.Context;
+import android.media.MediaRoute2ProviderService;
+import android.os.Looper;
+import android.os.UserHandle;
+
+/**
+ * Extends {@link SystemMediaRoute2Provider} by adding system routes provided by {@link
+ * MediaRoute2ProviderService provider services}.
+ *
+ * <p>System routes are those which can handle the system audio and/or video.
+ */
+/* package */ class SystemMediaRoute2Provider2 extends SystemMediaRoute2Provider {
+ SystemMediaRoute2Provider2(Context context, UserHandle user, Looper looper) {
+ super(context, user, looper);
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7156334..040b194 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -5875,6 +5875,67 @@
userId, callingPackage);
}
+ @Override
+ public void setPageSizeAppCompatFlagsSettingsOverride(String packageName, boolean enabled) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingAppId = UserHandle.getAppId(callingUid);
+
+ if (!PackageManagerServiceUtils.isSystemOrRoot(callingAppId)) {
+ throw new SecurityException("Caller must be the system or root.");
+ }
+
+ int settingsMode = enabled
+ ? ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_ENABLED
+ : ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_DISABLED;
+ PackageStateMutator.Result result =
+ commitPackageStateMutation(
+ null,
+ packageName,
+ packageState ->
+ packageState
+ .setPageSizeAppCompatFlags(settingsMode));
+ if (result.isSpecificPackageNull()) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+ scheduleWriteSettings();
+ }
+
+ @Override
+ public boolean isPageSizeCompatEnabled(String packageName) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingAppId = UserHandle.getAppId(callingUid);
+ final int userId = UserHandle.getCallingUserId();
+
+ if (!PackageManagerServiceUtils.isSystemOrRoot(callingAppId)) {
+ throw new SecurityException("Caller must be the system or root.");
+ }
+
+ PackageStateInternal packageState =
+ snapshotComputer().getPackageStateForInstalledAndFiltered(
+ packageName, callingUid, userId);
+
+ return packageState == null ? false : packageState.isPageSizeAppCompatEnabled();
+ }
+
+ @Override
+ public String getPageSizeCompatWarningMessage(String packageName) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingAppId = UserHandle.getAppId(callingUid);
+ final int userId = UserHandle.getCallingUserId();
+
+ if (!PackageManagerServiceUtils.isSystemOrRoot(callingAppId)) {
+ throw new SecurityException("Caller must be the system or root.");
+ }
+
+ PackageStateInternal packageState =
+ snapshotComputer().getPackageStateForInstalledAndFiltered(
+ packageName, callingUid, userId);
+
+ return packageState == null
+ ? null
+ : packageState.getPageSizeCompatWarningMessage(mContext);
+ }
+
@android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_USERS)
@Override
public boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden,
@@ -6579,6 +6640,20 @@
}
@Override
+ @NonNull
+ public List<String> getAllApexDirectories() {
+ PackageManagerServiceUtils.enforceSystemOrRoot(
+ "getAllApexDirectories can only be called by system or root");
+ List<String> apexDirectories = new ArrayList<>();
+ List<ApexManager.ActiveApexInfo> apexes = mApexManager.getActiveApexInfos();
+ for (int i = 0; i < apexes.size(); i++) {
+ ApexManager.ActiveApexInfo apex = apexes.get(i);
+ apexDirectories.add(apex.apexDirectory.getAbsolutePath());
+ }
+ return apexDirectories;
+ }
+
+ @Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
try {
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 9428de7..fb16b86 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -27,6 +27,7 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.ComponentName;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.SharedLibraryInfo;
@@ -221,6 +222,8 @@
/** @see PackageState#getCategoryOverride() */
private int categoryOverride = ApplicationInfo.CATEGORY_UNDEFINED;
+ private int mPageSizeAppCompatFlags = ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED;
+
@NonNull
private final PackageStateUnserialized pkgState = new PackageStateUnserialized(this);
@@ -863,6 +866,8 @@
}
copyMimeGroups(other.mimeGroups);
+ mPageSizeAppCompatFlags = other.mPageSizeAppCompatFlags;
+
pkgState.updateFrom(other.pkgState);
onChanged();
}
@@ -1617,6 +1622,34 @@
return this;
}
+ /**
+ * @see Set page size app compat mode.
+ */
+ public PackageSetting setPageSizeAppCompatFlags(int mode) {
+ if (mode < 0 || mode >= ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_MAX) {
+ throw new IllegalArgumentException("Invalid page size compat mode specified");
+ }
+
+ // OR assignment is used here to avoid overriding the mode set by the manifest.
+ this.mPageSizeAppCompatFlags |= mode;
+
+ // Only one bit of the following can be set at same time. Both are needed to detect app
+ // compat 'disabled' state from settings vs bit was never set.
+ if (ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_ENABLED == mode) {
+ this.mPageSizeAppCompatFlags &=
+ ~ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_DISABLED;
+ } else if (ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_DISABLED == mode) {
+ this.mPageSizeAppCompatFlags &=
+ ~ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_ENABLED;
+ }
+ onChanged();
+ return this;
+ }
+
+ public int getPageSizeAppCompatFlags() {
+ return mPageSizeAppCompatFlags;
+ }
+
public PackageSetting setLegacyNativeLibraryPath(
String legacyNativeLibraryPathString) {
this.legacyNativeLibraryPath = legacyNativeLibraryPathString;
@@ -1787,6 +1820,63 @@
return getBoolean(Booleans.SCANNED_AS_STOPPED_SYSTEM_APP);
}
+ /** Returns true if ELF files will be loaded in Page size compatibility mode */
+ @Override
+ public boolean isPageSizeAppCompatEnabled() {
+ // If manifest or settings has disabled the compat mode, don't run app in compat mode.
+ boolean manifestOverrideDisabled = (mPageSizeAppCompatFlags
+ & ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_DISABLED) != 0;
+ boolean settingsOverrideDisabled = (mPageSizeAppCompatFlags
+ & ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_DISABLED) != 0;
+ if (manifestOverrideDisabled || settingsOverrideDisabled) {
+ return false;
+ }
+
+ int mask =
+ ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_ELF_NOT_ALIGNED
+ | ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_ENABLED
+ | ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_ENABLED;
+ return (mPageSizeAppCompatFlags & mask) != 0;
+ }
+
+ /**
+ * Returns dialog string based on alignment of uncompressed shared libs inside the APK and ELF
+ * alignment.
+ */
+ @Override
+ public String getPageSizeCompatWarningMessage(Context context) {
+ boolean manifestOverrideEnabled = (mPageSizeAppCompatFlags
+ & ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_ENABLED) != 0;
+ boolean settingsOverrideEnabled = (mPageSizeAppCompatFlags
+ & ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_ENABLED) != 0;
+ if (manifestOverrideEnabled || settingsOverrideEnabled) {
+ return null;
+ }
+
+ boolean uncompressedLibsNotAligned = (mPageSizeAppCompatFlags
+ & ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_UNCOMPRESSED_LIBS_NOT_ALIGNED) != 0;
+ boolean elfNotAligned = (mPageSizeAppCompatFlags
+ & ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_ELF_NOT_ALIGNED) != 0;
+
+ if (uncompressedLibsNotAligned && elfNotAligned) {
+ return context.getText(
+ com.android.internal.R.string.page_size_compat_apk_and_elf_warning)
+ .toString();
+ }
+
+ if (uncompressedLibsNotAligned) {
+ return context.getText(com.android.internal.R.string.page_size_compat_apk_warning)
+ .toString();
+ }
+
+ if (elfNotAligned) {
+ return context.getText(com.android.internal.R.string.page_size_compat_elf_warning)
+ .toString();
+ }
+
+ return null;
+ }
+
// Code below generated by codegen v1.0.23.
@@ -1952,7 +2042,6 @@
@Deprecated
private void __metadata() {}
-
//@formatter:on
// End of generated code
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 1f672a0..485a280 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3313,6 +3313,11 @@
if (pkg.getBaseRevisionCode() != 0) {
serializer.attributeInt(null, "baseRevisionCode", pkg.getBaseRevisionCode());
}
+ if (pkg.getPageSizeAppCompatFlags()
+ != ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED) {
+ serializer.attributeInt(null, "pageSizeCompat", pkg.getPageSizeAppCompatFlags());
+ }
+
serializer.attributeFloat(null, "loadingProgress", pkg.getLoadingProgress());
serializer.attributeLongHex(null, "loadingCompletedTime", pkg.getLoadingCompletedTime());
@@ -4129,6 +4134,7 @@
boolean isScannedAsStoppedSystemApp = false;
boolean isSdkLibrary = false;
int baseRevisionCode = 0;
+ int PageSizeCompat = 0;
try {
name = parser.getAttributeValue(null, ATTR_NAME);
realName = parser.getAttributeValue(null, "realName");
@@ -4175,6 +4181,8 @@
appMetadataSource = parser.getAttributeInt(null, "appMetadataSource",
PackageManager.APP_METADATA_SOURCE_UNKNOWN);
baseRevisionCode = parser.getAttributeInt(null, "baseRevisionCode", 0);
+ PageSizeCompat = parser.getAttributeInt(null, "pageSizeCompat",
+ ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED);
isScannedAsStoppedSystemApp = parser.getAttributeBoolean(null,
"scannedAsStoppedSystemApp", false);
@@ -4330,7 +4338,8 @@
.setTargetSdkVersion(targetSdkVersion)
.setBaseRevisionCode(baseRevisionCode)
.setRestrictUpdateHash(restrictUpdateHash)
- .setScannedAsStoppedSystemApp(isScannedAsStoppedSystemApp);
+ .setScannedAsStoppedSystemApp(isScannedAsStoppedSystemApp)
+ .setPageSizeAppCompatFlags(PageSizeCompat);
// Handle legacy string here for single-user mode
final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED);
if (enabledStr != null) {
@@ -5211,6 +5220,10 @@
pw.print(" (override=true)");
}
pw.println();
+ pw.print(prefix);
+ pw.print(" pageSizeCompat=");
+ pw.print(ps.getPageSizeAppCompatFlags());
+ pw.println();
if (!ps.getPkg().getQueriesPackages().isEmpty()) {
pw.append(prefix).append(" queriesPackages=")
.println(ps.getPkg().getQueriesPackages());
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index b2b8aaf..066fce0 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -6122,8 +6122,11 @@
// If the user switch hasn't been explicitly toggled on or off by the user, turn it on.
if (android.provider.Settings.Global.getString(mContext.getContentResolver(),
android.provider.Settings.Global.USER_SWITCHER_ENABLED) == null) {
- android.provider.Settings.Global.putInt(mContext.getContentResolver(),
- android.provider.Settings.Global.USER_SWITCHER_ENABLED, 1);
+ if (Resources.getSystem().getBoolean(
+ com.android.internal.R.bool.config_enableUserSwitcherUponUserCreation)) {
+ android.provider.Settings.Global.putInt(mContext.getContentResolver(),
+ android.provider.Settings.Global.USER_SWITCHER_ENABLED, 1);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageState.java b/services/core/java/com/android/server/pm/pkg/PackageState.java
index bbc17c8..33fc066 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageState.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageState.java
@@ -22,6 +22,7 @@
import android.annotation.Size;
import android.annotation.SystemApi;
import android.annotation.UserIdInt;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -198,6 +199,21 @@
int getCategoryOverride();
/**
+ * Returns true if ELF files will be loaded in Page size compatibility mode
+ *
+ * @hide
+ */
+ boolean isPageSizeAppCompatEnabled();
+
+ /**
+ * Returns dialog string based on alignment of uncompressed shared libs inside the APK and ELF
+ * alignment.
+ *
+ * @hide
+ */
+ String getPageSizeCompatWarningMessage(Context context);
+
+ /**
* The install time CPU override, if any. This value is written at install time
* and doesn't change during the life of an install. If non-null,
* {@link #getPrimaryCpuAbiLegacy()} will also contain the same value.
diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java
index 253eb40..a46c4a6 100644
--- a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java
+++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java
@@ -257,6 +257,16 @@
@NonNull
@Override
+ public PackageStateWrite setPageSizeAppCompatFlags(
+ @ApplicationInfo.PageSizeAppCompatFlags int mode) {
+ if (mState != null) {
+ mState.setPageSizeAppCompatFlags(mode);
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
public PackageStateWrite setUpdateAvailable(boolean updateAvailable) {
if (mState != null) {
mState.setUpdateAvailable(updateAvailable);
diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateWrite.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateWrite.java
index 55d96f3..f8f8695 100644
--- a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateWrite.java
+++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateWrite.java
@@ -46,6 +46,10 @@
@NonNull
PackageStateWrite setCategoryOverride(@ApplicationInfo.Category int category);
+ /** set 16Kb App compat mode. @see ApplicationInfo.PageSizeAppCompatFlags */
+ @NonNull
+ PackageStateWrite setPageSizeAppCompatFlags(@ApplicationInfo.PageSizeAppCompatFlags int mode);
+
@NonNull
PackageStateWrite setUpdateAvailable(boolean updateAvailable);
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index 71f67d8..aba15c8 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -34,7 +34,9 @@
import android.content.pm.PackageManager;
import android.hardware.power.ChannelConfig;
import android.hardware.power.CpuHeadroomParams;
+import android.hardware.power.CpuHeadroomResult;
import android.hardware.power.GpuHeadroomParams;
+import android.hardware.power.GpuHeadroomResult;
import android.hardware.power.IPower;
import android.hardware.power.SessionConfig;
import android.hardware.power.SessionTag;
@@ -79,6 +81,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
@@ -96,10 +99,10 @@
private static final int EVENT_CLEAN_UP_UID = 3;
@VisibleForTesting static final int CLEAN_UP_UID_DELAY_MILLIS = 1000;
+ // The minimum interval between the headroom calls as rate limiting.
private static final int DEFAULT_GPU_HEADROOM_INTERVAL_MILLIS = 1000;
private static final int DEFAULT_CPU_HEADROOM_INTERVAL_MILLIS = 1000;
private static final int HEADROOM_INTERVAL_UNSUPPORTED = -1;
- @VisibleForTesting static final int DEFAULT_HEADROOM_PID = -1;
@VisibleForTesting final long mHintSessionPreferredRate;
@@ -184,73 +187,77 @@
private static final String PROPERTY_SF_ENABLE_CPU_HINT = "debug.sf.enable_adpf_cpu_hint";
private static final String PROPERTY_HWUI_ENABLE_HINT_MANAGER = "debug.hwui.use_hint_manager";
private static final String PROPERTY_USE_HAL_HEADROOMS = "persist.hms.use_hal_headrooms";
+ private static final String PROPERTY_CHECK_HEADROOM_TID = "persist.hms.check_headroom_tid";
private Boolean mFMQUsesIntegratedEventFlag = false;
private final Object mCpuHeadroomLock = new Object();
- private static class CpuHeadroomCacheItem {
- long mExpiredTimeMillis;
- CpuHeadroomParamsInternal mParams;
- float[] mHeadroom;
- long mPid;
- CpuHeadroomCacheItem(long expiredTimeMillis, CpuHeadroomParamsInternal params,
- float[] headroom, long pid) {
- mExpiredTimeMillis = expiredTimeMillis;
- mParams = params;
- mPid = pid;
- mHeadroom = headroom;
- }
+ // this cache tracks the expiration time of the items and performs cleanup on lookup
+ private static class HeadroomCache<K, V> {
+ final List<HeadroomCacheItem> mItemList;
+ final Map<K, HeadroomCacheItem> mKeyItemMap;
+ final long mItemExpDurationMillis;
- private boolean match(CpuHeadroomParamsInternal params, long pid) {
- if (mParams == null && params == null) return true;
- if (mParams != null) {
- return mParams.equals(params) && pid == mPid;
+ class HeadroomCacheItem {
+ long mExpTime;
+ K mKey;
+ V mValue;
+
+ HeadroomCacheItem(K k, V v) {
+ mExpTime = System.currentTimeMillis() + mItemExpDurationMillis;
+ mKey = k;
+ mValue = v;
}
- return false;
+
+ boolean isExpired() {
+ return mExpTime <= System.currentTimeMillis();
+ }
}
- private boolean isExpired() {
- return System.currentTimeMillis() > mExpiredTimeMillis;
+ void add(K key, V value) {
+ if (mKeyItemMap.containsKey(key)) {
+ final HeadroomCacheItem item = mKeyItemMap.get(key);
+ mItemList.remove(item);
+ }
+ final HeadroomCacheItem item = new HeadroomCacheItem(key, value);
+ mItemList.add(item);
+ mKeyItemMap.put(key, item);
+ }
+
+ V get(K key) {
+ while (!mItemList.isEmpty() && mItemList.getFirst().isExpired()) {
+ mKeyItemMap.remove(mItemList.removeFirst().mKey);
+ }
+ final HeadroomCacheItem item = mKeyItemMap.get(key);
+ if (item == null) {
+ return null;
+ }
+ return item.mValue;
+ }
+
+ HeadroomCache(int size, long expDurationMillis) {
+ mItemList = new LinkedList<>();
+ mKeyItemMap = new ArrayMap<>(size);
+ mItemExpDurationMillis = expDurationMillis;
}
}
@GuardedBy("mCpuHeadroomLock")
- private final List<CpuHeadroomCacheItem> mCpuHeadroomCache;
+ private final HeadroomCache<CpuHeadroomParams, CpuHeadroomResult> mCpuHeadroomCache;
private final long mCpuHeadroomIntervalMillis;
private final Object mGpuHeadroomLock = new Object();
- private static class GpuHeadroomCacheItem {
- long mExpiredTimeMillis;
- GpuHeadroomParamsInternal mParams;
- float mHeadroom;
-
- GpuHeadroomCacheItem(long expiredTimeMillis, GpuHeadroomParamsInternal params,
- float headroom) {
- mExpiredTimeMillis = expiredTimeMillis;
- mParams = params;
- mHeadroom = headroom;
- }
-
- private boolean match(GpuHeadroomParamsInternal params) {
- if (mParams == null && params == null) return true;
- if (mParams != null) {
- return mParams.equals(params);
- }
- return false;
- }
-
- private boolean isExpired() {
- return System.currentTimeMillis() > mExpiredTimeMillis;
- }
- }
-
@GuardedBy("mGpuHeadroomLock")
- private final List<GpuHeadroomCacheItem> mGpuHeadroomCache;
+ private final HeadroomCache<GpuHeadroomParams, GpuHeadroomResult> mGpuHeadroomCache;
private final long mGpuHeadroomIntervalMillis;
+ // these are set to default values in CpuHeadroomParamsInternal and GpuHeadroomParamsInternal
+ private final int mDefaultCpuHeadroomCalculationWindowMillis;
+ private final int mDefaultGpuHeadroomCalculationWindowMillis;
+
@VisibleForTesting
final IHintManager.Stub mService = new BinderService();
@@ -303,26 +310,31 @@
}
}
mCpuHeadroomIntervalMillis = cpuHeadroomIntervalMillis;
+ mDefaultCpuHeadroomCalculationWindowMillis =
+ new CpuHeadroomParamsInternal().calculationWindowMillis;
+ mDefaultGpuHeadroomCalculationWindowMillis =
+ new GpuHeadroomParamsInternal().calculationWindowMillis;
mGpuHeadroomIntervalMillis = gpuHeadroomIntervalMillis;
if (mCpuHeadroomIntervalMillis > 0) {
- mCpuHeadroomCache = new ArrayList<>(4);
+ mCpuHeadroomCache = new HeadroomCache<>(2, mCpuHeadroomIntervalMillis);
} else {
mCpuHeadroomCache = null;
}
if (mGpuHeadroomIntervalMillis > 0) {
- mGpuHeadroomCache = new ArrayList<>(2);
+ mGpuHeadroomCache = new HeadroomCache<>(2, mGpuHeadroomIntervalMillis);
} else {
mGpuHeadroomCache = null;
}
}
private long checkCpuHeadroomSupport() {
+ final CpuHeadroomParams params = new CpuHeadroomParams();
+ params.tids = new int[]{Process.myPid()};
try {
synchronized (mCpuHeadroomLock) {
- final CpuHeadroomParams defaultParams = new CpuHeadroomParams();
- defaultParams.pid = Process.myPid();
- float[] ret = mPowerHal.getCpuHeadroom(defaultParams);
- if (ret != null && ret.length > 0) {
+ final CpuHeadroomResult ret = mPowerHal.getCpuHeadroom(params);
+ if (ret != null && ret.getTag() == CpuHeadroomResult.globalHeadroom
+ && !Float.isNaN(ret.getGlobalHeadroom())) {
return Math.max(
DEFAULT_CPU_HEADROOM_INTERVAL_MILLIS,
mPowerHal.getCpuHeadroomMinIntervalMillis());
@@ -330,27 +342,29 @@
}
} catch (UnsupportedOperationException e) {
- Slog.w(TAG, "getCpuHeadroom HAL API is not supported", e);
+ Slog.w(TAG, "getCpuHeadroom HAL API is not supported, params: " + params, e);
} catch (RemoteException e) {
- Slog.e(TAG, "getCpuHeadroom HAL API fails, disabling the API", e);
+ Slog.e(TAG, "getCpuHeadroom HAL API fails, disabling the API, params: " + params, e);
}
return HEADROOM_INTERVAL_UNSUPPORTED;
}
private long checkGpuHeadroomSupport() {
+ final GpuHeadroomParams params = new GpuHeadroomParams();
try {
synchronized (mGpuHeadroomLock) {
- float ret = mPowerHal.getGpuHeadroom(new GpuHeadroomParams());
- if (!Float.isNaN(ret)) {
+ final GpuHeadroomResult ret = mPowerHal.getGpuHeadroom(params);
+ if (ret != null && ret.getTag() == GpuHeadroomResult.globalHeadroom && !Float.isNaN(
+ ret.getGlobalHeadroom())) {
return Math.max(
DEFAULT_GPU_HEADROOM_INTERVAL_MILLIS,
mPowerHal.getGpuHeadroomMinIntervalMillis());
}
}
} catch (UnsupportedOperationException e) {
- Slog.w(TAG, "getGpuHeadroom HAL API is not supported", e);
+ Slog.w(TAG, "getGpuHeadroom HAL API is not supported, params: " + params, e);
} catch (RemoteException e) {
- Slog.e(TAG, "getGpuHeadroom HAL API fails, disabling the API", e);
+ Slog.e(TAG, "getGpuHeadroom HAL API fails, disabling the API, params: " + params, e);
}
return HEADROOM_INTERVAL_UNSUPPORTED;
}
@@ -1445,109 +1459,98 @@
}
@Override
- public float[] getCpuHeadroom(@Nullable CpuHeadroomParamsInternal params) {
+ public CpuHeadroomResult getCpuHeadroom(@NonNull CpuHeadroomParamsInternal params) {
if (mCpuHeadroomIntervalMillis <= 0) {
throw new UnsupportedOperationException();
}
- CpuHeadroomParams halParams = new CpuHeadroomParams();
- halParams.pid = Binder.getCallingPid();
- if (params != null) {
- halParams.calculationType = params.calculationType;
- halParams.selectionType = params.selectionType;
- if (params.usesDeviceHeadroom) {
- halParams.pid = DEFAULT_HEADROOM_PID;
+ final CpuHeadroomParams halParams = new CpuHeadroomParams();
+ halParams.tids = new int[]{Binder.getCallingPid()};
+ halParams.calculationType = params.calculationType;
+ halParams.calculationWindowMillis = params.calculationWindowMillis;
+ halParams.selectionType = params.selectionType;
+ if (params.usesDeviceHeadroom) {
+ halParams.tids = new int[]{};
+ } else if (params.tids != null && params.tids.length > 0) {
+ if (params.tids.length > 5) {
+ throw new IllegalArgumentException(
+ "More than 5 TIDs is requested: " + params.tids.length);
}
+ if (SystemProperties.getBoolean(PROPERTY_CHECK_HEADROOM_TID, true)) {
+ final int tgid = Process.getThreadGroupLeader(Binder.getCallingPid());
+ for (int tid : params.tids) {
+ if (Process.getThreadGroupLeader(tid) != tgid) {
+ throw new SecurityException("TID " + tid
+ + " doesn't belong to the calling process with pid "
+ + tgid);
+ }
+ }
+ }
+ halParams.tids = params.tids;
}
- synchronized (mCpuHeadroomLock) {
- while (!mCpuHeadroomCache.isEmpty()) {
- if (mCpuHeadroomCache.getFirst().isExpired()) {
- mCpuHeadroomCache.removeFirst();
- } else {
- break;
- }
- }
- for (int i = 0; i < mCpuHeadroomCache.size(); ++i) {
- final CpuHeadroomCacheItem item = mCpuHeadroomCache.get(i);
- if (item.match(params, halParams.pid)) {
- item.mExpiredTimeMillis =
- System.currentTimeMillis() + mCpuHeadroomIntervalMillis;
- mCpuHeadroomCache.remove(i);
- mCpuHeadroomCache.add(item);
- return item.mHeadroom;
- }
+ if (halParams.calculationWindowMillis
+ == mDefaultCpuHeadroomCalculationWindowMillis) {
+ synchronized (mCpuHeadroomLock) {
+ final CpuHeadroomResult res = mCpuHeadroomCache.get(halParams);
+ if (res != null) return res;
}
}
// return from HAL directly
try {
- float[] headroom = mPowerHal.getCpuHeadroom(halParams);
- if (headroom == null || headroom.length == 0) {
- Slog.wtf(TAG,
- "CPU headroom from Power HAL is invalid: " + Arrays.toString(headroom));
- return new float[]{Float.NaN};
+ final CpuHeadroomResult result = mPowerHal.getCpuHeadroom(halParams);
+ if (result == null) {
+ Slog.wtf(TAG, "CPU headroom from Power HAL is invalid");
+ return null;
}
- synchronized (mCpuHeadroomLock) {
- mCpuHeadroomCache.add(new CpuHeadroomCacheItem(
- System.currentTimeMillis() + mCpuHeadroomIntervalMillis,
- params, headroom, halParams.pid
- ));
+ if (halParams.calculationWindowMillis
+ == mDefaultCpuHeadroomCalculationWindowMillis) {
+ synchronized (mCpuHeadroomLock) {
+ mCpuHeadroomCache.add(halParams, result);
+ }
}
- return headroom;
-
+ return result;
} catch (RemoteException e) {
- return new float[]{Float.NaN};
+ Slog.e(TAG, "Failed to get CPU headroom from Power HAL", e);
+ return null;
}
}
@Override
- public float getGpuHeadroom(@Nullable GpuHeadroomParamsInternal params) {
+ public GpuHeadroomResult getGpuHeadroom(@NonNull GpuHeadroomParamsInternal params) {
if (mGpuHeadroomIntervalMillis <= 0) {
throw new UnsupportedOperationException();
}
- GpuHeadroomParams halParams = new GpuHeadroomParams();
- if (params != null) {
- halParams.calculationType = params.calculationType;
- }
- synchronized (mGpuHeadroomLock) {
- while (!mGpuHeadroomCache.isEmpty()) {
- if (mGpuHeadroomCache.getFirst().isExpired()) {
- mGpuHeadroomCache.removeFirst();
- } else {
- break;
- }
- }
- for (int i = 0; i < mGpuHeadroomCache.size(); ++i) {
- final GpuHeadroomCacheItem item = mGpuHeadroomCache.get(i);
- if (item.match(params)) {
- item.mExpiredTimeMillis =
- System.currentTimeMillis() + mGpuHeadroomIntervalMillis;
- mGpuHeadroomCache.remove(i);
- mGpuHeadroomCache.add(item);
- return item.mHeadroom;
- }
+ final GpuHeadroomParams halParams = new GpuHeadroomParams();
+ halParams.calculationType = params.calculationType;
+ halParams.calculationWindowMillis = params.calculationWindowMillis;
+ if (halParams.calculationWindowMillis
+ == mDefaultGpuHeadroomCalculationWindowMillis) {
+ synchronized (mGpuHeadroomLock) {
+ final GpuHeadroomResult res = mGpuHeadroomCache.get(halParams);
+ if (res != null) return res;
}
}
// return from HAL directly
try {
- float headroom = mPowerHal.getGpuHeadroom(halParams);
- if (Float.isNaN(headroom)) {
- Slog.wtf(TAG,
- "GPU headroom from Power HAL is NaN");
- return Float.NaN;
+ final GpuHeadroomResult headroom = mPowerHal.getGpuHeadroom(halParams);
+ if (headroom == null) {
+ Slog.wtf(TAG, "GPU headroom from Power HAL is invalid");
+ return null;
}
- synchronized (mGpuHeadroomLock) {
- mGpuHeadroomCache.add(new GpuHeadroomCacheItem(
- System.currentTimeMillis() + mGpuHeadroomIntervalMillis,
- params, headroom
- ));
+ if (halParams.calculationWindowMillis
+ == mDefaultGpuHeadroomCalculationWindowMillis) {
+ synchronized (mGpuHeadroomLock) {
+ mGpuHeadroomCache.add(halParams, headroom);
+ }
}
return headroom;
} catch (RemoteException e) {
- return Float.NaN;
+ Slog.e(TAG, "Failed to get GPU headroom from Power HAL", e);
+ return null;
}
}
@Override
- public long getCpuHeadroomMinIntervalMillis() throws RemoteException {
+ public long getCpuHeadroomMinIntervalMillis() {
if (mCpuHeadroomIntervalMillis <= 0) {
throw new UnsupportedOperationException();
}
@@ -1555,7 +1558,7 @@
}
@Override
- public long getGpuHeadroomMinIntervalMillis() throws RemoteException {
+ public long getGpuHeadroomMinIntervalMillis() {
if (mGpuHeadroomIntervalMillis <= 0) {
throw new UnsupportedOperationException();
}
@@ -1591,17 +1594,23 @@
CpuHeadroomParamsInternal params = new CpuHeadroomParamsInternal();
params.selectionType = CpuHeadroomParams.SelectionType.ALL;
params.usesDeviceHeadroom = true;
- pw.println("CPU headroom: " + Arrays.toString(getCpuHeadroom(params)));
+ CpuHeadroomResult ret = getCpuHeadroom(params);
+ pw.println("CPU headroom: " + (ret == null ? "N/A" : ret.getGlobalHeadroom()));
params = new CpuHeadroomParamsInternal();
params.selectionType = CpuHeadroomParams.SelectionType.PER_CORE;
params.usesDeviceHeadroom = true;
- pw.println("CPU headroom per core: " + Arrays.toString(getCpuHeadroom(params)));
+ ret = getCpuHeadroom(params);
+ pw.println("CPU headroom per core: " + (ret == null ? "N/A"
+ : Arrays.toString(ret.getPerCoreHeadroom())));
} catch (Exception e) {
+ Slog.d(TAG, "Failed to dump CPU headroom", e);
pw.println("CPU headroom: N/A");
}
try {
- pw.println("GPU headroom: " + getGpuHeadroom(null));
+ GpuHeadroomResult ret = getGpuHeadroom(new GpuHeadroomParamsInternal());
+ pw.println("GPU headroom: " + (ret == null ? "N/A" : ret.getGlobalHeadroom()));
} catch (Exception e) {
+ Slog.d(TAG, "Failed to dump GPU headroom", e);
pw.println("GPU headroom: N/A");
}
}
diff --git a/services/core/java/com/android/server/power/hint/flags.aconfig b/services/core/java/com/android/server/power/hint/flags.aconfig
index e56b68c..c231f5a 100644
--- a/services/core/java/com/android/server/power/hint/flags.aconfig
+++ b/services/core/java/com/android/server/power/hint/flags.aconfig
@@ -21,3 +21,10 @@
description: "Set reset_on_fork flag."
bug: "370988407"
}
+
+flag {
+ name: "cpu_headroom_affinity_check"
+ namespace: "game"
+ description: "Check affinity on CPU headroom."
+ bug: "346604998"
+}
diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
index 606bd1d..63e8d99 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
@@ -26,6 +26,7 @@
import android.os.Handler;
import android.os.Process;
import android.util.Log;
+import android.util.LogWriter;
import android.util.Slog;
import android.util.SparseArray;
@@ -34,6 +35,7 @@
import com.android.internal.os.MonotonicClock;
import com.android.internal.os.PowerProfile;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -44,6 +46,8 @@
*/
public class BatteryUsageStatsProvider {
private static final String TAG = "BatteryUsageStatsProv";
+ private static final boolean DEBUG = false;
+
private final Context mContext;
private final PowerAttributor mPowerAttributor;
private final PowerStatsStore mPowerStatsStore;
@@ -262,17 +266,25 @@
private BatteryUsageStats getBatteryUsageStats(BatteryStatsImpl stats,
BatteryUsageStatsQuery query, long currentTimeMs) {
+ BatteryUsageStats batteryUsageStats;
if ((query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_ACCUMULATED) != 0) {
- return getAccumulatedBatteryUsageStats(stats, query, currentTimeMs);
+ batteryUsageStats = getAccumulatedBatteryUsageStats(stats, query, currentTimeMs);
} else if (query.getAggregatedToTimestamp() == 0) {
BatteryUsageStats.Builder builder = computeBatteryUsageStats(stats, query,
query.getMonotonicStartTime(),
query.getMonotonicEndTime(), currentTimeMs);
- return builder.build();
+ batteryUsageStats = builder.build();
} else {
- return getAggregatedBatteryUsageStats(stats, query);
+ batteryUsageStats = getAggregatedBatteryUsageStats(stats, query);
}
+ if (DEBUG) {
+ Slog.d(TAG, "query = " + query);
+ PrintWriter pw = new PrintWriter(new LogWriter(Log.DEBUG, TAG));
+ batteryUsageStats.dump(pw, "");
+ pw.flush();
+ }
+ return batteryUsageStats;
}
private BatteryUsageStats getAccumulatedBatteryUsageStats(BatteryStatsImpl stats,
@@ -319,7 +331,7 @@
if (accumulatedStats.builder == null) {
accumulatedStats.builder = new BatteryUsageStats.Builder(
- stats.getCustomEnergyConsumerNames(), false, true, true, true, 0);
+ stats.getCustomEnergyConsumerNames(), true, true, true, 0);
accumulatedStats.startWallClockTime = stats.getStartClockTime();
accumulatedStats.builder.setStatsStartTimestamp(accumulatedStats.startWallClockTime);
}
@@ -338,8 +350,6 @@
private BatteryUsageStats.Builder computeBatteryUsageStats(BatteryStatsImpl stats,
BatteryUsageStatsQuery query, long monotonicStartTime, long monotonicEndTime,
long currentTimeMs) {
- final boolean includePowerModels = (query.getFlags()
- & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0;
final boolean includeProcessStateData = ((query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA) != 0)
&& stats.isProcessStateDataAvailable();
@@ -353,8 +363,7 @@
}
final BatteryUsageStats.Builder batteryUsageStatsBuilder = new BatteryUsageStats.Builder(
- customEnergyConsumerNames, includePowerModels,
- includeProcessStateData, query.isScreenStateDataNeeded(),
+ customEnergyConsumerNames, includeProcessStateData, query.isScreenStateDataNeeded(),
query.isPowerStateDataNeeded(), minConsumedPowerThreshold);
synchronized (stats) {
@@ -444,8 +453,6 @@
private BatteryUsageStats getAggregatedBatteryUsageStats(BatteryStatsImpl stats,
BatteryUsageStatsQuery query) {
- final boolean includePowerModels = (query.getFlags()
- & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0;
final boolean includeProcessStateData = ((query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA) != 0)
&& stats.isProcessStateDataAvailable();
@@ -453,7 +460,7 @@
final String[] customEnergyConsumerNames = stats.getCustomEnergyConsumerNames();
final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
- customEnergyConsumerNames, includePowerModels, includeProcessStateData,
+ customEnergyConsumerNames, includeProcessStateData,
query.isScreenStateDataNeeded(), query.isPowerStateDataNeeded(),
minConsumedPowerThreshold);
if (mPowerStatsStore == null) {
diff --git a/services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java b/services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java
index b2442c8..ef0e63b 100644
--- a/services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java
+++ b/services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java
@@ -224,13 +224,12 @@
BatteryConsumer.Key key = getKeyForPartialTotal(batteryUsageStatsBuilder, deviceScope,
powerComponentId, screenState, powerState);
if (key != null) {
- deviceScope.addConsumedPower(key, totalPower[0],
- BatteryConsumer.POWER_MODEL_UNDEFINED);
+ deviceScope.addConsumedPower(key, totalPower[0]);
deviceScope.addUsageDurationMillis(key, durationMs[0]);
}
key = deviceScope.getKey(powerComponentId, BatteryConsumer.PROCESS_STATE_UNSPECIFIED);
if (key != null) {
- deviceScope.addConsumedPower(key, totalPower[0], BatteryConsumer.POWER_MODEL_UNDEFINED);
+ deviceScope.addConsumedPower(key, totalPower[0]);
deviceScope.addUsageDurationMillis(key, durationMs[0]);
}
}
@@ -373,7 +372,7 @@
BatteryConsumer.Key key = builder.getKey(powerComponentId, procState,
resultScreenState, resultPowerState);
if (key != null) {
- builder.addConsumedPower(key, power, BatteryConsumer.POWER_MODEL_UNDEFINED);
+ builder.addConsumedPower(key, power);
builder.addUsageDurationMillis(key, duration);
}
}
@@ -384,8 +383,7 @@
BatteryConsumer.PROCESS_STATE_UNSPECIFIED);
if (key != null) {
builder.addConsumedPower(key,
- powerByProcState[BatteryConsumer.PROCESS_STATE_UNSPECIFIED],
- BatteryConsumer.POWER_MODEL_UNDEFINED);
+ powerByProcState[BatteryConsumer.PROCESS_STATE_UNSPECIFIED]);
builder.addUsageDurationMillis(key,
durationByProcState[BatteryConsumer.PROCESS_STATE_UNSPECIFIED]);
}
@@ -399,11 +397,9 @@
BatteryConsumer.Key key = getKeyForPartialTotal(batteryUsageStatsBuilder, allAppsScope,
powerComponentId, screenState, powerState);
if (key != null) {
- allAppsScope.addConsumedPower(key, powerAllApps,
- BatteryConsumer.POWER_MODEL_UNDEFINED);
+ allAppsScope.addConsumedPower(key, powerAllApps);
}
- allAppsScope.addConsumedPower(powerComponentId, powerAllApps,
- BatteryConsumer.POWER_MODEL_UNDEFINED);
+ allAppsScope.addConsumedPower(powerComponentId, powerAllApps);
}
@Nullable
diff --git a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
index c9655c6..2cc08c3 100644
--- a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
+++ b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
@@ -46,6 +46,7 @@
import com.android.server.pm.UserManagerInternal;
import com.android.server.security.advancedprotection.features.AdvancedProtectionHook;
import com.android.server.security.advancedprotection.features.AdvancedProtectionProvider;
+import com.android.server.security.advancedprotection.features.DisallowCellular2GAdvancedProtectionHook;
import com.android.server.security.advancedprotection.features.DisallowInstallUnknownSourcesAdvancedProtectionHook;
import com.android.server.security.advancedprotection.features.MemoryTaggingExtensionHook;
@@ -84,6 +85,9 @@
if (android.security.Flags.aapmFeatureMemoryTaggingExtension()) {
mHooks.add(new MemoryTaggingExtensionHook(mContext, enabled));
}
+ if (android.security.Flags.aapmFeatureDisableCellular2g()) {
+ mHooks.add(new DisallowCellular2GAdvancedProtectionHook(mContext, enabled));
+ }
}
// Only for tests
diff --git a/services/core/java/com/android/server/security/advancedprotection/features/DisallowCellular2GAdvancedProtectionHook.java b/services/core/java/com/android/server/security/advancedprotection/features/DisallowCellular2GAdvancedProtectionHook.java
new file mode 100644
index 0000000..b9c8d3d
--- /dev/null
+++ b/services/core/java/com/android/server/security/advancedprotection/features/DisallowCellular2GAdvancedProtectionHook.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.security.advancedprotection.features;
+
+import static android.security.advancedprotection.AdvancedProtectionManager.ADVANCED_PROTECTION_SYSTEM_ENTITY;
+import static android.security.advancedprotection.AdvancedProtectionManager.FEATURE_ID_DISALLOW_CELLULAR_2G;
+
+import android.annotation.NonNull;
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.os.UserManager;
+import android.security.advancedprotection.AdvancedProtectionFeature;
+import android.telephony.TelephonyManager;
+import android.util.Slog;
+
+/** @hide */
+public final class DisallowCellular2GAdvancedProtectionHook extends AdvancedProtectionHook {
+ private static final String TAG = "AdvancedProtectionDisallowCellular2G";
+
+ private final AdvancedProtectionFeature mFeature =
+ new AdvancedProtectionFeature(FEATURE_ID_DISALLOW_CELLULAR_2G);
+ private final DevicePolicyManager mDevicePolicyManager;
+ private final TelephonyManager mTelephonyManager;
+
+ public DisallowCellular2GAdvancedProtectionHook(@NonNull Context context, boolean enabled) {
+ super(context, enabled);
+ mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class);
+ mTelephonyManager = context.getSystemService(TelephonyManager.class);
+
+ setPolicy(enabled);
+ }
+
+ @NonNull
+ @Override
+ public AdvancedProtectionFeature getFeature() {
+ return mFeature;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return mTelephonyManager.isDataCapable();
+ }
+
+ private void setPolicy(boolean enabled) {
+ Slog.i(TAG, "setPolicy called with " + enabled);
+
+ if (enabled) {
+ Slog.d(TAG, "Setting DISALLOW_CELLULAR_2G_GLOBALLY restriction");
+ mDevicePolicyManager.addUserRestrictionGlobally(
+ ADVANCED_PROTECTION_SYSTEM_ENTITY, UserManager.DISALLOW_CELLULAR_2G);
+ } else {
+ Slog.d(TAG, "Clearing DISALLOW_CELLULAR_2G_GLOBALLY restriction");
+ mDevicePolicyManager.clearUserRestrictionGlobally(
+ ADVANCED_PROTECTION_SYSTEM_ENTITY, UserManager.DISALLOW_CELLULAR_2G);
+ }
+ }
+
+ @Override
+ public void onAdvancedProtectionChanged(boolean enabled) {
+ setPolicy(enabled);
+
+ // Leave 2G disabled even if APM is disabled.
+ if (!enabled) {
+ long oldAllowedTypes =
+ mTelephonyManager.getAllowedNetworkTypesForReason(
+ TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G);
+ long newAllowedTypes = oldAllowedTypes & ~TelephonyManager.NETWORK_CLASS_BITMASK_2G;
+ mTelephonyManager.setAllowedNetworkTypesForReason(
+ TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G, newAllowedTypes);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/security/advancedprotection/features/DisallowInstallUnknownSourcesAdvancedProtectionHook.java b/services/core/java/com/android/server/security/advancedprotection/features/DisallowInstallUnknownSourcesAdvancedProtectionHook.java
index 21752e5..a2933d9 100644
--- a/services/core/java/com/android/server/security/advancedprotection/features/DisallowInstallUnknownSourcesAdvancedProtectionHook.java
+++ b/services/core/java/com/android/server/security/advancedprotection/features/DisallowInstallUnknownSourcesAdvancedProtectionHook.java
@@ -19,13 +19,24 @@
import static android.security.advancedprotection.AdvancedProtectionManager.ADVANCED_PROTECTION_SYSTEM_ENTITY;
import static android.security.advancedprotection.AdvancedProtectionManager.FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES;
+import android.Manifest;
import android.annotation.NonNull;
+import android.app.ActivityManagerInternal;
+import android.app.AppGlobals;
+import android.app.AppOpsManager;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.os.Process;
+import android.os.RemoteException;
import android.os.UserManager;
import android.security.advancedprotection.AdvancedProtectionFeature;
import android.util.Slog;
+import com.android.server.LocalServices;
+
/** @hide */
public final class DisallowInstallUnknownSourcesAdvancedProtectionHook
extends AdvancedProtectionHook {
@@ -33,13 +44,25 @@
private final AdvancedProtectionFeature mFeature = new AdvancedProtectionFeature(
FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES);
+
+ private final ActivityManagerInternal mActivityManagerInternal;
+ private final AppOpsManager mAppOpsManager;
private final DevicePolicyManager mDevicePolicyManager;
+ private final IPackageManager mIPackageManager;
+ private final PackageManager mPackageManager;
+ private final UserManager mUserManager;
public DisallowInstallUnknownSourcesAdvancedProtectionHook(@NonNull Context context,
boolean enabled) {
super(context, enabled);
+ mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+ mAppOpsManager = context.getSystemService(AppOpsManager.class);
mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class);
- onAdvancedProtectionChanged(enabled);
+ mIPackageManager = AppGlobals.getPackageManager();
+ mUserManager = context.getSystemService(UserManager.class);
+ mPackageManager = context.getPackageManager();
+
+ setRestriction(enabled);
}
@NonNull
@@ -53,21 +76,48 @@
return true;
}
- @Override
- public void onAdvancedProtectionChanged(boolean enabled) {
+ private void setRestriction(boolean enabled) {
if (enabled) {
Slog.d(TAG, "Setting DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY restriction");
mDevicePolicyManager.addUserRestrictionGlobally(ADVANCED_PROTECTION_SYSTEM_ENTITY,
UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY);
- return;
+ } else {
+ Slog.d(TAG, "Clearing DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY restriction");
+ mDevicePolicyManager.clearUserRestrictionGlobally(ADVANCED_PROTECTION_SYSTEM_ENTITY,
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY);
}
- Slog.d(TAG, "Clearing DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY restriction");
- mDevicePolicyManager.clearUserRestrictionGlobally(ADVANCED_PROTECTION_SYSTEM_ENTITY,
- UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY);
+ }
- // TODO(b/369361373):
- // 1. After clearing the restriction, set AppOpsManager.OP_REQUEST_INSTALL_PACKAGES to
- // disabled.
- // 2. Update dialog strings.
+ @Override
+ public void onAdvancedProtectionChanged(boolean enabled) {
+ setRestriction(enabled);
+ if (enabled) return;
+
+ // Leave OP_REQUEST_INSTALL_PACKAGES disabled when APM is disabled.
+ Slog.d(TAG, "Setting all OP_REQUEST_INSTALL_PACKAGES to MODE_ERRORED");
+ for (UserInfo userInfo : mUserManager.getAliveUsers()) {
+ try {
+ final String[] packagesWithRequestInstallPermission = mIPackageManager
+ .getAppOpPermissionPackages(
+ Manifest.permission.REQUEST_INSTALL_PACKAGES, userInfo.id);
+ for (String packageName : packagesWithRequestInstallPermission) {
+ try {
+ int uid = mPackageManager.getPackageUidAsUser(packageName, userInfo.id);
+ boolean isCallerInstrumented = mActivityManagerInternal
+ .getInstrumentationSourceUid(uid) != Process.INVALID_UID;
+ if (!isCallerInstrumented) {
+ mAppOpsManager.setMode(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES, uid,
+ packageName, AppOpsManager.MODE_ERRORED);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "Couldn't retrieve uid for a package: " + e);
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Couldn't retrieve packages with REQUEST_INSTALL_PACKAGES."
+ + " getAppOpPermissionPackages() threw the following exception: " + e);
+ }
+ }
+ // TODO(b/369361373): Update dialog strings.
}
}
diff --git a/services/core/java/com/android/server/utils/WatchableImpl.java b/services/core/java/com/android/server/utils/WatchableImpl.java
index 8a04ccf..fec4351 100644
--- a/services/core/java/com/android/server/utils/WatchableImpl.java
+++ b/services/core/java/com/android/server/utils/WatchableImpl.java
@@ -33,6 +33,7 @@
/**
* The list of observers.
*/
+ @GuardedBy("mObservers")
protected final ArrayList<Watcher> mObservers = new ArrayList<>();
/**
@@ -83,7 +84,9 @@
* @return The number of registered observers.
*/
public int registeredObserverCount() {
- return mObservers.size();
+ synchronized (mObservers) {
+ return mObservers.size();
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 4b7e74a..dd76917 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -25,7 +25,6 @@
import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
-import static com.android.internal.util.DumpUtils.dumpSparseArray;
import static com.android.internal.util.DumpUtils.dumpSparseArrayValues;
import static com.android.server.accessibility.AccessibilityTraceFileProto.ENTRY;
import static com.android.server.accessibility.AccessibilityTraceFileProto.MAGIC_NUMBER;
@@ -49,23 +48,15 @@
import static com.android.server.wm.WindowTracingLegacy.WINSCOPE_EXT;
import android.accessibilityservice.AccessibilityTrace;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Application;
import android.content.Context;
import android.content.pm.PackageManagerInternal;
-import android.graphics.BLASTBufferQueue;
-import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Matrix;
-import android.graphics.Paint;
import android.graphics.Path;
-import android.graphics.PixelFormat;
import android.graphics.Point;
-import android.graphics.PorterDuff.Mode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
@@ -83,25 +74,16 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
-import android.util.TypedValue;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.MagnificationSpec;
import android.view.Surface;
-import android.view.Surface.OutOfResourcesException;
-import android.view.SurfaceControl;
import android.view.ViewConfiguration;
import android.view.WindowInfo;
import android.view.WindowManager;
import android.view.WindowManager.TransitionFlags;
import android.view.WindowManager.TransitionType;
-import android.view.WindowManagerPolicyConstants;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-import com.android.internal.R;
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.TraceBuffer;
import com.android.internal.util.function.pooled.PooledLambda;
@@ -111,7 +93,6 @@
import com.android.server.wm.WindowManagerInternal.AccessibilityControllerInternal;
import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks;
import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback;
-import com.android.window.flags.Flags;
import java.io.File;
import java.io.IOException;
@@ -301,36 +282,6 @@
}
}
- /** It is only used by unit test. */
- @VisibleForTesting
- Surface forceShowMagnifierSurface(int displayId) {
- final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
- if (displayMagnifier != null) {
- displayMagnifier.mMagnifiedViewport.mWindow.setAlpha(DisplayMagnifier.MagnifiedViewport
- .ViewportWindow.AnimationController.MAX_ALPHA);
- return displayMagnifier.mMagnifiedViewport.mWindow.mSurface;
- }
- return null;
- }
-
- void onWindowLayersChanged(int displayId) {
- if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
- | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
- mAccessibilityTracing.logTrace(TAG + ".onWindowLayersChanged",
- FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
- "displayId=" + displayId);
- }
- final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
- if (displayMagnifier != null) {
- displayMagnifier.onWindowLayersChanged();
- }
- final WindowsForAccessibilityObserver windowsForA11yObserver =
- mWindowsForAccessibilityObserver.get(displayId);
- if (windowsForA11yObserver != null) {
- windowsForA11yObserver.scheduleComputeChangedWindows();
- }
- }
-
void onDisplaySizeChanged(DisplayContent displayContent) {
if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
@@ -563,9 +514,6 @@
}
void dump(PrintWriter pw, String prefix) {
- dumpSparseArray(pw, prefix, mDisplayMagnifiers, "magnification display",
- (index, key) -> pw.printf("%sDisplay #%d:", prefix + " ", key),
- dm -> dm.dump(pw, ""));
dumpSparseArrayValues(pw, prefix, mWindowsForAccessibilityObserver,
"windows for accessibility observer");
mAccessibilityWindowsPopulator.dump(pw, prefix);
@@ -623,7 +571,6 @@
private final Context mDisplayContext;
private final WindowManagerService mService;
- private final MagnifiedViewport mMagnifiedViewport;
private final Handler mHandler;
private final DisplayContent mDisplayContent;
private final Display mDisplay;
@@ -660,8 +607,6 @@
mDisplay = display;
mHandler = new MyHandler(mService.mH.getLooper());
mUserContextChangedNotifier = new UserContextChangedNotifier(mHandler);
- mMagnifiedViewport = Flags.alwaysDrawMagnificationFullscreenBorder()
- ? null : new MagnifiedViewport();
mAccessibilityTracing =
AccessibilityController.getAccessibilityControllerInternal(mService);
mLongAnimationDuration = mDisplayContext.getResources().getInteger(
@@ -703,10 +648,6 @@
} else {
mMagnificationSpec.clear();
}
-
- if (!Flags.alwaysDrawMagnificationFullscreenBorder()) {
- mMagnifiedViewport.setShowMagnifiedBorderIfNeeded();
- }
}
void setFullscreenMagnificationActivated(boolean activated) {
@@ -715,10 +656,6 @@
FLAGS_MAGNIFICATION_CALLBACK, "activated=" + activated);
}
mIsFullscreenMagnificationActivated = activated;
- if (!Flags.alwaysDrawMagnificationFullscreenBorder()) {
- mMagnifiedViewport.setMagnifiedRegionBorderShown(activated, true);
- mMagnifiedViewport.showMagnificationBoundsIfNeeded();
- }
}
boolean isFullscreenMagnificationActivated() {
@@ -729,18 +666,6 @@
return mIsFullscreenMagnificationActivated;
}
- void onWindowLayersChanged() {
- if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
- mAccessibilityTracing.logTrace(
- LOG_TAG + ".onWindowLayersChanged", FLAGS_MAGNIFICATION_CALLBACK);
- }
- if (DEBUG_LAYERS) {
- Slog.i(LOG_TAG, "Layers changed.");
- }
- recomputeBounds();
- mService.scheduleAnimationLocked();
- }
-
void onDisplaySizeChanged(DisplayContent displayContent) {
if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
mAccessibilityTracing.logTrace(LOG_TAG + ".onDisplaySizeChanged",
@@ -753,9 +678,6 @@
}
recomputeBounds();
- if (!Flags.alwaysDrawMagnificationFullscreenBorder()) {
- mMagnifiedViewport.onDisplaySizeChanged();
- }
mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_DISPLAY_SIZE_CHANGED);
}
@@ -926,10 +848,6 @@
if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
mAccessibilityTracing.logTrace(LOG_TAG + ".destroy", FLAGS_MAGNIFICATION_CALLBACK);
}
-
- if (!Flags.alwaysDrawMagnificationFullscreenBorder()) {
- mMagnifiedViewport.destroyWindow();
- }
}
void recomputeMagnifiedRegionAndDrawMagnifiedRegionBorderIfNeeded() {
@@ -939,10 +857,6 @@
FLAGS_MAGNIFICATION_CALLBACK);
}
recomputeBounds();
-
- if (!Flags.alwaysDrawMagnificationFullscreenBorder()) {
- mMagnifiedViewport.drawWindowIfNeeded();
- }
}
void recomputeBounds() {
@@ -1050,16 +964,9 @@
}
visibleWindows.clear();
- if (!Flags.alwaysDrawMagnificationFullscreenBorder()) {
- mMagnifiedViewport.intersectWithDrawBorderInset(screenWidth, screenHeight);
- }
-
final boolean magnifiedChanged =
!mOldMagnificationRegion.equals(mMagnificationRegion);
if (magnifiedChanged) {
- if (!Flags.alwaysDrawMagnificationFullscreenBorder()) {
- mMagnifiedViewport.updateBorderDrawingStatus(screenWidth, screenHeight);
- }
mOldMagnificationRegion.set(mMagnificationRegion);
final SomeArgs args = SomeArgs.obtain();
args.arg1 = Region.obtain(mMagnificationRegion);
@@ -1139,420 +1046,11 @@
outSize.set(bounds.width(), bounds.height());
}
- void dump(PrintWriter pw, String prefix) {
- if (!Flags.alwaysDrawMagnificationFullscreenBorder()) {
- mMagnifiedViewport.dump(pw, prefix);
- }
- }
-
- private final class MagnifiedViewport {
-
- private final float mBorderWidth;
- private final int mHalfBorderWidth;
- private final int mDrawBorderInset;
-
- @Nullable private final ViewportWindow mWindow;
-
- private boolean mFullRedrawNeeded;
-
- MagnifiedViewport() {
- mBorderWidth = mDisplayContext.getResources().getDimension(
- com.android.internal.R.dimen.accessibility_magnification_indicator_width);
- mHalfBorderWidth = (int) Math.ceil(mBorderWidth / 2);
- mDrawBorderInset = (int) mBorderWidth / 2;
- mWindow = new ViewportWindow(mDisplayContext);
- }
-
- void updateBorderDrawingStatus(int screenWidth, int screenHeight) {
- mWindow.setBounds(mMagnificationRegion);
- final Rect dirtyRect = mTempRect1;
- if (mFullRedrawNeeded) {
- mFullRedrawNeeded = false;
- dirtyRect.set(mDrawBorderInset, mDrawBorderInset,
- screenWidth - mDrawBorderInset,
- screenHeight - mDrawBorderInset);
- mWindow.invalidate(dirtyRect);
- } else {
- final Region dirtyRegion = mTempRegion3;
- dirtyRegion.set(mMagnificationRegion);
- dirtyRegion.op(mOldMagnificationRegion, Region.Op.XOR);
- dirtyRegion.getBounds(dirtyRect);
- mWindow.invalidate(dirtyRect);
- }
- }
-
- void setShowMagnifiedBorderIfNeeded() {
- // If this message is pending, we are in a rotation animation and do not want
- // to show the border. We will do so when the pending message is handled.
- if (!mHandler.hasMessages(
- MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
- setMagnifiedRegionBorderShown(
- isFullscreenMagnificationActivated(), true);
- }
- }
-
- // Can be called outside of a surface transaction
- void showMagnificationBoundsIfNeeded() {
- if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
- mAccessibilityTracing.logTrace(LOG_TAG + ".showMagnificationBoundsIfNeeded",
- FLAGS_MAGNIFICATION_CALLBACK);
- }
- mHandler.obtainMessage(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)
- .sendToTarget();
- }
-
- void intersectWithDrawBorderInset(int screenWidth, int screenHeight) {
- mMagnificationRegion.op(mDrawBorderInset, mDrawBorderInset,
- screenWidth - mDrawBorderInset, screenHeight - mDrawBorderInset,
- Region.Op.INTERSECT);
- }
-
- void onDisplaySizeChanged() {
- // If fullscreen magnification is activated, hide the border immediately so
- // the user does not see strange artifacts during display size changed caused by
- // rotation or folding/unfolding the device. In the rotation case, the
- // screenshot used for rotation already has the border. After the rotation is
- // completed we will show the border.
- if (isFullscreenMagnificationActivated()) {
- setMagnifiedRegionBorderShown(false, false);
- final long delay = (long) (mLongAnimationDuration
- * mService.getWindowAnimationScaleLocked());
- Message message = mHandler.obtainMessage(
- MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED);
- mHandler.sendMessageDelayed(message, delay);
- }
- mWindow.updateSize();
- }
-
- void setMagnifiedRegionBorderShown(boolean shown, boolean animate) {
- if (mWindow.setShown(shown, animate)) {
- mFullRedrawNeeded = true;
- // Clear the old region, so recomputeBounds will refresh the current region.
- mOldMagnificationRegion.set(0, 0, 0, 0);
- }
- }
-
- void drawWindowIfNeeded() {
- mWindow.postDrawIfNeeded();
- }
-
- void destroyWindow() {
- mWindow.releaseSurface();
- }
-
- void dump(PrintWriter pw, String prefix) {
- mWindow.dump(pw, prefix);
- }
-
- // TODO(291891390): Remove this class when we clean up the flag
- // alwaysDrawMagnificationFullscreenBorder
- private final class ViewportWindow implements Runnable {
- private static final String SURFACE_TITLE = "Magnification Overlay";
-
- private final Region mBounds = new Region();
- private final Rect mDirtyRect = new Rect();
- private final Paint mPaint = new Paint();
-
- private final SurfaceControl mSurfaceControl;
- /** After initialization, it should only be accessed from animation thread. */
- private final SurfaceControl.Transaction mTransaction;
- private final BLASTBufferQueue mBlastBufferQueue;
- private final Surface mSurface;
-
- private final AnimationController mAnimationController;
-
- private boolean mShown;
- private boolean mLastSurfaceShown;
- private int mAlpha;
- private int mPreviousAlpha;
-
- private volatile boolean mInvalidated;
-
- ViewportWindow(Context context) {
- SurfaceControl surfaceControl = null;
- try {
- surfaceControl = mDisplayContent
- .makeOverlay()
- .setName(SURFACE_TITLE)
- .setBLASTLayer()
- .setFormat(PixelFormat.TRANSLUCENT)
- .setCallsite("ViewportWindow")
- .build();
- } catch (OutOfResourcesException oore) {
- /* ignore */
- }
- mSurfaceControl = surfaceControl;
- mDisplay.getRealSize(mScreenSize);
- mBlastBufferQueue = new BLASTBufferQueue(SURFACE_TITLE, mSurfaceControl,
- mScreenSize.x, mScreenSize.y, PixelFormat.RGBA_8888);
-
- final SurfaceControl.Transaction t = mService.mTransactionFactory.get();
- final int layer =
- mService.mPolicy.getWindowLayerFromTypeLw(TYPE_MAGNIFICATION_OVERLAY) *
- WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER;
- t.setLayer(mSurfaceControl, layer).setPosition(mSurfaceControl, 0, 0);
- InputMonitor.setTrustedOverlayInputInfo(mSurfaceControl, t,
- mDisplayContent.getDisplayId(), "Magnification Overlay");
- t.apply();
- mTransaction = t;
- mSurface = mBlastBufferQueue.createSurface();
-
- mAnimationController = new AnimationController(context,
- mService.mH.getLooper());
-
- TypedValue typedValue = new TypedValue();
- context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight,
- typedValue, true);
- final int borderColor = context.getColor(typedValue.resourceId);
-
- mPaint.setStyle(Paint.Style.STROKE);
- mPaint.setStrokeWidth(mBorderWidth);
- mPaint.setColor(borderColor);
-
- mInvalidated = true;
- }
-
- /** Returns {@code true} if the state is changed to shown. */
- boolean setShown(boolean shown, boolean animate) {
- synchronized (mService.mGlobalLock) {
- if (mShown == shown) {
- return false;
- }
- mShown = shown;
- mAnimationController.onFrameShownStateChanged(shown, animate);
- if (DEBUG_VIEWPORT_WINDOW) {
- Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown);
- }
- }
- return shown;
- }
-
- @SuppressWarnings("unused")
- // Called reflectively from an animator.
- int getAlpha() {
- synchronized (mService.mGlobalLock) {
- return mAlpha;
- }
- }
-
- void setAlpha(int alpha) {
- synchronized (mService.mGlobalLock) {
- if (mAlpha == alpha) {
- return;
- }
- mAlpha = alpha;
- invalidate(null);
- if (DEBUG_VIEWPORT_WINDOW) {
- Slog.i(LOG_TAG, "ViewportWindow set alpha: " + alpha);
- }
- }
- }
-
- void setBounds(Region bounds) {
- synchronized (mService.mGlobalLock) {
- if (mBounds.equals(bounds)) {
- return;
- }
- mBounds.set(bounds);
- invalidate(mDirtyRect);
- if (DEBUG_VIEWPORT_WINDOW) {
- Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds);
- }
- }
- }
-
- void updateSize() {
- synchronized (mService.mGlobalLock) {
- getDisplaySizeLocked(mScreenSize);
- mBlastBufferQueue.update(mSurfaceControl, mScreenSize.x, mScreenSize.y,
- PixelFormat.RGBA_8888);
- invalidate(mDirtyRect);
- }
- }
-
- void invalidate(Rect dirtyRect) {
- if (dirtyRect != null) {
- mDirtyRect.set(dirtyRect);
- } else {
- mDirtyRect.setEmpty();
- }
- mInvalidated = true;
- mService.scheduleAnimationLocked();
- }
-
- void postDrawIfNeeded() {
- if (mInvalidated) {
- mService.mAnimationHandler.post(this);
- }
- }
-
- @Override
- public void run() {
- drawOrRemoveIfNeeded();
- }
-
- /**
- * This method must only be called by animation handler directly to make sure
- * thread safe and there is no lock held outside.
- */
- private void drawOrRemoveIfNeeded() {
- // Drawing variables (alpha, dirty rect, and bounds) access is synchronized
- // using WindowManagerGlobalLock. Grab copies of these values before
- // drawing on the canvas so that drawing can be performed outside of the lock.
- int alpha;
- boolean redrawBounds;
- Rect drawingRect = null;
- Region drawingBounds = null;
- synchronized (mService.mGlobalLock) {
- if (mBlastBufferQueue.mNativeObject == 0) {
- // Complete removal since releaseSurface has been called.
- if (mSurface.isValid()) {
- mTransaction.remove(mSurfaceControl).apply();
- mSurface.release();
- }
- return;
- }
- if (!mInvalidated) {
- return;
- }
- mInvalidated = false;
-
- alpha = mAlpha;
- // For b/325863281, we should ensure the drawn border path is cleared when
- // alpha = 0. Therefore, we cache the last used alpha when drawing as
- // mPreviousAlpha and check it here. If mPreviousAlpha > 0, which means
- // the border is showing now, then we should still redraw the clear path
- // on the canvas so the border is cleared.
- redrawBounds = mAlpha > 0 || mPreviousAlpha > 0;
- if (redrawBounds) {
- drawingBounds = new Region(mBounds);
- // Empty dirty rectangle means unspecified.
- if (mDirtyRect.isEmpty()) {
- mBounds.getBounds(mDirtyRect);
- }
- mDirtyRect.inset(-mHalfBorderWidth, -mHalfBorderWidth);
- drawingRect = new Rect(mDirtyRect);
- if (DEBUG_VIEWPORT_WINDOW) {
- Slog.i(LOG_TAG, "ViewportWindow bounds: " + mBounds);
- Slog.i(LOG_TAG, "ViewportWindow dirty rect: " + mDirtyRect);
- }
- }
- }
-
- final boolean showSurface;
- // Draw without holding WindowManagerGlobalLock.
- if (redrawBounds) {
- Canvas canvas = null;
- try {
- canvas = mSurface.lockCanvas(drawingRect);
- } catch (IllegalArgumentException | OutOfResourcesException e) {
- /* ignore */
- }
- if (canvas == null) {
- return;
- }
- canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
- mPaint.setAlpha(alpha);
- canvas.drawPath(drawingBounds.getBoundaryPath(), mPaint);
- mSurface.unlockCanvasAndPost(canvas);
- mPreviousAlpha = alpha;
- }
-
- showSurface = alpha > 0;
-
- if (showSurface && !mLastSurfaceShown) {
- mTransaction.show(mSurfaceControl).apply();
- mLastSurfaceShown = true;
- } else if (!showSurface && mLastSurfaceShown) {
- mTransaction.hide(mSurfaceControl).apply();
- mLastSurfaceShown = false;
- }
- }
-
- @GuardedBy("mService.mGlobalLock")
- void releaseSurface() {
- mBlastBufferQueue.destroy();
- // Post to perform cleanup on the thread which handles mSurface.
- mService.mAnimationHandler.post(this);
- }
-
- void dump(PrintWriter pw, String prefix) {
- pw.println(prefix
- + " mBounds= " + mBounds
- + " mDirtyRect= " + mDirtyRect
- + " mWidth= " + mScreenSize.x
- + " mHeight= " + mScreenSize.y);
- }
-
- private final class AnimationController extends Handler {
- private static final String PROPERTY_NAME_ALPHA = "alpha";
-
- private static final int MIN_ALPHA = 0;
- private static final int MAX_ALPHA = 255;
-
- private static final int MSG_FRAME_SHOWN_STATE_CHANGED = 1;
-
- private final ValueAnimator mShowHideFrameAnimator;
-
- AnimationController(Context context, Looper looper) {
- super(looper);
- mShowHideFrameAnimator = ObjectAnimator.ofInt(ViewportWindow.this,
- PROPERTY_NAME_ALPHA, MIN_ALPHA, MAX_ALPHA);
-
- Interpolator interpolator = new DecelerateInterpolator(2.5f);
- final long longAnimationDuration = context.getResources().getInteger(
- com.android.internal.R.integer.config_longAnimTime);
-
- mShowHideFrameAnimator.setInterpolator(interpolator);
- mShowHideFrameAnimator.setDuration(longAnimationDuration);
- }
-
- void onFrameShownStateChanged(boolean shown, boolean animate) {
- obtainMessage(MSG_FRAME_SHOWN_STATE_CHANGED,
- shown ? 1 : 0, animate ? 1 : 0).sendToTarget();
- }
-
- @Override
- public void handleMessage(Message message) {
- switch (message.what) {
- case MSG_FRAME_SHOWN_STATE_CHANGED: {
- final boolean shown = message.arg1 == 1;
- final boolean animate = message.arg2 == 1;
-
- if (animate) {
- if (mShowHideFrameAnimator.isRunning()) {
- mShowHideFrameAnimator.reverse();
- } else {
- if (shown) {
- mShowHideFrameAnimator.start();
- } else {
- mShowHideFrameAnimator.reverse();
- }
- }
- } else {
- mShowHideFrameAnimator.cancel();
- if (shown) {
- setAlpha(MAX_ALPHA);
- } else {
- setAlpha(MIN_ALPHA);
- }
- }
- } break;
- }
- }
- }
- }
- }
-
private class MyHandler extends Handler {
public static final int MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED = 1;
public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
public static final int MESSAGE_NOTIFY_DISPLAY_SIZE_CHANGED = 4;
-
- // TODO(291891390): Remove this field when we clean up the flag
- // alwaysDrawMagnificationFullscreenBorder
- public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
- public static final int MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED = 6;
+ public static final int MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED = 5;
MyHandler(Looper looper) {
super(looper);
@@ -1576,17 +1074,6 @@
mCallbacks.onDisplaySizeChanged();
} break;
- case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
- synchronized (mService.mGlobalLock) {
- if (isFullscreenMagnificationActivated()) {
- if (!Flags.alwaysDrawMagnificationFullscreenBorder()) {
- mMagnifiedViewport.setMagnifiedRegionBorderShown(true, true);
- }
- mService.scheduleAnimationLocked();
- }
- }
- } break;
-
case MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED: {
final boolean shown = message.arg1 == 1;
mCallbacks.onImeWindowVisibilityChanged(shown);
diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index 2401f90..c7667b4 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -19,6 +19,7 @@
wilsonshih@google.com
jiamingliu@google.com
pdwilliams@google.com
+charlesccchen@google.com
# Files related to background activity launches
per-file Background*Start* = set noparent
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index cb333f0..1c8d06e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -152,19 +152,20 @@
mAdminPolicySize = new SparseArray<>();
}
- private void maybeForceEnforcementRefreshLocked(@NonNull PolicyDefinition<?> policyDefinition) {
+ private void forceEnforcementRefreshIfUserRestrictionLocked(
+ @NonNull PolicyDefinition<?> policyDefinition) {
try {
- if (shouldForceEnforcementRefresh(policyDefinition)) {
+ if (isUserRestrictionPolicy(policyDefinition)) {
// This is okay because it's only true for user restrictions which are all <Boolean>
forceEnforcementRefreshLocked((PolicyDefinition<Boolean>) policyDefinition);
}
} catch (Throwable e) {
// Catch any possible exceptions just to be on the safe side
- Log.e(TAG, "Exception throw during maybeForceEnforcementRefreshLocked", e);
+ Log.e(TAG, "Exception thrown during forceEnforcementRefreshIfUserRestrictionLocked", e);
}
}
- private boolean shouldForceEnforcementRefresh(@NonNull PolicyDefinition<?> policyDefinition) {
+ private boolean isUserRestrictionPolicy(@NonNull PolicyDefinition<?> policyDefinition) {
// These are all "not nullable" but for the purposes of maximum safety for a lightly tested
// change we check here
if (policyDefinition == null) {
@@ -257,7 +258,7 @@
// No need to notify admins as no new policy is actually enforced, we're just filling in
// the data structures.
if (!skipEnforcePolicy) {
- maybeForceEnforcementRefreshLocked(policyDefinition);
+ forceEnforcementRefreshIfUserRestrictionLocked(policyDefinition);
if (policyChanged) {
onLocalPolicyChangedLocked(policyDefinition, enforcingAdmin, userId);
}
@@ -347,7 +348,7 @@
Objects.requireNonNull(enforcingAdmin);
synchronized (mLock) {
- maybeForceEnforcementRefreshLocked(policyDefinition);
+ forceEnforcementRefreshIfUserRestrictionLocked(policyDefinition);
if (!hasLocalPolicyLocked(policyDefinition, userId)) {
return;
}
@@ -517,7 +518,7 @@
// No need to notify admins as no new policy is actually enforced, we're just filling in
// the data structures.
if (!skipEnforcePolicy) {
- maybeForceEnforcementRefreshLocked(policyDefinition);
+ forceEnforcementRefreshIfUserRestrictionLocked(policyDefinition);
if (policyChanged) {
onGlobalPolicyChangedLocked(policyDefinition, enforcingAdmin);
}
@@ -570,7 +571,7 @@
boolean policyChanged = policyState.removePolicy(enforcingAdmin);
- maybeForceEnforcementRefreshLocked(policyDefinition);
+ forceEnforcementRefreshIfUserRestrictionLocked(policyDefinition);
if (policyChanged) {
onGlobalPolicyChangedLocked(policyDefinition, enforcingAdmin);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 5bd70ef..6f0d26a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -23178,6 +23178,10 @@
MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_KEYGUARD,
MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL);
+ if (Flags.lockNowCoexistence()) {
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCK,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL);
+ }
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL);
@@ -23252,8 +23256,10 @@
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCATION,
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCK,
- MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+ if (!Flags.lockNowCoexistence()) {
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCK,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+ }
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCK_TASK,
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_MODIFY_USERS,
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 82d49fc..112414e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -422,6 +422,8 @@
"com.android.server.wifi.aware.WifiAwareService";
private static final String WIFI_P2P_SERVICE_CLASS =
"com.android.server.wifi.p2p.WifiP2pService";
+ private static final String WIFI_USD_SERVICE_CLASS =
+ "com.android.server.wifi.usd.UsdService";
private static final String CONNECTIVITY_SERVICE_APEX_PATH =
"/apex/com.android.tethering/javalib/service-connectivity.jar";
private static final String CONNECTIVITY_SERVICE_INITIALIZER_CLASS =
@@ -2145,6 +2147,13 @@
mSystemServiceManager.startServiceFromJar(
WIFI_SCANNING_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
t.traceEnd();
+ // Start USD service
+ if (android.net.wifi.flags.Flags.usd()) {
+ t.traceBegin("StartUsd");
+ mSystemServiceManager.startServiceFromJar(
+ WIFI_USD_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
+ t.traceEnd();
+ }
}
if (context.getPackageManager().hasSystemFeature(
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
index 3fdb53f..31f0370 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
@@ -289,6 +289,7 @@
AndroidPackage::getEmergencyInstaller,
AndroidPackage::isAllowCrossUidActivitySwitchFromBelow,
AndroidPackage::getIntentMatchingFlags,
+ AndroidPackage::getPageSizeAppCompatFlags,
)
override fun extraParams() = listOf(
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 993569f..0d25426 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -392,3 +392,10 @@
],
include_filters: ["com.android.server.StorageManagerServiceTest"],
}
+
+test_module_config {
+ name: "FrameworksMockingServicesTests_service_batteryServiceTest",
+ base: "FrameworksMockingServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.BatteryServiceTest"],
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/BatteryServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/BatteryServiceTest.java
index 5e2f80b..1fbd53a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/BatteryServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/BatteryServiceTest.java
@@ -79,6 +79,8 @@
private static final int UPDATED_BATTERY_HEALTH = 3;
private static final int CURRENT_CHARGE_COUNTER = 4680000;
private static final int UPDATED_CHARGE_COUNTER = 4218000;
+ private static final int CURRENT_MAX_CHARGING_CURRENT = 298125;
+ private static final int UPDATED_MAX_CHARGING_CURRENT = 398125;
private static final int HANDLER_IDLE_TIME_MS = 5000;
@Rule
public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this)
@@ -143,7 +145,7 @@
@EnableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST)
public void onlyVoltageUpdated_lessThenOnePercent_broadcastNotSent() {
mBatteryService.update(createHealthInfo(VOLTAGE_LESS_THEN_ONE_PERCENT, CURRENT_BATTERY_TEMP,
- CURRENT_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH));
+ CURRENT_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH, CURRENT_MAX_CHARGING_CURRENT));
waitForHandlerToExecute();
@@ -156,7 +158,8 @@
mBatteryService.update(
createHealthInfo(VOLTAGE_MORE_THEN_ONE_PERCENT, CURRENT_BATTERY_TEMP,
CURRENT_CHARGE_COUNTER,
- CURRENT_BATTERY_HEALTH));
+ CURRENT_BATTERY_HEALTH,
+ CURRENT_MAX_CHARGING_CURRENT));
waitForHandlerToExecute();
@@ -165,13 +168,17 @@
@Test
@EnableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST)
- public void onlyVoltageUpdated_broadcastSent() {
+ public void voltageUpdated_withUpdateInChargingCurrent_broadcastSent() {
mBatteryService.mLastBroadcastVoltageUpdateTime = SystemClock.elapsedRealtime() - 20000;
+ long lastChargingCurrentUpdateTime =
+ mBatteryService.mLastBroadcastMaxChargingCurrentUpdateTime;
mBatteryService.update(createHealthInfo(VOLTAGE_MORE_THEN_ONE_PERCENT, CURRENT_BATTERY_TEMP,
- CURRENT_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH));
+ CURRENT_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH, UPDATED_MAX_CHARGING_CURRENT));
waitForHandlerToExecute();
+ assertTrue(lastChargingCurrentUpdateTime
+ < mBatteryService.mLastBroadcastMaxChargingCurrentUpdateTime);
verifyNumberOfTimesBroadcastSent(1);
}
@@ -180,7 +187,8 @@
public void onlyTempUpdated_lessThenOneDegreeCelsius_broadcastNotSent() {
mBatteryService.update(
createHealthInfo(CURRENT_BATTERY_VOLTAGE, TEMP_LESS_THEN_ONE_DEGREE_CELSIUS,
- CURRENT_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH));
+ CURRENT_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH,
+ CURRENT_MAX_CHARGING_CURRENT));
waitForHandlerToExecute();
@@ -191,23 +199,31 @@
@EnableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST)
public void tempUpdated_broadcastSent() {
long lastVoltageUpdateTime = mBatteryService.mLastBroadcastVoltageUpdateTime;
+ long lastChargingCurrentUpdateTime =
+ mBatteryService.mLastBroadcastMaxChargingCurrentUpdateTime;
mBatteryService.update(
createHealthInfo(VOLTAGE_LESS_THEN_ONE_PERCENT, TEMP_MORE_THEN_ONE_DEGREE_CELSIUS,
- UPDATED_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH));
+ UPDATED_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH,
+ UPDATED_MAX_CHARGING_CURRENT));
waitForHandlerToExecute();
assertTrue(lastVoltageUpdateTime < mBatteryService.mLastBroadcastVoltageUpdateTime);
+ assertTrue(lastChargingCurrentUpdateTime
+ < mBatteryService.mLastBroadcastMaxChargingCurrentUpdateTime);
verifyNumberOfTimesBroadcastSent(1);
}
@Test
@EnableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST)
- public void batteryHealthUpdated_voltageAndTempConst_broadcastSent() {
+ public void batteryHealthUpdated_withOtherExtrasConstant_broadcastSent() {
+ long lastVoltageUpdateTime = mBatteryService.mLastBroadcastVoltageUpdateTime;
+ long lastChargingCurrentUpdateTime =
+ mBatteryService.mLastBroadcastMaxChargingCurrentUpdateTime;
mBatteryService.update(
- createHealthInfo(CURRENT_BATTERY_VOLTAGE, CURRENT_BATTERY_TEMP,
+ createHealthInfo(VOLTAGE_LESS_THEN_ONE_PERCENT, CURRENT_BATTERY_TEMP,
CURRENT_CHARGE_COUNTER,
- UPDATED_BATTERY_HEALTH));
+ UPDATED_BATTERY_HEALTH, UPDATED_MAX_CHARGING_CURRENT));
waitForHandlerToExecute();
@@ -217,10 +233,13 @@
mBatteryService.update(
createHealthInfo(CURRENT_BATTERY_VOLTAGE, CURRENT_BATTERY_TEMP,
UPDATED_CHARGE_COUNTER,
- UPDATED_BATTERY_HEALTH));
+ UPDATED_BATTERY_HEALTH, CURRENT_MAX_CHARGING_CURRENT));
waitForHandlerToExecute();
+ assertTrue(lastVoltageUpdateTime < mBatteryService.mLastBroadcastVoltageUpdateTime);
+ assertTrue(lastChargingCurrentUpdateTime
+ < mBatteryService.mLastBroadcastMaxChargingCurrentUpdateTime);
verifyNumberOfTimesBroadcastSent(1);
}
@@ -228,7 +247,7 @@
@DisableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST)
public void voltageUpdated_lessThanOnePercent_flagDisabled_broadcastSent() {
mBatteryService.update(createHealthInfo(VOLTAGE_LESS_THEN_ONE_PERCENT, CURRENT_BATTERY_TEMP,
- CURRENT_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH));
+ CURRENT_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH, CURRENT_MAX_CHARGING_CURRENT));
waitForHandlerToExecute();
@@ -241,7 +260,7 @@
mBatteryService.update(
createHealthInfo(CURRENT_BATTERY_VOLTAGE, CURRENT_BATTERY_TEMP,
UPDATED_CHARGE_COUNTER,
- CURRENT_BATTERY_HEALTH));
+ CURRENT_BATTERY_HEALTH, CURRENT_MAX_CHARGING_CURRENT));
waitForHandlerToExecute();
@@ -254,7 +273,7 @@
mBatteryService.update(
createHealthInfo(CURRENT_BATTERY_VOLTAGE, TEMP_LESS_THEN_ONE_DEGREE_CELSIUS,
UPDATED_CHARGE_COUNTER,
- CURRENT_BATTERY_HEALTH));
+ CURRENT_BATTERY_HEALTH, CURRENT_MAX_CHARGING_CURRENT));
waitForHandlerToExecute();
@@ -267,18 +286,51 @@
mBatteryService.update(
createHealthInfo(CURRENT_BATTERY_VOLTAGE, CURRENT_BATTERY_TEMP,
UPDATED_CHARGE_COUNTER,
- CURRENT_BATTERY_HEALTH));
+ CURRENT_BATTERY_HEALTH, CURRENT_MAX_CHARGING_CURRENT));
waitForHandlerToExecute();
verifyNumberOfTimesBroadcastSent(1);
}
+ @Test
+ @EnableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST)
+ public void onlyMaxChargingCurrentUpdated_beforeFiveSeconds_broadcastNotSent() {
+ mBatteryService.update(
+ createHealthInfo(CURRENT_BATTERY_VOLTAGE, CURRENT_BATTERY_TEMP,
+ CURRENT_CHARGE_COUNTER,
+ CURRENT_BATTERY_HEALTH,
+ UPDATED_MAX_CHARGING_CURRENT));
+
+ waitForHandlerToExecute();
+
+ verifyNumberOfTimesBroadcastSent(0);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST)
+ public void maxChargingCurrentUpdated_afterFiveSeconds_broadcastSent() {
+ mBatteryService.mLastBroadcastMaxChargingCurrentUpdateTime =
+ SystemClock.elapsedRealtime() - 5000;
+ long lastVoltageUpdateTime = mBatteryService.mLastBroadcastVoltageUpdateTime;
+ mBatteryService.update(
+ createHealthInfo(VOLTAGE_MORE_THEN_ONE_PERCENT, CURRENT_BATTERY_TEMP,
+ CURRENT_CHARGE_COUNTER,
+ CURRENT_BATTERY_HEALTH,
+ UPDATED_MAX_CHARGING_CURRENT));
+
+ waitForHandlerToExecute();
+
+ assertTrue(lastVoltageUpdateTime < mBatteryService.mLastBroadcastVoltageUpdateTime);
+ verifyNumberOfTimesBroadcastSent(1);
+ }
+
private HealthInfo createHealthInfo(
int batteryVoltage,
int batteryTemperature,
int batteryChargeCounter,
- int batteryHealth) {
+ int batteryHealth,
+ int maxChargingCurrent) {
HealthInfo h = new HealthInfo();
h.batteryVoltageMillivolts = batteryVoltage;
h.batteryTemperatureTenthsCelsius = batteryTemperature;
@@ -287,7 +339,7 @@
h.batteryHealth = batteryHealth;
h.batteryPresent = true;
h.batteryLevel = 100;
- h.maxChargingCurrentMicroamps = 298125;
+ h.maxChargingCurrentMicroamps = maxChargingCurrent;
h.batteryCurrentAverageMicroamps = -2812;
h.batteryCurrentMicroamps = 298125;
h.maxChargingVoltageMicrovolts = 3000;
@@ -308,7 +360,8 @@
mBatteryService.update(
createHealthInfo(CURRENT_BATTERY_VOLTAGE, CURRENT_BATTERY_TEMP,
CURRENT_CHARGE_COUNTER,
- CURRENT_BATTERY_HEALTH));
+ CURRENT_BATTERY_HEALTH,
+ CURRENT_MAX_CHARGING_CURRENT));
waitForHandlerToExecute();
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index dd7ce21..c831475 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -16,6 +16,7 @@
package com.android.server.job;
+import static android.app.job.Flags.FLAG_HANDLE_ABANDONED_JOBS;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
@@ -82,6 +83,7 @@
import android.os.SystemClock;
import android.os.WorkSource;
import android.os.WorkSource.WorkChain;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
@@ -1056,6 +1058,75 @@
/**
* Confirm that
* {@link JobSchedulerService#getRescheduleJobForFailureLocked(JobStatus, int, int)}
+ * returns a job with the correct delay for abandoned jobs.
+ */
+ @Test
+ @EnableFlags(FLAG_HANDLE_ABANDONED_JOBS)
+ public void testGetRescheduleJobForFailure_abandonedJob() {
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ final long initialBackoffMs = MINUTE_IN_MILLIS;
+ mService.mConstants.SYSTEM_STOP_TO_FAILURE_RATIO = 3;
+
+ JobStatus originalJob = createJobStatus("testGetRescheduleJobForFailure",
+ createJobInfo()
+ .setBackoffCriteria(initialBackoffMs, JobInfo.BACKOFF_POLICY_LINEAR));
+ assertEquals(JobStatus.NO_EARLIEST_RUNTIME, originalJob.getEarliestRunTime());
+ assertEquals(JobStatus.NO_LATEST_RUNTIME, originalJob.getLatestRunTimeElapsed());
+
+ // failure = 1, systemStop = 0, abandoned = 1
+ JobStatus rescheduledJob = mService.getRescheduleJobForFailureLocked(originalJob,
+ JobParameters.STOP_REASON_DEVICE_STATE,
+ JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED);
+ assertEquals(nowElapsed + initialBackoffMs, rescheduledJob.getEarliestRunTime());
+ assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed());
+
+ // failure = 2, systemstop = 0, abandoned = 2
+ rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob,
+ JobParameters.STOP_REASON_DEVICE_STATE,
+ JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED);
+ assertEquals(nowElapsed + (2 * initialBackoffMs), rescheduledJob.getEarliestRunTime());
+ assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed());
+
+ // failure = 3, systemstop = 0, abandoned = 3
+ rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob,
+ JobParameters.STOP_REASON_DEVICE_STATE,
+ JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED);
+ assertEquals(nowElapsed + (3 * initialBackoffMs), rescheduledJob.getEarliestRunTime());
+ assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed());
+
+ // failure = 4, systemstop = 0, abandoned = 4
+ rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob,
+ JobParameters.STOP_REASON_DEVICE_STATE,
+ JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED);
+ assertEquals(
+ nowElapsed + ((long) Math.scalb((float) initialBackoffMs, 3)),
+ rescheduledJob.getEarliestRunTime());
+ assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed());
+
+ // failure = 4, systemstop = 1, abandoned = 4
+ rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob,
+ JobParameters.STOP_REASON_DEVICE_STATE,
+ JobParameters.INTERNAL_STOP_REASON_DEVICE_THERMAL);
+ assertEquals(
+ nowElapsed + ((long) Math.scalb((float) initialBackoffMs, 3)),
+ rescheduledJob.getEarliestRunTime());
+ assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed());
+
+ // failure = 4, systemStop = 4 / SYSTEM_STOP_TO_FAILURE_RATIO, abandoned = 4
+ for (int i = 0; i < mService.mConstants.SYSTEM_STOP_TO_FAILURE_RATIO; ++i) {
+ rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob,
+ JobParameters.STOP_REASON_SYSTEM_PROCESSING,
+ JobParameters.INTERNAL_STOP_REASON_RTC_UPDATED);
+ }
+ assertEquals(
+ nowElapsed + ((long) Math.scalb((float) initialBackoffMs, 4)),
+ rescheduledJob.getEarliestRunTime());
+ assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed());
+ }
+
+ /**
+ * Confirm that
+ * {@link JobSchedulerService#getRescheduleJobForFailureLocked(JobStatus, int, int)}
* returns a job that is correctly marked as demoted by the user.
*/
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
index c6a6865..c64973a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
@@ -706,14 +706,14 @@
// "True" start is nowElapsed + HOUR_IN_MILLIS
nowElapsed + HOUR_IN_MILLIS + adjustmentMs,
nowElapsed + 2 * HOUR_IN_MILLIS,
- 0 /* numFailures */, 0 /* numSystemStops */,
+ 0 /* numFailures */, 0 /* numAbandonedFailures */, 0 /* numSystemStops */,
JobSchedulerService.sSystemClock.millis() /* lastSuccessfulRunTime */,
0, 0);
jsFlex = new JobStatus(jsFlex,
// "True" start is nowElapsed + 2 * HOUR_IN_MILLIS - 20 * MINUTE_IN_MILLIS
nowElapsed + 2 * HOUR_IN_MILLIS - 20 * MINUTE_IN_MILLIS + adjustmentMs,
nowElapsed + 2 * HOUR_IN_MILLIS,
- 0 /* numFailures */, 0 /* numSystemStops */,
+ 0 /* numFailures */, 0 /* numAbandonedFailures */, 0 /* numSystemStops */,
JobSchedulerService.sSystemClock.millis() /* lastSuccessfulRunTime */,
0, 0);
@@ -726,13 +726,13 @@
jsBasic = new JobStatus(jsBasic,
nowElapsed + 30 * MINUTE_IN_MILLIS,
NO_LATEST_RUNTIME,
- 1 /* numFailures */, 1 /* numSystemStops */,
+ 1 /* numFailures */, 0 /* numAbandonedFailures */, 1 /* numSystemStops */,
JobSchedulerService.sSystemClock.millis() /* lastSuccessfulRunTime */,
0, 0);
jsFlex = new JobStatus(jsFlex,
nowElapsed + 30 * MINUTE_IN_MILLIS,
NO_LATEST_RUNTIME,
- 1 /* numFailures */, 1 /* numSystemStops */,
+ 1 /* numFailures */, 0 /* numAbandonedFailures */, 1 /* numSystemStops */,
JobSchedulerService.sSystemClock.millis() /* lastSuccessfulRunTime */,
0, 0);
@@ -847,21 +847,24 @@
JobInfo.Builder jb = createJob(0).setOverrideDeadline(HOUR_IN_MILLIS);
JobStatus js = createJobStatus("time", jb);
js = new JobStatus(
- js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 2, /* numSystemStops */ 0,
+ js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 2,
+ 0 /* numAbandonedFailures */, /* numSystemStops */ 0,
0, FROZEN_TIME, FROZEN_TIME);
assertEquals(mFcConfig.RESCHEDULED_JOB_DEADLINE_MS,
mFlexibilityController.getLifeCycleEndElapsedLocked(js, nowElapsed, 0));
js = new JobStatus(
- js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 2, /* numSystemStops */ 1,
+ js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 2,
+ 0 /* numAbandonedFailures */, /* numSystemStops */ 1,
0, FROZEN_TIME, FROZEN_TIME);
assertEquals(2 * mFcConfig.RESCHEDULED_JOB_DEADLINE_MS,
mFlexibilityController.getLifeCycleEndElapsedLocked(js, nowElapsed, 0));
js = new JobStatus(
- js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 0, /* numSystemStops */ 10,
+ js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 0,
+ 0 /* numAbandonedFailures */, /* numSystemStops */ 10,
0, FROZEN_TIME, FROZEN_TIME);
assertEquals(mFcConfig.MAX_RESCHEDULED_DEADLINE_MS,
mFlexibilityController.getLifeCycleEndElapsedLocked(js, nowElapsed, 0));
@@ -1092,11 +1095,13 @@
JobInfo.Builder jb = createJob(0);
JobStatus js = createJobStatus("time", jb);
js = new JobStatus(
- js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 1, /* numSystemStops */ 0,
+ js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 1,
+ /* numAbandonedFailures */ 0, /* numSystemStops */ 0,
0, FROZEN_TIME, FROZEN_TIME);
assertFalse(js.hasFlexibilityConstraint());
js = new JobStatus(
- js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 0, /* numSystemStops */ 1,
+ js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 0,
+ /* numAbandonedFailures */ 0, /* numSystemStops */ 1,
0, FROZEN_TIME, FROZEN_TIME);
assertFalse(js.hasFlexibilityConstraint());
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
index 2d0f4b6..86101cf 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
@@ -459,35 +459,35 @@
int numFailures = 1;
int numSystemStops = 0;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
assertEquals(JobInfo.PRIORITY_MAX, job.getEffectivePriority());
// 2+ failures, priority should be lowered as much as possible.
numFailures = 2;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority());
numFailures = 5;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority());
numFailures = 8;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority());
// System stops shouldn't factor in the downgrade.
numSystemStops = 10;
numFailures = 0;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
assertEquals(JobInfo.PRIORITY_MAX, job.getEffectivePriority());
// Less than 2 failures, but job is downgraded.
numFailures = 1;
numSystemStops = 0;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
job.addInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER);
assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority());
}
@@ -505,44 +505,44 @@
int numFailures = 1;
int numSystemStops = 0;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority());
// Failures in [2,4), priority should be lowered slightly.
numFailures = 2;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
assertEquals(JobInfo.PRIORITY_DEFAULT, job.getEffectivePriority());
numFailures = 3;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
assertEquals(JobInfo.PRIORITY_DEFAULT, job.getEffectivePriority());
// Failures in [4,6), priority should be lowered more.
numFailures = 4;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority());
numFailures = 5;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority());
// 6+ failures, priority should be lowered as much as possible.
numFailures = 6;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
assertEquals(JobInfo.PRIORITY_MIN, job.getEffectivePriority());
numFailures = 12;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
assertEquals(JobInfo.PRIORITY_MIN, job.getEffectivePriority());
// System stops shouldn't factor in the downgrade.
numSystemStops = 10;
numFailures = 0;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority());
}
@@ -563,32 +563,32 @@
int numFailures = 1;
int numSystemStops = 0;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority());
numFailures = 4;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority());
numFailures = 5;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority());
// 6+ failures, priority should be lowered as much as possible.
numFailures = 6;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
assertEquals(JobInfo.PRIORITY_MIN, job.getEffectivePriority());
numFailures = 12;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
assertEquals(JobInfo.PRIORITY_MIN, job.getEffectivePriority());
// System stops shouldn't factor in the downgrade.
numSystemStops = 10;
numFailures = 0;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority());
}
@@ -606,28 +606,28 @@
int numFailures = 1;
int numSystemStops = 0;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
assertEquals(JobInfo.PRIORITY_MAX, job.getEffectivePriority());
// 2+ failures, priority shouldn't be affected while job is still a UI job
numFailures = 2;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
assertEquals(JobInfo.PRIORITY_MAX, job.getEffectivePriority());
numFailures = 5;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
assertEquals(JobInfo.PRIORITY_MAX, job.getEffectivePriority());
numFailures = 8;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
assertEquals(JobInfo.PRIORITY_MAX, job.getEffectivePriority());
// System stops shouldn't factor in the downgrade.
numSystemStops = 10;
numFailures = 0;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
assertEquals(JobInfo.PRIORITY_MAX, job.getEffectivePriority());
// Job can no long run as user-initiated. Downgrades should be effective.
@@ -641,28 +641,28 @@
numFailures = 1;
numSystemStops = 0;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority());
// 2+ failures, priority should start getting lower
numFailures = 2;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
assertEquals(JobInfo.PRIORITY_DEFAULT, job.getEffectivePriority());
numFailures = 5;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority());
numFailures = 8;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
assertEquals(JobInfo.PRIORITY_MIN, job.getEffectivePriority());
// System stops shouldn't factor in the downgrade.
numSystemStops = 10;
numFailures = 0;
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
- numSystemStops, 0, 0, 0);
+ 0, numSystemStops, 0, 0, 0);
assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority());
}
@@ -772,14 +772,14 @@
assertTrue(job.shouldTreatAsUserInitiatedJob());
JobStatus rescheduledJob = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME,
- 0, 0, 0, 0, 0);
+ 0, 0, 0, 0, 0, 0);
assertTrue(rescheduledJob.shouldTreatAsUserInitiatedJob());
job.addInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER);
assertFalse(job.shouldTreatAsUserInitiatedJob());
rescheduledJob = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME,
- 0, 0, 0, 0, 0);
+ 0, 0, 0, 0, 0, 0);
assertFalse(rescheduledJob.shouldTreatAsUserInitiatedJob());
rescheduledJob.removeInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER);
@@ -797,14 +797,14 @@
assertTrue(job.shouldTreatAsUserInitiatedJob());
JobStatus rescheduledJob = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME,
- 0, 0, 0, 0, 0);
+ 0, 0, 0, 0, 0, 0);
assertTrue(rescheduledJob.shouldTreatAsUserInitiatedJob());
job.addInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ);
assertFalse(job.shouldTreatAsUserInitiatedJob());
rescheduledJob = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME,
- 0, 0, 0, 0, 0);
+ 0, 0, 0, 0, 0, 0);
assertFalse(rescheduledJob.shouldTreatAsUserInitiatedJob());
rescheduledJob.removeInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ);
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/geometry/S2CellIdUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/location/geometry/S2CellIdUtilsTest.java
new file mode 100644
index 0000000..9e43b81
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/geometry/S2CellIdUtilsTest.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.geometry;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.location.geometry.S2CellIdUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class S2CellIdUtilsTest {
+
+ // S2 cell ID of a level-30 cell in Times Square.
+ private static final long TIMES_SQUARE_S2_ID =
+ S2CellIdUtils.fromLatLngDegrees(40.758896, -73.985130);
+
+ // Position of the Eiffel tower (outside of any parent cell from Times Square).
+ private static final double[] EIFFEL_TOWER_LATLNG = {48.858093, 2.294694};
+
+ // Test vector around TIMES_SQUARE_S2_ID: Cell IDs and the centers for levels 0 to 30.
+ // This test vector has been computed using the public S2 library in
+ // external/s2-geometry-library-java
+ private static final CellAndCenter[] TIMES_SQUARE_CELLS = {
+ new CellAndCenter("9", -0.0, -90.0),
+ new CellAndCenter("8c", 21.037511025421814, -67.38013505195958),
+ new CellAndCenter("89", 34.04786296943431, -79.38034472384487),
+ new CellAndCenter("89c", 38.79459515585768, -73.46516214265485),
+ new CellAndCenter("89d", 41.74704688465104, -76.45630866778862),
+ new CellAndCenter("89c4", 40.29416073145462, -74.96763653470293),
+ new CellAndCenter("89c3", 40.827706513259564, -74.21793256064282),
+ new CellAndCenter("89c24", 40.45771021423038, -73.84190634077625),
+ new CellAndCenter("89c25", 40.64307662867646, -74.03001224983848),
+ new CellAndCenter("89c25c", 40.708880489804564, -73.93598211433742),
+ new CellAndCenter("89c259", 40.75509755935301, -73.9830029344863),
+ new CellAndCenter("89c2584", 40.7781887758716, -74.00650903621303),
+ new CellAndCenter("89c2585", 40.766644611813284, -73.99475634561863),
+ new CellAndCenter("89c25854", 40.76087144655763, -73.98887973002674),
+ new CellAndCenter("89c25855", 40.75798459318946, -73.98594135473846),
+ new CellAndCenter("89c25855c", 40.75901097797799, -73.98447215023141),
+ new CellAndCenter("89c25855b", 40.758497791893824, -73.98520675388987),
+ new CellAndCenter("89c25855bc", 40.75875438651343, -73.98483945241185),
+ new CellAndCenter("89c25855b9", 40.758934819692875, -73.98502310323867),
+ new CellAndCenter("89c25855b9c", 40.75887067137071, -73.98511492858621),
+ new CellAndCenter("89c25855b9d", 40.75891577956465, -73.98516084124353),
+ new CellAndCenter("89c25855b9c4", 40.758893225473194, -73.98513788491626),
+ new CellAndCenter("89c25855b9c7", 40.75890124402366, -73.98512640675159),
+ new CellAndCenter("89c25855b9c6c", 40.758897234748815, -73.985132145834),
+ new CellAndCenter("89c25855b9c6d", 40.75889441548664, -73.98512927629281),
+ new CellAndCenter("89c25855b9c6c4", 40.75889582511775, -73.98513071106342),
+ new CellAndCenter("89c25855b9c6c3", 40.758896326277146, -73.98512999367811),
+ new CellAndCenter("89c25855b9c6c3c", 40.75889607569745, -73.98513035237076),
+ new CellAndCenter("89c25855b9c6c39", 40.75889589949357, -73.98513017302443),
+ new CellAndCenter("89c25855b9c6c39c", 40.75889596213849, -73.98513008335128),
+ new CellAndCenter("89c25855b9c6c39f", 40.75889599346095, -73.9851300385147)};
+
+ @Test
+ public void toLatLngDegrees_matchesTestVector() {
+ for (int level = 0; level <= 30; level++) {
+ double[] expected = TIMES_SQUARE_CELLS[level].mCenter;
+ long cellId = S2CellIdUtils.getParent(TIMES_SQUARE_S2_ID, level);
+
+ double[] centerPoint = {0.0, 0.0};
+ S2CellIdUtils.toLatLngDegrees(cellId, centerPoint);
+
+ assertThat(approxEquals(centerPoint[0], expected[0])).isTrue();
+ assertThat(approxEquals(centerPoint[1], expected[1])).isTrue();
+ }
+ }
+
+ private static boolean approxEquals(double a, double b) {
+ return Math.abs(a - b) <= 1e-14;
+ }
+
+ @Test
+ public void containsLatLngDegrees_eachCellContainsItsCenter_works() {
+ for (int level = 0; level <= 30; level++) {
+ long cellId = TIMES_SQUARE_CELLS[level].toCellId();
+ double[] center = TIMES_SQUARE_CELLS[level].mCenter;
+
+ boolean isContained = S2CellIdUtils.containsLatLngDegrees(cellId, center[0], center[1]);
+
+ assertThat(isContained).isTrue();
+ }
+ }
+
+ @Test
+ public void containsLatLngDegrees_testWithOutsidePoint() {
+ for (int level = 0; level <= 30; level++) {
+ long cellId = TIMES_SQUARE_CELLS[level].toCellId();
+
+ assertThat(S2CellIdUtils.containsLatLngDegrees(cellId, EIFFEL_TOWER_LATLNG[0],
+ EIFFEL_TOWER_LATLNG[1])).isFalse();
+ }
+ }
+
+ // A tuple with a S2 cell id, and a S2LatLng representing its center.
+ private static class CellAndCenter {
+ public String mToken;
+ public double[] mCenter;
+
+ CellAndCenter(String token, double latDegrees, double lngDegrees) {
+ this.mToken = token;
+ this.mCenter = new double[] {latDegrees, lngDegrees};
+ }
+
+ // Converts from hex representation to long format.
+ long toCellId() {
+ long value = 0;
+ for (int pos = 0; pos < mToken.length(); pos++) {
+ int digitValue = Character.digit(mToken.charAt(pos), 16);
+ if (digitValue == -1) {
+ return -1;
+ }
+ value = value * 16 + digitValue;
+ }
+ value = value << (4 * (16 - mToken.length())); // remove implicit zeros
+ return value;
+ }
+ }
+}
diff --git a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
index e631cb6..313c01d 100644
--- a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
+++ b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
@@ -18,7 +18,6 @@
import static com.android.server.power.hint.HintManagerService.CLEAN_UP_UID_DELAY_MILLIS;
-import static com.android.server.power.hint.HintManagerService.DEFAULT_HEADROOM_PID;
import static com.google.common.truth.Truth.assertThat;
@@ -53,7 +52,9 @@
import android.hardware.power.ChannelConfig;
import android.hardware.power.ChannelMessage;
import android.hardware.power.CpuHeadroomParams;
+import android.hardware.power.CpuHeadroomResult;
import android.hardware.power.GpuHeadroomParams;
+import android.hardware.power.GpuHeadroomResult;
import android.hardware.power.IPower;
import android.hardware.power.SessionConfig;
import android.hardware.power.SessionTag;
@@ -1251,67 +1252,98 @@
CpuHeadroomParams halParams1 = new CpuHeadroomParams();
halParams1.calculationType = CpuHeadroomParams.CalculationType.MIN;
halParams1.selectionType = CpuHeadroomParams.SelectionType.ALL;
- halParams1.pid = Process.myPid();
+ halParams1.tids = new int[]{Process.myPid()};
CpuHeadroomParamsInternal params2 = new CpuHeadroomParamsInternal();
params2.usesDeviceHeadroom = true;
- params2.calculationType = CpuHeadroomParams.CalculationType.AVERAGE;
+ params2.calculationType = CpuHeadroomParams.CalculationType.MIN;
params2.selectionType = CpuHeadroomParams.SelectionType.PER_CORE;
CpuHeadroomParams halParams2 = new CpuHeadroomParams();
- halParams2.calculationType = CpuHeadroomParams.CalculationType.AVERAGE;
+ halParams2.calculationType = CpuHeadroomParams.CalculationType.MIN;
halParams2.selectionType = CpuHeadroomParams.SelectionType.PER_CORE;
- halParams2.pid = DEFAULT_HEADROOM_PID;
+ halParams2.tids = new int[]{};
- float[] headroom1 = new float[] {0.1f};
- when(mIPowerMock.getCpuHeadroom(eq(halParams1))).thenReturn(headroom1);
- float[] headroom2 = new float[] {0.1f, 0.5f};
- when(mIPowerMock.getCpuHeadroom(eq(halParams2))).thenReturn(headroom2);
+ CpuHeadroomParamsInternal params3 = new CpuHeadroomParamsInternal();
+ params3.calculationType = CpuHeadroomParams.CalculationType.AVERAGE;
+ params3.selectionType = CpuHeadroomParams.SelectionType.ALL;
+ CpuHeadroomParams halParams3 = new CpuHeadroomParams();
+ halParams3.calculationType = CpuHeadroomParams.CalculationType.AVERAGE;
+ halParams3.selectionType = CpuHeadroomParams.SelectionType.ALL;
+ halParams3.tids = new int[]{Process.myPid()};
+
+ // this params should not be cached as the window is not default
+ CpuHeadroomParamsInternal params4 = new CpuHeadroomParamsInternal();
+ params4.calculationWindowMillis = 123;
+ CpuHeadroomParams halParams4 = new CpuHeadroomParams();
+ halParams4.calculationType = CpuHeadroomParams.CalculationType.MIN;
+ halParams4.selectionType = CpuHeadroomParams.SelectionType.ALL;
+ halParams4.calculationWindowMillis = 123;
+ halParams4.tids = new int[]{Process.myPid()};
+
+ float headroom1 = 0.1f;
+ CpuHeadroomResult halRet1 = CpuHeadroomResult.globalHeadroom(headroom1);
+ when(mIPowerMock.getCpuHeadroom(eq(halParams1))).thenReturn(halRet1);
+ float[] headroom2 = new float[] {0.2f, 0.2f};
+ CpuHeadroomResult halRet2 = CpuHeadroomResult.perCoreHeadroom(headroom2);
+ when(mIPowerMock.getCpuHeadroom(eq(halParams2))).thenReturn(halRet2);
+ float headroom3 = 0.3f;
+ CpuHeadroomResult halRet3 = CpuHeadroomResult.globalHeadroom(headroom3);
+ when(mIPowerMock.getCpuHeadroom(eq(halParams3))).thenReturn(halRet3);
+ float headroom4 = 0.4f;
+ CpuHeadroomResult halRet4 = CpuHeadroomResult.globalHeadroom(headroom4);
+ when(mIPowerMock.getCpuHeadroom(eq(halParams4))).thenReturn(halRet4);
HintManagerService service = createService();
clearInvocations(mIPowerMock);
service.getBinderServiceInstance().getCpuHeadroomMinIntervalMillis();
verify(mIPowerMock, times(0)).getCpuHeadroomMinIntervalMillis();
- service.getBinderServiceInstance().getCpuHeadroom(params1);
+ assertEquals(halRet1, service.getBinderServiceInstance().getCpuHeadroom(params1));
verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams1));
- service.getBinderServiceInstance().getCpuHeadroom(params2);
+ assertEquals(halRet2, service.getBinderServiceInstance().getCpuHeadroom(params2));
verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams2));
+ assertEquals(halRet3, service.getBinderServiceInstance().getCpuHeadroom(params3));
+ verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams3));
+ assertEquals(halRet4, service.getBinderServiceInstance().getCpuHeadroom(params4));
+ verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams4));
// verify cache is working
clearInvocations(mIPowerMock);
- assertArrayEquals(headroom1, service.getBinderServiceInstance().getCpuHeadroom(params1),
- 0.01f);
- assertArrayEquals(headroom2, service.getBinderServiceInstance().getCpuHeadroom(params2),
- 0.01f);
- verify(mIPowerMock, times(0)).getCpuHeadroom(any());
+ assertEquals(halRet1, service.getBinderServiceInstance().getCpuHeadroom(params1));
+ assertEquals(halRet2, service.getBinderServiceInstance().getCpuHeadroom(params2));
+ assertEquals(halRet3, service.getBinderServiceInstance().getCpuHeadroom(params3));
+ assertEquals(halRet4, service.getBinderServiceInstance().getCpuHeadroom(params4));
+ verify(mIPowerMock, times(1)).getCpuHeadroom(any());
+ verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams1));
+ verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams2));
+ verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams3));
+ verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams4));
// after 1 more second it should be served with cache still
Thread.sleep(1000);
clearInvocations(mIPowerMock);
- assertArrayEquals(headroom1, service.getBinderServiceInstance().getCpuHeadroom(params1),
- 0.01f);
- assertArrayEquals(headroom2, service.getBinderServiceInstance().getCpuHeadroom(params2),
- 0.01f);
- verify(mIPowerMock, times(0)).getCpuHeadroom(any());
-
- // after 1.5 more second it should be served with cache still as timer reset
- Thread.sleep(1500);
- clearInvocations(mIPowerMock);
- assertArrayEquals(headroom1, service.getBinderServiceInstance().getCpuHeadroom(params1),
- 0.01f);
- assertArrayEquals(headroom2, service.getBinderServiceInstance().getCpuHeadroom(params2),
- 0.01f);
- verify(mIPowerMock, times(0)).getCpuHeadroom(any());
+ assertEquals(halRet1, service.getBinderServiceInstance().getCpuHeadroom(params1));
+ assertEquals(halRet2, service.getBinderServiceInstance().getCpuHeadroom(params2));
+ assertEquals(halRet3, service.getBinderServiceInstance().getCpuHeadroom(params3));
+ assertEquals(halRet4, service.getBinderServiceInstance().getCpuHeadroom(params4));
+ verify(mIPowerMock, times(1)).getCpuHeadroom(any());
+ verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams1));
+ verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams2));
+ verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams3));
+ verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams4));
// after 2+ seconds it should be served from HAL as it exceeds 2000 millis interval
- Thread.sleep(2100);
+ Thread.sleep(1100);
clearInvocations(mIPowerMock);
- assertArrayEquals(headroom1, service.getBinderServiceInstance().getCpuHeadroom(params1),
- 0.01f);
- assertArrayEquals(headroom2, service.getBinderServiceInstance().getCpuHeadroom(params2),
- 0.01f);
+ assertEquals(halRet1, service.getBinderServiceInstance().getCpuHeadroom(params1));
+ assertEquals(halRet2, service.getBinderServiceInstance().getCpuHeadroom(params2));
+ assertEquals(halRet3, service.getBinderServiceInstance().getCpuHeadroom(params3));
+ assertEquals(halRet4, service.getBinderServiceInstance().getCpuHeadroom(params4));
+ verify(mIPowerMock, times(4)).getCpuHeadroom(any());
verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams1));
verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams2));
+ verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams3));
+ verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams4));
}
@Test
@@ -1322,59 +1354,52 @@
halParams1.calculationType = GpuHeadroomParams.CalculationType.MIN;
GpuHeadroomParamsInternal params2 = new GpuHeadroomParamsInternal();
+ params2.calculationType = GpuHeadroomParams.CalculationType.AVERAGE;
+ params2.calculationWindowMillis = 123;
GpuHeadroomParams halParams2 = new GpuHeadroomParams();
- params2.calculationType =
- halParams2.calculationType = GpuHeadroomParams.CalculationType.AVERAGE;
+ halParams2.calculationType = GpuHeadroomParams.CalculationType.AVERAGE;
+ halParams2.calculationWindowMillis = 123;
float headroom1 = 0.1f;
- when(mIPowerMock.getGpuHeadroom(eq(halParams1))).thenReturn(headroom1);
+ GpuHeadroomResult halRet1 = GpuHeadroomResult.globalHeadroom(headroom1);
+ when(mIPowerMock.getGpuHeadroom(eq(halParams1))).thenReturn(halRet1);
float headroom2 = 0.2f;
- when(mIPowerMock.getGpuHeadroom(eq(halParams2))).thenReturn(headroom2);
+ GpuHeadroomResult halRet2 = GpuHeadroomResult.globalHeadroom(headroom2);
+ when(mIPowerMock.getGpuHeadroom(eq(halParams2))).thenReturn(halRet2);
HintManagerService service = createService();
clearInvocations(mIPowerMock);
service.getBinderServiceInstance().getGpuHeadroomMinIntervalMillis();
verify(mIPowerMock, times(0)).getGpuHeadroomMinIntervalMillis();
- assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1),
- 0.01f);
- assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2),
- 0.01f);
+ assertEquals(halRet1, service.getBinderServiceInstance().getGpuHeadroom(params1));
+ assertEquals(halRet2, service.getBinderServiceInstance().getGpuHeadroom(params2));
+ verify(mIPowerMock, times(2)).getGpuHeadroom(any());
verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams1));
verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2));
// verify cache is working
clearInvocations(mIPowerMock);
- assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1),
- 0.01f);
- assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2),
- 0.01f);
- verify(mIPowerMock, times(0)).getGpuHeadroom(any());
+ assertEquals(halRet1, service.getBinderServiceInstance().getGpuHeadroom(params1));
+ assertEquals(halRet2, service.getBinderServiceInstance().getGpuHeadroom(params2));
+ verify(mIPowerMock, times(1)).getGpuHeadroom(any());
+ verify(mIPowerMock, times(0)).getGpuHeadroom(eq(halParams1));
+ verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2));
// after 1 more second it should be served with cache still
Thread.sleep(1000);
clearInvocations(mIPowerMock);
- assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1),
- 0.01f);
- assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2),
- 0.01f);
- verify(mIPowerMock, times(0)).getGpuHeadroom(any());
-
- // after 1.5 more second it should be served with cache still as timer reset
- Thread.sleep(1500);
- clearInvocations(mIPowerMock);
- assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1),
- 0.01f);
- assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2),
- 0.01f);
- verify(mIPowerMock, times(0)).getGpuHeadroom(any());
+ assertEquals(halRet1, service.getBinderServiceInstance().getGpuHeadroom(params1));
+ assertEquals(halRet2, service.getBinderServiceInstance().getGpuHeadroom(params2));
+ verify(mIPowerMock, times(1)).getGpuHeadroom(any());
+ verify(mIPowerMock, times(0)).getGpuHeadroom(eq(halParams1));
+ verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2));
// after 2+ seconds it should be served from HAL as it exceeds 2000 millis interval
- Thread.sleep(2100);
+ Thread.sleep(1100);
clearInvocations(mIPowerMock);
- assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1),
- 0.01f);
- assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2),
- 0.01f);
+ assertEquals(halRet1, service.getBinderServiceInstance().getGpuHeadroom(params1));
+ assertEquals(halRet2, service.getBinderServiceInstance().getGpuHeadroom(params2));
+ verify(mIPowerMock, times(2)).getGpuHeadroom(any());
verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams1));
verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2));
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/AmbientDisplayPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/AmbientDisplayPowerCalculatorTest.java
index c0be865..4b91d84 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/AmbientDisplayPowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/AmbientDisplayPowerCalculatorTest.java
@@ -79,8 +79,6 @@
// 100,000,00 uC / 1000 (micro-/milli-) / 360 (seconds/hour) = 27.777778 mAh
assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY))
.isWithin(PRECISION).of(27.777778);
- assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
}
@Test
@@ -140,8 +138,6 @@
// (seconds/hour) = 27.777778 mAh
assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY))
.isWithin(PRECISION).of(83.33333);
- assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
}
@Test
@@ -163,8 +159,6 @@
.isEqualTo(90 * MINUTE_IN_MS);
assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY))
.isWithin(PRECISION).of(15.0);
- assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
}
@Test
@@ -195,7 +189,5 @@
.isEqualTo(120 * MINUTE_IN_MS);
assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY))
.isWithin(PRECISION).of(35.0);
- assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
}
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java
index 5d50e6c..9da89fc 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java
@@ -311,10 +311,6 @@
bus.getAggregateBatteryConsumer(AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE),
proto.deviceBatteryConsumer);
- for (int i = 0; i < BatteryConsumer.POWER_COMPONENT_COUNT; i++) {
- assertPowerComponentModel(i, abc.getPowerModel(i), proto);
- }
-
// Now for the UidBatteryConsumers.
final List<android.os.UidBatteryConsumer> uidConsumers = bus.getUidBatteryConsumers();
uidConsumers.sort((a, b) -> a.getUid() - b.getUid());
@@ -450,34 +446,6 @@
}
}
- /**
- * Validates the PowerComponentModel object that matches powerComponent.
- */
- private void assertPowerComponentModel(int powerComponent,
- @BatteryConsumer.PowerModel int powerModel, BatteryUsageStatsAtomsProto proto) {
- boolean found = false;
- for (BatteryUsageStatsAtomsProto.PowerComponentModel powerComponentModel :
- proto.componentModels) {
- if (powerComponentModel.component == powerComponent) {
- if (found) {
- fail("Power component " + BatteryConsumer.powerComponentIdToString(
- powerComponent) + " found multiple times in the proto");
- }
- found = true;
- final int expectedPowerModel = BatteryConsumer.powerModelToProtoEnum(powerModel);
- assertEquals(expectedPowerModel, powerComponentModel.powerModel);
- }
- }
- if (!found) {
- final int model = BatteryConsumer.powerModelToProtoEnum(powerModel);
- assertEquals(
- "Power component " + BatteryConsumer.powerComponentIdToString(powerComponent)
- + " was not found in the proto but has a defined power model.",
- BatteryUsageStatsAtomsProto.PowerComponentModel.UNDEFINED,
- model);
- }
- }
-
/** Converts charge from milliamp hours (mAh) to decicoulombs (dC). */
private long convertMahToDc(double powerMah) {
return (long) (powerMah * 36 + 0.5);
@@ -486,7 +454,6 @@
private BatteryUsageStats buildBatteryUsageStats() {
final BatteryUsageStats.Builder builder =
new BatteryUsageStats.Builder(new String[]{"CustomConsumer1", "CustomConsumer2"},
- /* includePowerModels */ true,
/* includeProcessStats */ true,
/* includeScreenStateData */ false,
/* includePowerStateData */ false,
@@ -524,13 +491,13 @@
final BatteryConsumer.Key keyCached = uidBuilder.getKey(BatteryConsumer.POWER_COMPONENT_CPU,
BatteryConsumer.PROCESS_STATE_CACHED);
- uidBuilder.addConsumedPower(keyFg, 9100, BatteryConsumer.POWER_MODEL_POWER_PROFILE)
+ uidBuilder.addConsumedPower(keyFg, 9100)
.addUsageDurationMillis(keyFg, 8100)
- .addConsumedPower(keyBg, 9200, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION)
+ .addConsumedPower(keyBg, (double) 9200)
.addUsageDurationMillis(keyBg, 8200)
- .addConsumedPower(keyFgs, 9300, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION)
+ .addConsumedPower(keyFgs, (double) 9300)
.addUsageDurationMillis(keyFgs, 8300)
- .addConsumedPower(keyCached, 9400, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION)
+ .addConsumedPower(keyCached, (double) 9400)
.addUsageDurationMillis(keyCached, 8400);
final BatteryConsumer.Key keyCustomFg = uidBuilder.getKey(
@@ -539,10 +506,8 @@
final BatteryConsumer.Key keyCustomBg = uidBuilder.getKey(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID,
BatteryConsumer.PROCESS_STATE_BACKGROUND);
- uidBuilder.addConsumedPower(
- keyCustomFg, 100, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
- uidBuilder.addConsumedPower(
- keyCustomBg, 350, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
+ uidBuilder.addConsumedPower(keyCustomFg, 100);
+ uidBuilder.addConsumedPower(keyCustomBg, 350);
builder.getOrCreateUidBatteryConsumerBuilder(UID_1)
.setPackageWithHighestDrain("myPackage1")
@@ -557,14 +522,11 @@
builder.getAggregateBatteryConsumerBuilder(AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
.addConsumedPower(30000)
.addConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU, 20100,
- BatteryConsumer.POWER_MODEL_POWER_PROFILE)
+ BatteryConsumer.POWER_COMPONENT_CPU, 20100)
.addConsumedPower(
- BatteryConsumer.POWER_COMPONENT_AUDIO, 0,
- BatteryConsumer.POWER_MODEL_POWER_PROFILE) // Empty
+ BatteryConsumer.POWER_COMPONENT_AUDIO, 0) // Empty
.addConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CAMERA, 20150,
- BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION)
+ BatteryConsumer.POWER_COMPONENT_CAMERA, 20150)
.addConsumedPower(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20200)
.addUsageDurationMillis(
@@ -576,8 +538,7 @@
builder.getAggregateBatteryConsumerBuilder(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
.addConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU, 10100,
- BatteryConsumer.POWER_MODEL_POWER_PROFILE)
+ BatteryConsumer.POWER_COMPONENT_CPU, 10100)
.addConsumedPower(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200);
@@ -587,7 +548,7 @@
@Test
public void testLargeAtomTruncated() throws Exception {
final BatteryUsageStats.Builder builder =
- new BatteryUsageStats.Builder(new String[0], true, false, false, false, 0);
+ new BatteryUsageStats.Builder(new String[0], false, false, false, 0);
// If not truncated, this BatteryUsageStats object would generate a proto buffer
// significantly larger than 50 Kb
for (int i = 0; i < 3000; i++) {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
index 383616e..a3c7ece 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
@@ -423,8 +423,6 @@
mBatteryUsageStats = null;
}
final String[] customPowerComponentNames = mBatteryStats.getCustomEnergyConsumerNames();
- final boolean includePowerModels = (query.getFlags()
- & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0;
final boolean includeProcessStateData = (query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA) != 0;
final boolean includeScreenStateData = (query.getFlags()
@@ -433,7 +431,7 @@
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_STATE) != 0;
final double minConsumedPowerThreshold = query.getMinConsumedPowerThreshold();
BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
- customPowerComponentNames, includePowerModels, includeProcessStateData,
+ customPowerComponentNames, includeProcessStateData,
includeScreenStateData, includePowerStateData, minConsumedPowerThreshold);
SparseArray<? extends BatteryStats.Uid> uidStats = mBatteryStats.getUidStats();
for (int i = 0; i < uidStats.size(); i++) {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
index 9771da5..dd50431 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
@@ -17,8 +17,6 @@
package com.android.server.power.stats;
import static android.os.BatteryConsumer.POWER_COMPONENT_ANY;
-import static android.os.BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION;
-import static android.os.BatteryConsumer.POWER_MODEL_UNDEFINED;
import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
@@ -237,7 +235,7 @@
final BatteryUsageStats stats1 = buildBatteryUsageStats1(false).build();
final BatteryUsageStats stats2 = buildBatteryUsageStats2(new String[]{"FOO"}, true).build();
final BatteryUsageStats sum =
- new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true, true, true, 0)
+ new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true, true, 0)
.add(stats1)
.add(stats2)
.build();
@@ -248,15 +246,13 @@
for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) {
if (uidBatteryConsumer.getUid() == APP_UID1) {
assertUidBatteryConsumer(uidBatteryConsumer, 1200 + 924, null,
- 5321, 6900, 532, 423, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 400 + 345,
- POWER_MODEL_UNDEFINED,
+ 5321, 6900, 532, 423, 400 + 345,
500 + 456, 1167, 1478,
true, 3554, 4732, 3998, 444, 3554, 15542, 3776, 17762, 3998, 19982,
444, 1110);
} else if (uidBatteryConsumer.getUid() == APP_UID2) {
assertUidBatteryConsumer(uidBatteryConsumer, 1332, "bar",
- 1111, 2220, 2, 333, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 444,
- BatteryConsumer.POWER_MODEL_POWER_PROFILE,
+ 1111, 2220, 2, 333, 444,
555, 666, 777,
true, 1777, 2443, 1999, 321, 1777, 7771, 1888, 8881, 1999, 9991,
321, 654);
@@ -280,7 +276,7 @@
@Test
public void testAdd_customComponentMismatch() throws Exception {
final BatteryUsageStats.Builder builder =
- new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true, true, true, 0);
+ new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true, true, 0);
final BatteryUsageStats stats = buildBatteryUsageStats2(new String[]{"BAR"}, false).build();
assertThrows(IllegalArgumentException.class, () -> builder.add(stats));
@@ -291,7 +287,7 @@
@Test
public void testAdd_processStateDataMismatch() throws Exception {
final BatteryUsageStats.Builder builder =
- new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true, true, true, 0);
+ new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true, true, 0);
final BatteryUsageStats stats = buildBatteryUsageStats2(new String[]{"FOO"}, false).build();
assertThrows(IllegalArgumentException.class, () -> builder.add(stats));
@@ -328,7 +324,7 @@
final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks);
final BatteryUsageStats.Builder builder =
- new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true,
+ new BatteryUsageStats.Builder(new String[]{"FOO"}, true,
includeScreenState, includePowerState, 0)
.setBatteryCapacity(4000)
.setDischargePercentage(20)
@@ -339,8 +335,8 @@
addUidBatteryConsumer(builder, batteryStats, APP_UID1, "foo",
1000, 1500, 500,
- 300, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 400,
- BatteryConsumer.POWER_MODEL_POWER_PROFILE, 500, 600, 800,
+ 300, 400,
+ 500, 600, 800,
1777, 7771, 1888, 8881, 1999, 9991, 123, 456);
addAggregateBatteryConsumer(builder,
@@ -373,7 +369,7 @@
final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks);
final BatteryUsageStats.Builder builder =
- new BatteryUsageStats.Builder(customPowerComponentNames, true,
+ new BatteryUsageStats.Builder(customPowerComponentNames,
includeProcessStateData, true, true, 0);
builder.setDischargePercentage(30)
.setDischargedPowerRange(1234, 2345)
@@ -382,14 +378,14 @@
addUidBatteryConsumer(builder, batteryStats, APP_UID1, null,
4321, 5400, 32,
- 123, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 345, POWER_MODEL_ENERGY_CONSUMPTION,
+ 123, 345,
456, 567, 678,
1777, 7771, 1888, 8881, 1999, 9991, 321, 654);
addUidBatteryConsumer(builder, batteryStats, APP_UID2, "bar",
1111, 2220, 2,
- 333, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 444,
- BatteryConsumer.POWER_MODEL_POWER_PROFILE, 555, 666, 777,
+ 333, 444,
+ 555, 666, 777,
1777, 7771, 1888, 8881, 1999, 9991, 321, 654);
addAggregateBatteryConsumer(builder,
@@ -409,7 +405,7 @@
MockBatteryStatsImpl batteryStats, int uid, String packageWithHighestDrain,
int timeInProcessStateForeground, int timeInProcessStateBackground,
int timeInProcessStateForegroundService, double screenPower,
- int screenPowerModel, double cpuPower, int cpuPowerModel, double customComponentPower,
+ double cpuPower, double customComponentPower,
int cpuDuration, int customComponentDuration, double cpuPowerForeground,
int cpuDurationForeground, double cpuPowerBackground, int cpuDurationBackground,
double cpuPowerFgs, int cpuDurationFgs, double cpuPowerCached, long cpuDurationCached) {
@@ -423,9 +419,9 @@
.setTimeInProcessStateMs(PROCESS_STATE_FOREGROUND_SERVICE,
timeInProcessStateForegroundService)
.addConsumedPower(
- BatteryConsumer.POWER_COMPONENT_SCREEN, screenPower, screenPowerModel)
+ BatteryConsumer.POWER_COMPONENT_SCREEN, screenPower)
.addConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU, cpuPower, cpuPowerModel)
+ BatteryConsumer.POWER_COMPONENT_CPU, cpuPower)
.addConsumedPower(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, customComponentPower)
.addUsageDurationMillis(
@@ -460,21 +456,15 @@
: uidBuilder.getKey(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID,
BatteryConsumer.PROCESS_STATE_BACKGROUND);
- uidBuilder
- .addConsumedPower(cpuFgKey, cpuPowerForeground,
- BatteryConsumer.POWER_MODEL_POWER_PROFILE)
+ uidBuilder.addConsumedPower(cpuFgKey, cpuPowerForeground)
.addUsageDurationMillis(cpuFgKey, cpuDurationForeground)
- .addConsumedPower(cpuBgKey, cpuPowerBackground,
- BatteryConsumer.POWER_MODEL_POWER_PROFILE)
+ .addConsumedPower(cpuBgKey, cpuPowerBackground)
.addUsageDurationMillis(cpuBgKey, cpuDurationBackground)
- .addConsumedPower(cpuFgsKey, cpuPowerFgs,
- BatteryConsumer.POWER_MODEL_POWER_PROFILE)
+ .addConsumedPower(cpuFgsKey, cpuPowerFgs)
.addUsageDurationMillis(cpuFgsKey, cpuDurationFgs)
- .addConsumedPower(cachedKey, cpuPowerCached,
- BatteryConsumer.POWER_MODEL_POWER_PROFILE)
+ .addConsumedPower(cachedKey, cpuPowerCached)
.addUsageDurationMillis(cachedKey, cpuDurationCached)
- .addConsumedPower(customBgKey, customComponentPower,
- BatteryConsumer.POWER_MODEL_UNDEFINED)
+ .addConsumedPower(customBgKey, customComponentPower)
.addUsageDurationMillis(customBgKey, customComponentDuration);
}
}
@@ -518,18 +508,13 @@
BatteryConsumer.PROCESS_STATE_UNSPECIFIED,
BatteryConsumer.SCREEN_STATE_OTHER,
BatteryConsumer.POWER_STATE_OTHER);
- aggBuilder
- .addConsumedPower(cpuBatScrOn, cpuPowerBatScrOn,
- BatteryConsumer.POWER_MODEL_POWER_PROFILE)
+ aggBuilder.addConsumedPower(cpuBatScrOn, cpuPowerBatScrOn)
.addUsageDurationMillis(cpuBatScrOn, cpuDurationBatScrOn)
- .addConsumedPower(cpuBatScrOff, cpuPowerBatScrOff,
- BatteryConsumer.POWER_MODEL_POWER_PROFILE)
+ .addConsumedPower(cpuBatScrOff, cpuPowerBatScrOff)
.addUsageDurationMillis(cpuBatScrOff, cpuDurationBatScrOff)
- .addConsumedPower(cpuChgScrOn, cpuPowerChgScrOn,
- BatteryConsumer.POWER_MODEL_POWER_PROFILE)
+ .addConsumedPower(cpuChgScrOn, cpuPowerChgScrOn)
.addUsageDurationMillis(cpuChgScrOn, cpuDurationChgScrOn)
- .addConsumedPower(cpuChgScrOff, cpuPowerChgScrOff,
- BatteryConsumer.POWER_MODEL_POWER_PROFILE)
+ .addConsumedPower(cpuChgScrOff, cpuPowerChgScrOff)
.addUsageDurationMillis(cpuChgScrOff, cpuDurationChgScrOff);
}
}
@@ -544,8 +529,7 @@
for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) {
if (uidBatteryConsumer.getUid() == APP_UID1) {
assertUidBatteryConsumer(uidBatteryConsumer, 1200, "foo",
- 1000, 1500, 500, 300, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 400,
- BatteryConsumer.POWER_MODEL_POWER_PROFILE,
+ 1000, 1500, 500, 300, 400,
500, 600, 800,
true, 1777, 2388, 1999, 123, 1777, 7771, 1888, 8881, 1999, 9991, 123, 456);
} else {
@@ -596,8 +580,8 @@
private void assertUidBatteryConsumer(UidBatteryConsumer uidBatteryConsumer,
double consumedPower, String packageWithHighestDrain, int timeInProcessStateForeground,
int timeInProcessStateBackground, int timeInProcessStateForegroundService,
- int screenPower, int screenPowerModel, double cpuPower,
- int cpuPowerModel, double customComponentPower, int cpuDuration,
+ int screenPower, double cpuPower,
+ double customComponentPower, int cpuDuration,
int customComponentDuration, boolean processStateDataIncluded,
double totalPowerForeground, double totalPowerBackground, double totalPowerFgs,
double totalPowerCached, double cpuPowerForeground, int cpuDurationForeground,
@@ -620,12 +604,8 @@
PROCESS_STATE_FOREGROUND_SERVICE)).isEqualTo(timeInProcessStateForegroundService);
assertThat(uidBatteryConsumer.getConsumedPower(
BatteryConsumer.POWER_COMPONENT_SCREEN)).isEqualTo(screenPower);
- assertThat(uidBatteryConsumer.getPowerModel(
- BatteryConsumer.POWER_COMPONENT_SCREEN)).isEqualTo(screenPowerModel);
assertThat(uidBatteryConsumer.getConsumedPower(
BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuPower);
- assertThat(uidBatteryConsumer.getPowerModel(
- BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuPowerModel);
assertThat(uidBatteryConsumer.getConsumedPower(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(customComponentPower);
assertThat(uidBatteryConsumer.getUsageDurationMillis(
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerCalculatorTest.java
index fe6424f..c9cb0df 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerCalculatorTest.java
@@ -82,16 +82,16 @@
assertBluetoothPowerAndDuration(
mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
- 0.06944, 3000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ 0.06944, 3000);
assertBluetoothPowerAndDuration(
mStatsRule.getUidBatteryConsumer(APP_UID),
- 0.19444, 9000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ 0.19444, 9000);
assertBluetoothPowerAndDuration(
mStatsRule.getDeviceBatteryConsumer(),
- 0.26388, 12000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ 0.26388, 12000);
assertBluetoothPowerAndDuration(
mStatsRule.getAppsBatteryConsumer(),
- 0.26388, 12000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ 0.26388, 12000);
}
@Test
@@ -144,8 +144,6 @@
.isEqualTo(6166);
assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH))
.isWithin(PRECISION).of(0.1226666);
- assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_BLUETOOTH))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
final BatteryConsumer.Key foreground = uidConsumer.getKey(
BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
@@ -178,16 +176,16 @@
assertBluetoothPowerAndDuration(
mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
- 0.08216, 3583, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ 0.08216, 3583);
assertBluetoothPowerAndDuration(
mStatsRule.getUidBatteryConsumer(APP_UID),
- 0.18169, 8416, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ 0.18169, 8416);
assertBluetoothPowerAndDuration(
mStatsRule.getDeviceBatteryConsumer(),
- 0.30030, 12000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ 0.30030, 12000);
assertBluetoothPowerAndDuration(
mStatsRule.getAppsBatteryConsumer(),
- 0.26386, 11999, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ 0.26386, 11999);
}
@Test
@@ -202,16 +200,16 @@
assertBluetoothPowerAndDuration(
mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
- 0.10378, 3583, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
+ 0.10378, 3583);
assertBluetoothPowerAndDuration(
mStatsRule.getUidBatteryConsumer(APP_UID),
- 0.22950, 8416, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
+ 0.22950, 8416);
assertBluetoothPowerAndDuration(
mStatsRule.getDeviceBatteryConsumer(),
- 0.33333, 12000, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
+ 0.33333, 12000);
assertBluetoothPowerAndDuration(
mStatsRule.getAppsBatteryConsumer(),
- 0.33329, 11999, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
+ 0.33329, 11999);
}
@Test
@@ -264,8 +262,6 @@
.isEqualTo(6166);
assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH))
.isWithin(PRECISION).of(0.8220561);
- assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_BLUETOOTH))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
final BatteryConsumer.Key foreground = uidConsumer.getKey(
BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
@@ -299,16 +295,16 @@
assertBluetoothPowerAndDuration(
mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
- 0.08216, 3583, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ 0.08216, 3583);
assertBluetoothPowerAndDuration(
mStatsRule.getUidBatteryConsumer(APP_UID),
- 0.18169, 8416, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ 0.18169, 8416);
assertBluetoothPowerAndDuration(
mStatsRule.getDeviceBatteryConsumer(),
- 0.26388, 12000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ 0.26388, 12000);
assertBluetoothPowerAndDuration(
mStatsRule.getAppsBatteryConsumer(),
- 0.26386, 11999, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ 0.26386, 11999);
}
private void setupBluetoothEnergyInfo(long reportedEnergyUc, long consumedEnergyUc) {
@@ -326,14 +322,12 @@
}
private void assertBluetoothPowerAndDuration(@Nullable BatteryConsumer batteryConsumer,
- double powerMah, int durationMs, @BatteryConsumer.PowerModel int powerModel) {
+ double powerMah, int durationMs) {
assertThat(batteryConsumer).isNotNull();
double consumedPower = batteryConsumer.getConsumedPower(
BatteryConsumer.POWER_COMPONENT_BLUETOOTH);
assertThat(consumedPower).isWithin(PRECISION).of(powerMah);
- assertThat(batteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_BLUETOOTH))
- .isEqualTo(powerModel);
long usageDurationMillis = batteryConsumer.getUsageDurationMillis(
BatteryConsumer.POWER_COMPONENT_BLUETOOTH);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerCalculatorTest.java
index 7225f2d..4cd3857 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerCalculatorTest.java
@@ -70,16 +70,12 @@
.isEqualTo(1000);
assertThat(app1Consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA))
.isWithin(PRECISION).of(0.1);
- assertThat(app1Consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
UidBatteryConsumer app2Consumer = mStatsRule.getUidBatteryConsumer(APP2_UID);
assertThat(app2Consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CAMERA))
.isEqualTo(2000);
assertThat(app2Consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA))
.isWithin(PRECISION).of(0.2);
- assertThat(app2Consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
final BatteryConsumer deviceBatteryConsumer = mStatsRule.getDeviceBatteryConsumer();
assertThat(deviceBatteryConsumer
@@ -87,8 +83,6 @@
.isEqualTo(3000);
assertThat(deviceBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA))
.isWithin(PRECISION).of(0.3);
- assertThat(deviceBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
final BatteryConsumer appsBatteryConsumer = mStatsRule.getAppsBatteryConsumer();
assertThat(appsBatteryConsumer
@@ -96,8 +90,6 @@
.isEqualTo(3000);
assertThat(appsBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA))
.isWithin(PRECISION).of(0.3);
- assertThat(appsBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
}
@Test
@@ -122,16 +114,12 @@
.isEqualTo(1000);
assertThat(app1Consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA))
.isWithin(PRECISION).of(0.2);
- assertThat(app1Consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
UidBatteryConsumer app2Consumer = mStatsRule.getUidBatteryConsumer(APP2_UID);
assertThat(app2Consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CAMERA))
.isEqualTo(2000);
assertThat(app2Consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA))
.isWithin(PRECISION).of(0.3);
- assertThat(app2Consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
final BatteryConsumer deviceBatteryConsumer = mStatsRule.getDeviceBatteryConsumer();
assertThat(deviceBatteryConsumer
@@ -139,8 +127,6 @@
.isEqualTo(3000);
assertThat(deviceBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA))
.isWithin(PRECISION).of(0.5);
- assertThat(deviceBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
final BatteryConsumer appsBatteryConsumer = mStatsRule.getAppsBatteryConsumer();
assertThat(appsBatteryConsumer
@@ -148,7 +134,5 @@
.isEqualTo(3000);
assertThat(appsBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA))
.isWithin(PRECISION).of(0.5);
- assertThat(appsBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
}
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java
index 4cea728..527db67 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java
@@ -192,8 +192,6 @@
.isEqualTo(3333);
assertThat(uidConsumer1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU))
.isWithin(PRECISION).of(1.031677);
- assertThat(uidConsumer1.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
assertThat(uidConsumer1.getPackageWithHighestDrain()).isEqualTo("bar");
UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
@@ -201,21 +199,15 @@
.isEqualTo(7777);
assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU))
.isWithin(PRECISION).of(2.489544);
- assertThat(uidConsumer2.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
assertThat(uidConsumer2.getPackageWithHighestDrain()).isNull();
final BatteryConsumer deviceBatteryConsumer = mStatsRule.getDeviceBatteryConsumer();
assertThat(deviceBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU))
.isWithin(PRECISION).of(3.52122);
- assertThat(deviceBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
final BatteryConsumer appsBatteryConsumer = mStatsRule.getAppsBatteryConsumer();
assertThat(appsBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU))
.isWithin(PRECISION).of(3.52122);
- assertThat(appsBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
}
@Test
@@ -264,8 +256,6 @@
.isEqualTo(3333);
assertThat(uidConsumer1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU))
.isWithin(PRECISION).of(3.18877);
- assertThat(uidConsumer1.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
assertThat(uidConsumer1.getPackageWithHighestDrain()).isEqualTo("bar");
UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
@@ -273,21 +263,15 @@
.isEqualTo(7777);
assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU))
.isWithin(PRECISION).of(7.44072);
- assertThat(uidConsumer2.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
assertThat(uidConsumer2.getPackageWithHighestDrain()).isNull();
final BatteryConsumer deviceBatteryConsumer = mStatsRule.getDeviceBatteryConsumer();
assertThat(deviceBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU))
.isWithin(PRECISION).of(10.62949);
- assertThat(deviceBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
final BatteryConsumer appsBatteryConsumer = mStatsRule.getDeviceBatteryConsumer();
assertThat(appsBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU))
.isWithin(PRECISION).of(10.62949);
- assertThat(appsBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
}
@Test
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerCalculatorTest.java
index 3b5658c..506bab4 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerCalculatorTest.java
@@ -68,20 +68,14 @@
.isEqualTo(1000);
assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS))
.isWithin(PRECISION).of(0.1);
- assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer();
assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS))
.isWithin(PRECISION).of(0.1);
- assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer();
assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS))
.isWithin(PRECISION).of(0.1);
- assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
}
@Test
@@ -107,27 +101,19 @@
.isEqualTo(1000);
assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS))
.isWithin(PRECISION).of(2.77777);
- assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
UidBatteryConsumer consumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
assertThat(consumer2.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_GNSS))
.isEqualTo(2000);
assertThat(consumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS))
.isWithin(PRECISION).of(5.55555);
- assertThat(consumer2.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer();
assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS))
.isWithin(PRECISION).of(8.333333);
- assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer();
assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS))
.isWithin(PRECISION).of(8.333333);
- assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
}
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java
index 9b810bc..eba820e 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java
@@ -164,8 +164,6 @@
// = 4604000 mA-ms or 1.27888 mA-h
assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isWithin(PRECISION).of(1.27888);
- assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
// 720 mA * 100 ms (level 0 TX drain rate * level 0 TX duration)
// + 1080 mA * 200 ms (level 1 TX drain rate * level 1 TX duration)
@@ -178,22 +176,16 @@
BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer();
assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isWithin(PRECISION).of(0.94);
- assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
// 3/4 of total packets were sent by APP_UID so 75% of total
UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isWithin(PRECISION).of(0.705);
- assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
// Rest should go to the other app
UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isWithin(PRECISION).of(0.235);
- assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
}
@Test
@@ -321,8 +313,6 @@
// = 5177753 mA-ms or 1.43826 mA-h total consumption
assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isWithin(PRECISION).of(1.43826);
- assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
// 720 mA * 100 ms (level 0 TX drain rate * level 0 TX duration)
// + 1080 mA * 200 ms (level 1 TX drain rate * level 1 TX duration)
@@ -335,22 +325,16 @@
BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer();
assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isWithin(PRECISION).of(1.09938);
- assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
// 3/4 of total packets were sent by APP_UID so 75% of total
UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isWithin(PRECISION).of(0.82453);
- assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
// Rest should go to the other app
UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isWithin(PRECISION).of(0.27484);
- assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
}
@Test
@@ -441,8 +425,6 @@
// = 4604000 mA-ms or 1.27888 mA-h
assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isWithin(PRECISION).of(1.27888);
- assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
// 720 mA * 100 ms (level 0 TX drain rate * level 0 TX duration)
// + 1080 mA * 200 ms (level 1 TX drain rate * level 1 TX duration)
@@ -455,22 +437,16 @@
BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer();
assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isWithin(PRECISION).of(0.94);
- assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
// 3/4 of total packets were sent by APP_UID so 75% of total
UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isWithin(PRECISION).of(0.705);
- assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
// Rest should go to the other app
UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isWithin(PRECISION).of(0.235);
- assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
}
@Test
@@ -648,24 +624,16 @@
// 200ms phone on duration / 2000 total duration * 2.77778 mAh = 0.27777
assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isWithin(PRECISION).of(2.5);
- assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_PHONE))
.isWithin(PRECISION).of(0.27778);
- assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_PHONE))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer();
assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isWithin(PRECISION).of(1.38541);
- assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isWithin(PRECISION).of(1.38541);
- assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
}
@Test
@@ -782,12 +750,8 @@
// 1000ms phone on duration / 10000 total duration * 2.77778 mAh = 0.27777
assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isWithin(PRECISION).of(2.5);
- assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_PHONE))
.isWithin(PRECISION).of(0.27778);
- assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_PHONE))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
// CDMA2000 [Tx0, Tx1, Tx2, Tx3, Tx4, Rx] drain * duration
// [720, 1080, 1440, 1800, 2160, 1440] mA . [10, 11, 12, 13, 14, 15] ms = 111600 mA-ms
@@ -817,8 +781,6 @@
BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer();
assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isWithin(PRECISION).of(1.91094);
- assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
// 240 ms Rx Time, 1110 ms Tx Time, 1350 ms active time
// 150 App 1 Rx Packets, 10 App 1 Tx packets
@@ -841,15 +803,11 @@
UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isWithin(PRECISION).of(1.27574);
- assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
// Rest should go to the other app
UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isWithin(PRECISION).of(0.63520);
- assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
}
@Test
@@ -936,12 +894,8 @@
// 1000ms phone on duration / 10000 total duration * 2.77778 mAh = 0.27777
assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isWithin(PRECISION).of(2.5);
- assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_PHONE))
.isWithin(PRECISION).of(0.27778);
- assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_PHONE))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer();
// Estimated Rx/Tx modem consumption = 0.94 mAh
@@ -949,14 +903,10 @@
// 2.5 * 0.94 / 1.27888 = 1.83754 mAh
assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isWithin(PRECISION).of(1.83754);
- assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isWithin(PRECISION).of(1.83754);
- assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
}
@Test
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerCalculatorTest.java
index 2da98e8..7f20035 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerCalculatorTest.java
@@ -104,8 +104,6 @@
// Uid1 charge = 200000000 + 5 / 45 * 300000000 mAs = 64.81 mAh
assertThat(uid1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
.isWithin(PRECISION).of(64.81481);
- assertThat(uid1.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
UidBatteryConsumer uid2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
assertThat(uid2.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
@@ -116,8 +114,6 @@
// Uid2 charge = 40 / 45 * 300000000 + 100000000 mAs = 101.85 mAh
assertThat(uid2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
.isWithin(PRECISION).of(101.85185);
- assertThat(uid2.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer();
assertThat(deviceConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
@@ -126,8 +122,6 @@
// 600000000 uAs * (1 mA / 1000 uA) * (1 h / 3600 s) = 166.66666 mAh
assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
.isWithin(PRECISION).of(166.66666);
- assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer();
assertThat(appsConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
@@ -135,11 +129,8 @@
assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
.isWithin(PRECISION).of(166.66666);
- assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
}
-
@Test
public void testMeasuredEnergyBasedModel_multiDisplay() {
mStatsRule.initMeasuredEnergyStatsLocked()
@@ -202,8 +193,6 @@
// (600000000 + 800000000) uAs * (1 mA / 1000 uA) * (1 h / 3600 s) = 166.66666 mAh
assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
.isWithin(PRECISION).of(388.88888);
- assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
UidBatteryConsumer uid1 = mStatsRule.getUidBatteryConsumer(APP_UID1);
assertThat(uid1.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
@@ -214,8 +203,6 @@
// Uid1 charge = 20 / 80 * 600000000 mAs = 41.66666 mAh
assertThat(uid1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
.isWithin(PRECISION).of(41.66666);
- assertThat(uid1.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
UidBatteryConsumer uid2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
assertThat(uid2.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
@@ -226,17 +213,12 @@
// Uid1 charge = 60 / 80 * 600000000 + 800000000 mAs = 347.22222 mAh
assertThat(uid2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
.isWithin(PRECISION).of(347.22222);
- assertThat(uid2.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer();
assertThat(appsConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
.isEqualTo(110 * MINUTE_IN_MS);
assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
.isWithin(PRECISION).of(388.88888);
- assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
-
}
@Test
@@ -277,8 +259,6 @@
// Uid1 charge = 20 / 80 * 92.0 = 23.0 mAh
assertThat(uid1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
.isWithin(PRECISION).of(23.0);
- assertThat(uid1.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
UidBatteryConsumer uid2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
assertThat(uid2.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
@@ -288,27 +268,20 @@
// Uid2 charge = 60 / 80 * 92.0 = 69.0 mAh
assertThat(uid2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
.isWithin(PRECISION).of(69.0);
- assertThat(uid2.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer();
assertThat(deviceConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
.isEqualTo(80 * MINUTE_IN_MS);
assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
.isWithin(PRECISION).of(92);
- assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer();
assertThat(appsConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
.isEqualTo(80 * MINUTE_IN_MS);
assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
.isWithin(PRECISION).of(92);
- assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
}
-
@Test
public void testPowerProfileBasedModel_multiDisplay() {
mStatsRule.setAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_ON, 1, 60.0)
@@ -364,8 +337,6 @@
// 92 + 60 * 0.5 + 10 * 0.1 + 90 * 0.2 + 30 * 0.2 = 147
assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
.isWithin(PRECISION).of(147);
- assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
UidBatteryConsumer uid1 = mStatsRule.getUidBatteryConsumer(APP_UID1);
assertThat(uid1.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
@@ -375,8 +346,6 @@
// Uid1 charge = 20 / 110 * 147.0 = 23.0 mAh
assertThat(uid1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
.isWithin(PRECISION).of(26.72727);
- assertThat(uid1.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
UidBatteryConsumer uid2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
assertThat(uid2.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
@@ -386,17 +355,12 @@
// Uid2 charge = 90 / 110 * 92.0 = 69.0 mAh
assertThat(uid2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
.isWithin(PRECISION).of(120.272727);
- assertThat(uid2.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer();
assertThat(appsConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
.isEqualTo(110 * MINUTE_IN_MS);
assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
.isWithin(PRECISION).of(147);
- assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
-
}
private void setProcState(int uid, int procState, boolean resumed, long realtimeMs,
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerCalculatorTest.java
index 8e221be..827d2f8 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerCalculatorTest.java
@@ -159,22 +159,16 @@
.isEqualTo(2473);
assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
.isWithin(PRECISION).of(0.3964);
- assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer();
assertThat(deviceConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI))
.isEqualTo(4001);
assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
.isWithin(PRECISION).of(0.86666);
- assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
BatteryConsumer appsConsumer = mStatsRule.getDeviceBatteryConsumer();
assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
.isWithin(PRECISION).of(0.866666);
- assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
}
@Test
@@ -214,8 +208,6 @@
.isEqualTo(12423);
assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
.isWithin(PRECISION).of(2.0214666);
- assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
final BatteryConsumer.Key foreground = uidConsumer.getKey(
BatteryConsumer.POWER_COMPONENT_WIFI,
@@ -248,22 +240,16 @@
/* Same ratio as in testPowerControllerBasedModel_nonMeasured but scaled by 1_000_000uC. */
assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
.isWithin(PRECISION).of(0.2214666 / (0.2214666 + 0.645200) * 1_000_000 / 3600000);
- assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer();
assertThat(deviceConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI))
.isEqualTo(4002);
assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
.isWithin(PRECISION).of(0.27777);
- assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
BatteryConsumer appsConsumer = mStatsRule.getDeviceBatteryConsumer();
assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
.isWithin(PRECISION).of(0.277777);
- assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
}
@Test
@@ -302,8 +288,6 @@
.isEqualTo(12423);
assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
.isWithin(PRECISION).of(1.0325211);
- assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
final BatteryConsumer.Key foreground = uidConsumer.getKey(
BatteryConsumer.POWER_COMPONENT_WIFI,
@@ -349,8 +333,6 @@
.isEqualTo(1000);
assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
.isWithin(PRECISION).of(0.8231573);
- assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
}
@Test
@@ -371,8 +353,6 @@
/* Same ratio as in testTimerBasedModel_nonMeasured but scaled by 1_000_000uC. */
assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
.isWithin(PRECISION).of(0.8231573 / (0.8231573 + 0.8759216) * 1_000_000 / 3600000);
- assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
- .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
}
private WifiActivityEnergyInfo buildWifiActivityEnergyInfo(long timeSinceBoot,
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BasePowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BasePowerStatsProcessorTest.java
index f7a1638..cca6033 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BasePowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BasePowerStatsProcessorTest.java
@@ -176,7 +176,6 @@
powerStatsAggregator, /* batterySessionTimeSpanSlackMillis */ 0);
BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(new String[0],
- /* includePowerModels */ false,
/* includeProcessStateData */ true,
/* includeScreenStateData */ true,
/* includesPowerStateData */ true,
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java
index 4643ddd..38fe613 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java
@@ -342,7 +342,7 @@
PowerStatsExporter exporter = new PowerStatsExporter(mPowerStatsStore,
mPowerStatsAggregator, /* batterySessionTimeSpanSlackMillis */ 0);
- BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(new String[0], false,
+ BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(new String[0],
includeProcessStateData, includeScreenStateData, includesPowerStateData, 0);
exporter.populateBatteryUsageStatsBuilder(builder, aps);
return builder.build();
@@ -361,7 +361,7 @@
private void breakdownByProcState_fullRange(boolean includeScreenStateData,
boolean includePowerStateData) throws Exception {
BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
- new String[]{"cu570m"}, /* includePowerModels */ false,
+ new String[]{"cu570m"},
/* includeProcessStateData */ true, includeScreenStateData,
includePowerStateData, /* powerThreshold */ 0);
exportAggregatedPowerStats(builder, 1000, 10000);
@@ -406,7 +406,7 @@
@Test
public void breakdownByProcState_subRange() throws Exception {
BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
- new String[]{"cu570m"}, /* includePowerModels */ false,
+ new String[]{"cu570m"},
/* includeProcessStateData */ true, true, true, /* powerThreshold */ 0);
exportAggregatedPowerStats(builder, 3700, 6700);
@@ -438,7 +438,7 @@
@Test
public void combinedProcessStates() throws Exception {
BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
- new String[]{"cu570m"}, /* includePowerModels */ false,
+ new String[]{"cu570m"},
/* includeProcessStateData */ false, true, true, /* powerThreshold */ 0);
exportAggregatedPowerStats(builder, 1000, 10000);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
index c7574bd..dd4101e 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
@@ -41,11 +41,14 @@
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.media.tv.flags.Flags;
import android.os.Binder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.test.TestLooper;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.stats.hdmi.HdmiStatsEnums;
import androidx.test.InstrumentationRegistry;
@@ -54,6 +57,7 @@
import com.android.server.SystemService;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -83,6 +87,9 @@
private HdmiEarcController mHdmiEarcController;
private FakeEarcNativeWrapper mEarcNativeWrapper;
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Before
public void setUp() throws RemoteException {
mHdmiCecAtomWriterSpy = spy(new HdmiCecAtomWriter());
@@ -232,7 +239,8 @@
HdmiStatsEnums.SEND_MESSAGE_RESULT_UNKNOWN,
HdmiStatsEnums.VOLUME_MUTE,
HdmiCecAtomWriter.FEATURE_ABORT_OPCODE_UNKNOWN,
- HdmiStatsEnums.FEATURE_ABORT_REASON_UNKNOWN);
+ HdmiStatsEnums.FEATURE_ABORT_REASON_UNKNOWN,
+ HdmiCecAtomWriter.PHYSICAL_ADDRESS_INVALID);
}
@Test
@@ -258,7 +266,8 @@
HdmiStatsEnums.SEND_MESSAGE_RESULT_UNKNOWN,
HdmiStatsEnums.USER_CONTROL_PRESSED_COMMAND_UNKNOWN,
HdmiCecAtomWriter.FEATURE_ABORT_OPCODE_UNKNOWN,
- HdmiStatsEnums.FEATURE_ABORT_REASON_UNKNOWN);
+ HdmiStatsEnums.FEATURE_ABORT_REASON_UNKNOWN,
+ HdmiCecAtomWriter.PHYSICAL_ADDRESS_INVALID);
}
@Test
@@ -285,7 +294,8 @@
HdmiStatsEnums.SEND_MESSAGE_RESULT_UNKNOWN,
HdmiStatsEnums.USER_CONTROL_PRESSED_COMMAND_UNKNOWN,
Constants.MESSAGE_RECORD_ON,
- HdmiStatsEnums.UNRECOGNIZED_OPCODE);
+ HdmiStatsEnums.UNRECOGNIZED_OPCODE,
+ HdmiCecAtomWriter.PHYSICAL_ADDRESS_INVALID);
}
@Test
@@ -311,7 +321,8 @@
HdmiStatsEnums.SEND_MESSAGE_RESULT_UNKNOWN,
HdmiStatsEnums.USER_CONTROL_PRESSED_COMMAND_UNKNOWN,
HdmiCecAtomWriter.FEATURE_ABORT_OPCODE_UNKNOWN,
- HdmiStatsEnums.FEATURE_ABORT_REASON_UNKNOWN);
+ HdmiStatsEnums.FEATURE_ABORT_REASON_UNKNOWN,
+ HdmiCecAtomWriter.PHYSICAL_ADDRESS_INVALID);
}
@Test
@@ -337,6 +348,59 @@
}
@Test
+ @EnableFlags({Flags.FLAG_HDMI_CONTROL_COLLECT_PHYSICAL_ADDRESS})
+ public void testMessageReported_writesAtom_reportPhysicalAddress() {
+ HdmiCecMessage message = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ ADDR_PLAYBACK_1, 0x1234, HdmiDeviceInfo.DEVICE_PLAYBACK);
+
+ mHdmiCecAtomWriterSpy.messageReported(
+ message,
+ HdmiStatsEnums.INCOMING,
+ 1234);
+
+ verify(mHdmiCecAtomWriterSpy, times(1))
+ .writeHdmiCecMessageReportedAtom(
+ 1234,
+ HdmiStatsEnums.INCOMING,
+ Constants.ADDR_PLAYBACK_1,
+ Constants.ADDR_BROADCAST,
+ Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS,
+ HdmiStatsEnums.SEND_MESSAGE_RESULT_UNKNOWN,
+ HdmiStatsEnums.USER_CONTROL_PRESSED_COMMAND_UNKNOWN,
+ HdmiCecAtomWriter.FEATURE_ABORT_OPCODE_UNKNOWN,
+ HdmiStatsEnums.FEATURE_ABORT_REASON_UNKNOWN,
+ 0x1234);
+ }
+
+ @Test
+ @EnableFlags({Flags.FLAG_HDMI_CONTROL_COLLECT_PHYSICAL_ADDRESS})
+ public void testMessageReported_writesAtom_reportPhysicalAddress_noParams() {
+ HdmiCecMessage message = HdmiCecMessage.build(
+ Constants.ADDR_PLAYBACK_1,
+ Constants.ADDR_BROADCAST,
+ Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS,
+ new byte[0]);
+
+ mHdmiCecAtomWriterSpy.messageReported(
+ message,
+ HdmiStatsEnums.INCOMING,
+ 1234);
+
+ verify(mHdmiCecAtomWriterSpy, times(1))
+ .writeHdmiCecMessageReportedAtom(
+ 1234,
+ HdmiStatsEnums.INCOMING,
+ Constants.ADDR_PLAYBACK_1,
+ Constants.ADDR_BROADCAST,
+ Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS,
+ HdmiStatsEnums.SEND_MESSAGE_RESULT_UNKNOWN,
+ HdmiStatsEnums.USER_CONTROL_PRESSED_COMMAND_UNKNOWN,
+ HdmiCecAtomWriter.FEATURE_ABORT_OPCODE_UNKNOWN,
+ HdmiStatsEnums.FEATURE_ABORT_REASON_UNKNOWN,
+ HdmiCecAtomWriter.PHYSICAL_ADDRESS_INVALID);
+ }
+
+ @Test
public void testDsmStatusChanged_onWakeUp_ArcSupported_writesAtom_logReasonWake() {
mHdmiControlServiceSpy.setSoundbarMode(HdmiControlManager.SOUNDBAR_MODE_DISABLED);
Mockito.clearInvocations(mHdmiCecAtomWriterSpy);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 8bbba1b..a425401 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -42,7 +42,6 @@
import static android.view.flags.Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION;
import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
@@ -85,7 +84,6 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
@@ -97,7 +95,6 @@
import android.view.InputDevice;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
-import android.view.Surface;
import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowInsets;
@@ -1192,55 +1189,6 @@
argThat(h -> (h.inputConfig & InputConfig.SENSITIVE_FOR_PRIVACY) != 0));
}
- @RequiresFlagsDisabled(Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER)
- @Test
- public void testDrawMagnifiedViewport() {
- final int displayId = mDisplayContent.mDisplayId;
- // Use real surface, so ViewportWindow's BlastBufferQueue can be created.
- final ArrayList<SurfaceControl> surfaceControls = new ArrayList<>();
- mWm.mSurfaceControlFactory = () -> new SurfaceControl.Builder() {
- @Override
- public SurfaceControl build() {
- final SurfaceControl sc = super.build();
- surfaceControls.add(sc);
- return sc;
- }
- };
- mWm.mAccessibilityController.setMagnificationCallbacks(displayId,
- mock(WindowManagerInternal.MagnificationCallbacks.class));
- final boolean[] lockCanvasInWmLock = { false };
- final Surface surface = mWm.mAccessibilityController.forceShowMagnifierSurface(displayId);
- spyOn(surface);
- doAnswer(invocationOnMock -> {
- lockCanvasInWmLock[0] |= Thread.holdsLock(mWm.mGlobalLock);
- invocationOnMock.callRealMethod();
- return null;
- }).when(surface).lockCanvas(any());
- mWm.mAccessibilityController
- .recomputeMagnifiedRegionAndDrawMagnifiedRegionBorderIfNeeded(displayId);
- waitUntilHandlersIdle();
- try {
- verify(surface).lockCanvas(any());
-
- clearInvocations(surface);
- // Invalidate and redraw.
- mWm.mAccessibilityController.onDisplaySizeChanged(mDisplayContent);
- mWm.mAccessibilityController
- .recomputeMagnifiedRegionAndDrawMagnifiedRegionBorderIfNeeded(displayId);
- // Turn off magnification to release surface.
- mWm.mAccessibilityController.setMagnificationCallbacks(displayId, null);
- waitUntilHandlersIdle();
- // lockCanvas must not be called after releasing.
- verify(surface, never()).lockCanvas(any());
- verify(surface).release();
- assertFalse(lockCanvasInWmLock[0]);
- } finally {
- for (int i = surfaceControls.size() - 1; i >= 0; --i) {
- surfaceControls.get(i).release();
- }
- }
- }
-
@Test
public void testRequestKeyboardShortcuts_noWindow() {
doNothing().when(mWm.mContext).enforceCallingOrSelfPermission(anyString(), anyString());
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index b739666..131f46b 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -3168,7 +3168,7 @@
*/
boolean setSatelliteAccessControlOverlayConfigs(in boolean reset, in boolean isAllowed,
in String s2CellFile, in long locationFreshDurationNanos,
- in List<String> satelliteCountryCodes);
+ in List<String> satelliteCountryCodes, String satelliteAccessConfigurationFile);
/**
* This API can be used in only testing to override oem-enabled satellite provision status.
diff --git a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
index bfce3d2..6d818d7 100644
--- a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
+++ b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
@@ -159,7 +159,7 @@
private static BatteryUsageStats buildBatteryUsageStats() {
final BatteryUsageStats.Builder builder =
- new BatteryUsageStats.Builder(new String[]{"FOO"}, true, false, false, false, 0)
+ new BatteryUsageStats.Builder(new String[]{"FOO"}, false, false, false, 0)
.setBatteryCapacity(4000)
.setDischargePercentage(20)
.setDischargedPowerRange(1000, 2000)
@@ -182,8 +182,7 @@
.setTimeInProcessStateMs(UidBatteryConsumer.STATE_BACKGROUND, i * 1000);
for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT;
componentId++) {
- consumerBuilder.addConsumedPower(componentId, componentId * 123.0,
- BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ consumerBuilder.addConsumedPower(componentId, componentId * 123.0);
consumerBuilder.addUsageDurationMillis(componentId, componentId * 1000);
}
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/BottomHalfPipAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/BottomHalfPipAppHelper.kt
deleted file mode 100644
index 6573c2c..0000000
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/BottomHalfPipAppHelper.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.helpers
-
-import android.app.Instrumentation
-import android.content.Intent
-import android.tools.traces.parsers.toFlickerComponent
-import com.android.server.wm.flicker.testapp.ActivityOptions
-
-class BottomHalfPipAppHelper(
- instrumentation: Instrumentation,
- private val useLaunchingActivity: Boolean = false,
-) : PipAppHelper(
- instrumentation,
- appName = ActivityOptions.BottomHalfPip.LABEL,
- componentNameMatcher = ActivityOptions.BottomHalfPip.COMPONENT
- .toFlickerComponent()
-) {
- override val openAppIntent: Intent
- get() = super.openAppIntent.apply {
- component = if (useLaunchingActivity) {
- ActivityOptions.BottomHalfPip.LAUNCHING_APP_COMPONENT
- } else {
- ActivityOptions.BottomHalfPip.COMPONENT
- }
- }
-}
\ No newline at end of file
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
index e0900a6..c1c5dc6 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
@@ -39,6 +39,7 @@
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.helpers.MotionEventHelper.InputMethod.TOUCH
import java.time.Duration
+import kotlin.math.abs
/**
* Wrapper class around App helper classes. This class adds functionality to the apps that the
@@ -222,11 +223,16 @@
val expectedRect = Rect(displayRect).apply {
if (toLeft) right -= expectedWidth else left += expectedWidth
}
-
- wmHelper
- .StateSyncBuilder()
+ wmHelper.StateSyncBuilder()
.withAppTransitionIdle()
- .withSurfaceVisibleRegion(this, Region(expectedRect))
+ .withSurfaceMatchingVisibleRegion(
+ this,
+ Region(expectedRect),
+ { surfaceRegion, expectedRegion ->
+ areSnapWindowRegionsMatchingWithinThreshold(
+ surfaceRegion, expectedRegion, toLeft
+ )
+ })
.waitForAndVerify()
}
@@ -460,6 +466,33 @@
private fun isInDesktopWindowingMode(wmHelper: WindowManagerStateHelper) =
wmHelper.getWindow(innerHelper)?.windowingMode == WINDOWING_MODE_FREEFORM
+ private fun areSnapWindowRegionsMatchingWithinThreshold(
+ surfaceRegion: Region, expectedRegion: Region, toLeft: Boolean
+ ): Boolean {
+ val surfaceBounds = surfaceRegion.bounds
+ val expectedBounds = expectedRegion.bounds
+ // If snapped to left, right bounds will be cut off by the center divider.
+ // Else if snapped to right, the left bounds will be cut off.
+ val leftSideMatching: Boolean
+ val rightSideMatching: Boolean
+ if (toLeft) {
+ leftSideMatching = surfaceBounds.left == expectedBounds.left
+ rightSideMatching =
+ abs(surfaceBounds.right - expectedBounds.right) <=
+ surfaceBounds.right * SNAP_WINDOW_MAX_THRESHOLD_DIFF
+ } else {
+ leftSideMatching =
+ abs(surfaceBounds.left - expectedBounds.left) <=
+ surfaceBounds.left * SNAP_WINDOW_MAX_THRESHOLD_DIFF
+ rightSideMatching = surfaceBounds.right == expectedBounds.right
+ }
+
+ return surfaceBounds.top == expectedBounds.top &&
+ surfaceBounds.bottom == expectedBounds.bottom &&
+ leftSideMatching &&
+ rightSideMatching
+ }
+
private companion object {
val TIMEOUT: Duration = Duration.ofSeconds(3)
const val SNAP_RESIZE_DRAG_INSET: Int = 5 // inset to avoid dragging to display edge
@@ -475,5 +508,12 @@
const val HEADER_EMPTY_VIEW: String = "caption_handle"
val caption: BySelector
get() = By.res(SYSTEMUI_PACKAGE, CAPTION)
+ // In DesktopMode, window snap can be done with just a single window. In this case, the
+ // divider tiling between left and right window won't be shown, and hence its states are not
+ // obtainable in test.
+ // As the test should just focus on ensuring window goes to one side of the screen, an
+ // acceptable approach is to ensure snapped window still fills > 95% of either side of the
+ // screen.
+ const val SNAP_WINDOW_MAX_THRESHOLD_DIFF = 0.05
}
}
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
index 2fe84b0..db4838e 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
@@ -27,7 +27,6 @@
import android.tools.helpers.FIND_TIMEOUT
import android.tools.helpers.SYSTEMUI_PACKAGE
import android.tools.traces.ConditionsFactory
-import android.tools.traces.component.ComponentNameMatcher
import android.tools.traces.component.IComponentMatcher
import android.tools.traces.parsers.WindowManagerStateHelper
import android.tools.traces.parsers.toFlickerComponent
@@ -36,11 +35,12 @@
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
-open class PipAppHelper(
- instrumentation: Instrumentation,
- appName: String = ActivityOptions.Pip.LABEL,
- componentNameMatcher: ComponentNameMatcher = ActivityOptions.Pip.COMPONENT.toFlickerComponent(),
-) : StandardAppHelper(instrumentation, appName, componentNameMatcher) {
+open class PipAppHelper(instrumentation: Instrumentation) :
+ StandardAppHelper(
+ instrumentation,
+ ActivityOptions.Pip.LABEL,
+ ActivityOptions.Pip.COMPONENT.toFlickerComponent()
+ ) {
private val mediaSessionManager: MediaSessionManager
get() =
context.getSystemService(MediaSessionManager::class.java)
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 7c24a4a..9ce8e80 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -347,27 +347,6 @@
<category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
</intent-filter>
</activity>
- <activity android:name=".BottomHalfPipLaunchingActivity"
- android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
- android:taskAffinity="com.android.server.wm.flicker.testapp.BottomHalfPipLaunchingActivity"
- android:theme="@style/CutoutShortEdges"
- android:label="BottomHalfPipLaunchingActivity"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- </activity>
- <activity
- android:name=".BottomHalfPipActivity"
- android:resizeableActivity="true"
- android:supportsPictureInPicture="true"
- android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
- android:taskAffinity="com.android.server.wm.flicker.testapp.BottomHalfPipLaunchingActivity"
- android:theme="@style/TranslucentTheme"
- android:label="BottomHalfPipActivity"
- android:exported="true">
- </activity>
<activity android:name=".SplitScreenActivity"
android:resizeableActivity="true"
android:taskAffinity="com.android.server.wm.flicker.testapp.SplitScreenActivity"
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
index 837d050..47d1137 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
@@ -62,12 +62,6 @@
<item name="android:backgroundDimEnabled">false</item>
</style>
- <style name="TranslucentTheme" parent="@style/OptOutEdgeToEdge">
- <item name="android:windowIsTranslucent">true</item>
- <item name="android:windowContentOverlay">@null</item>
- <item name="android:backgroundDimEnabled">false</item>
- </style>
-
<style name="no_starting_window" parent="@style/OptOutEdgeToEdge">
<item name="android:windowDisablePreview">true</item>
</style>
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
index 0c1ac99..73625da 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -241,21 +241,6 @@
FLICKER_APP_PACKAGE + ".PipActivity");
}
- public static class BottomHalfPip {
- public static final String LAUNCHING_APP_LABEL = "BottomHalfPipLaunchingActivity";
- // Test App > Bottom Half PIP Activity
- public static final String LABEL = "BottomHalfPipActivity";
-
- // Use the bottom half layout for PIP Activity
- public static final String EXTRA_BOTTOM_HALF_LAYOUT = "bottom_half";
-
- public static final ComponentName LAUNCHING_APP_COMPONENT = new ComponentName(
- FLICKER_APP_PACKAGE, FLICKER_APP_PACKAGE + ".BottomHalfPipLaunchingActivity");
-
- public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
- FLICKER_APP_PACKAGE + ".BottomHalfPipActivity");
- }
-
public static class SplitScreen {
public static class Primary {
public static final String LABEL = "SplitScreenPrimaryActivity";
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BottomHalfPipActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BottomHalfPipActivity.java
deleted file mode 100644
index 3d48655..0000000
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BottomHalfPipActivity.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.testapp;
-
-import android.app.Activity;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.view.ViewGroup.LayoutParams;
-import android.view.WindowManager;
-
-import androidx.annotation.NonNull;
-
-public class BottomHalfPipActivity extends PipActivity {
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setTheme(R.style.TranslucentTheme);
- updateLayout();
- }
-
- @Override
- public void onConfigurationChanged(@NonNull Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
-
- updateLayout();
- }
-
- /**
- * Sets to match parent layout if the activity is
- * {@link Activity#isInPictureInPictureMode()}. Otherwise, set to bottom half
- * layout.
- *
- * @see #setToBottomHalfMode(boolean)
- */
- private void updateLayout() {
- setToBottomHalfMode(!isInPictureInPictureMode());
- }
-
- /**
- * Sets `useBottomHalfLayout` to `true` to use the bottom half layout. Use the
- * [LayoutParams.MATCH_PARENT] layout.
- */
- private void setToBottomHalfMode(boolean useBottomHalfLayout) {
- final WindowManager.LayoutParams attrs = getWindow().getAttributes();
- if (useBottomHalfLayout) {
- final int taskHeight = getWindowManager().getCurrentWindowMetrics().getBounds()
- .height();
- attrs.y = taskHeight / 2;
- attrs.height = taskHeight / 2;
- } else {
- attrs.y = 0;
- attrs.height = LayoutParams.MATCH_PARENT;
- }
- getWindow().setAttributes(attrs);
- }
-}