Merge "Update Special App Access Compose Components for ECM" into main
diff --git a/OWNERS b/OWNERS
index 023bdef..733157f 100644
--- a/OWNERS
+++ b/OWNERS
@@ -37,3 +37,5 @@
per-file *ravenwood* = file:ravenwood/OWNERS
per-file *Ravenwood* = file:ravenwood/OWNERS
+
+per-file PERFORMANCE_OWNERS = file:/PERFORMANCE_OWNERS
diff --git a/PERFORMANCE_OWNERS b/PERFORMANCE_OWNERS
index 9452ea3..48a0201 100644
--- a/PERFORMANCE_OWNERS
+++ b/PERFORMANCE_OWNERS
@@ -3,3 +3,6 @@
dualli@google.com
carmenjackson@google.com
philipcuadra@google.com
+shayba@google.com
+jdduke@google.com
+shombert@google.com
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
index 73a80b1..ad3e422 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -98,7 +98,10 @@
switch (action) {
case Intent.ACTION_PACKAGE_RESTARTED: {
synchronized (mLock) {
- mPackageStoppedState.add(pkgUid, pkgName, Boolean.TRUE);
+ // ACTION_PACKAGE_RESTARTED doesn't always mean the app is placed and kept
+ // in the stopped state, so don't put TRUE in the cache. Remove any existing
+ // entry and rely on an explicit call to PackageManager's isStopped() API.
+ mPackageStoppedState.delete(pkgUid, pkgName);
updateJobRestrictionsForUidLocked(pkgUid, false);
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index e06006f..f405083 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -1769,7 +1769,8 @@
@VisibleForTesting
class CcConfig {
- private boolean mFlexIsEnabled = FlexibilityController.FcConfig.DEFAULT_FLEXIBILITY_ENABLED;
+ private boolean mFlexIsEnabled =
+ FlexibilityController.FcConfig.DEFAULT_APPLIED_CONSTRAINTS != 0;
private boolean mShouldReprocessNetworkCapabilities = false;
/**
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
index 13a474cc..0e67b9a 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
@@ -24,7 +24,6 @@
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BATTERY_NOT_LOW;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CHARGING;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CONNECTIVITY;
-import static com.android.server.job.controllers.JobStatus.CONSTRAINT_FLEXIBLE;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_IDLE;
import android.annotation.ElapsedRealtimeLong;
@@ -74,17 +73,10 @@
private static final int JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS = CONSTRAINT_CONNECTIVITY;
/** List of all flexible constraints. */
- private static final int FLEXIBLE_CONSTRAINTS =
+ @VisibleForTesting
+ static final int FLEXIBLE_CONSTRAINTS =
JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS | SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS;
- private static final int NUM_JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS =
- Integer.bitCount(JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS);
-
- static final int NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS =
- Integer.bitCount(SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS);
-
- static final int NUM_FLEXIBLE_CONSTRAINTS = Integer.bitCount(FLEXIBLE_CONSTRAINTS);
-
private static final long NO_LIFECYCLE_END = Long.MAX_VALUE;
/**
@@ -100,9 +92,15 @@
private long mUnseenConstraintGracePeriodMs =
FcConfig.DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS;
- @VisibleForTesting
+ /** Set of constraints supported on this device for flex scheduling. */
+ private final int mSupportedFlexConstraints;
+
@GuardedBy("mLock")
- boolean mFlexibilityEnabled = FcConfig.DEFAULT_FLEXIBILITY_ENABLED;
+ private boolean mFlexibilityEnabled;
+
+ /** Set of constraints that will be used in the flex policy. */
+ @GuardedBy("mLock")
+ private int mAppliedConstraints = FcConfig.DEFAULT_APPLIED_CONSTRAINTS;
private long mMinTimeBetweenFlexibilityAlarmsMs =
FcConfig.DEFAULT_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS;
@@ -118,9 +116,6 @@
*/
private int[] mPercentToDropConstraints;
- @VisibleForTesting
- boolean mDeviceSupportsFlexConstraints;
-
/**
* Keeps track of what flexible constraints are satisfied at the moment.
* Is updated by the other controllers.
@@ -178,7 +173,7 @@
if (!js.hasFlexibilityConstraint()) {
continue;
}
- mFlexibilityTracker.resetJobNumDroppedConstraints(js, nowElapsed);
+ mFlexibilityTracker.calculateNumDroppedConstraints(js, nowElapsed);
mFlexibilityAlarmQueue.scheduleDropNumConstraintsAlarm(js, nowElapsed);
}
}
@@ -186,15 +181,23 @@
};
private static final int MSG_UPDATE_JOBS = 0;
+ private static final int MSG_UPDATE_JOB = 1;
public FlexibilityController(
JobSchedulerService service, PrefetchController prefetchController) {
super(service);
mHandler = new FcHandler(AppSchedulingModuleThread.get().getLooper());
- mDeviceSupportsFlexConstraints = !mContext.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_AUTOMOTIVE);
- mFlexibilityEnabled &= mDeviceSupportsFlexConstraints;
- mFlexibilityTracker = new FlexibilityTracker(NUM_FLEXIBLE_CONSTRAINTS);
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ || mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EMBEDDED)) {
+ // Embedded devices have no user-installable apps. Assume all jobs are critical
+ // and can't be flexed.
+ mSupportedFlexConstraints = 0;
+ } else {
+ // TODO(236261941): handle devices without a battery
+ mSupportedFlexConstraints = FLEXIBLE_CONSTRAINTS;
+ }
+ mFlexibilityEnabled = (mAppliedConstraints & mSupportedFlexConstraints) != 0;
+ mFlexibilityTracker = new FlexibilityTracker(Integer.bitCount(mSupportedFlexConstraints));
mFcConfig = new FcConfig();
mFlexibilityAlarmQueue = new FlexibilityAlarmQueue(
mContext, AppSchedulingModuleThread.get().getLooper());
@@ -218,10 +221,12 @@
public void maybeStartTrackingJobLocked(JobStatus js, JobStatus lastJob) {
if (js.hasFlexibilityConstraint()) {
final long nowElapsed = sElapsedRealtimeClock.millis();
- if (!mDeviceSupportsFlexConstraints) {
+ if (mSupportedFlexConstraints == 0) {
js.setFlexibilityConstraintSatisfied(nowElapsed, true);
return;
}
+ js.setNumAppliedFlexibleConstraints(
+ Integer.bitCount(getRelevantAppliedConstraintsLocked(js)));
js.setFlexibilityConstraintSatisfied(nowElapsed, isFlexibilitySatisfiedLocked(js));
mFlexibilityTracker.add(js);
js.setTrackingController(JobStatus.TRACKING_FLEXIBILITY);
@@ -266,6 +271,14 @@
|| mService.isCurrentlyRunningLocked(js);
}
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ int getRelevantAppliedConstraintsLocked(@NonNull JobStatus js) {
+ final int relevantConstraints = SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS
+ | (js.canApplyTransportAffinities() ? CONSTRAINT_CONNECTIVITY : 0);
+ return mAppliedConstraints & relevantConstraints;
+ }
+
/**
* Returns whether there are enough constraints satisfied to allow running the job from flex's
* perspective. This takes into account unseen constraint combinations and expectations around
@@ -274,7 +287,7 @@
@VisibleForTesting
@GuardedBy("mLock")
boolean hasEnoughSatisfiedConstraintsLocked(@NonNull JobStatus js) {
- final int satisfiedConstraints = mSatisfiedFlexibleConstraints
+ final int satisfiedConstraints = mSatisfiedFlexibleConstraints & mAppliedConstraints
& (SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS
| (js.areTransportAffinitiesSatisfied() ? CONSTRAINT_CONNECTIVITY : 0));
final int numSatisfied = Integer.bitCount(satisfiedConstraints);
@@ -296,8 +309,7 @@
// count have not been seen recently enough, then assume they won't be seen anytime soon,
// so don't force the job to wait longer. If any combinations with a higher count have been
// seen recently, then the job can potentially wait for those combinations.
- final int irrelevantConstraints = ~(SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS
- | (js.canApplyTransportAffinities() ? CONSTRAINT_CONNECTIVITY : 0));
+ final int irrelevantConstraints = ~getRelevantAppliedConstraintsLocked(js);
for (int i = mLastSeenConstraintTimesElapsed.size() - 1; i >= 0; --i) {
final int constraints = mLastSeenConstraintTimesElapsed.keyAt(i);
if ((constraints & irrelevantConstraints) != 0) {
@@ -515,9 +527,9 @@
for (int j = 0; j < mFlexibilityTracker.size(); j++) {
final ArraySet<JobStatus> jobs = mFlexibilityTracker
.getJobsByNumRequiredConstraints(j);
- for (int i = 0; i < jobs.size(); i++) {
+ for (int i = jobs.size() - 1; i >= 0; --i) {
JobStatus js = jobs.valueAt(i);
- mFlexibilityTracker.resetJobNumDroppedConstraints(js, nowElapsed);
+ mFlexibilityTracker.updateFlexibleConstraints(js, nowElapsed);
mFlexibilityAlarmQueue.scheduleDropNumConstraintsAlarm(js, nowElapsed);
if (js.setFlexibilityConstraintSatisfied(
nowElapsed, isFlexibilitySatisfiedLocked(js))) {
@@ -579,18 +591,46 @@
mTrackedJobs.get(js.getNumRequiredFlexibleConstraints()).remove(js);
}
- public void resetJobNumDroppedConstraints(JobStatus js, long nowElapsed) {
+ /**
+ * Updates applied and dropped constraints for the job.
+ */
+ public void updateFlexibleConstraints(JobStatus js, long nowElapsed) {
+ final int prevNumRequired = js.getNumRequiredFlexibleConstraints();
+
+ final int numAppliedConstraints =
+ Integer.bitCount(getRelevantAppliedConstraintsLocked(js));
+ js.setNumAppliedFlexibleConstraints(numAppliedConstraints);
+
final int curPercent = getCurPercentOfLifecycleLocked(js, nowElapsed);
int toDrop = 0;
- final int jsMaxFlexibleConstraints = NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS
- + (js.canApplyTransportAffinities() ? 1 : 0);
+ for (int i = 0; i < numAppliedConstraints; i++) {
+ if (curPercent >= mPercentToDropConstraints[i]) {
+ toDrop++;
+ }
+ }
+ js.setNumDroppedFlexibleConstraints(toDrop);
+
+ if (prevNumRequired == js.getNumRequiredFlexibleConstraints()) {
+ return;
+ }
+ mTrackedJobs.get(prevNumRequired).remove(js);
+ add(js);
+ }
+
+ /**
+ * Calculates the number of constraints that should be dropped for the job, based on how
+ * far along the job is into its lifecycle.
+ */
+ public void calculateNumDroppedConstraints(JobStatus js, long nowElapsed) {
+ final int curPercent = getCurPercentOfLifecycleLocked(js, nowElapsed);
+ int toDrop = 0;
+ final int jsMaxFlexibleConstraints = js.getNumAppliedFlexibleConstraints();
for (int i = 0; i < jsMaxFlexibleConstraints; i++) {
if (curPercent >= mPercentToDropConstraints[i]) {
toDrop++;
}
}
- adjustJobsRequiredConstraints(
- js, js.getNumDroppedFlexibleConstraints() - toDrop, nowElapsed);
+ setNumDroppedFlexibleConstraints(js, toDrop);
}
/** Returns all tracked jobs. */
@@ -599,17 +639,14 @@
}
/**
- * Adjusts number of required flexible constraints and sorts it into the tracker.
- * Returns false if the job status's number of flexible constraints is now 0.
+ * Updates the number of dropped flexible constraints and sorts it into the tracker.
*/
- public boolean adjustJobsRequiredConstraints(JobStatus js, int adjustBy, long nowElapsed) {
- if (adjustBy != 0) {
+ public void setNumDroppedFlexibleConstraints(JobStatus js, int numDropped) {
+ if (numDropped != js.getNumDroppedFlexibleConstraints()) {
remove(js);
- js.adjustNumRequiredFlexibleConstraints(adjustBy);
- js.setFlexibilityConstraintSatisfied(nowElapsed, isFlexibilitySatisfiedLocked(js));
+ js.setNumDroppedFlexibleConstraints(numDropped);
add(js);
}
- return js.getNumRequiredFlexibleConstraints() > 0;
}
public int size() {
@@ -658,8 +695,10 @@
if (DEBUG) {
Slog.d(TAG, "scheduleDropNumConstraintsAlarm: "
+ js.getSourcePackageName() + " " + js.getSourceUserId()
+ + " numApplied: " + js.getNumAppliedFlexibleConstraints()
+ " numRequired: " + js.getNumRequiredFlexibleConstraints()
- + " numSatisfied: " + Integer.bitCount(mSatisfiedFlexibleConstraints)
+ + " numSatisfied: " + Integer.bitCount(
+ mSatisfiedFlexibleConstraints & getRelevantAppliedConstraintsLocked(js))
+ " curTime: " + nowElapsed
+ " earliest: " + earliest
+ " latest: " + latest
@@ -669,8 +708,9 @@
if (DEBUG) {
Slog.d(TAG, "deadline proximity met: " + js);
}
- mFlexibilityTracker.adjustJobsRequiredConstraints(js,
- -js.getNumRequiredFlexibleConstraints(), nowElapsed);
+ mFlexibilityTracker.setNumDroppedFlexibleConstraints(js,
+ js.getNumAppliedFlexibleConstraints());
+ mHandler.obtainMessage(MSG_UPDATE_JOB, js).sendToTarget();
return;
}
if (nextTimeElapsed == NO_LIFECYCLE_END) {
@@ -696,12 +736,15 @@
final long nowElapsed = sElapsedRealtimeClock.millis();
for (int i = 0; i < expired.size(); i++) {
JobStatus js = expired.valueAt(i);
- boolean wasFlexibilitySatisfied = js.isConstraintSatisfied(CONSTRAINT_FLEXIBLE);
-
- if (mFlexibilityTracker.adjustJobsRequiredConstraints(js, -1, nowElapsed)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Alarm fired for " + js.toShortString());
+ }
+ mFlexibilityTracker.calculateNumDroppedConstraints(js, nowElapsed);
+ if (js.getNumRequiredFlexibleConstraints() > 0) {
scheduleDropNumConstraintsAlarm(js, nowElapsed);
}
- if (wasFlexibilitySatisfied != js.isConstraintSatisfied(CONSTRAINT_FLEXIBLE)) {
+ if (js.setFlexibilityConstraintSatisfied(nowElapsed,
+ isFlexibilitySatisfiedLocked(js))) {
changedJobs.add(js);
}
}
@@ -725,7 +768,9 @@
final long nowElapsed = sElapsedRealtimeClock.millis();
final ArraySet<JobStatus> changedJobs = new ArraySet<>();
- for (int o = 0; o <= NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS; ++o) {
+ final int numAppliedSystemWideConstraints = Integer.bitCount(
+ mAppliedConstraints & SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS);
+ for (int o = 0; o <= numAppliedSystemWideConstraints; ++o) {
final ArraySet<JobStatus> jobsByNumConstraints = mFlexibilityTracker
.getJobsByNumRequiredConstraints(o);
@@ -744,6 +789,23 @@
}
}
break;
+
+ case MSG_UPDATE_JOB:
+ synchronized (mLock) {
+ final JobStatus js = (JobStatus) msg.obj;
+ if (DEBUG) {
+ Slog.d("blah", "Checking on " + js.toShortString());
+ }
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ if (js.setFlexibilityConstraintSatisfied(
+ nowElapsed, isFlexibilitySatisfiedLocked(js))) {
+ // TODO(141645789): add method that will take a single job
+ ArraySet<JobStatus> changedJob = new ArraySet<>();
+ changedJob.add(js);
+ mStateChangedListener.onControllerStateChanged(changedJob);
+ }
+ }
+ break;
}
}
}
@@ -754,7 +816,8 @@
/** Prefix to use with all constant keys in order to "sub-namespace" the keys. */
private static final String FC_CONFIG_PREFIX = "fc_";
- static final String KEY_FLEXIBILITY_ENABLED = FC_CONFIG_PREFIX + "enable_flexibility";
+ @VisibleForTesting
+ static final String KEY_APPLIED_CONSTRAINTS = FC_CONFIG_PREFIX + "applied_constraints";
static final String KEY_DEADLINE_PROXIMITY_LIMIT =
FC_CONFIG_PREFIX + "flexibility_deadline_proximity_limit_ms";
static final String KEY_FALLBACK_FLEXIBILITY_DEADLINE =
@@ -770,7 +833,7 @@
static final String KEY_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS =
FC_CONFIG_PREFIX + "unseen_constraint_grace_period_ms";
- static final boolean DEFAULT_FLEXIBILITY_ENABLED = false;
+ static final int DEFAULT_APPLIED_CONSTRAINTS = 0;
@VisibleForTesting
static final long DEFAULT_DEADLINE_PROXIMITY_LIMIT_MS = 15 * MINUTE_IN_MILLIS;
@VisibleForTesting
@@ -783,11 +846,8 @@
@VisibleForTesting
static final long DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS = 3 * DAY_IN_MILLIS;
- /**
- * If false the controller will not track new jobs
- * and the flexibility constraint will always be satisfied.
- */
- public boolean FLEXIBILITY_ENABLED = DEFAULT_FLEXIBILITY_ENABLED;
+ /** Which constraints to apply/consider in flex policy. */
+ public int APPLIED_CONSTRAINTS = DEFAULT_APPLIED_CONSTRAINTS;
/** How close to a jobs' deadline all flexible constraints will be dropped. */
public long DEADLINE_PROXIMITY_LIMIT_MS = DEFAULT_DEADLINE_PROXIMITY_LIMIT_MS;
/** For jobs that lack a deadline, the time that will be used to drop all constraints by. */
@@ -811,16 +871,19 @@
public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
@NonNull String key) {
switch (key) {
- case KEY_FLEXIBILITY_ENABLED:
- FLEXIBILITY_ENABLED = properties.getBoolean(key, DEFAULT_FLEXIBILITY_ENABLED)
- && mDeviceSupportsFlexConstraints;
- if (mFlexibilityEnabled != FLEXIBILITY_ENABLED) {
- mFlexibilityEnabled = FLEXIBILITY_ENABLED;
+ case KEY_APPLIED_CONSTRAINTS:
+ APPLIED_CONSTRAINTS =
+ properties.getInt(key, DEFAULT_APPLIED_CONSTRAINTS)
+ & mSupportedFlexConstraints;
+ if (mAppliedConstraints != APPLIED_CONSTRAINTS) {
+ mAppliedConstraints = APPLIED_CONSTRAINTS;
mShouldReevaluateConstraints = true;
- if (mFlexibilityEnabled) {
+ if (mAppliedConstraints != 0) {
+ mFlexibilityEnabled = true;
mPrefetchController
.registerPrefetchChangedListener(mPrefetchChangedListener);
} else {
+ mFlexibilityEnabled = false;
mPrefetchController
.unRegisterPrefetchChangedListener(mPrefetchChangedListener);
}
@@ -893,7 +956,7 @@
private int[] parsePercentToDropString(String s) {
String[] dropPercentString = s.split(",");
- int[] dropPercentInt = new int[NUM_FLEXIBLE_CONSTRAINTS];
+ int[] dropPercentInt = new int[Integer.bitCount(FLEXIBLE_CONSTRAINTS)];
if (dropPercentInt.length != dropPercentString.length) {
return DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS;
}
@@ -922,7 +985,7 @@
pw.println(":");
pw.increaseIndent();
- pw.print(KEY_FLEXIBILITY_ENABLED, FLEXIBILITY_ENABLED).println();
+ pw.print(KEY_APPLIED_CONSTRAINTS, APPLIED_CONSTRAINTS).println();
pw.print(KEY_DEADLINE_PROXIMITY_LIMIT, DEADLINE_PROXIMITY_LIMIT_MS).println();
pw.print(KEY_FALLBACK_FLEXIBILITY_DEADLINE, FALLBACK_FLEXIBILITY_DEADLINE_MS).println();
pw.print(KEY_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS,
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 d1f575e..0d5d11e 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
@@ -24,7 +24,6 @@
import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
import static com.android.server.job.JobSchedulerService.WORKING_INDEX;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
-import static com.android.server.job.controllers.FlexibilityController.NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS;
import static com.android.server.job.controllers.FlexibilityController.SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS;
import android.annotation.ElapsedRealtimeLong;
@@ -155,7 +154,7 @@
/**
* Keeps track of how many flexible constraints must be satisfied for the job to execute.
*/
- private final int mNumRequiredFlexibleConstraints;
+ private int mNumAppliedFlexibleConstraints;
/**
* Number of required flexible constraints that have been dropped.
@@ -697,11 +696,7 @@
&& satisfiesMinWindowException
&& (numFailures + numSystemStops) != 1
&& lacksSomeFlexibleConstraints) {
- mNumRequiredFlexibleConstraints =
- NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS + (mCanApplyTransportAffinities ? 1 : 0);
requiredConstraints |= CONSTRAINT_FLEXIBLE;
- } else {
- mNumRequiredFlexibleConstraints = 0;
}
this.requiredConstraints = requiredConstraints;
@@ -1527,9 +1522,14 @@
return (requiredConstraints & CONSTRAINT_FLEXIBLE) != 0;
}
+ /** Returns the number of flexible job constraints being applied to the job. */
+ public int getNumAppliedFlexibleConstraints() {
+ return mNumAppliedFlexibleConstraints;
+ }
+
/** Returns the number of flexible job constraints required to be satisfied to execute */
public int getNumRequiredFlexibleConstraints() {
- return mNumRequiredFlexibleConstraints - mNumDroppedFlexibleConstraints;
+ return mNumAppliedFlexibleConstraints - mNumDroppedFlexibleConstraints;
}
/**
@@ -2112,9 +2112,14 @@
}
/** Adjusts the number of required flexible constraints by the given number */
- public void adjustNumRequiredFlexibleConstraints(int adjustment) {
- mNumDroppedFlexibleConstraints = Math.max(0, Math.min(mNumRequiredFlexibleConstraints,
- mNumDroppedFlexibleConstraints - adjustment));
+ public void setNumAppliedFlexibleConstraints(int count) {
+ mNumAppliedFlexibleConstraints = count;
+ }
+
+ /** Sets the number of dropped flexible constraints to the given number */
+ public void setNumDroppedFlexibleConstraints(int count) {
+ mNumDroppedFlexibleConstraints = Math.max(0,
+ Math.min(mNumAppliedFlexibleConstraints, count));
}
/**
diff --git a/core/api/current.txt b/core/api/current.txt
index d46d07e..a761674 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -4516,7 +4516,7 @@
method @NonNull public final <T extends android.view.View> T requireViewById(@IdRes int);
method public final void runOnUiThread(Runnable);
method public void setActionBar(@Nullable android.widget.Toolbar);
- method public void setAllowCrossUidActivitySwitchFromBelow(boolean);
+ method @FlaggedApi("android.security.asm_restrictions_enabled") public void setAllowCrossUidActivitySwitchFromBelow(boolean);
method public void setContentTransitionManager(android.transition.TransitionManager);
method public void setContentView(@LayoutRes int);
method public void setContentView(android.view.View);
@@ -9706,9 +9706,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 @Deprecated @MainThread public void onDeviceAppeared(@NonNull android.companion.AssociationInfo);
+ method @MainThread public void onDeviceAppeared(@NonNull android.companion.AssociationInfo);
method @Deprecated @MainThread public void onDeviceDisappeared(@NonNull String);
- method @Deprecated @MainThread public void onDeviceDisappeared(@NonNull android.companion.AssociationInfo);
+ method @MainThread public void onDeviceDisappeared(@NonNull android.companion.AssociationInfo);
method @FlaggedApi("android.companion.device_presence") @MainThread public void onDeviceEvent(@NonNull android.companion.AssociationInfo, int);
field @FlaggedApi("android.companion.device_presence") public static final int DEVICE_EVENT_BLE_APPEARED = 0; // 0x0
field @FlaggedApi("android.companion.device_presence") public static final int DEVICE_EVENT_BLE_DISAPPEARED = 1; // 0x1
@@ -18787,6 +18787,7 @@
method @NonNull public java.util.List<android.hardware.camera2.CaptureRequest.Key<?>> getAvailableCaptureRequestKeys();
method @NonNull public java.util.List<android.hardware.camera2.CaptureResult.Key<?>> getAvailableCaptureResultKeys();
method public java.util.List<android.hardware.camera2.CaptureRequest.Key<?>> getAvailablePhysicalCameraRequestKeys();
+ method @FlaggedApi("com.android.internal.camera.flags.feature_combination_query") @NonNull public java.util.List<android.hardware.camera2.CameraCharacteristics.Key<?>> getAvailableSessionCharacteristicsKeys();
method public java.util.List<android.hardware.camera2.CaptureRequest.Key<?>> getAvailableSessionKeys();
method @NonNull public java.util.List<android.hardware.camera2.CameraCharacteristics.Key<?>> getKeys();
method @NonNull public java.util.List<android.hardware.camera2.CameraCharacteristics.Key<?>> getKeysNeedingPermission();
@@ -18939,6 +18940,7 @@
method @Deprecated public abstract void createReprocessableCaptureSessionByConfigurations(@NonNull android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public int getCameraAudioRestriction() throws android.hardware.camera2.CameraAccessException;
method @NonNull public abstract String getId();
+ method @FlaggedApi("com.android.internal.camera.flags.feature_combination_query") @NonNull public android.hardware.camera2.CameraCharacteristics getSessionCharacteristics(@NonNull android.hardware.camera2.params.SessionConfiguration) throws android.hardware.camera2.CameraAccessException;
method @Deprecated public boolean isSessionConfigurationSupported(@NonNull android.hardware.camera2.params.SessionConfiguration) throws android.hardware.camera2.CameraAccessException;
method public void setCameraAudioRestriction(int) throws android.hardware.camera2.CameraAccessException;
field public static final int AUDIO_RESTRICTION_NONE = 0; // 0x0
@@ -39062,7 +39064,6 @@
public class NetworkSecurityPolicy {
method public static android.security.NetworkSecurityPolicy getInstance();
- method @FlaggedApi("android.security.certificate_transparency_configuration") public boolean isCertificateTransparencyVerificationRequired(@NonNull String);
method public boolean isCleartextTrafficPermitted();
method public boolean isCleartextTrafficPermitted(String);
}
@@ -39299,7 +39300,7 @@
method @Nullable public java.util.Date getKeyValidityStart();
method @NonNull public String getKeystoreAlias();
method public int getMaxUsageCount();
- method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public java.util.Set<java.lang.String> getMgf1Digests();
+ method @FlaggedApi("android.security.mgf1_digest_setter") @NonNull public java.util.Set<java.lang.String> getMgf1Digests();
method public int getPurposes();
method @NonNull public String[] getSignaturePaddings();
method public int getUserAuthenticationType();
@@ -39307,7 +39308,7 @@
method public boolean isDevicePropertiesAttestationIncluded();
method @NonNull public boolean isDigestsSpecified();
method public boolean isInvalidatedByBiometricEnrollment();
- method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public boolean isMgf1DigestsSpecified();
+ method @FlaggedApi("android.security.mgf1_digest_setter") @NonNull public boolean isMgf1DigestsSpecified();
method public boolean isRandomizedEncryptionRequired();
method public boolean isStrongBoxBacked();
method public boolean isUnlockedDeviceRequired();
@@ -39339,7 +39340,7 @@
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityForOriginationEnd(java.util.Date);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityStart(java.util.Date);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setMaxUsageCount(int);
- method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setMgf1Digests(@NonNull java.lang.String...);
+ method @FlaggedApi("android.security.mgf1_digest_setter") @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setMgf1Digests(@NonNull java.lang.String...);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setRandomizedEncryptionRequired(boolean);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setSignaturePaddings(java.lang.String...);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUnlockedDeviceRequired(boolean);
@@ -39444,14 +39445,14 @@
method @Nullable public java.util.Date getKeyValidityForOriginationEnd();
method @Nullable public java.util.Date getKeyValidityStart();
method public int getMaxUsageCount();
- method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public java.util.Set<java.lang.String> getMgf1Digests();
+ method @FlaggedApi("android.security.mgf1_digest_setter") @NonNull public java.util.Set<java.lang.String> getMgf1Digests();
method public int getPurposes();
method @NonNull public String[] getSignaturePaddings();
method public int getUserAuthenticationType();
method public int getUserAuthenticationValidityDurationSeconds();
method public boolean isDigestsSpecified();
method public boolean isInvalidatedByBiometricEnrollment();
- method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public boolean isMgf1DigestsSpecified();
+ method @FlaggedApi("android.security.mgf1_digest_setter") @NonNull public boolean isMgf1DigestsSpecified();
method public boolean isRandomizedEncryptionRequired();
method public boolean isUnlockedDeviceRequired();
method public boolean isUserAuthenticationRequired();
@@ -39473,7 +39474,7 @@
method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityForOriginationEnd(java.util.Date);
method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityStart(java.util.Date);
method @NonNull public android.security.keystore.KeyProtection.Builder setMaxUsageCount(int);
- method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public android.security.keystore.KeyProtection.Builder setMgf1Digests(@Nullable java.lang.String...);
+ method @FlaggedApi("android.security.mgf1_digest_setter") @NonNull public android.security.keystore.KeyProtection.Builder setMgf1Digests(@Nullable java.lang.String...);
method @NonNull public android.security.keystore.KeyProtection.Builder setRandomizedEncryptionRequired(boolean);
method @NonNull public android.security.keystore.KeyProtection.Builder setSignaturePaddings(java.lang.String...);
method @NonNull public android.security.keystore.KeyProtection.Builder setUnlockedDeviceRequired(boolean);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index a1465df..c64f230 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -4089,7 +4089,8 @@
field @Deprecated public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1; // 0x1
field @Deprecated public static final int MASK_PERMISSION_FLAGS = 255; // 0xff
field public static final int MATCH_ANY_USER = 4194304; // 0x400000
- field public static final int MATCH_CLONE_PROFILE = 536870912; // 0x20000000
+ field @Deprecated public static final int MATCH_CLONE_PROFILE = 536870912; // 0x20000000
+ field @FlaggedApi("android.content.pm.fix_duplicated_flags") public static final long MATCH_CLONE_PROFILE_LONG = 17179869184L; // 0x400000000L
field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
field public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 536870912; // 0x20000000
field public static final int MATCH_INSTANT = 8388608; // 0x800000
@@ -4195,12 +4196,15 @@
public final class UserProperties implements android.os.Parcelable {
method public int describeContents();
+ method public int getCrossProfileContentSharingStrategy();
method public int getShowInQuietMode();
method public int getShowInSharingSurfaces();
method public boolean isCredentialShareableWithParent();
method public boolean isMediaSharedWithParent();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.UserProperties> CREATOR;
+ field public static final int CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT = 1; // 0x1
+ field public static final int CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION = 0; // 0x0
field public static final int SHOW_IN_QUIET_MODE_DEFAULT = 2; // 0x2
field public static final int SHOW_IN_QUIET_MODE_HIDDEN = 1; // 0x1
field public static final int SHOW_IN_QUIET_MODE_PAUSED = 0; // 0x0
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 98a78cf..3f2376d 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -259,6 +259,7 @@
field public static final String OPSTR_ACTIVITY_RECOGNITION_SOURCE = "android:activity_recognition_source";
field public static final String OPSTR_MANAGE_ONGOING_CALLS = "android:manage_ongoing_calls";
field public static final String OPSTR_RECORD_AUDIO_HOTWORD = "android:record_audio_hotword";
+ field public static final String OPSTR_RESERVED_FOR_TESTING = "android:reserved_for_testing";
field public static final String OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER = "android:use_icc_auth_with_device_identifier";
field public static final int OP_COARSE_LOCATION = 0; // 0x0
field public static final int OP_RECORD_AUDIO = 27; // 0x1b
@@ -1165,6 +1166,7 @@
public static final class UserProperties.Builder {
ctor public UserProperties.Builder();
method @NonNull public android.content.pm.UserProperties build();
+ method @NonNull public android.content.pm.UserProperties.Builder setCrossProfileContentSharingStrategy(int);
method @NonNull public android.content.pm.UserProperties.Builder setShowInQuietMode(int);
method @NonNull public android.content.pm.UserProperties.Builder setShowInSharingSurfaces(int);
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index c52d27ea..cb08dad 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -9387,6 +9387,7 @@
* @param allowed {@code true} to disable the UID restrictions; {@code false} to revert back to
* the default behaviour
*/
+ @FlaggedApi(android.security.Flags.FLAG_ASM_RESTRICTIONS_ENABLED)
public void setAllowCrossUidActivitySwitchFromBelow(boolean allowed) {
ActivityClient.getInstance().setAllowCrossUidActivitySwitchFromBelow(mToken, allowed);
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 71fe47e..ec43184 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -16,8 +16,8 @@
package android.app;
-import static android.view.contentprotection.flags.Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED;
import static android.permission.flags.Flags.FLAG_OP_ENABLE_MOBILE_DATA_BY_USER;
+import static android.view.contentprotection.flags.Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED;
import static java.lang.Long.max;
@@ -1521,9 +1521,17 @@
*/
public static final int OP_MEDIA_ROUTING_CONTROL = AppProtoEnums.APP_OP_MEDIA_ROUTING_CONTROL;
+ /**
+ * Op code for use by tests to avoid interfering history logs that the wider system might
+ * trigger.
+ *
+ * @hide
+ */
+ public static final int OP_RESERVED_FOR_TESTING = AppProtoEnums.APP_OP_RESERVED_FOR_TESTING;
+
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final int _NUM_OP = 141;
+ public static final int _NUM_OP = 142;
/**
* All app ops represented as strings.
@@ -1671,6 +1679,7 @@
OPSTR_CREATE_ACCESSIBILITY_OVERLAY,
OPSTR_MEDIA_ROUTING_CONTROL,
OPSTR_ENABLE_MOBILE_DATA_BY_USER,
+ OPSTR_RESERVED_FOR_TESTING,
})
public @interface AppOpString {}
@@ -2330,6 +2339,17 @@
public static final String OPSTR_ENABLE_MOBILE_DATA_BY_USER =
"android:enable_mobile_data_by_user";
+ /**
+ * Reserved for use by appop tests so that operations done legitimately by the platform don't
+ * interfere with expected results. Platform code should never use this.
+ *
+ * @hide
+ */
+ @TestApi
+ @SuppressLint("UnflaggedApi")
+ public static final String OPSTR_RESERVED_FOR_TESTING =
+ "android:reserved_for_testing";
+
/** {@link #sAppOpsToNote} not initialized yet for this op */
private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
/** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -2887,6 +2907,8 @@
.setPermission(Manifest.permission.MEDIA_ROUTING_CONTROL).build(),
new AppOpInfo.Builder(OP_ENABLE_MOBILE_DATA_BY_USER, OPSTR_ENABLE_MOBILE_DATA_BY_USER,
"ENABLE_MOBILE_DATA_BY_USER").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+ new AppOpInfo.Builder(OP_RESERVED_FOR_TESTING, OPSTR_RESERVED_FOR_TESTING,
+ "OP_RESERVED_FOR_TESTING").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
};
// The number of longs needed to form a full bitmask of app ops
diff --git a/core/java/android/companion/CompanionDeviceService.java b/core/java/android/companion/CompanionDeviceService.java
index c99a457..4d0267c 100644
--- a/core/java/android/companion/CompanionDeviceService.java
+++ b/core/java/android/companion/CompanionDeviceService.java
@@ -40,7 +40,6 @@
/**
* A service that receives calls from the system with device events.
- * See {@link #onDeviceEvent(AssociationInfo, int)}.
*
* <p>
* Companion applications must create a service that {@code extends}
@@ -311,10 +310,7 @@
* Called by system whenever a device associated with this app is connected.
*
* @param associationInfo A record for the companion device.
- *
- * @deprecated please override {@link #onDeviceEvent(AssociationInfo, int)} instead.
*/
- @Deprecated
@MainThread
public void onDeviceAppeared(@NonNull AssociationInfo associationInfo) {
if (!associationInfo.isSelfManaged()) {
@@ -326,10 +322,7 @@
* Called by system whenever a device associated with this app is disconnected.
*
* @param associationInfo A record for the companion device.
- *
- * @deprecated please override {@link #onDeviceEvent(AssociationInfo, int)} instead.
*/
- @Deprecated
@MainThread
public void onDeviceDisappeared(@NonNull AssociationInfo associationInfo) {
if (!associationInfo.isSelfManaged()) {
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index c86ccfd..c7a75ed 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -117,6 +117,7 @@
* developer guide.</p>
* </div>
*/
+@android.ravenwood.annotation.RavenwoodKeepPartialClass
public abstract class ContentProvider implements ContentInterface, ComponentCallbacks2 {
private static final String TAG = "ContentProvider";
@@ -2781,6 +2782,7 @@
}
/** @hide */
+ @android.ravenwood.annotation.RavenwoodKeep
private Uri maybeGetUriWithoutUserId(Uri uri) {
if (mSingleUser) {
return uri;
@@ -2789,6 +2791,7 @@
}
/** @hide */
+ @android.ravenwood.annotation.RavenwoodKeep
public static int getUserIdFromAuthority(String auth, int defaultUserId) {
if (auth == null) return defaultUserId;
int end = auth.lastIndexOf('@');
@@ -2803,17 +2806,20 @@
}
/** @hide */
+ @android.ravenwood.annotation.RavenwoodKeep
public static int getUserIdFromAuthority(String auth) {
return getUserIdFromAuthority(auth, UserHandle.USER_CURRENT);
}
/** @hide */
+ @android.ravenwood.annotation.RavenwoodKeep
public static int getUserIdFromUri(Uri uri, int defaultUserId) {
if (uri == null) return defaultUserId;
return getUserIdFromAuthority(uri.getAuthority(), defaultUserId);
}
/** @hide */
+ @android.ravenwood.annotation.RavenwoodKeep
public static int getUserIdFromUri(Uri uri) {
return getUserIdFromUri(uri, UserHandle.USER_CURRENT);
}
@@ -2824,6 +2830,7 @@
* @hide
*/
@TestApi
+ @android.ravenwood.annotation.RavenwoodKeep
public @NonNull static UserHandle getUserHandleFromUri(@NonNull Uri uri) {
return UserHandle.of(getUserIdFromUri(uri, Process.myUserHandle().getIdentifier()));
}
@@ -2834,6 +2841,7 @@
* If there is no userId in the authority, it symply returns the argument
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static String getAuthorityWithoutUserId(String auth) {
if (auth == null) return null;
int end = auth.lastIndexOf('@');
@@ -2841,6 +2849,7 @@
}
/** @hide */
+ @android.ravenwood.annotation.RavenwoodKeep
public static Uri getUriWithoutUserId(Uri uri) {
if (uri == null) return null;
Uri.Builder builder = uri.buildUpon();
@@ -2849,6 +2858,7 @@
}
/** @hide */
+ @android.ravenwood.annotation.RavenwoodKeep
public static boolean uriHasUserId(Uri uri) {
if (uri == null) return false;
return !TextUtils.isEmpty(uri.getUserInfo());
@@ -2872,6 +2882,7 @@
*/
@NonNull
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @android.ravenwood.annotation.RavenwoodKeep
public static Uri createContentUriForUser(
@NonNull Uri contentUri, @NonNull UserHandle userHandle) {
if (!ContentResolver.SCHEME_CONTENT.equals(contentUri.getScheme())) {
@@ -2898,6 +2909,7 @@
/** @hide */
@UnsupportedAppUsage
+ @android.ravenwood.annotation.RavenwoodKeep
public static Uri maybeAddUserId(Uri uri, int userId) {
if (uri == null) return null;
if (userId != UserHandle.USER_CURRENT
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 457fd63..e395127 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -379,7 +379,8 @@
/**
* If true, the requestor of the unarchival has specified that the app should be unarchived
- * for all users.
+ * for all users. Sent as part of the {@link android.content.Intent#ACTION_UNARCHIVE_PACKAGE}
+ * intent.
*/
@FlaggedApi(Flags.FLAG_ARCHIVING)
public static final String EXTRA_UNARCHIVE_ALL_USERS =
@@ -396,6 +397,9 @@
* with an intent for a corresponding follow-up action (e.g. storage clearing dialog) or a
* failure dialog.
*
+ * <p> Used as part of {@link #requestUnarchive} to return the status of the unarchival through
+ * the {@link IntentSender}.
+ *
* @see #requestUnarchive
*/
@FlaggedApi(Flags.FLAG_ARCHIVING)
@@ -704,7 +708,8 @@
/**
* The installer responsible for the unarchival is disabled.
*
- * <p> Should only be used by the system.
+ * <p> The system will return this status if appropriate. Installers do not need to verify for
+ * this error.
*/
@FlaggedApi(Flags.FLAG_ARCHIVING)
public static final int UNARCHIVAL_ERROR_INSTALLER_DISABLED = 4;
@@ -712,7 +717,8 @@
/**
* The installer responsible for the unarchival has been uninstalled
*
- * <p> Should only be used by the system.
+ * <p> The system will return this status if appropriate. Installers do not need to verify for
+ * this error.
*/
@FlaggedApi(Flags.FLAG_ARCHIVING)
public static final int UNARCHIVAL_ERROR_INSTALLER_UNINSTALLED = 5;
@@ -1238,7 +1244,7 @@
* {@code statusReceiver} if timeout happens before commit.
* @throws IllegalArgumentException if the {@code statusReceiver} from an immutable
* {@link android.app.PendingIntent} when caller has a target SDK of API
- * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM} or above.
+ * 35 or above.
*/
public void commitSessionAfterInstallConstraintsAreMet(int sessionId,
@NonNull IntentSender statusReceiver, @NonNull InstallConstraints constraints,
@@ -1948,7 +1954,7 @@
* {@link #openWrite(String, long, long)} are still open.
* @throws IllegalArgumentException if the {@code statusReceiver} from an immutable
* {@link android.app.PendingIntent} when caller has a target SDK of API
- * version {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM} or above.
+ * version 35 or above.
*
* @see android.app.admin.DevicePolicyManager
* @see #requestUserPreapproval
@@ -1979,7 +1985,7 @@
* individual status codes on how to handle them.
* @throws IllegalArgumentException if the {@code statusReceiver} from an immutable
* {@link android.app.PendingIntent} when caller has a target SDK of API
- * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM} or above.
+ * 35 or above.
*
* @hide
*/
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 607e904..f865a36 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -919,6 +919,10 @@
@Retention(RetentionPolicy.SOURCE)
public @interface InstrumentationInfoFlags {}
+ //-------------------------------------------------------------------------
+ // Beginning of GET_ and MATCH_ flags
+ //-------------------------------------------------------------------------
+
/**
* {@link PackageInfo} flag: return information about
* activities in the package in {@link PackageInfo#activities}.
@@ -1216,30 +1220,21 @@
*/
public static final int MATCH_DIRECT_BOOT_AUTO = 0x10000000;
- /**
- * {@link ResolveInfo} flag: allow matching components across clone profile
- * <p>
- * This flag is used only for query and not resolution, the default behaviour would be to
- * restrict querying across clone profile. This flag would be honored only if caller have
- * permission {@link Manifest.permission.QUERY_CLONED_APPS}.
- *
- * @hide
- * <p>
- */
- @SystemApi
- public static final int MATCH_CLONE_PROFILE = 0x20000000;
-
- /**
- * @deprecated Use {@link #GET_ATTRIBUTIONS_LONG} to avoid unintended sign extension.
- */
- @Deprecated
- public static final int GET_ATTRIBUTIONS = 0x80000000;
-
/** @hide */
@Deprecated
public static final int MATCH_DEBUG_TRIAGED_MISSING = MATCH_DIRECT_BOOT_AUTO;
/**
+ * @deprecated Use {@link #MATCH_CLONE_PROFILE_LONG} instead.
+ *
+ * @hide
+ */
+ @SuppressLint("UnflaggedApi") // Just adding the @Deprecated annotation
+ @Deprecated
+ @SystemApi
+ public static final int MATCH_CLONE_PROFILE = 0x20000000;
+
+ /**
* {@link PackageInfo} flag: include system apps that are in the uninstalled state and have
* been set to be hidden until installed via {@link #setSystemAppState}.
* @hide
@@ -1257,6 +1252,12 @@
public static final int MATCH_APEX = 0x40000000;
/**
+ * @deprecated Use {@link #GET_ATTRIBUTIONS_LONG} to avoid unintended sign extension.
+ */
+ @Deprecated
+ public static final int GET_ATTRIBUTIONS = 0x80000000;
+
+ /**
* {@link PackageInfo} flag: return all attributions declared in the package manifest
*/
public static final long GET_ATTRIBUTIONS_LONG = 0x80000000L;
@@ -1282,6 +1283,23 @@
public static final long MATCH_QUARANTINED_COMPONENTS = 1L << 33;
/**
+ * {@link ResolveInfo} flag: allow matching components across clone profile
+ * <p>
+ * This flag is used only for query and not resolution, the default behaviour would be to
+ * restrict querying across clone profile. This flag would be honored only if caller have
+ * permission {@link Manifest.permission.QUERY_CLONED_APPS}.
+ *
+ * @hide
+ */
+ @FlaggedApi(android.content.pm.Flags.FLAG_FIX_DUPLICATED_FLAGS)
+ @SystemApi
+ public static final long MATCH_CLONE_PROFILE_LONG = 1L << 34;
+
+ //-------------------------------------------------------------------------
+ // End of GET_ and MATCH_ flags
+ //-------------------------------------------------------------------------
+
+ /**
* Flag for {@link #addCrossProfileIntentFilter}: if this flag is set: when
* resolving an intent that matches the {@code CrossProfileIntentFilter},
* the current profile will be skipped. Only activities in the target user
diff --git a/core/java/android/content/pm/UserProperties.java b/core/java/android/content/pm/UserProperties.java
index 56e8291..c401268 100644
--- a/core/java/android/content/pm/UserProperties.java
+++ b/core/java/android/content/pm/UserProperties.java
@@ -69,6 +69,9 @@
private static final String ATTR_DELETE_APP_WITH_PARENT = "deleteAppWithParent";
private static final String ATTR_ALWAYS_VISIBLE = "alwaysVisible";
+ private static final String ATTR_CROSS_PROFILE_CONTENT_SHARING_STRATEGY =
+ "crossProfileContentSharingStrategy";
+
/** Index values of each property (to indicate whether they are present in this object). */
@IntDef(prefix = "INDEX_", value = {
INDEX_SHOW_IN_LAUNCHER,
@@ -86,6 +89,7 @@
INDEX_SHOW_IN_QUIET_MODE,
INDEX_SHOW_IN_SHARING_SURFACES,
INDEX_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE,
+ INDEX_CROSS_PROFILE_CONTENT_SHARING_STRATEGY
})
@Retention(RetentionPolicy.SOURCE)
private @interface PropertyIndex {
@@ -105,6 +109,7 @@
private static final int INDEX_SHOW_IN_QUIET_MODE = 12;
private static final int INDEX_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE = 13;
private static final int INDEX_SHOW_IN_SHARING_SURFACES = 14;
+ private static final int INDEX_CROSS_PROFILE_CONTENT_SHARING_STRATEGY = 15;
/** A bit set, mapping each PropertyIndex to whether it is present (1) or absent (0). */
private long mPropertiesPresent = 0;
@@ -365,6 +370,45 @@
*/
@SuppressLint("UnflaggedApi") // b/306636213
public static final int SHOW_IN_SHARING_SURFACES_NO = SHOW_IN_LAUNCHER_NO;
+ /**
+ * Possible values for cross profile content sharing strategy for this profile.
+ *
+ * @hide
+ */
+ @IntDef(prefix = {"CROSS_PROFILE_CONTENT_SHARING_STRATEGY_"}, value = {
+ CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION,
+ CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CrossProfileContentSharingStrategy {
+ }
+
+ /**
+ * Signifies that cross-profile content sharing strategy, both to and from this profile, should
+ * not be delegated to any other user/profile.
+ * For ex:
+ * If this property is set for a profile, content sharing applications (such as Android
+ * Sharesheet), should not delegate the decision to share content between that profile and
+ * another profile to whether content sharing is allowed between any other profile/user related
+ * to those profiles. They should instead decide, based upon whether content sharing is
+ * specifically allowed between the two profiles in question.
+ */
+ @SuppressLint("UnflaggedApi") // b/306636213
+ public static final int CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION = 0;
+
+ /**
+ * Signifies that cross-profile content sharing strategy, both to and from this profile, should
+ * be based upon the strategy used by the parent user of the profile.
+ * For ex:
+ * If this property is set for a profile A, content sharing applications (such as Android
+ * Sharesheet), should share content between profile A and profile B, based upon whether content
+ * sharing is allowed between the parent of profile A and profile B.
+ * If it's also set for profile B, then decision should, in turn be made by considering content
+ * sharing strategy between the parents of both profiles.
+ */
+ @SuppressLint("UnflaggedApi") // b/306636213
+ public static final int CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT = 1;
+
/**
* Creates a UserProperties (intended for the SystemServer) that stores a reference to the given
@@ -423,6 +467,7 @@
setCredentialShareableWithParent(orig.isCredentialShareableWithParent());
setShowInQuietMode(orig.getShowInQuietMode());
setShowInSharingSurfaces(orig.getShowInSharingSurfaces());
+ setCrossProfileContentSharingStrategy(orig.getCrossProfileContentSharingStrategy());
}
/**
@@ -776,8 +821,7 @@
private @CrossProfileIntentFilterAccessControlLevel int mCrossProfileIntentFilterAccessControl;
/**
- * Returns the user's {@link CrossProfileIntentResolutionStrategy}. If not explicitly
- * configured, default value is {@link #CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY_DEFAULT}.
+ * Returns the user's {@link CrossProfileIntentResolutionStrategy}.
* @return user's {@link CrossProfileIntentResolutionStrategy}.
*
* @hide
@@ -792,11 +836,8 @@
throw new SecurityException("You don't have permission to query "
+ "crossProfileIntentResolutionStrategy");
}
- /**
- * Sets {@link CrossProfileIntentResolutionStrategy} for the user.
- * @param val resolution strategy for user
- * @hide
- */
+
+ /** @hide */
public void setCrossProfileIntentResolutionStrategy(
@CrossProfileIntentResolutionStrategy int val) {
this.mCrossProfileIntentResolutionStrategy = val;
@@ -804,6 +845,39 @@
}
private @CrossProfileIntentResolutionStrategy int mCrossProfileIntentResolutionStrategy;
+ /**
+ * Returns the user's {@link CrossProfileContentSharingStrategy}.
+ *
+ * Content sharing applications, such as Android Sharesheet allow sharing of content
+ * (an image, for ex.) between profiles, based upon cross-profile access checks between the
+ * originating and destined profile.
+ * In some cases however, we may want another user (such as profile parent) to serve as the
+ * delegated user to be used for such checks.
+ * To effect the same, clients can fetch this property and accordingly replace the
+ * originating/destined profile by another user for cross-profile access checks.
+ *
+ * @return user's {@link CrossProfileContentSharingStrategy}.
+ */
+ @SuppressLint("UnflaggedApi") // b/306636213
+ public @CrossProfileContentSharingStrategy int getCrossProfileContentSharingStrategy() {
+ if (isPresent(INDEX_CROSS_PROFILE_CONTENT_SHARING_STRATEGY)) {
+ return mCrossProfileContentSharingStrategy;
+ }
+ if (mDefaultProperties != null) {
+ return mDefaultProperties.mCrossProfileContentSharingStrategy;
+ }
+ throw new SecurityException("You don't have permission to query "
+ + "crossProfileContentSharingStrategy");
+ }
+
+ /** @hide */
+ public void setCrossProfileContentSharingStrategy(
+ @CrossProfileContentSharingStrategy int val) {
+ this.mCrossProfileContentSharingStrategy = val;
+ setPresent(INDEX_CROSS_PROFILE_CONTENT_SHARING_STRATEGY);
+ }
+ private @CrossProfileContentSharingStrategy int mCrossProfileContentSharingStrategy;
+
@Override
public String toString() {
@@ -827,6 +901,7 @@
+ isAuthAlwaysRequiredToDisableQuietMode()
+ ", mDeleteAppWithParent=" + getDeleteAppWithParent()
+ ", mAlwaysVisible=" + getAlwaysVisible()
+ + ", mCrossProfileContentSharingStrategy=" + getCrossProfileContentSharingStrategy()
+ "}";
}
@@ -856,6 +931,8 @@
+ isAuthAlwaysRequiredToDisableQuietMode());
pw.println(prefix + " mDeleteAppWithParent=" + getDeleteAppWithParent());
pw.println(prefix + " mAlwaysVisible=" + getAlwaysVisible());
+ pw.println(prefix + " mCrossProfileContentSharingStrategy="
+ + getCrossProfileContentSharingStrategy());
}
/**
@@ -934,6 +1011,8 @@
case ATTR_ALWAYS_VISIBLE:
setAlwaysVisible(parser.getAttributeBoolean(i));
break;
+ case ATTR_CROSS_PROFILE_CONTENT_SHARING_STRATEGY:
+ setCrossProfileContentSharingStrategy(parser.getAttributeInt(i));
default:
Slog.w(LOG_TAG, "Skipping unknown property " + attributeName);
}
@@ -1008,6 +1087,10 @@
serializer.attributeBoolean(null, ATTR_ALWAYS_VISIBLE,
mAlwaysVisible);
}
+ if (isPresent(INDEX_CROSS_PROFILE_CONTENT_SHARING_STRATEGY)) {
+ serializer.attributeInt(null, ATTR_CROSS_PROFILE_CONTENT_SHARING_STRATEGY,
+ mCrossProfileContentSharingStrategy);
+ }
}
// For use only with an object that has already had any permission-lacking fields stripped out.
@@ -1029,6 +1112,7 @@
dest.writeBoolean(mAuthAlwaysRequiredToDisableQuietMode);
dest.writeBoolean(mDeleteAppWithParent);
dest.writeBoolean(mAlwaysVisible);
+ dest.writeInt(mCrossProfileContentSharingStrategy);
}
/**
@@ -1054,6 +1138,7 @@
mAuthAlwaysRequiredToDisableQuietMode = source.readBoolean();
mDeleteAppWithParent = source.readBoolean();
mAlwaysVisible = source.readBoolean();
+ mCrossProfileContentSharingStrategy = source.readInt();
}
@Override
@@ -1100,6 +1185,8 @@
private boolean mAuthAlwaysRequiredToDisableQuietMode = false;
private boolean mDeleteAppWithParent = false;
private boolean mAlwaysVisible = false;
+ private @CrossProfileContentSharingStrategy int mCrossProfileContentSharingStrategy =
+ CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION;
/**
* @hide
@@ -1231,6 +1318,19 @@
return this;
}
+ /** Sets the value for {@link #mCrossProfileContentSharingStrategy}
+ * @hide
+ */
+
+ @TestApi
+ @SuppressLint("UnflaggedApi") // b/306636213
+ @NonNull
+ public Builder setCrossProfileContentSharingStrategy(@CrossProfileContentSharingStrategy
+ int crossProfileContentSharingStrategy) {
+ mCrossProfileContentSharingStrategy = crossProfileContentSharingStrategy;
+ return this;
+ }
+
/** Builds a UserProperties object with *all* values populated.
* @hide
*/
@@ -1253,7 +1353,8 @@
mCredentialShareableWithParent,
mAuthAlwaysRequiredToDisableQuietMode,
mDeleteAppWithParent,
- mAlwaysVisible);
+ mAlwaysVisible,
+ mCrossProfileContentSharingStrategy);
}
} // end Builder
@@ -1272,7 +1373,8 @@
boolean credentialShareableWithParent,
boolean authAlwaysRequiredToDisableQuietMode,
boolean deleteAppWithParent,
- boolean alwaysVisible) {
+ boolean alwaysVisible,
+ @CrossProfileContentSharingStrategy int crossProfileContentSharingStrategy) {
mDefaultProperties = null;
setShowInLauncher(showInLauncher);
setStartWithParent(startWithParent);
@@ -1290,5 +1392,6 @@
authAlwaysRequiredToDisableQuietMode);
setDeleteAppWithParent(deleteAppWithParent);
setAlwaysVisible(alwaysVisible);
+ setCrossProfileContentSharingStrategy(crossProfileContentSharingStrategy);
}
}
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 1b90570..b04b7ba 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -108,3 +108,10 @@
description: "Feature flag to reduce app crashes caused by split installs with INSTALL_DONT_KILL"
bug: "291212866"
}
+
+flag {
+ name: "fix_duplicated_flags"
+ namespace: "package_manager_service"
+ description: "Feature flag to fix duplicated PackageManager flag values"
+ bug: "314815969"
+}
diff --git a/core/java/android/credentials/flags.aconfig b/core/java/android/credentials/flags.aconfig
index bab84aa..f876eeb 100644
--- a/core/java/android/credentials/flags.aconfig
+++ b/core/java/android/credentials/flags.aconfig
@@ -26,4 +26,11 @@
name: "new_settings_intents"
description: "Enables settings intents to redirect to new settings page"
bug: "307587989"
+}
+
+flag {
+ namespace: "credential_manager"
+ name: "new_settings_ui"
+ description: "Enables new settings UI for VIC"
+ bug: "315209085"
}
\ No newline at end of file
diff --git a/core/java/android/database/ContentObserver.java b/core/java/android/database/ContentObserver.java
index 39c9400..4322bed 100644
--- a/core/java/android/database/ContentObserver.java
+++ b/core/java/android/database/ContentObserver.java
@@ -31,6 +31,7 @@
import java.util.Arrays;
import java.util.Collection;
+import java.util.concurrent.Executor;
/**
* Receives call backs for changes to content.
@@ -54,6 +55,7 @@
private Transport mTransport; // guarded by mLock
Handler mHandler;
+ private final Executor mExecutor;
/**
* Creates a content observer.
@@ -62,6 +64,18 @@
*/
public ContentObserver(Handler handler) {
mHandler = handler;
+ mExecutor = null;
+ }
+
+ /**
+ * @hide
+ * Creates a content observer with an executor.
+ *
+ * @param executor The executor to run {@link #onChange} on, or null if none.
+ * @param unused a second argument to avoid source incompatibility.
+ */
+ public ContentObserver(@Nullable Executor executor, int unused) {
+ mExecutor = executor;
}
/**
@@ -306,12 +320,19 @@
/** @hide */
public final void dispatchChange(boolean selfChange, @NonNull Collection<Uri> uris,
@NotifyFlags int flags, @UserIdInt int userId) {
- if (mHandler == null) {
- onChange(selfChange, uris, flags, userId);
- } else {
+ if (mExecutor != null) {
+ mExecutor.execute(() -> {
+ onChange(selfChange, uris, flags, userId);
+ });
+ } else if (mHandler != null) {
+ // Supporting Handler directly rather than wrapping in a HandlerExecutor
+ // avoids introducing a RejectedExecutionException for legacy code when
+ // the post fails.
mHandler.post(() -> {
onChange(selfChange, uris, flags, userId);
});
+ } else {
+ onChange(selfChange, uris, flags, userId);
}
}
diff --git a/core/java/android/database/ExecutorContentObserver.java b/core/java/android/database/ExecutorContentObserver.java
new file mode 100644
index 0000000..3ea807d
--- /dev/null
+++ b/core/java/android/database/ExecutorContentObserver.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.database;
+
+import android.annotation.Nullable;
+
+import java.util.concurrent.Executor;
+
+/**
+ * @hide
+ *
+ * Receives callbacks for changes to content.
+ * Must be implemented by objects which are added to a {@link ContentObservable}.
+ */
+public abstract class ExecutorContentObserver extends ContentObserver {
+ /**
+ * Creates a content observer that uses an executor for change handling.
+ *
+ * @param executor The executor to run {@link #onChange} on, or null if none.
+ */
+ public ExecutorContentObserver(@Nullable Executor executor) {
+ super(executor, 0);
+ }
+}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index bb8924c..dec5b9c 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -35,6 +35,7 @@
import com.android.internal.camera.flags.Flags;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -206,6 +207,7 @@
private List<CameraCharacteristics.Key<?>> mKeysNeedingPermission;
private List<CaptureRequest.Key<?>> mAvailableRequestKeys;
private List<CaptureRequest.Key<?>> mAvailableSessionKeys;
+ private List<CameraCharacteristics.Key<?>> mAvailableSessionCharacteristicsKeys;
private List<CaptureRequest.Key<?>> mAvailablePhysicalRequestKeys;
private List<CaptureResult.Key<?>> mAvailableResultKeys;
private ArrayList<RecommendedStreamConfigurationMap> mRecommendedConfigurations;
@@ -546,6 +548,27 @@
}
/**
+ * <p>Get the keys in Camera Characteristics whose values are capture session specific.
+ * The session specific characteristics can be acquired by calling
+ * CameraDevice.getSessionCharacteristics(). </p>
+ *
+ * <p>Note that getAvailableSessionKeys returns the CaptureRequest keys that are difficult to
+ * apply per-frame, whereas this function returns CameraCharacteristics keys that are dependent
+ * on a particular SessionConfiguration.</p>
+ *
+ * @return List of CameraCharacteristic keys containing characterisitics specific to a session
+ * configuration. For Android 15, this list only contains CONTROL_ZOOM_RATIO_RANGE.
+ */
+ @NonNull
+ @FlaggedApi(Flags.FLAG_FEATURE_COMBINATION_QUERY)
+ public List<CameraCharacteristics.Key<?>> getAvailableSessionCharacteristicsKeys() {
+ if (mAvailableSessionCharacteristicsKeys == null) {
+ mAvailableSessionCharacteristicsKeys = Arrays.asList(CONTROL_ZOOM_RATIO_RANGE);
+ }
+ return mAvailableSessionCharacteristicsKeys;
+ }
+
+ /**
* <p>Returns a subset of {@link #getAvailableCaptureRequestKeys} keys that can
* be overridden for physical devices backing a logical multi-camera.</p>
*
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 58cba41..3835c52 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -16,6 +16,7 @@
package android.hardware.camera2;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -28,6 +29,8 @@
import android.os.Handler;
import android.view.Surface;
+import com.android.internal.camera.flags.Flags;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
@@ -1412,7 +1415,35 @@
throw new UnsupportedOperationException("Subclasses must override this method");
}
- /**
+ /**
+ * <p>Get camera characteristics for a particular session configuration by the camera device.</p>
+ *
+ * <p>The camera characteristics returned here is typically more limited than the characteristics
+ * returned from {@link CameraManager#getCameraCharacteristics}. The keys that have more limited
+ * values are listed in
+ * {@link CameraCharacteristics#getAvailableSessionCharacteristicsKeys}. </p>
+ *
+ * <p>Other than that, the characteristics returned here can be used in the same way as those
+ * returned from {@link CameraManager#getCameraCharacteristics}.</p>
+ *
+ * @param sessionConfig : The session configuration for which characteristics are fetched.
+ * @return CameraCharacteristics specific to a given session configuration.
+ * @throws UnsupportedOperationException if the query operation is not supported by the camera
+ * device
+ * @throws IllegalArgumentException if the session configuration is invalid
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if the camera device has been closed
+ * @see android.hardware.camera2.CameraCharacteristics#getAvailableSessionCharacteristicsKeys
+ */
+ @NonNull
+ @FlaggedApi(Flags.FLAG_FEATURE_COMBINATION_QUERY)
+ public CameraCharacteristics getSessionCharacteristics(
+ @NonNull SessionConfiguration sessionConfig) throws CameraAccessException {
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /**
* A callback objects for receiving updates about the state of a camera device.
*
* <p>A callback instance must be provided to the {@link CameraManager#openCamera} method to
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index d4d1ab3..8196bf5 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -1058,6 +1058,12 @@
* <p>The set returned is not modifiable, so any attempts to modify it will throw
* a {@code UnsupportedOperationException}.</p>
*
+ * <p>Devices launching on Android {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}
+ * or newer versions are required to support {@link CaptureRequest#CONTROL_AF_MODE},
+ * {@link CaptureRequest#CONTROL_AF_REGIONS}, {@link CaptureRequest#CONTROL_AF_TRIGGER},
+ * {@link CaptureRequest#CONTROL_ZOOM_RATIO} for
+ * {@link CameraExtensionCharacteristics#EXTENSION_NIGHT}.</p>
+ *
* @param extension the extension type
*
* @return non-modifiable set of capture keys supported by camera extension session initialized
@@ -1139,6 +1145,12 @@
* and the {@link CameraExtensionSession.ExtensionCaptureCallback#onCaptureResultAvailable}
* callback will not be fired.</p>
*
+ * <p>Devices launching on Android {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}
+ * or newer versions are required to support {@link CaptureResult#CONTROL_AF_MODE},
+ * {@link CaptureResult#CONTROL_AF_REGIONS}, {@link CaptureResult#CONTROL_AF_TRIGGER},
+ * {@link CaptureResult#CONTROL_AF_STATE}, {@link CaptureResult#CONTROL_ZOOM_RATIO} for
+ * {@link CameraExtensionCharacteristics#EXTENSION_NIGHT}.</p>
+ *
* @param extension the extension type
*
* @return non-modifiable set of capture result keys supported by camera extension session
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 3851e36..ccb24e7 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -786,6 +786,18 @@
}
}
+ @Override
+ public CameraCharacteristics getSessionCharacteristics(
+ @NonNull SessionConfiguration sessionConfig) throws CameraAccessException,
+ UnsupportedOperationException, IllegalArgumentException {
+ synchronized (mInterfaceLock) {
+ checkIfCameraClosedOrInError();
+ CameraMetadataNative info = mRemoteDevice.getSessionCharacteristics(sessionConfig);
+
+ return new CameraCharacteristics(info);
+ }
+ }
+
/**
* For use by backwards-compatibility code only.
*/
diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
index b6b1968..2129260 100644
--- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
+++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
@@ -16,19 +16,12 @@
package android.hardware.camera2.impl;
-import static android.hardware.camera2.CameraAccessException.CAMERA_DISABLED;
-import static android.hardware.camera2.CameraAccessException.CAMERA_DISCONNECTED;
-import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE;
-import static android.hardware.camera2.CameraAccessException.CAMERA_ERROR;
-import static android.hardware.camera2.CameraAccessException.MAX_CAMERAS_IN_USE;
-import static android.hardware.camera2.CameraAccessException.CAMERA_DEPRECATED_HAL;
-
import android.hardware.ICameraService;
-import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.ICameraDeviceUser;
import android.hardware.camera2.ICameraDeviceCallbacks;
+import android.hardware.camera2.ICameraDeviceUser;
import android.hardware.camera2.ICameraOfflineSession;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.params.OutputConfiguration;
@@ -205,6 +198,28 @@
}
}
+ /**
+ * Fetches the CameraCharacteristics for a given session configuration.
+ */
+ public CameraMetadataNative getSessionCharacteristics(SessionConfiguration sessionConfig)
+ throws CameraAccessException {
+ try {
+ return mRemoteDevice.getSessionCharacteristics(sessionConfig);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ICameraService.ERROR_INVALID_OPERATION) {
+ throw new UnsupportedOperationException("Session characteristics query not "
+ + "supported");
+ } else if (e.errorCode == ICameraService.ERROR_ILLEGAL_ARGUMENT) {
+ throw new IllegalArgumentException("Invalid session configuration");
+ }
+
+ throw e;
+ } catch (Throwable t) {
+ CameraManager.throwAsPublicException(t);
+ throw new UnsupportedOperationException("Unexpected exception", t);
+ }
+ }
+
public long flush() throws CameraAccessException {
try {
return mRemoteDevice.flush();
diff --git a/core/java/android/os/IVibratorManagerService.aidl b/core/java/android/os/IVibratorManagerService.aidl
index 0f27569..65e498e 100644
--- a/core/java/android/os/IVibratorManagerService.aidl
+++ b/core/java/android/os/IVibratorManagerService.aidl
@@ -41,5 +41,5 @@
// There is no order guarantee with respect to the two-way APIs above like
// vibrate/isVibrating/cancel.
oneway void performHapticFeedback(int uid, int deviceId, String opPkg, int constant,
- boolean always, String reason, IBinder token);
+ boolean always, String reason);
}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 7e07e1f..fc8523e 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -1599,7 +1599,7 @@
* fully removed, otherwise system resources may leak.
* @hide
*/
- public static final native int sendSignalToProcessGroup(int uid, int pid, int signal);
+ public static final native boolean sendSignalToProcessGroup(int uid, int pid, int signal);
/**
* Freeze the cgroup for the given UID.
diff --git a/core/java/android/os/SystemVibratorManager.java b/core/java/android/os/SystemVibratorManager.java
index bc85412e..8e83923 100644
--- a/core/java/android/os/SystemVibratorManager.java
+++ b/core/java/android/os/SystemVibratorManager.java
@@ -39,6 +39,7 @@
private final IVibratorManagerService mService;
private final Context mContext;
+ private final int mUid;
private final Binder mToken = new Binder();
private final Object mLock = new Object();
@GuardedBy("mLock")
@@ -56,6 +57,7 @@
public SystemVibratorManager(Context context) {
super(context);
mContext = context;
+ mUid = Process.myUid();
mService = IVibratorManagerService.Stub.asInterface(
ServiceManager.getService(Context.VIBRATOR_MANAGER_SERVICE));
}
@@ -152,8 +154,7 @@
}
try {
mService.performHapticFeedback(
- Process.myUid(), mContext.getDeviceId(), mPackageName, constant, always, reason,
- mToken);
+ mUid, mContext.getDeviceId(), mPackageName, constant, always, reason);
} catch (RemoteException e) {
Log.w(TAG, "Failed to perform haptic feedback.", e);
}
diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig
index 437668c..69d86a6 100644
--- a/core/java/android/os/vibrator/flags.aconfig
+++ b/core/java/android/os/vibrator/flags.aconfig
@@ -44,10 +44,3 @@
description: "Enables the independent keyboard vibration settings feature"
bug: "289107579"
}
-
-flag {
- namespace: "haptics"
- name: "adaptive_haptics_enabled"
- description: "Enables the adaptive haptics feature"
- bug: "305961689"
-}
diff --git a/core/java/android/security/NetworkSecurityPolicy.java b/core/java/android/security/NetworkSecurityPolicy.java
index e679d20..0c4eeda 100644
--- a/core/java/android/security/NetworkSecurityPolicy.java
+++ b/core/java/android/security/NetworkSecurityPolicy.java
@@ -16,8 +16,6 @@
package android.security;
-import android.annotation.FlaggedApi;
-import android.annotation.NonNull;
import android.content.Context;
import android.content.pm.PackageManager;
import android.security.net.config.ApplicationConfig;
@@ -28,6 +26,9 @@
*
* <p>Network stacks/components should honor this policy to make it possible to centrally control
* the relevant aspects of network security behavior.
+ *
+ * <p>The policy currently consists of a single flag: whether cleartext network traffic is
+ * permitted. See {@link #isCleartextTrafficPermitted()}.
*/
public class NetworkSecurityPolicy {
@@ -93,22 +94,6 @@
}
/**
- * Returns {@code true} if Certificate Transparency information is required to be verified by
- * the client in TLS connections to {@code hostname}.
- *
- * <p>See RFC6962 section 3.3 for more details.
- *
- * @param hostname hostname to check whether certificate transparency verification is required
- * @return {@code true} if certificate transparency verification is required and {@code false}
- * otherwise
- */
- @FlaggedApi(Flags.FLAG_CERTIFICATE_TRANSPARENCY_CONFIGURATION)
- public boolean isCertificateTransparencyVerificationRequired(@NonNull String hostname) {
- return libcore.net.NetworkSecurityPolicy.getInstance()
- .isCertificateTransparencyVerificationRequired(hostname);
- }
-
- /**
* Handle an update to the system or user certificate stores.
* @hide
*/
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index 1a33b25..28ef70b 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -1,13 +1,6 @@
package: "android.security"
flag {
- name: "certificate_transparency_configuration"
- namespace: "network_security"
- description: "Enable certificate transparency setting in the network security config"
- bug: "28746284"
-}
-
-flag {
name: "fsverity_api"
namespace: "hardware_backed_security"
description: "Feature flag for fs-verity API"
@@ -15,6 +8,13 @@
}
flag {
+ name: "mgf1_digest_setter"
+ namespace: "hardware_backed_security"
+ description: "Feature flag for mgf1 digest setter in key generation and import parameters."
+ bug: "308378912"
+}
+
+flag {
name: "fix_unlocked_device_required_keys_v2"
namespace: "hardware_backed_security"
description: "Fix bugs in behavior of UnlockedDeviceRequired keystore keys"
diff --git a/core/java/android/security/net/config/ApplicationConfig.java b/core/java/android/security/net/config/ApplicationConfig.java
index 4cc870b..801eceb 100644
--- a/core/java/android/security/net/config/ApplicationConfig.java
+++ b/core/java/android/security/net/config/ApplicationConfig.java
@@ -16,15 +16,10 @@
package android.security.net.config;
-import static android.security.Flags.certificateTransparencyConfiguration;
-
-import android.annotation.NonNull;
import android.util.Pair;
-
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
-
import javax.net.ssl.X509TrustManager;
/**
@@ -152,22 +147,6 @@
return getConfigForHostname(hostname).isCleartextTrafficPermitted();
}
- /**
- * Returns {@code true} if Certificate Transparency information is required to be verified by
- * the client in TLS connections to {@code hostname}.
- *
- * <p>See RFC6962 section 3.3 for more details.
- *
- * @param hostname hostname to check whether certificate transparency verification is required
- * @return {@code true} if certificate transparency verification is required and {@code false}
- * otherwise
- */
- public boolean isCertificateTransparencyVerificationRequired(@NonNull String hostname) {
- return certificateTransparencyConfiguration()
- ? getConfigForHostname(hostname).isCertificateTransparencyVerificationRequired()
- : NetworkSecurityConfig.DEFAULT_CERTIFICATE_TRANSPARENCY_VERIFICATION_REQUIRED;
- }
-
public void handleTrustStorageUpdate() {
synchronized(mLock) {
// If the config is uninitialized then there is no work to be done to handle an update,
diff --git a/core/java/android/security/net/config/ConfigNetworkSecurityPolicy.java b/core/java/android/security/net/config/ConfigNetworkSecurityPolicy.java
index 801b32b..a708f5b 100644
--- a/core/java/android/security/net/config/ConfigNetworkSecurityPolicy.java
+++ b/core/java/android/security/net/config/ConfigNetworkSecurityPolicy.java
@@ -40,6 +40,6 @@
@Override
public boolean isCertificateTransparencyVerificationRequired(String hostname) {
- return mConfig.isCertificateTransparencyVerificationRequired(hostname);
+ return false;
}
}
diff --git a/core/java/android/security/net/config/NetworkSecurityConfig.java b/core/java/android/security/net/config/NetworkSecurityConfig.java
index 129ae63..00872fb 100644
--- a/core/java/android/security/net/config/NetworkSecurityConfig.java
+++ b/core/java/android/security/net/config/NetworkSecurityConfig.java
@@ -38,12 +38,9 @@
public static final boolean DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED = true;
/** @hide */
public static final boolean DEFAULT_HSTS_ENFORCED = false;
- /** @hide */
- public static final boolean DEFAULT_CERTIFICATE_TRANSPARENCY_VERIFICATION_REQUIRED = false;
private final boolean mCleartextTrafficPermitted;
private final boolean mHstsEnforced;
- private final boolean mCertificateTransparencyVerificationRequired;
private final PinSet mPins;
private final List<CertificatesEntryRef> mCertificatesEntryRefs;
private Set<TrustAnchor> mAnchors;
@@ -51,15 +48,10 @@
private NetworkSecurityTrustManager mTrustManager;
private final Object mTrustManagerLock = new Object();
- private NetworkSecurityConfig(
- boolean cleartextTrafficPermitted,
- boolean hstsEnforced,
- boolean certificateTransparencyVerificationRequired,
- PinSet pins,
- List<CertificatesEntryRef> certificatesEntryRefs) {
+ private NetworkSecurityConfig(boolean cleartextTrafficPermitted, boolean hstsEnforced,
+ PinSet pins, List<CertificatesEntryRef> certificatesEntryRefs) {
mCleartextTrafficPermitted = cleartextTrafficPermitted;
mHstsEnforced = hstsEnforced;
- mCertificateTransparencyVerificationRequired = certificateTransparencyVerificationRequired;
mPins = pins;
mCertificatesEntryRefs = certificatesEntryRefs;
// Sort the certificates entry refs so that all entries that override pins come before
@@ -112,11 +104,6 @@
return mHstsEnforced;
}
- // TODO(b/28746284): add exceptions for user-added certificates and enterprise overrides.
- public boolean isCertificateTransparencyVerificationRequired() {
- return mCertificateTransparencyVerificationRequired;
- }
-
public PinSet getPins() {
return mPins;
}
@@ -221,9 +208,6 @@
private boolean mHstsEnforced = DEFAULT_HSTS_ENFORCED;
private boolean mCleartextTrafficPermittedSet = false;
private boolean mHstsEnforcedSet = false;
- private boolean mCertificateTransparencyVerificationRequired =
- DEFAULT_CERTIFICATE_TRANSPARENCY_VERIFICATION_REQUIRED;
- private boolean mCertificateTransparencyVerificationRequiredSet = false;
private Builder mParentBuilder;
/**
@@ -329,35 +313,12 @@
return mCertificatesEntryRefs;
}
- Builder setCertificateTransparencyVerificationRequired(boolean required) {
- mCertificateTransparencyVerificationRequired = required;
- mCertificateTransparencyVerificationRequiredSet = true;
- return this;
- }
-
- private boolean getCertificateTransparencyVerificationRequired() {
- if (mCertificateTransparencyVerificationRequiredSet) {
- return mCertificateTransparencyVerificationRequired;
- }
- if (mParentBuilder != null) {
- return mParentBuilder.getCertificateTransparencyVerificationRequired();
- }
- return DEFAULT_CERTIFICATE_TRANSPARENCY_VERIFICATION_REQUIRED;
- }
-
public NetworkSecurityConfig build() {
boolean cleartextPermitted = getEffectiveCleartextTrafficPermitted();
boolean hstsEnforced = getEffectiveHstsEnforced();
- boolean certificateTransparencyVerificationRequired =
- getCertificateTransparencyVerificationRequired();
PinSet pinSet = getEffectivePinSet();
List<CertificatesEntryRef> entryRefs = getEffectiveCertificatesEntryRefs();
- return new NetworkSecurityConfig(
- cleartextPermitted,
- hstsEnforced,
- certificateTransparencyVerificationRequired,
- pinSet,
- entryRefs);
+ return new NetworkSecurityConfig(cleartextPermitted, hstsEnforced, pinSet, entryRefs);
}
}
}
diff --git a/core/java/android/security/net/config/XmlConfigSource.java b/core/java/android/security/net/config/XmlConfigSource.java
index b1c1479..311a8d2 100644
--- a/core/java/android/security/net/config/XmlConfigSource.java
+++ b/core/java/android/security/net/config/XmlConfigSource.java
@@ -171,11 +171,6 @@
return new Domain(domain, includeSubdomains);
}
- private boolean parseCertificateTransparency(XmlResourceParser parser)
- throws IOException, XmlPullParserException, ParserException {
- return parser.getAttributeBooleanValue(null, "enabled", false);
- }
-
private CertificatesEntryRef parseCertificatesEntry(XmlResourceParser parser,
boolean defaultOverridePins)
throws IOException, XmlPullParserException, ParserException {
@@ -231,6 +226,7 @@
boolean seenPinSet = false;
boolean seenTrustAnchors = false;
boolean defaultOverridePins = configType == CONFIG_DEBUG;
+ String configName = parser.getName();
int outerDepth = parser.getDepth();
// Add this builder now so that this builder occurs before any of its children. This
// makes the final build pass easier.
@@ -283,15 +279,6 @@
"Nested domain-config not allowed in " + getConfigString(configType));
}
builders.addAll(parseConfigEntry(parser, seenDomains, builder, configType));
- } else if ("certificateTransparency".equals(tagName)) {
- if (configType != CONFIG_BASE && configType != CONFIG_DOMAIN) {
- throw new ParserException(
- parser,
- "certificateTransparency not allowed in "
- + getConfigString(configType));
- }
- builder.setCertificateTransparencyVerificationRequired(
- parseCertificateTransparency(parser));
} else {
XmlUtils.skipCurrentTag(parser);
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1530aa7..7134529 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3216,6 +3216,12 @@
endDragResizing();
destroyHardwareResources();
}
+
+ if (sToolkitSetFrameRateReadOnlyFlagValue && viewVisibility == View.VISIBLE) {
+ // Boost frame rate when the viewVisibility becomes true.
+ // This is mainly for lanuchers that lanuch new windows.
+ boostFrameRate(FRAME_RATE_TOUCH_BOOST_TIME);
+ }
}
// Non-visible windows can't hold accessibility focus.
@@ -3925,6 +3931,11 @@
focused.restoreDefaultFocus();
}
}
+
+ if (sToolkitSetFrameRateReadOnlyFlagValue) {
+ // Boost the frame rate when the ViewRootImpl first becomes available.
+ boostFrameRate(FRAME_RATE_TOUCH_BOOST_TIME);
+ }
}
final boolean changedVisibility = (viewVisibilityChanged || mFirst) && isViewVisible;
@@ -12036,7 +12047,7 @@
try {
if (mLastPreferredFrameRateCategory != frameRateCategory) {
mFrameRateTransaction.setFrameRateCategory(mSurfaceControl,
- frameRateCategory, false).applyAsyncUnsafe();
+ frameRateCategory, false).applyAsyncUnsafe();
mLastPreferredFrameRateCategory = frameRateCategory;
}
} catch (Exception e) {
@@ -12159,6 +12170,22 @@
return mPreferredFrameRate;
}
+ /**
+ * Get the value of mIsFrameRateBoosting
+ */
+ @VisibleForTesting
+ public boolean getIsFrameRateBoosting() {
+ return mIsFrameRateBoosting;
+ }
+
+ private void boostFrameRate(int boostTimeOut) {
+ mIsFrameRateBoosting = true;
+ setPreferredFrameRateCategory(mPreferredFrameRateCategory);
+ mHandler.removeMessages(MSG_TOUCH_BOOST_TIMEOUT);
+ mHandler.sendEmptyMessageDelayed(MSG_TOUCH_BOOST_TIMEOUT,
+ boostTimeOut);
+ }
+
@Override
public boolean transferHostTouchGestureToEmbedded(
@NonNull SurfaceControlViewHost.SurfacePackage surfacePackage) {
diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java
index 1e0b2a0..efc1455 100644
--- a/core/java/com/android/internal/app/SuspendedAppActivity.java
+++ b/core/java/com/android/internal/app/SuspendedAppActivity.java
@@ -80,7 +80,8 @@
// Suspension conditions were modified, dismiss any related visible dialogs.
final String[] modified = intent.getStringArrayExtra(
Intent.EXTRA_CHANGED_PACKAGE_LIST);
- if (ArrayUtils.contains(modified, mSuspendedPackage)) {
+ if (ArrayUtils.contains(modified, mSuspendedPackage)
+ && !isPackageSuspended(mSuspendedPackage)) {
if (!isFinishing()) {
Slog.w(TAG, "Package " + mSuspendedPackage + " has modified"
+ " suspension conditions while dialog was visible. Finishing.");
@@ -92,6 +93,15 @@
}
};
+ private boolean isPackageSuspended(String packageName) {
+ try {
+ return mPm.isPackageSuspended(packageName);
+ } catch (PackageManager.NameNotFoundException ne) {
+ Slog.e(TAG, "Package " + packageName + " not found", ne);
+ }
+ return false;
+ }
+
private CharSequence getAppLabel(String packageName) {
try {
return mPm.getApplicationInfoAsUser(packageName, 0, mUserId).loadLabel(mPm);
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 91dfc60..43e0c34 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -1238,7 +1238,7 @@
return killProcessGroup(uid, pid, SIGKILL);
}
-jint android_os_Process_sendSignalToProcessGroup(JNIEnv* env, jobject clazz, jint uid, jint pid,
+jboolean android_os_Process_sendSignalToProcessGroup(JNIEnv* env, jobject clazz, jint uid, jint pid,
jint signal) {
return sendSignalToProcessGroup(uid, pid, signal);
}
@@ -1310,7 +1310,7 @@
//{"setApplicationObject", "(Landroid/os/IBinder;)V",
//(void*)android_os_Process_setApplicationObject},
{"killProcessGroup", "(II)I", (void*)android_os_Process_killProcessGroup},
- {"sendSignalToProcessGroup", "(III)I", (void*)android_os_Process_sendSignalToProcessGroup},
+ {"sendSignalToProcessGroup", "(III)Z", (void*)android_os_Process_sendSignalToProcessGroup},
{"removeAllProcessGroups", "()V", (void*)android_os_Process_removeAllProcessGroups},
{"nativePidFdOpen", "(II)I", (void*)android_os_Process_nativePidFdOpen},
{"freezeCgroupUid", "(IZ)V", (void*)android_os_Process_freezeCgroupUID},
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 0ad349b..88abaa1 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -179,8 +179,10 @@
"androidx.test.ext.junit",
"mockito_ravenwood",
"platform-test-annotations",
+ "flag-junit",
],
srcs: [
+ "src/android/os/BuildTest.java",
"src/android/os/FileUtilsTest.java",
"src/android/util/**/*.java",
"src/com/android/internal/util/**/*.java",
diff --git a/core/tests/coretests/src/android/os/BuildTest.java b/core/tests/coretests/src/android/os/BuildTest.java
index 2295eb9..3162e6d 100644
--- a/core/tests/coretests/src/android/os/BuildTest.java
+++ b/core/tests/coretests/src/android/os/BuildTest.java
@@ -16,19 +16,37 @@
package android.os;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.platform.test.ravenwood.RavenwoodRule;
+
import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import junit.framework.Assert;
-import junit.framework.TestCase;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Provides test cases for android.os.Build and, in turn, many of the
* system properties set by the build system.
*/
-public class BuildTest extends TestCase {
-
+@RunWith(AndroidJUnit4.class)
+public class BuildTest {
private static final String TAG = "BuildTest";
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
/**
* Asserts that a String is non-null and non-empty. If it is not,
* an AssertionFailedError is thrown with the given message.
@@ -50,7 +68,9 @@
/**
* Asserts that all android.os.Build fields are non-empty and/or in a valid range.
*/
+ @Test
@SmallTest
+ @IgnoreUnderRavenwood(blockedBy = Build.class)
public void testBuildFields() throws Exception {
assertNotEmpty("ID", Build.ID);
assertNotEmpty("DISPLAY", Build.DISPLAY);
@@ -72,4 +92,16 @@
// (e.g., must be a C identifier, must be a valid filename, must not contain any spaces)
// add tests for them.
}
+
+ @Test
+ public void testFlagEnabled() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ANDROID_OS_BUILD_VANILLA_ICE_CREAM);
+ assertTrue(Flags.androidOsBuildVanillaIceCream());
+ }
+
+ @Test
+ public void testFlagDisabled() throws Exception {
+ mSetFlagsRule.disableFlags(Flags.FLAG_ANDROID_OS_BUILD_VANILLA_ICE_CREAM);
+ assertFalse(Flags.androidOsBuildVanillaIceCream());
+ }
}
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 6172622..b30a0c8 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -472,6 +472,7 @@
* Test the value of the frame rate cateogry based on the visibility of a view
* Invsible: FRAME_RATE_CATEGORY_NO_PREFERENCE
* Visible: FRAME_RATE_CATEGORY_NORMAL
+ * Also, mIsFrameRateBoosting should be true when the visibility becomes visible
*/
@Test
@RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
@@ -485,6 +486,7 @@
assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
FRAME_RATE_CATEGORY_NO_PREFERENCE);
});
+ sInstrumentation.waitForIdleSync();
sInstrumentation.runOnMainSync(() -> {
view.setVisibility(View.VISIBLE);
@@ -492,6 +494,11 @@
assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
FRAME_RATE_CATEGORY_NORMAL);
});
+ sInstrumentation.waitForIdleSync();
+
+ sInstrumentation.runOnMainSync(() -> {
+ assertEquals(viewRootImpl.getIsFrameRateBoosting(), true);
+ });
}
/**
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 92c4de6..f10cdb8 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1739,10 +1739,6 @@
/**
* Get the elegant metrics flag.
*
- * From API {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, the default value will be true by
- * default if the app has a target SDK of API {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} or
- * later.
- *
* @return true if elegant metrics are enabled for text drawing.
*/
public boolean isElegantTextHeight() {
diff --git a/keystore/java/android/security/Authorization.java b/keystore/java/android/security/Authorization.java
index b4b3e92..4ec5e1b 100644
--- a/keystore/java/android/security/Authorization.java
+++ b/keystore/java/android/security/Authorization.java
@@ -26,7 +26,6 @@
import android.os.ServiceSpecificException;
import android.os.StrictMode;
import android.security.authorization.IKeystoreAuthorization;
-import android.security.authorization.LockScreenEvent;
import android.system.keystore2.ResponseCode;
import android.util.Log;
@@ -76,26 +75,37 @@
}
/**
- * Informs keystore2 about lock screen event.
+ * Tells Keystore that the device is now unlocked for a user.
*
- * @param locked - whether it is a lock (true) or unlock (false) event
- * @param syntheticPassword - if it is an unlock event with the password, pass the synthetic
- * password provided by the LockSettingService
- * @param unlockingSids - KeyMint secure user IDs that should be permitted to unlock
- * UNLOCKED_DEVICE_REQUIRED keys.
- *
+ * @param userId - the user's Android user ID
+ * @param password - a secret derived from the user's synthetic password, if the unlock method
+ * is LSKF (or equivalent) and thus has made the synthetic password available
* @return 0 if successful or a {@code ResponseCode}.
*/
- public static int onLockScreenEvent(@NonNull boolean locked, @NonNull int userId,
- @Nullable byte[] syntheticPassword, @Nullable long[] unlockingSids) {
+ public static int onDeviceUnlocked(int userId, @Nullable byte[] password) {
StrictMode.noteDiskWrite();
try {
- if (locked) {
- getService().onLockScreenEvent(LockScreenEvent.LOCK, userId, null, unlockingSids);
- } else {
- getService().onLockScreenEvent(
- LockScreenEvent.UNLOCK, userId, syntheticPassword, unlockingSids);
- }
+ getService().onDeviceUnlocked(userId, password);
+ return 0;
+ } catch (RemoteException | NullPointerException e) {
+ Log.w(TAG, "Can not connect to keystore", e);
+ return SYSTEM_ERROR;
+ } catch (ServiceSpecificException e) {
+ return e.errorCode;
+ }
+ }
+
+ /**
+ * Tells Keystore that the device is now locked for a user.
+ *
+ * @param userId - the user's Android user ID
+ * @param unlockingSids - list of biometric SIDs with which the device may be unlocked again
+ * @return 0 if successful or a {@code ResponseCode}.
+ */
+ public static int onDeviceLocked(int userId, @NonNull long[] unlockingSids) {
+ StrictMode.noteDiskWrite();
+ try {
+ getService().onDeviceLocked(userId, unlockingSids);
return 0;
} catch (RemoteException | NullPointerException e) {
Log.w(TAG, "Can not connect to keystore", e);
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 231fa48..4982f37 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -618,7 +618,7 @@
* @see #isMgf1DigestsSpecified()
*/
@NonNull
- @FlaggedApi("MGF1_DIGEST_SETTER")
+ @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER)
public @KeyProperties.DigestEnum Set<String> getMgf1Digests() {
if (mMgf1Digests.isEmpty()) {
throw new IllegalStateException("Mask generation function (MGF) not specified");
@@ -633,7 +633,7 @@
* @see #getMgf1Digests()
*/
@NonNull
- @FlaggedApi("MGF1_DIGEST_SETTER")
+ @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER)
public boolean isMgf1DigestsSpecified() {
return !mMgf1Digests.isEmpty();
}
@@ -1292,7 +1292,7 @@
* <p>See {@link KeyProperties}.{@code DIGEST} constants.
*/
@NonNull
- @FlaggedApi("MGF1_DIGEST_SETTER")
+ @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER)
public Builder setMgf1Digests(@NonNull @KeyProperties.DigestEnum String... mgf1Digests) {
mMgf1Digests = Set.of(mgf1Digests);
return this;
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index c1e3bab..7b6b2d1 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -401,7 +401,7 @@
* @see #isMgf1DigestsSpecified()
*/
@NonNull
- @FlaggedApi("MGF1_DIGEST_SETTER")
+ @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER)
public @KeyProperties.DigestEnum Set<String> getMgf1Digests() {
if (mMgf1Digests.isEmpty()) {
throw new IllegalStateException("Mask generation function (MGF) not specified");
@@ -416,7 +416,7 @@
* @see #getMgf1Digests()
*/
@NonNull
- @FlaggedApi("MGF1_DIGEST_SETTER")
+ @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER)
public boolean isMgf1DigestsSpecified() {
return !mMgf1Digests.isEmpty();
}
@@ -799,7 +799,7 @@
* <p>See {@link KeyProperties}.{@code DIGEST} constants.
*/
@NonNull
- @FlaggedApi("MGF1_DIGEST_SETTER")
+ @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER)
public Builder setMgf1Digests(@Nullable @KeyProperties.DigestEnum String... mgf1Digests) {
mMgf1Digests = Set.of(mgf1Digests);
return this;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index ed4b485..9c05a3a 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -28,6 +28,7 @@
import android.hardware.security.keymint.Tag;
import android.os.Build;
import android.os.StrictMode;
+import android.security.Flags;
import android.security.KeyPairGeneratorSpec;
import android.security.KeyStore2;
import android.security.KeyStoreException;
@@ -853,6 +854,22 @@
KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, mgf1Digest
));
});
+
+ /* If the MGF1 Digest setter is not set, fall back to the previous behaviour:
+ * Add, as MGF1 Digest function, all the primary digests.
+ * Avoid adding the default MGF1 digest as it will have been included in the
+ * mKeymasterMgf1Digests field.
+ */
+ if (!getMgf1DigestSetterFlag()) {
+ final int defaultMgf1Digest = KeyProperties.Digest.toKeymaster(
+ DEFAULT_MGF1_DIGEST);
+ ArrayUtils.forEach(mKeymasterDigests, (digest) -> {
+ if (digest != defaultMgf1Digest) {
+ params.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, digest));
+ }
+ });
+ }
}
});
ArrayUtils.forEach(mKeymasterSignaturePaddings, (padding) -> {
@@ -928,6 +945,16 @@
return params;
}
+ private static boolean getMgf1DigestSetterFlag() {
+ try {
+ return Flags.mgf1DigestSetter();
+ } catch (SecurityException e) {
+ Log.w(TAG, "Cannot read MGF1 Digest setter flag value", e);
+ return false;
+ }
+ }
+
+
private void addAlgorithmSpecificParameters(List<KeyParameter> params) {
switch (mKeymasterAlgorithm) {
case KeymasterDefs.KM_ALGORITHM_RSA:
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index ddbd93e..2d8c5a3 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -25,6 +25,7 @@
import android.hardware.security.keymint.KeyParameter;
import android.hardware.security.keymint.SecurityLevel;
import android.os.StrictMode;
+import android.security.Flags;
import android.security.GateKeeper;
import android.security.KeyStore2;
import android.security.KeyStoreParameter;
@@ -256,6 +257,15 @@
}
}
+ private static boolean getMgf1DigestSetterFlag() {
+ try {
+ return Flags.mgf1DigestSetter();
+ } catch (SecurityException e) {
+ Log.w(NAME, "Cannot read MGF1 Digest setter flag value", e);
+ return false;
+ }
+ }
+
@Override
public Date engineGetCreationDate(String alias) {
KeyEntryResponse response = getKeyMetadata(alias);
@@ -537,11 +547,31 @@
/* Because of default MGF1 digest is SHA-1. It has to be added in Key
* characteristics. Otherwise, crypto operations will fail with Incompatible
* MGF1 digest.
+ * If the MGF1 Digest setter flag isn't set, then the condition in the
+ * if clause above must be false (cannot have MGF1 digests specified if the
+ * flag was off). In that case, in addition to adding the default MGF1
+ * digest, we have to add all the other digests as MGF1 Digests.
+ *
*/
importArgs.add(KeyStore2ParameterUtils.makeEnum(
KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST,
KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST)
));
+ if (!getMgf1DigestSetterFlag()) {
+ final int defaultMgf1Digest = KeyProperties.Digest.toKeymaster(
+ DEFAULT_MGF1_DIGEST);
+ for (String digest : spec.getDigests()) {
+ int digestToAddAsMgf1Digest = KeyProperties.Digest.toKeymaster(
+ digest);
+ // Do not add the default MGF1 digest as it has been added above.
+ if (digestToAddAsMgf1Digest != defaultMgf1Digest) {
+ importArgs.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST,
+ digestToAddAsMgf1Digest
+ ));
+ }
+ }
+ }
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 85ea809..7a3210e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -637,7 +637,7 @@
* @return the last time this bubble was updated or accessed, whichever is most recent.
*/
long getLastActivity() {
- return isAppBubble() ? Long.MAX_VALUE : Math.max(mLastUpdated, mLastAccessed);
+ return Math.max(mLastUpdated, mLastAccessed);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 595a4af..bbb4b74 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -784,8 +784,7 @@
if (bubble.getPendingIntentCanceled()
|| !(reason == Bubbles.DISMISS_AGED
|| reason == Bubbles.DISMISS_USER_GESTURE
- || reason == Bubbles.DISMISS_RELOAD_FROM_DISK)
- || bubble.isAppBubble()) {
+ || reason == Bubbles.DISMISS_RELOAD_FROM_DISK)) {
return;
}
if (DEBUG_BUBBLE_DATA) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index 4bca96b..dab762f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -221,6 +221,22 @@
}
@Test
+ public void testAddAppBubble_setsTime() {
+ // Setup
+ mBubbleData.setListener(mListener);
+
+ // Test
+ assertThat(mAppBubble.getLastActivity()).isEqualTo(0);
+ setCurrentTime(1000);
+ mBubbleData.notificationEntryUpdated(mAppBubble, true /* suppressFlyout*/,
+ false /* showInShade */);
+
+ // Verify
+ assertThat(mBubbleData.getBubbleInStackWithKey(mAppBubble.getKey())).isEqualTo(mAppBubble);
+ assertThat(mAppBubble.getLastActivity()).isEqualTo(1000);
+ }
+
+ @Test
public void testRemoveBubble() {
// Setup
sendUpdatedEntryAtTime(mEntryA1, 1000);
@@ -1162,7 +1178,7 @@
}
@Test
- public void test_removeAppBubble_skipsOverflow() {
+ public void test_removeAppBubble_overflows() {
String appBubbleKey = mAppBubble.getKey();
mBubbleData.notificationEntryUpdated(mAppBubble, true /* suppressFlyout*/,
false /* showInShade */);
@@ -1170,7 +1186,7 @@
mBubbleData.dismissBubbleWithKey(appBubbleKey, Bubbles.DISMISS_USER_GESTURE);
- assertThat(mBubbleData.getOverflowBubbleWithKey(appBubbleKey)).isNull();
+ assertThat(mBubbleData.getOverflowBubbleWithKey(appBubbleKey)).isEqualTo(mAppBubble);
assertThat(mBubbleData.getBubbleInStackWithKey(appBubbleKey)).isNull();
}
diff --git a/libs/hwui/jni/YuvToJpegEncoder.cpp b/libs/hwui/jni/YuvToJpegEncoder.cpp
index 0275e4f..3533001 100644
--- a/libs/hwui/jni/YuvToJpegEncoder.cpp
+++ b/libs/hwui/jni/YuvToJpegEncoder.cpp
@@ -401,7 +401,7 @@
if (int success = jpegREncoder.encodeJPEGR(&p010, &yuv420,
hdrTransferFunction,
&jpegR, jpegQuality,
- exif.length > 0 ? &exif : NULL); success != android::OK) {
+ exif.length > 0 ? &exif : NULL); success != JPEGR_NO_ERROR) {
ALOGW("Encode JPEG/R failed, error code: %d.", success);
return false;
}
diff --git a/location/java/android/location/GnssNavigationMessage.java b/location/java/android/location/GnssNavigationMessage.java
index 32e636f..5e3f803 100644
--- a/location/java/android/location/GnssNavigationMessage.java
+++ b/location/java/android/location/GnssNavigationMessage.java
@@ -78,7 +78,7 @@
public static final int TYPE_GAL_F = 0x0602;
/**
* NavIC L5 C/A message contained in the structure.
- * @deprecated Use {@link #TYPE_IRN_L5} instead.
+ * @deprecated deprecated.
*/
@Deprecated
public static final int TYPE_IRN_L5CA = 0x0701;
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/chart/BarChart.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/chart/BarChart.kt
index e761a33..caceb6f 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/chart/BarChart.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/chart/BarChart.kt
@@ -38,6 +38,8 @@
import com.android.settingslib.spa.framework.theme.divider
import com.github.mikephil.charting.charts.BarChart
import com.github.mikephil.charting.components.XAxis
+import com.github.mikephil.charting.components.YAxis
+import com.github.mikephil.charting.components.YAxis.AxisDependency
import com.github.mikephil.charting.data.BarData
import com.github.mikephil.charting.data.BarDataSet
import com.github.mikephil.charting.data.BarEntry
@@ -90,6 +92,10 @@
/** If set to true, touch gestures are enabled on the [BarChart]. */
val enableBarchartTouch: Boolean
get() = true
+
+ /** The renderer provider for x-axis. */
+ val xAxisRendererProvider: XAxisRendererProvider?
+ get() = null
}
data class BarChartData(
@@ -143,6 +149,16 @@
yOffset = 10f
}
+ barChartModel.xAxisRendererProvider?.let {
+ setXAxisRenderer(
+ it.provideXAxisRenderer(
+ getViewPortHandler(),
+ getXAxis(),
+ getTransformer(YAxis.AxisDependency.LEFT)
+ )
+ )
+ }
+
axisLeft.apply {
axisMaximum = barChartModel.yAxisMaxValue
axisMinimum = barChartModel.yAxisMinValue
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/chart/XAxisRendererProvider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/chart/XAxisRendererProvider.kt
new file mode 100644
index 0000000..6569d25
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/chart/XAxisRendererProvider.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.settingslib.spa.widget.chart
+
+import com.github.mikephil.charting.components.XAxis
+import com.github.mikephil.charting.renderer.XAxisRenderer
+import com.github.mikephil.charting.utils.Transformer
+import com.github.mikephil.charting.utils.ViewPortHandler
+
+/** A provider for [XAxisRenderer] objects. */
+fun interface XAxisRendererProvider {
+
+ /** Provides an object of [XAxisRenderer] type. */
+ fun provideXAxisRenderer(
+ viewPortHandler: ViewPortHandler,
+ xAxis: XAxis,
+ transformer: Transformer
+ ): XAxisRenderer
+}
\ No newline at end of file
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index c2c5e00..7061e2c 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -281,6 +281,7 @@
"com_android_systemui_flags_lib",
"com_android_systemui_shared_flags_lib",
"flag-junit-base",
+ "platform-parametric-runner-lib",
"androidx.viewpager2_viewpager2",
"androidx.legacy_legacy-support-v4",
"androidx.recyclerview_recyclerview",
diff --git a/packages/SystemUI/aconfig/Android.bp b/packages/SystemUI/aconfig/Android.bp
index e842967..50ed7ab 100644
--- a/packages/SystemUI/aconfig/Android.bp
+++ b/packages/SystemUI/aconfig/Android.bp
@@ -23,6 +23,7 @@
default_visibility: [
"//visibility:override",
"//frameworks/base/packages/SystemUI:__subpackages__",
+ "//platform_testing:__subpackages__"
],
}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 2970aaa..a26b311 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -211,6 +211,13 @@
}
flag {
+ name: "enable_layout_tracing"
+ namespace: "systemui"
+ description: "Enables detailed traversal slices during measure and layout in perfetto traces"
+ bug: "315274804"
+}
+
+flag {
name: "quick_settings_visual_haptics_longpress"
namespace: "systemui"
description: "Enable special visual and haptic effects for quick settings tiles with long-press actions"
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/GestureHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/GestureHandler.kt
index 5d8eaf7..58052cd 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/GestureHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/GestureHandler.kt
@@ -2,10 +2,9 @@
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
-import androidx.compose.ui.unit.IntSize
interface DraggableHandler {
- fun onDragStarted(layoutSize: IntSize, startedPosition: Offset, pointersDown: Int = 1)
+ fun onDragStarted(startedPosition: Offset, overSlop: Float, pointersDown: Int = 1)
fun onDelta(pixels: Float)
fun onDragStopped(velocity: Float)
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
index a0fba80..3873878 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
@@ -66,8 +66,8 @@
orientation: Orientation,
enabled: Boolean,
startDragImmediately: Boolean,
- onDragStarted: (layoutSize: IntSize, startedPosition: Offset, pointersDown: Int) -> Unit,
- onDragDelta: (Float) -> Unit,
+ onDragStarted: (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> Unit,
+ onDragDelta: (delta: Float) -> Unit,
onDragStopped: (velocity: Float) -> Unit,
): Modifier =
this.then(
@@ -86,7 +86,7 @@
private val enabled: Boolean,
private val startDragImmediately: Boolean,
private val onDragStarted:
- (layoutSize: IntSize, startedPosition: Offset, pointersDown: Int) -> Unit,
+ (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> Unit,
private val onDragDelta: (Float) -> Unit,
private val onDragStopped: (velocity: Float) -> Unit,
) : ModifierNodeElement<MultiPointerDraggableNode>() {
@@ -114,7 +114,7 @@
orientation: Orientation,
enabled: Boolean,
var startDragImmediately: Boolean,
- var onDragStarted: (layoutSize: IntSize, startedPosition: Offset, pointersDown: Int) -> Unit,
+ var onDragStarted: (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> Unit,
var onDragDelta: (Float) -> Unit,
var onDragStopped: (velocity: Float) -> Unit,
) : PointerInputModifierNode, DelegatingNode(), CompositionLocalConsumerModifierNode {
@@ -153,9 +153,9 @@
return
}
- val onDragStart: (Offset, Int) -> Unit = { startedPosition, pointersDown ->
+ val onDragStart: (Offset, Float, Int) -> Unit = { startedPosition, overSlop, pointersDown ->
velocityTracker.resetTracking()
- onDragStarted(size, startedPosition, pointersDown)
+ onDragStarted(startedPosition, overSlop, pointersDown)
}
val onDragCancel: () -> Unit = { onDragStopped(/* velocity= */ 0f) }
@@ -203,7 +203,7 @@
private suspend fun PointerInputScope.detectDragGestures(
orientation: Orientation,
startDragImmediately: () -> Boolean,
- onDragStart: (startedPosition: Offset, pointersDown: Int) -> Unit,
+ onDragStart: (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> Unit,
onDragEnd: () -> Unit,
onDragCancel: () -> Unit,
onDrag: (change: PointerInputChange, dragAmount: Float) -> Unit,
@@ -241,7 +241,7 @@
}
}
- onDragStart(drag.position, pressed.size)
+ onDragStart(drag.position, overSlop, pressed.size)
onDrag(drag, overSlop)
val successful =
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
index 2986504..ded6cc1 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
@@ -44,22 +44,22 @@
* Overscroll will only be used by the [SceneTransitionLayout] to move to the next scene if the
* gesture begins at the edge of the scrollable component (so that a scroll in that direction
* can no longer be consumed). If the gesture is partially consumed by the scrollable component,
- * there will be NO overscroll effect between scenes.
+ * there will be NO preview of the next scene.
*
* In addition, during scene transitions, scroll events are consumed by the
* [SceneTransitionLayout] instead of the scrollable component.
*/
- EdgeNoOverscroll(canStartOnPostFling = false),
+ EdgeNoPreview(canStartOnPostFling = false),
/**
* Overscroll will only be used by the [SceneTransitionLayout] to move to the next scene if the
* gesture begins at the edge of the scrollable component. If the gesture is partially consumed
- * by the scrollable component, there will be an overscroll effect between scenes.
+ * by the scrollable component, there will be a preview of the next scene.
*
* In addition, during scene transitions, scroll events are consumed by the
* [SceneTransitionLayout] instead of the scrollable component.
*/
- EdgeWithOverscroll(canStartOnPostFling = true),
+ EdgeWithPreview(canStartOnPostFling = true),
/**
* Any overscroll will be used by the [SceneTransitionLayout] to move to the next scene.
@@ -67,7 +67,7 @@
* In addition, during scene transitions, scroll events are consumed by the
* [SceneTransitionLayout] instead of the scrollable component.
*/
- Always(canStartOnPostFling = true),
+ EdgeAlways(canStartOnPostFling = true),
}
internal fun Modifier.nestedScrollToScene(
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
index b00c886..212c9eb6 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:Suppress("NOTHING_TO_INLINE")
+
package com.android.compose.animation.scene
import android.util.Log
@@ -26,7 +28,6 @@
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.round
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
@@ -48,14 +49,13 @@
layoutImpl.state.transitionState = value
}
- /**
- * The transition controlled by this gesture handler. It will be set as the [transitionState] in
- * the [SceneTransitionLayoutImpl] whenever this handler is driving the current transition.
- *
- * Note: the initialScene here does not matter, it's only used for initializing the transition
- * and will be replaced when a drag event starts.
- */
- internal val swipeTransition = SwipeTransition(initialScene = currentScene)
+ internal var swipeTransition: SwipeTransition = SwipeTransition(currentScene, currentScene, 1f)
+ private set
+
+ private fun updateTransition(newTransition: SwipeTransition, force: Boolean = false) {
+ if (isDrivingTransition || force) transitionState = newTransition
+ swipeTransition = newTransition
+ }
internal val currentScene: Scene
get() = layoutImpl.scene(transitionState.currentScene)
@@ -63,15 +63,6 @@
internal val isDrivingTransition
get() = transitionState == swipeTransition
- internal var isAnimatingOffset
- get() = swipeTransition.isAnimatingOffset
- private set(value) {
- swipeTransition.isAnimatingOffset = value
- }
-
- internal val swipeTransitionToScene
- get() = swipeTransition._toScene
-
/**
* The velocity threshold at which the intent of the user is to swipe up or down. It is the same
* as SwipeableV2Defaults.VelocityThreshold.
@@ -86,11 +77,17 @@
internal var gestureWithPriority: Any? = null
- internal fun onDragStarted(pointersDown: Int, layoutSize: IntSize, startedPosition: Offset?) {
+ /** The [UserAction]s associated to the current swipe. */
+ private var actionUpOrLeft: UserAction? = null
+ private var actionDownOrRight: UserAction? = null
+ private var actionUpOrLeftNoEdge: UserAction? = null
+ private var actionDownOrRightNoEdge: UserAction? = null
+
+ internal fun onDragStarted(pointersDown: Int, startedPosition: Offset?, overSlop: Float) {
if (isDrivingTransition) {
// This [transition] was already driving the animation: simply take over it.
// Stop animating and start from where the current offset.
- swipeTransition.stopOffsetAnimation()
+ swipeTransition.cancelOffsetAnimation()
return
}
@@ -106,37 +103,29 @@
}
val fromScene = currentScene
+ setCurrentActions(fromScene, startedPosition, pointersDown)
- swipeTransition._currentScene = fromScene
- swipeTransition._fromScene = fromScene
+ if (fromScene.upOrLeft() == null && fromScene.downOrRight() == null) {
+ return
+ }
- // We don't know where we are transitioning to yet given that the drag just started, so set
- // it to fromScene, which will effectively be treated the same as Idle(fromScene).
- swipeTransition._toScene = fromScene
+ val (targetScene, distance) = fromScene.findTargetSceneAndDistance(overSlop)
- swipeTransition.stopOffsetAnimation()
- swipeTransition.dragOffset = 0f
+ updateTransition(SwipeTransition(fromScene, targetScene, distance), force = true)
+ }
- // Use the layout size in the swipe orientation for swipe distance.
- // TODO(b/290184746): Also handle custom distances for transitions. With smaller distances,
- // we will also have to make sure that we correctly handle overscroll.
- swipeTransition.absoluteDistance =
- when (orientation) {
- Orientation.Horizontal -> layoutSize.width
- Orientation.Vertical -> layoutSize.height
- }.toFloat()
-
+ private fun setCurrentActions(fromScene: Scene, startedPosition: Offset?, pointersDown: Int) {
val fromEdge =
startedPosition?.let { position ->
layoutImpl.edgeDetector.edge(
- layoutSize,
+ fromScene.targetSize,
position.round(),
layoutImpl.density,
orientation,
)
}
- swipeTransition.actionUpOrLeft =
+ val upOrLeft =
Swipe(
direction =
when (orientation) {
@@ -147,7 +136,7 @@
fromEdge = fromEdge,
)
- swipeTransition.actionDownOrRight =
+ val downOrRight =
Swipe(
direction =
when (orientation) {
@@ -159,108 +148,114 @@
)
if (fromEdge == null) {
- swipeTransition.actionUpOrLeftNoEdge = null
- swipeTransition.actionDownOrRightNoEdge = null
+ actionUpOrLeft = null
+ actionDownOrRight = null
+ actionUpOrLeftNoEdge = upOrLeft
+ actionDownOrRightNoEdge = downOrRight
} else {
- swipeTransition.actionUpOrLeftNoEdge =
- (swipeTransition.actionUpOrLeft as Swipe).copy(fromEdge = null)
- swipeTransition.actionDownOrRightNoEdge =
- (swipeTransition.actionDownOrRight as Swipe).copy(fromEdge = null)
- }
-
- if (swipeTransition.absoluteDistance > 0f) {
- transitionState = swipeTransition
+ actionUpOrLeft = upOrLeft
+ actionDownOrRight = downOrRight
+ actionUpOrLeftNoEdge = upOrLeft.copy(fromEdge = null)
+ actionDownOrRightNoEdge = downOrRight.copy(fromEdge = null)
}
}
- internal fun onDrag(delta: Float) {
- if (delta == 0f) return
+ /**
+ * Use the layout size in the swipe orientation for swipe distance.
+ *
+ * TODO(b/290184746): Also handle custom distances for transitions. With smaller distances, we
+ * will also have to make sure that we correctly handle overscroll.
+ */
+ private fun Scene.getAbsoluteDistance(): Float {
+ return when (orientation) {
+ Orientation.Horizontal -> targetSize.width
+ Orientation.Vertical -> targetSize.height
+ }.toFloat()
+ }
+ internal fun onDrag(delta: Float) {
+ if (delta == 0f || !isDrivingTransition) return
swipeTransition.dragOffset += delta
- // First check transition.fromScene should be changed for the case where the user quickly
- // swiped twice in a row to accelerate the transition and go from A => B then B => C really
- // fast.
- maybeHandleAcceleratedSwipe()
-
- val offset = swipeTransition.dragOffset
- val fromScene = swipeTransition._fromScene
+ val (fromScene, acceleratedOffset) =
+ computeFromSceneConsideringAcceleratedSwipe(swipeTransition)
+ swipeTransition.dragOffset += acceleratedOffset
// Compute the target scene depending on the current offset.
- val target = fromScene.findTargetSceneAndDistance(offset)
+ val (targetScene, distance) =
+ fromScene.findTargetSceneAndDistance(swipeTransition.dragOffset)
- if (swipeTransition._toScene.key != target.sceneKey) {
- swipeTransition._toScene = layoutImpl.scenes.getValue(target.sceneKey)
- }
-
- if (swipeTransition._distance != target.distance) {
- swipeTransition._distance = target.distance
+ // TODO(b/290184746): support long scroll A => B => C? especially for non fullscreen scenes
+ if (
+ fromScene.key != swipeTransition.fromScene || targetScene.key != swipeTransition.toScene
+ ) {
+ updateTransition(
+ SwipeTransition(fromScene, targetScene, distance).apply {
+ this.dragOffset = swipeTransition.dragOffset
+ }
+ )
}
}
/**
* Change fromScene in the case where the user quickly swiped multiple times in the same
* direction to accelerate the transition from A => B then B => C.
+ *
+ * @return the new fromScene and a dragOffset to be added in case the scene has changed
+ *
+ * TODO(b/290184746): the second drag needs to pass B to work. Add support for flinging twice
+ * before B has been reached
*/
- private fun maybeHandleAcceleratedSwipe() {
+ private inline fun computeFromSceneConsideringAcceleratedSwipe(
+ swipeTransition: SwipeTransition,
+ ): Pair<Scene, Float> {
val toScene = swipeTransition._toScene
val fromScene = swipeTransition._fromScene
+ val absoluteDistance = swipeTransition.distance.absoluteValue
// If the swipe was not committed, don't do anything.
if (fromScene == toScene || swipeTransition._currentScene != toScene) {
- return
+ return Pair(fromScene, 0f)
}
// If the offset is past the distance then let's change fromScene so that the user can swipe
// to the next screen or go back to the previous one.
val offset = swipeTransition.dragOffset
- val absoluteDistance = swipeTransition.absoluteDistance
- if (offset <= -absoluteDistance && swipeTransition.upOrLeft(fromScene) == toScene.key) {
- swipeTransition.dragOffset += absoluteDistance
- swipeTransition._fromScene = toScene
- } else if (
- offset >= absoluteDistance && swipeTransition.downOrRight(fromScene) == toScene.key
- ) {
- swipeTransition.dragOffset -= absoluteDistance
- swipeTransition._fromScene = toScene
+ return if (offset <= -absoluteDistance && fromScene.upOrLeft() == toScene.key) {
+ Pair(toScene, absoluteDistance)
+ } else if (offset >= absoluteDistance && fromScene.downOrRight() == toScene.key) {
+ Pair(toScene, -absoluteDistance)
+ } else {
+ Pair(fromScene, 0f)
}
-
- // Important note: toScene and distance will be updated right after this function is called,
- // using fromScene and dragOffset.
}
- private class TargetScene(
- val sceneKey: SceneKey,
- val distance: Float,
- )
-
- private fun Scene.findTargetSceneAndDistance(directionOffset: Float): TargetScene {
- val upOrLeft = swipeTransition.upOrLeft(this)
- val downOrRight = swipeTransition.downOrRight(this)
+ // TODO(b/290184746): there are two bugs here:
+ // 1. if both upOrLeft and downOrRight become `null` during a transition this will crash
+ // 2. if one of them changes during a transition, the transition will jump cut to the new target
+ private inline fun Scene.findTargetSceneAndDistance(
+ directionOffset: Float
+ ): Pair<Scene, Float> {
+ val upOrLeft = upOrLeft()
+ val downOrRight = downOrRight()
+ val absoluteDistance = getAbsoluteDistance()
// Compute the target scene depending on the current offset.
- return when {
- directionOffset < 0f && upOrLeft != null -> {
- TargetScene(
- sceneKey = upOrLeft,
- distance = -swipeTransition.absoluteDistance,
- )
- }
- directionOffset > 0f && downOrRight != null -> {
- TargetScene(
- sceneKey = downOrRight,
- distance = swipeTransition.absoluteDistance,
- )
- }
- else -> {
- TargetScene(
- sceneKey = key,
- distance = 0f,
- )
- }
+ return if ((directionOffset < 0f && upOrLeft != null) || downOrRight == null) {
+ Pair(layoutImpl.scene(upOrLeft!!), -absoluteDistance)
+ } else {
+ Pair(layoutImpl.scene(downOrRight), absoluteDistance)
}
}
+ private fun Scene.upOrLeft(): SceneKey? {
+ return userActions[actionUpOrLeft] ?: userActions[actionUpOrLeftNoEdge]
+ }
+
+ private fun Scene.downOrRight(): SceneKey? {
+ return userActions[actionDownOrRight] ?: userActions[actionDownOrRightNoEdge]
+ }
+
internal fun onDragStopped(velocity: Float, canChangeScene: Boolean) {
// The state was changed since the drag started; don't do anything.
if (!isDrivingTransition) {
@@ -291,11 +286,6 @@
// velocity and offset of the transition, then we launch the animation.
val toScene = swipeTransition._toScene
- if (fromScene == toScene) {
- // We were not animating.
- transitionState = TransitionState.Idle(fromScene.key)
- return
- }
// Compute the destination scene (and therefore offset) to settle in.
val offset = swipeTransition.dragOffset
@@ -322,12 +312,14 @@
if (startFromIdlePosition) {
// If there is a next scene, we start the overscroll animation.
- val target = fromScene.findTargetSceneAndDistance(velocity)
- val isValidTarget = target.distance != 0f && target.sceneKey != fromScene.key
+ val (targetScene, distance) = fromScene.findTargetSceneAndDistance(velocity)
+ val isValidTarget = distance != 0f && targetScene.key != fromScene.key
if (isValidTarget) {
- swipeTransition._toScene = layoutImpl.scene(target.sceneKey)
- swipeTransition._distance = target.distance
-
+ updateTransition(
+ SwipeTransition(fromScene, targetScene, distance).apply {
+ _currentScene = swipeTransition._currentScene
+ }
+ )
animateTo(targetScene = fromScene, targetOffset = 0f)
} else {
// We will not animate
@@ -382,10 +374,10 @@
) {
swipeTransition.startOffsetAnimation {
coroutineScope.launch {
- if (!isAnimatingOffset) {
+ if (!swipeTransition.isAnimatingOffset) {
swipeTransition.offsetAnimatable.snapTo(swipeTransition.dragOffset)
}
- isAnimatingOffset = true
+ swipeTransition.isAnimatingOffset = true
swipeTransition.offsetAnimatable.animateTo(
targetOffset,
@@ -397,7 +389,7 @@
initialVelocity = initialVelocity,
)
- isAnimatingOffset = false
+ swipeTransition.finishOffsetAnimation()
// Now that the animation is done, the state should be idle. Note that if the state
// was changed since this animation started, some external code changed it and we
@@ -410,29 +402,26 @@
}
}
- internal class SwipeTransition(initialScene: Scene) : TransitionState.Transition {
- var _currentScene by mutableStateOf(initialScene)
+ internal class SwipeTransition(
+ val _fromScene: Scene,
+ val _toScene: Scene,
+ /**
+ * The signed distance between [fromScene] and [toScene]. It is negative if [fromScene] is
+ * above or to the left of [toScene].
+ */
+ val distance: Float
+ ) : TransitionState.Transition {
+ var _currentScene by mutableStateOf(_fromScene)
override val currentScene: SceneKey
get() = _currentScene.key
- var _fromScene by mutableStateOf(initialScene)
- override val fromScene: SceneKey
- get() = _fromScene.key
+ override val fromScene: SceneKey = _fromScene.key
- var _toScene by mutableStateOf(initialScene)
- override val toScene: SceneKey
- get() = _toScene.key
+ override val toScene: SceneKey = _toScene.key
override val progress: Float
get() {
val offset = if (isAnimatingOffset) offsetAnimatable.value else dragOffset
- if (distance == 0f) {
- // This can happen only if fromScene == toScene.
- error(
- "Transition.progress should be called only when Transition.fromScene != " +
- "Transition.toScene"
- )
- }
return offset / distance
}
@@ -459,46 +448,22 @@
/** Ends any previous [offsetAnimationJob] and runs the new [job]. */
fun startOffsetAnimation(job: () -> Job) {
- stopOffsetAnimation()
+ cancelOffsetAnimation()
offsetAnimationJob = job()
}
- /** Stops any ongoing offset animation. */
- fun stopOffsetAnimation() {
+ /** Cancel any ongoing offset animation. */
+ fun cancelOffsetAnimation() {
offsetAnimationJob?.cancel()
+ finishOffsetAnimation()
+ }
+ fun finishOffsetAnimation() {
if (isAnimatingOffset) {
isAnimatingOffset = false
dragOffset = offsetAnimatable.value
}
}
-
- /** The absolute distance between [fromScene] and [toScene]. */
- var absoluteDistance = 0f
-
- /**
- * The signed distance between [fromScene] and [toScene]. It is negative if [fromScene] is
- * above or to the left of [toScene].
- */
- var _distance by mutableFloatStateOf(0f)
- val distance: Float
- get() = _distance
-
- /** The [UserAction]s associated to this swipe. */
- var actionUpOrLeft: UserAction = Back
- var actionDownOrRight: UserAction = Back
- var actionUpOrLeftNoEdge: UserAction? = null
- var actionDownOrRightNoEdge: UserAction? = null
-
- fun upOrLeft(scene: Scene): SceneKey? {
- return scene.userActions[actionUpOrLeft]
- ?: actionUpOrLeftNoEdge?.let { scene.userActions[it] }
- }
-
- fun downOrRight(scene: Scene): SceneKey? {
- return scene.userActions[actionDownOrRight]
- ?: actionDownOrRightNoEdge?.let { scene.userActions[it] }
- }
}
companion object {
@@ -509,9 +474,9 @@
private class SceneDraggableHandler(
private val gestureHandler: SceneGestureHandler,
) : DraggableHandler {
- override fun onDragStarted(layoutSize: IntSize, startedPosition: Offset, pointersDown: Int) {
+ override fun onDragStarted(startedPosition: Offset, overSlop: Float, pointersDown: Int) {
gestureHandler.gestureWithPriority = this
- gestureHandler.onDragStarted(pointersDown, layoutSize, startedPosition)
+ gestureHandler.onDragStarted(pointersDown, startedPosition, overSlop)
}
override fun onDelta(pixels: Float) {
@@ -589,7 +554,7 @@
// The progress value can go beyond this range in the case of overscroll.
val shouldSnapToIdle = isProgressCloseTo(0f) || isProgressCloseTo(1f)
if (shouldSnapToIdle) {
- gestureHandler.swipeTransition.stopOffsetAnimation()
+ gestureHandler.swipeTransition.cancelOffsetAnimation()
gestureHandler.transitionState =
TransitionState.Idle(gestureHandler.swipeTransition.currentScene)
}
@@ -612,15 +577,15 @@
canChangeScene = false // unused: added for consistency
false
}
- NestedScrollBehavior.EdgeNoOverscroll -> {
+ NestedScrollBehavior.EdgeNoPreview -> {
canChangeScene = isZeroOffset
isZeroOffset && hasNextScene(offsetAvailable)
}
- NestedScrollBehavior.EdgeWithOverscroll -> {
+ NestedScrollBehavior.EdgeWithPreview -> {
canChangeScene = isZeroOffset
hasNextScene(offsetAvailable)
}
- NestedScrollBehavior.Always -> {
+ NestedScrollBehavior.EdgeAlways -> {
canChangeScene = true
hasNextScene(offsetAvailable)
}
@@ -639,12 +604,12 @@
behavior.canStartOnPostFling && hasNextScene(velocityAvailable)
},
canContinueScroll = { true },
- onStart = {
+ onStart = { offsetAvailable ->
gestureHandler.gestureWithPriority = this
gestureHandler.onDragStarted(
pointersDown = 1,
- layoutSize = gestureHandler.currentScene.targetSize,
startedPosition = null,
+ overSlop = offsetAvailable,
)
},
onScroll = { offsetAvailable ->
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index 07add77..afa184b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -132,8 +132,8 @@
*/
fun Modifier.nestedScrollToScene(
orientation: Orientation,
- startBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoOverscroll,
- endBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoOverscroll,
+ startBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoPreview,
+ endBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoPreview,
): Modifier
/**
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
index 2c78dee..116a666 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -46,7 +46,7 @@
// user can't swipe in the other direction.
startDragImmediately =
gestureHandler.isDrivingTransition &&
- gestureHandler.isAnimatingOffset &&
+ gestureHandler.swipeTransition.isAnimatingOffset &&
!canOppositeSwipe,
onDragStarted = gestureHandler.draggable::onDragStarted,
onDragDelta = gestureHandler.draggable::onDelta,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
index a5fd1bf..c49a2b8 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
@@ -38,7 +38,7 @@
private val canStartPostScroll: (offsetAvailable: Offset, offsetBeforeStart: Offset) -> Boolean,
private val canStartPostFling: (velocityAvailable: Velocity) -> Boolean,
private val canContinueScroll: () -> Boolean,
- private val onStart: () -> Unit,
+ private val onStart: (offsetAvailable: Offset) -> Unit,
private val onScroll: (offsetAvailable: Offset) -> Offset,
private val onStop: (velocityAvailable: Velocity) -> Velocity,
) : NestedScrollConnection {
@@ -131,7 +131,7 @@
// Note: onStop will be called if we cannot continue to scroll (step 3a), or the finger is
// lifted (step 3b), or this object has been destroyed (step 3c).
- onStart()
+ onStart(available)
return onScroll(available)
}
@@ -156,7 +156,7 @@
canStartPostScroll: (offsetAvailable: Float, offsetBeforeStart: Float) -> Boolean,
canStartPostFling: (velocityAvailable: Float) -> Boolean,
canContinueScroll: () -> Boolean,
- onStart: () -> Unit,
+ onStart: (offsetAvailable: Float) -> Unit,
onScroll: (offsetAvailable: Float) -> Float,
onStop: (velocityAvailable: Float) -> Float,
) =
@@ -172,7 +172,7 @@
canStartPostFling(velocityAvailable.toFloat())
},
canContinueScroll = canContinueScroll,
- onStart = onStart,
+ onStart = { offsetAvailable -> onStart(offsetAvailable.toFloat()) },
onScroll = { offsetAvailable: Offset ->
onScroll(offsetAvailable.toFloat()).toOffset()
},
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
index b84cb36..aa942e0 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
@@ -29,10 +29,10 @@
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.Velocity
import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.compose.animation.scene.NestedScrollBehavior.Always
import com.android.compose.animation.scene.NestedScrollBehavior.DuringTransitionBetweenScenes
-import com.android.compose.animation.scene.NestedScrollBehavior.EdgeNoOverscroll
-import com.android.compose.animation.scene.NestedScrollBehavior.EdgeWithOverscroll
+import com.android.compose.animation.scene.NestedScrollBehavior.EdgeAlways
+import com.android.compose.animation.scene.NestedScrollBehavior.EdgeNoPreview
+import com.android.compose.animation.scene.NestedScrollBehavior.EdgeWithPreview
import com.android.compose.animation.scene.TestScenes.SceneA
import com.android.compose.animation.scene.TestScenes.SceneB
import com.android.compose.animation.scene.TestScenes.SceneC
@@ -191,8 +191,13 @@
runMonotonicClockTest { TestGestureScope(coroutineScope = this).block() }
}
- private fun DraggableHandler.onDragStarted() {
- onDragStarted(layoutSize = LAYOUT_SIZE, startedPosition = Offset.Zero)
+ private fun DraggableHandler.onDragStarted(
+ overSlop: Float = 0f,
+ startedPosition: Offset = Offset.Zero,
+ ) {
+ onDragStarted(startedPosition, overSlop)
+ // MultiPointerDraggable will always call onDelta with the initial overSlop right after
+ onDelta(overSlop)
}
@Test fun testPreconditions() = runGestureTest { assertIdle(currentScene = SceneA) }
@@ -291,6 +296,86 @@
}
@Test
+ fun onDragStartedWithoutActionsInBothDirections_stayIdle() = runGestureTest {
+ horizontalSceneGestureHandler.draggable.onDragStarted(up(0.3f))
+ assertIdle(currentScene = SceneA)
+ horizontalSceneGestureHandler.draggable.onDragStarted(down(0.3f))
+ assertIdle(currentScene = SceneA)
+ }
+
+ @Test
+ fun onDragIntoNoAction_startTransitionToOppositeDirection() = runGestureTest {
+ navigateToSceneC()
+
+ // We are on SceneC which has no action in Down direction
+ draggable.onDragStarted(down(0.1f))
+ assertTransition(
+ currentScene = SceneC,
+ fromScene = SceneC,
+ toScene = SceneB,
+ progress = -0.1f
+ )
+
+ // Reverse drag direction, it will consume the previous drag
+ draggable.onDelta(up(0.1f))
+ assertTransition(
+ currentScene = SceneC,
+ fromScene = SceneC,
+ toScene = SceneB,
+ progress = 0.0f
+ )
+
+ // Continue reverse drag direction, it should record progress to Scene B
+ draggable.onDelta(up(0.1f))
+ assertTransition(
+ currentScene = SceneC,
+ fromScene = SceneC,
+ toScene = SceneB,
+ progress = 0.1f
+ )
+ }
+
+ @Test
+ fun onDragFromEdge_startTransitionToEdgeAction() = runGestureTest {
+ navigateToSceneC()
+
+ // Start dragging from the bottom
+ draggable.onDragStarted(up(0.1f), Offset(SCREEN_SIZE * 0.5f, SCREEN_SIZE))
+ assertTransition(
+ currentScene = SceneC,
+ fromScene = SceneC,
+ toScene = SceneA,
+ progress = 0.1f
+ )
+ }
+
+ @Test
+ fun onDragToExactlyZero_toSceneIsSet() = runGestureTest {
+ draggable.onDragStarted(down(0.3f))
+ assertTransition(
+ currentScene = SceneA,
+ fromScene = SceneA,
+ toScene = SceneC,
+ progress = 0.3f
+ )
+ draggable.onDelta(up(0.3f))
+ assertTransition(
+ currentScene = SceneA,
+ fromScene = SceneA,
+ toScene = SceneC,
+ progress = 0.0f
+ )
+ }
+
+ private fun TestGestureScope.navigateToSceneC() {
+ assertIdle(currentScene = SceneA)
+ draggable.onDragStarted(down(1f))
+ draggable.onDragStopped(0f)
+ advanceUntilIdle()
+ assertIdle(currentScene = SceneC)
+ }
+
+ @Test
fun onAccelaratedScroll_scrollToThirdScene() = runGestureTest {
// Drag A -> B with progress 0.2
draggable.onDragStarted()
@@ -339,29 +424,29 @@
)
// The stop animation is not started yet
- assertThat(sceneGestureHandler.isAnimatingOffset).isFalse()
+ assertThat(sceneGestureHandler.swipeTransition.isAnimatingOffset).isFalse()
runCurrent()
- assertThat(sceneGestureHandler.isAnimatingOffset).isTrue()
+ assertThat(sceneGestureHandler.swipeTransition.isAnimatingOffset).isTrue()
assertThat(sceneGestureHandler.isDrivingTransition).isTrue()
assertTransition(currentScene = SceneC)
// Start a new gesture while the offset is animating
draggable.onDragStarted()
- assertThat(sceneGestureHandler.isAnimatingOffset).isFalse()
+ assertThat(sceneGestureHandler.swipeTransition.isAnimatingOffset).isFalse()
}
@Test
fun onInitialPreScroll_EdgeWithOverscroll_doNotChangeState() = runGestureTest {
- val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithOverscroll)
+ val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithPreview)
nestedScroll.onPreScroll(available = offsetY10, source = NestedScrollSource.Drag)
assertIdle(currentScene = SceneA)
}
@Test
fun onPostScrollWithNothingAvailable_EdgeWithOverscroll_doNotChangeState() = runGestureTest {
- val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithOverscroll)
+ val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithPreview)
val consumed =
nestedScroll.onPostScroll(
consumed = Offset.Zero,
@@ -375,7 +460,7 @@
@Test
fun onPostScrollWithSomethingAvailable_startSceneTransition() = runGestureTest {
- val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithOverscroll)
+ val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithPreview)
val consumed =
nestedScroll.onPostScroll(
consumed = Offset.Zero,
@@ -404,7 +489,7 @@
@Test
fun afterSceneTransitionIsStarted_interceptPreScrollEvents() = runGestureTest {
- val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithOverscroll)
+ val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithPreview)
nestedScroll.scroll(available = offsetY10)
assertTransition(currentScene = SceneA)
@@ -432,7 +517,7 @@
firstScroll: Float,
secondScroll: Float
) {
- val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithOverscroll)
+ val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithPreview)
// start scene transition
nestedScroll.scroll(available = Offset(0f, SCREEN_SIZE * firstScroll))
@@ -485,7 +570,7 @@
@Test
fun onPreFling_velocityLowerThanThreshold_remainSameScene() = runGestureTest {
- val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithOverscroll)
+ val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithPreview)
nestedScroll.scroll(available = offsetY10)
assertTransition(currentScene = SceneA)
@@ -517,7 +602,7 @@
@Test
fun flingAfterScroll_EdgeNoOverscroll_goToNextScene() = runGestureTest {
- flingAfterScroll(use = EdgeNoOverscroll, idleAfterScroll = false)
+ flingAfterScroll(use = EdgeNoPreview, idleAfterScroll = false)
assertTransition(currentScene = SceneC)
@@ -528,7 +613,7 @@
@Test
fun flingAfterScroll_EdgeWithOverscroll_goToNextScene() = runGestureTest {
- flingAfterScroll(use = EdgeWithOverscroll, idleAfterScroll = false)
+ flingAfterScroll(use = EdgeWithPreview, idleAfterScroll = false)
assertTransition(currentScene = SceneC)
@@ -539,7 +624,7 @@
@Test
fun flingAfterScroll_Always_goToNextScene() = runGestureTest {
- flingAfterScroll(use = Always, idleAfterScroll = false)
+ flingAfterScroll(use = EdgeAlways, idleAfterScroll = false)
assertTransition(currentScene = SceneC)
@@ -573,14 +658,14 @@
@Test
fun flingAfterScrollStartedInScene_EdgeNoOverscroll_doNothing() = runGestureTest {
- flingAfterScrollStartedInScene(use = EdgeNoOverscroll, idleAfterScroll = true)
+ flingAfterScrollStartedInScene(use = EdgeNoPreview, idleAfterScroll = true)
assertIdle(currentScene = SceneA)
}
@Test
fun flingAfterScrollStartedInScene_EdgeWithOverscroll_doOverscrollAnimation() = runGestureTest {
- flingAfterScrollStartedInScene(use = EdgeWithOverscroll, idleAfterScroll = false)
+ flingAfterScrollStartedInScene(use = EdgeWithPreview, idleAfterScroll = false)
assertTransition(currentScene = SceneA)
@@ -591,7 +676,7 @@
@Test
fun flingAfterScrollStartedInScene_Always_goToNextScene() = runGestureTest {
- flingAfterScrollStartedInScene(use = Always, idleAfterScroll = false)
+ flingAfterScrollStartedInScene(use = EdgeAlways, idleAfterScroll = false)
assertTransition(currentScene = SceneC)
@@ -614,14 +699,14 @@
@Test
fun beforeNestedScrollStart_stop_shouldBeIgnored() = runGestureTest {
- val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithOverscroll)
+ val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithPreview)
nestedScroll.onPreFling(Velocity(0f, velocityThreshold))
assertIdle(currentScene = SceneA)
}
@Test
fun startNestedScrollWhileDragging() = runGestureTest {
- val nestedScroll = nestedScrollConnection(nestedScrollBehavior = Always)
+ val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeAlways)
draggable.onDragStarted()
assertTransition(currentScene = SceneA)
diff --git a/packages/SystemUI/docs/executors.md b/packages/SystemUI/docs/executors.md
index 8520ce2..2d9438c 100644
--- a/packages/SystemUI/docs/executors.md
+++ b/packages/SystemUI/docs/executors.md
@@ -14,10 +14,10 @@
[FakeExecutor][FakeExecutor] is available.
[Executor]: https://developer.android.com/reference/java/util/concurrent/Executor.html
-[Handler]: https://developer.android.com/reference/android/os/Handler
+[Handler]: https://developer.android.com/reference/android/os/Handler.html
[Runnable]: https://developer.android.com/reference/java/lang/Runnable.html
[DelayableExecutor]: /packages/SystemUI/src/com/android/systemui/util/concurrency/DelayableExecutor.java
-[FakeExecutor]: /packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutor.java
+[FakeExecutor]: /packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutor.java
## Rationale
@@ -117,7 +117,7 @@
postDelayed() | `none` | executeDelayed()
postAtTime() | `none` | executeAtTime()
-There is one notable gap in this implementation: `Handler.postAtFrontOfQueue()`.
+There are some notable gaps in this implementation: `Handler.postAtFrontOfQueue()`.
If you require this method, or similar, please reach out. The idea of a
PriorityQueueExecutor has been floated, but will not be implemented until there
is a clear need.
@@ -173,13 +173,20 @@
If you feel that you have a use case that this does not cover, please reach out.
+### ContentObserver
+
+One notable place where Handlers have been a requirement in the past is with
+[ContentObserver], which takes a Handler as an argument. However, we have created
+[ExecutorContentObserver], which is a hidden API that accepts an [Executor] in its
+constructor instead of a [Handler], and is otherwise identical.
+
+[ContentObserver]: https://developer.android.com/reference/android/database/ContentObserver.html
+[ExecutorContentObserver]: /core/java/android/database/ExecutorContentObserver.java
+
### Handlers Are Still Necessary
-Handlers aren't going away. There are Android APIs that still require them (even
-if future API development discourages them). A simple example is
-[ContentObserver][ContentObserver]. Use them where necessary.
-
-[ContentObserver]: https://developer.android.com/reference/android/database/ContentObserver
+Handlers aren't going away. There are other Android APIs that still require them.
+Avoid Handlers when possible, but use them where necessary.
## Testing (FakeExecutor)
@@ -314,6 +321,15 @@
The Runnables _will not_ interleave. All of one Executor's callbacks will run,
then all of the other's.
+### Testing Handlers without Loopers
+
+If a [Handler] is required because it is used by Android APIs, but is only
+used in simple ways (i.e. just `Handler.post(Runnable)`), you may still
+want the benefits of [FakeExecutor] when writing your tests, which
+you can get by wrapping the [Executor] in a mock for testing. This can be
+done with `com.android.systemui.util.concurrency.mockExecutorHandler` in
+`MockExecutorHandler.kt`.
+
### TestableLooper.RunWithLooper
As long as you're using FakeExecutors in all the code under test (and no
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
index d968c1b..2a02164 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
@@ -27,8 +27,6 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.scene.SceneTestUtils
import com.google.common.truth.Truth.assertThat
-import kotlin.time.Duration.Companion.milliseconds
-import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
@@ -315,6 +313,59 @@
}
@Test
+ fun isAutoConfirmEnabled_featureDisabled_returnsFalse() =
+ testScope.runTest {
+ val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
+ utils.authenticationRepository.setAutoConfirmFeatureEnabled(false)
+
+ assertThat(isAutoConfirmEnabled).isFalse()
+ }
+
+ @Test
+ fun isAutoConfirmEnabled_featureEnabled_returnsTrue() =
+ testScope.runTest {
+ val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
+ utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
+
+ assertThat(isAutoConfirmEnabled).isTrue()
+ }
+
+ @Test
+ fun isAutoConfirmEnabled_featureEnabledButDisabledByThrottling() =
+ testScope.runTest {
+ val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
+ val throttling by collectLastValue(underTest.throttling)
+ utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
+
+ // The feature is enabled.
+ assertThat(isAutoConfirmEnabled).isTrue()
+
+ // Make many wrong attempts to trigger throttling.
+ repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING) {
+ underTest.authenticate(listOf(5, 6, 7)) // Wrong PIN
+ }
+ assertThat(throttling).isNotNull()
+
+ // Throttling disabled auto-confirm.
+ assertThat(isAutoConfirmEnabled).isFalse()
+
+ // Move the clock forward one more second, to completely finish the throttling period:
+ advanceTimeBy(FakeAuthenticationRepository.THROTTLE_DURATION_MS + 1000L)
+ assertThat(throttling).isNull()
+
+ // Auto-confirm is still disabled, because throttling occurred at least once in this
+ // session.
+ assertThat(isAutoConfirmEnabled).isFalse()
+
+ // Correct PIN and unlocks successfully, resetting the 'session'.
+ assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
+ .isEqualTo(AuthenticationResult.SUCCEEDED)
+
+ // Auto-confirm is re-enabled.
+ assertThat(isAutoConfirmEnabled).isTrue()
+ }
+
+ @Test
fun throttling() =
testScope.runTest {
val throttling by collectLastValue(underTest.throttling)
@@ -335,7 +386,7 @@
AuthenticationThrottlingModel(
failedAttemptCount =
FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING,
- remainingMs = FakeAuthenticationRepository.THROTTLE_DURATION_MS,
+ remainingSeconds = FakeAuthenticationRepository.THROTTLE_DURATION_SECONDS,
)
)
@@ -347,15 +398,13 @@
AuthenticationThrottlingModel(
failedAttemptCount =
FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING,
- remainingMs = FakeAuthenticationRepository.THROTTLE_DURATION_MS,
+ remainingSeconds = FakeAuthenticationRepository.THROTTLE_DURATION_SECONDS,
)
)
// Move the clock forward to ALMOST skip the throttling, leaving one second to go:
- val throttleTimeoutSec =
- FakeAuthenticationRepository.THROTTLE_DURATION_MS.milliseconds.inWholeSeconds
- .toInt()
- repeat(throttleTimeoutSec - 1) { time ->
+ val throttleTimeoutSec = FakeAuthenticationRepository.THROTTLE_DURATION_SECONDS
+ repeat(FakeAuthenticationRepository.THROTTLE_DURATION_SECONDS - 1) { time ->
advanceTimeBy(1000)
assertThat(throttling)
.isEqualTo(
@@ -363,9 +412,7 @@
failedAttemptCount =
FakeAuthenticationRepository
.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING,
- remainingMs =
- ((throttleTimeoutSec - (time + 1)).seconds.inWholeMilliseconds)
- .toInt(),
+ remainingSeconds = throttleTimeoutSec - (time + 1),
)
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index 04f6cd3..3e3a1a9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -29,7 +29,6 @@
import com.android.systemui.res.R
import com.android.systemui.scene.SceneTestUtils
import com.google.common.truth.Truth.assertThat
-import kotlin.math.ceil
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.advanceTimeBy
@@ -268,7 +267,7 @@
AuthenticationThrottlingModel(
failedAttemptCount =
FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING,
- remainingMs = FakeAuthenticationRepository.THROTTLE_DURATION_MS,
+ remainingSeconds = FakeAuthenticationRepository.THROTTLE_DURATION_SECONDS,
)
)
assertTryAgainMessage(
@@ -286,8 +285,7 @@
.toInt()
)
- throttling?.remainingMs?.let { remainingMs ->
- val seconds = ceil(remainingMs / 1000f).toInt()
+ throttling?.remainingSeconds?.let { seconds ->
repeat(seconds) { time ->
advanceTimeBy(1000)
val remainingTimeSec = seconds - time - 1
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
index 75d6a00..2b64d8e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
@@ -26,6 +26,7 @@
import com.android.systemui.scene.SceneTestUtils
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
+import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flatMapLatest
@@ -144,7 +145,9 @@
}
assertThat(message?.isUpdateAnimated).isFalse()
- throttling?.remainingMs?.let { remainingMs -> advanceTimeBy(remainingMs.toLong()) }
+ throttling?.remainingSeconds?.let { remainingSeconds ->
+ advanceTimeBy(remainingSeconds.seconds.inWholeMilliseconds)
+ }
assertThat(message?.isUpdateAnimated).isTrue()
}
@@ -167,7 +170,9 @@
}
assertThat(isInputEnabled).isFalse()
- throttling?.remainingMs?.let { milliseconds -> advanceTimeBy(milliseconds.toLong()) }
+ throttling?.remainingSeconds?.let { remainingSeconds ->
+ advanceTimeBy(remainingSeconds.seconds.inWholeMilliseconds)
+ }
assertThat(isInputEnabled).isTrue()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
index 64f2946..a217d93 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
@@ -335,12 +335,12 @@
repeat(failedAttemptCount) {
authenticationRepository.reportAuthenticationAttempt(false)
}
- val remainingTimeMs = 30_000
- authenticationRepository.setThrottleDuration(remainingTimeMs)
+ val remainingTimeSeconds = 30
+ authenticationRepository.setThrottleDuration(remainingTimeSeconds * 1000)
authenticationRepository.throttling.value =
AuthenticationThrottlingModel(
failedAttemptCount = failedAttemptCount,
- remainingMs = remainingTimeMs,
+ remainingSeconds = remainingTimeSeconds,
)
} else {
authenticationRepository.reportAuthenticationAttempt(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
index 97ac8c6..d3049d9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
@@ -6,6 +6,7 @@
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -38,6 +39,7 @@
private val testUtils = SceneTestUtils(this)
private val testScope = testUtils.testScope
private val userRepository = FakeUserRepository()
+ private val keyguardRepository = FakeKeyguardRepository()
private lateinit var underTest: DeviceEntryRepository
@@ -55,6 +57,7 @@
lockPatternUtils = lockPatternUtils,
keyguardBypassController = keyguardBypassController,
keyguardStateController = keyguardStateController,
+ keyguardRepository = keyguardRepository,
)
testScope.runCurrent()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
index 9226c0d..a346e8b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
@@ -24,7 +24,7 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.flags.Flags
-import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -48,7 +48,7 @@
private val kosmos =
testKosmos().apply {
- featureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
+ fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
}
private val testScope = kosmos.testScope
private val repository = kosmos.fakeKeyguardTransitionRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
index bcad72b..274bde1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
@@ -25,7 +25,7 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.flags.Flags
-import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -49,7 +49,7 @@
class LockscreenToOccludedTransitionViewModelTest : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
- featureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
+ fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
}
private val testScope = kosmos.testScope
private val repository = kosmos.fakeKeyguardTransitionRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
index 78d87a6..f027bc8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
@@ -22,7 +22,7 @@
import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
import com.android.systemui.coroutines.collectValues
import com.android.systemui.flags.Flags
-import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -43,7 +43,7 @@
class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() {
val kosmos =
testKosmos().apply {
- featureFlagsClassic.apply {
+ fakeFeatureFlagsClassic.apply {
set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false)
set(Flags.FULL_SCREEN_USER_SWITCHER, false)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 3cb97e3..61d55f0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -19,6 +19,7 @@
package com.android.systemui.scene.domain.startable
import android.os.PowerManager
+import android.platform.test.annotations.EnableFlags
import android.view.Display
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -45,7 +46,6 @@
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.clearInvocations
@@ -55,6 +55,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
+@EnableFlags(AconfigFlags.FLAG_SCENE_CONTAINER)
class SceneContainerStartableTest : SysuiTestCase() {
private val utils = SceneTestUtils(this)
@@ -93,11 +94,6 @@
authenticationInteractor = authenticationInteractor,
)
- @Before
- fun setUp() {
- mSetFlagsRule.enableFlags(AconfigFlags.FLAG_SCENE_CONTAINER)
- }
-
@Test
fun hydrateVisibility() =
testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
index f04dfd1..28f3e93 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
@@ -24,7 +24,7 @@
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
-import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
@@ -51,7 +51,7 @@
private val kosmos =
testKosmos().apply {
sceneContainerFlags = FakeSceneContainerFlags(enabled = true)
- featureFlagsClassic.apply {
+ fakeFeatureFlagsClassic.apply {
set(Flags.FULL_SCREEN_USER_SWITCHER, false)
set(Flags.NSSL_DEBUG_LINES, false)
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
index f1a4007..e27a328 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
@@ -119,9 +119,8 @@
data class ReleasedFlag constructor(
override val name: String,
override val namespace: String,
- override val teamfood: Boolean = false,
override val overridden: Boolean = false
-) : BooleanFlag(name, namespace, true, teamfood, overridden)
+) : BooleanFlag(name, namespace, true, teamfood = false, overridden)
/**
* A Flag that reads its default values from a resource overlay instead of code.
@@ -132,8 +131,9 @@
override val name: String,
override val namespace: String,
@BoolRes override val resourceId: Int,
+) : ResourceFlag<Boolean> {
override val teamfood: Boolean = false
-) : ResourceFlag<Boolean>
+}
/**
* A Flag that can reads its overrides from System Properties.
@@ -147,7 +147,6 @@
override val namespace: String,
override val default: Boolean = false,
) : SysPropFlag<Boolean> {
- // TODO(b/268520433): Teamfood not supported for sysprop flags yet.
override val teamfood: Boolean = false
}
diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt
index aef8371..f9fe67a 100644
--- a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt
+++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt
@@ -42,7 +42,7 @@
name: String,
namespace: String = "systemui",
): ReleasedFlag {
- val flag = ReleasedFlag(name = name, namespace = namespace, teamfood = false)
+ val flag = ReleasedFlag(name = name, namespace = namespace)
checkForDupesAndAdd(flag)
return flag
}
@@ -57,7 +57,6 @@
name = name,
namespace = namespace,
resourceId = resourceId,
- teamfood = false,
)
checkForDupesAndAdd(flag)
return flag
diff --git a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt
index f4b4296..aedf0ce 100644
--- a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt
+++ b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt
@@ -42,7 +42,7 @@
name: String,
namespace: String = "systemui",
): ReleasedFlag {
- val flag = ReleasedFlag(name = name, namespace = namespace, teamfood = false)
+ val flag = ReleasedFlag(name = name, namespace = namespace)
flagMap[name] = flag
return flag
}
@@ -57,7 +57,6 @@
name = name,
namespace = namespace,
resourceId = resourceId,
- teamfood = false,
)
flagMap[name] = flag
return flag
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index be2c65f..cdd7b80 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -72,7 +72,7 @@
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.ui.SystemBarUtilsState;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.settings.SecureSettings;
@@ -105,7 +105,7 @@
private final NotificationIconContainerAlwaysOnDisplayViewModel mAodIconsViewModel;
private final KeyguardRootViewModel mKeyguardRootViewModel;
private final ConfigurationState mConfigurationState;
- private final ConfigurationController mConfigurationController;
+ private final SystemBarUtilsState mSystemBarUtilsState;
private final DozeParameters mDozeParameters;
private final ScreenOffAnimationController mScreenOffAnimationController;
private final AlwaysOnDisplayNotificationIconViewStore mAodIconViewStore;
@@ -183,7 +183,7 @@
KeyguardSliceViewController keyguardSliceViewController,
NotificationIconAreaController notificationIconAreaController,
LockscreenSmartspaceController smartspaceController,
- ConfigurationController configurationController,
+ SystemBarUtilsState systemBarUtilsState,
ScreenOffAnimationController screenOffAnimationController,
StatusBarIconViewBindingFailureTracker iconViewBindingFailureTracker,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
@@ -208,7 +208,7 @@
mKeyguardSliceViewController = keyguardSliceViewController;
mNotificationIconAreaController = notificationIconAreaController;
mSmartspaceController = smartspaceController;
- mConfigurationController = configurationController;
+ mSystemBarUtilsState = systemBarUtilsState;
mScreenOffAnimationController = screenOffAnimationController;
mIconViewBindingFailureTracker = iconViewBindingFailureTracker;
mSecureSettings = secureSettings;
@@ -619,13 +619,14 @@
mAodIconsBindHandle.dispose();
}
if (nic != null) {
- final DisposableHandle viewHandle = NotificationIconContainerViewBinder.bind(
- nic,
- mAodIconsViewModel,
- mConfigurationState,
- mConfigurationController,
- mIconViewBindingFailureTracker,
- mAodIconViewStore);
+ final DisposableHandle viewHandle =
+ NotificationIconContainerViewBinder.bindWhileAttached(
+ nic,
+ mAodIconsViewModel,
+ mConfigurationState,
+ mSystemBarUtilsState,
+ mIconViewBindingFailureTracker,
+ mAodIconViewStore);
final DisposableHandle visHandle = KeyguardRootViewBinder.bindAodIconVisibility(
nic,
mKeyguardRootViewModel.isNotifIconContainerVisible(),
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index bf44517..c3f6480 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -110,6 +110,10 @@
View.setTracedRequestLayoutClassClass(
SystemProperties.get("persist.debug.trace_request_layout_class", null));
+ if (Flags.enableLayoutTracing()) {
+ View.setTraceLayoutSteps(true);
+ }
+
if (Process.myUserHandle().equals(UserHandle.SYSTEM)) {
IntentFilter bootCompletedFilter = new
IntentFilter(Intent.ACTION_LOCKED_BOOT_COMPLETED);
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
index dd4ca92..bd84b28 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
@@ -60,14 +60,6 @@
/** Defines interface for classes that can access authentication-related application state. */
interface AuthenticationRepository {
/**
- * Whether the auto confirm feature is enabled for the currently-selected user.
- *
- * Note that the length of the PIN is also important to take into consideration, please see
- * [hintedPinLength].
- */
- val isAutoConfirmFeatureEnabled: StateFlow<Boolean>
-
- /**
* Emits the result whenever a PIN/Pattern/Password security challenge is attempted by the user
* in order to unlock the device.
*/
@@ -93,6 +85,17 @@
*/
val throttling: MutableStateFlow<AuthenticationThrottlingModel?>
+ /** Whether throttling has occurred at least once since the last successful authentication. */
+ val hasThrottlingOccurred: MutableStateFlow<Boolean>
+
+ /**
+ * Whether the auto confirm feature is enabled for the currently-selected user.
+ *
+ * Note that the length of the PIN is also important to take into consideration, please see
+ * [hintedPinLength].
+ */
+ val isAutoConfirmFeatureEnabled: StateFlow<Boolean>
+
/**
* The currently-configured authentication method. This determines how the authentication
* challenge needs to be completed in order to unlock an otherwise locked device.
@@ -172,11 +175,6 @@
mobileConnectionsRepository: MobileConnectionsRepository,
) : AuthenticationRepository {
- override val isAutoConfirmFeatureEnabled: StateFlow<Boolean> =
- refreshingFlow(
- initialValue = false,
- getFreshValue = lockPatternUtils::isAutoPinConfirmEnabled,
- )
override val authenticationChallengeResult = MutableSharedFlow<Boolean>()
override val hintedPinLength: Int = 6
@@ -190,8 +188,13 @@
override val throttling: MutableStateFlow<AuthenticationThrottlingModel?> =
MutableStateFlow(null)
- private val selectedUserId: Int
- get() = userRepository.getSelectedUserInfo().id
+ override val hasThrottlingOccurred: MutableStateFlow<Boolean> = MutableStateFlow(false)
+
+ override val isAutoConfirmFeatureEnabled: StateFlow<Boolean> =
+ refreshingFlow(
+ initialValue = false,
+ getFreshValue = lockPatternUtils::isAutoPinConfirmEnabled,
+ )
override val authenticationMethod: Flow<AuthenticationMethodModel> =
combine(userRepository.selectedUserInfo, mobileConnectionsRepository.isAnySimSecure) {
@@ -280,6 +283,9 @@
}
}
+ private val selectedUserId: Int
+ get() = userRepository.getSelectedUserInfo().id
+
/**
* Returns a [StateFlow] that's automatically kept fresh. The passed-in [getFreshValue] is
* invoked on a background thread every time the selected user is changed and every time a new
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
index 1ba0220..7f8f887 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
@@ -29,6 +29,7 @@
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
+import kotlin.math.ceil
import kotlin.math.max
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineDispatcher
@@ -94,15 +95,14 @@
*
* Note that the length of the PIN is also important to take into consideration, please see
* [hintedPinLength].
- *
- * During throttling, this is always disabled (`false`).
*/
val isAutoConfirmEnabled: StateFlow<Boolean> =
- combine(repository.isAutoConfirmFeatureEnabled, repository.throttling) {
+ combine(repository.isAutoConfirmFeatureEnabled, repository.hasThrottlingOccurred) {
featureEnabled,
- throttling ->
- // Disable auto-confirm during throttling.
- featureEnabled && throttling == null
+ hasThrottlingOccurred ->
+ // Disable auto-confirm if throttling occurred since the last successful
+ // authentication attempt.
+ featureEnabled && !hasThrottlingOccurred
}
.stateIn(
scope = applicationScope,
@@ -220,6 +220,7 @@
repository.setThrottleDuration(
durationMs = authenticationResult.throttleDurationMs,
)
+ repository.hasThrottlingOccurred.value = true
startThrottlingCountdown()
}
@@ -227,6 +228,8 @@
// Since authentication succeeded, we should refresh throttling to make sure that our
// state is completely reflecting the upstream source of truth.
refreshThrottling()
+
+ repository.hasThrottlingOccurred.value = false
}
return if (authenticationResult.isSuccessful) {
@@ -283,7 +286,7 @@
if (remainingMs > 0) {
AuthenticationThrottlingModel(
failedAttemptCount = failedAttemptCount.await(),
- remainingMs = remainingMs.toInt(),
+ remainingSeconds = ceil(remainingMs / 1000f).toInt(),
)
} else {
null // Throttling ended.
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationThrottlingModel.kt b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationThrottlingModel.kt
index d0d398e..8392528 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationThrottlingModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationThrottlingModel.kt
@@ -23,10 +23,13 @@
val failedAttemptCount: Int = 0,
/**
- * Remaining amount of time, in milliseconds, before another authentication attempt can be done.
- * If not throttling this will be `0`.
+ * Remaining amount of time, in seconds, before another authentication attempt can be done. If
+ * not throttling this will be `0`.
*
* This number is changed throughout the timeout.
+ *
+ * Note: this isn't precise (in milliseconds), but rounded up to ensure "at most" this amount of
+ * seconds remains.
*/
- val remainingMs: Int = 0,
+ val remainingSeconds: Int = 0,
)
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index 1122877..677f60d 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -32,7 +32,6 @@
import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.util.kotlin.pairwise
import javax.inject.Inject
-import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.MutableSharedFlow
@@ -259,7 +258,7 @@
throttlingModel != null ->
applicationContext.getString(
com.android.internal.R.string.lockscreen_too_many_failed_attempts_countdown,
- throttlingModel.remainingMs.milliseconds.inWholeSeconds,
+ throttlingModel.remainingSeconds,
)
message != null -> message
else -> ""
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
index 58fa857..fefc3e3 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
@@ -36,7 +36,6 @@
import com.android.systemui.user.ui.viewmodel.UserViewModel
import dagger.Module
import dagger.Provides
-import kotlin.math.ceil
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
@@ -205,7 +204,7 @@
applicationContext.getString(
authMethodViewModel.throttlingMessageId,
throttling.failedAttemptCount,
- ceil(throttling.remainingMs / 1000f).toInt(),
+ throttling.remainingSeconds,
)
} else {
null
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
index cd764c0..b915418 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
@@ -1,6 +1,5 @@
package com.android.systemui.deviceentry
-import com.android.systemui.deviceentry.data.repository.DeviceEntryHapticsRepositoryModule
import com.android.systemui.deviceentry.data.repository.DeviceEntryRepositoryModule
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import dagger.Module
@@ -10,7 +9,6 @@
includes =
[
DeviceEntryRepositoryModule::class,
- DeviceEntryHapticsRepositoryModule::class,
],
)
abstract class DeviceEntryModule {
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepository.kt
deleted file mode 100644
index 1458404..0000000
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepository.kt
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.deviceentry.data.repository
-
-import com.android.systemui.dagger.SysUISingleton
-import dagger.Binds
-import dagger.Module
-import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.asStateFlow
-
-/** Interface for classes that can access device-entry haptics application state. */
-interface DeviceEntryHapticsRepository {
- /**
- * Whether a successful biometric haptic has been requested. Has not yet been handled if true.
- */
- val successHapticRequest: Flow<Boolean>
-
- /** Whether an error biometric haptic has been requested. Has not yet been handled if true. */
- val errorHapticRequest: Flow<Boolean>
-
- fun requestSuccessHaptic()
- fun handleSuccessHaptic()
- fun requestErrorHaptic()
- fun handleErrorHaptic()
-}
-
-/** Encapsulates application state for device entry haptics. */
-@SysUISingleton
-class DeviceEntryHapticsRepositoryImpl @Inject constructor() : DeviceEntryHapticsRepository {
- private val _successHapticRequest = MutableStateFlow(false)
- override val successHapticRequest: Flow<Boolean> = _successHapticRequest.asStateFlow()
-
- private val _errorHapticRequest = MutableStateFlow(false)
- override val errorHapticRequest: Flow<Boolean> = _errorHapticRequest.asStateFlow()
-
- override fun requestSuccessHaptic() {
- _successHapticRequest.value = true
- }
-
- override fun handleSuccessHaptic() {
- _successHapticRequest.value = false
- }
-
- override fun requestErrorHaptic() {
- _errorHapticRequest.value = true
- }
-
- override fun handleErrorHaptic() {
- _errorHapticRequest.value = false
- }
-}
-
-@Module
-interface DeviceEntryHapticsRepositoryModule {
- @Binds fun repository(impl: DeviceEntryHapticsRepositoryImpl): DeviceEntryHapticsRepository
-}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt
index f27bbe6..08e8c2d 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt
@@ -7,26 +7,36 @@
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.KeyguardRepository
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.user.data.repository.UserRepository
+import com.android.systemui.util.kotlin.sample
import dagger.Binds
import dagger.Module
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.withContext
/** Interface for classes that can access device-entry-related application state. */
interface DeviceEntryRepository {
+ /** Whether the device is immediately entering the device after a biometric unlock. */
+ val enteringDeviceFromBiometricUnlock: Flow<BiometricUnlockSource>
+
/**
* Whether the device is unlocked.
*
@@ -73,7 +83,14 @@
private val lockPatternUtils: LockPatternUtils,
private val keyguardBypassController: KeyguardBypassController,
keyguardStateController: KeyguardStateController,
+ keyguardRepository: KeyguardRepository,
) : DeviceEntryRepository {
+ override val enteringDeviceFromBiometricUnlock =
+ keyguardRepository.biometricUnlockState
+ .filter { BiometricUnlockModel.dismissesKeyguard(it) }
+ .sample(
+ keyguardRepository.biometricUnlockSource.filterNotNull(),
+ )
private val _isUnlocked = MutableStateFlow(false)
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt
new file mode 100644
index 0000000..1a6bd04
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.deviceentry.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.shared.DeviceEntryBiometricMode
+import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
+import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+
+/** Business logic for device entry biometric states that may differ based on the biometric mode. */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class DeviceEntryBiometricAuthInteractor
+@Inject
+constructor(
+ biometricSettingsRepository: BiometricSettingsRepository,
+ deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor,
+) {
+ private val biometricMode: Flow<DeviceEntryBiometricMode> =
+ combine(
+ biometricSettingsRepository.isFingerprintEnrolledAndEnabled,
+ biometricSettingsRepository.isFaceAuthEnrolledAndEnabled,
+ ) { fingerprintEnrolled, faceEnrolled ->
+ if (fingerprintEnrolled && faceEnrolled) {
+ DeviceEntryBiometricMode.CO_EXPERIENCE
+ } else if (fingerprintEnrolled) {
+ DeviceEntryBiometricMode.FINGERPRINT_ONLY
+ } else if (faceEnrolled) {
+ DeviceEntryBiometricMode.FACE_ONLY
+ } else {
+ DeviceEntryBiometricMode.NONE
+ }
+ }
+ private val faceOnly: Flow<Boolean> =
+ biometricMode.map { it == DeviceEntryBiometricMode.FACE_ONLY }
+
+ /**
+ * Triggered if face is the only biometric that can be used for device entry and a face failure
+ * occurs.
+ */
+ val faceOnlyFaceFailure: Flow<FailedFaceAuthenticationStatus> =
+ faceOnly.flatMapLatest { faceOnly ->
+ if (faceOnly) {
+ deviceEntryFaceAuthInteractor.faceFailure
+ } else {
+ emptyFlow()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt
new file mode 100644
index 0000000..70716c6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.deviceentry.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
+import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filterIsInstance
+
+@SysUISingleton
+class DeviceEntryFaceAuthInteractor
+@Inject
+constructor(
+ repository: DeviceEntryFaceAuthRepository,
+) {
+ val faceFailure: Flow<FailedFaceAuthenticationStatus> =
+ repository.authenticationStatus.filterIsInstance<FailedFaceAuthenticationStatus>()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt
new file mode 100644
index 0000000..efa1c0a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.deviceentry.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filterIsInstance
+
+@SysUISingleton
+class DeviceEntryFingerprintAuthInteractor
+@Inject
+constructor(
+ repository: DeviceEntryFingerprintAuthRepository,
+) {
+ val fingerprintFailure: Flow<FailFingerprintAuthenticationStatus> =
+ repository.authenticationStatus.filterIsInstance<FailFingerprintAuthenticationStatus>()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt
index 53d6f73..649a971 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt
@@ -19,7 +19,6 @@
import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.deviceentry.data.repository.DeviceEntryHapticsRepository
import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.power.domain.interactor.PowerInteractor
@@ -34,11 +33,12 @@
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart
/**
* Business logic for device entry haptic events. Determines whether the haptic should play. In
- * particular, there are extra guards for whether device entry error and successes hatpics should
+ * particular, there are extra guards for whether device entry error and successes haptics should
* play when the physical fingerprint sensor is located on the power button.
*/
@ExperimentalCoroutinesApi
@@ -46,7 +46,9 @@
class DeviceEntryHapticsInteractor
@Inject
constructor(
- private val repository: DeviceEntryHapticsRepository,
+ deviceEntryInteractor: DeviceEntryInteractor,
+ deviceEntryFingerprintAuthInteractor: DeviceEntryFingerprintAuthInteractor,
+ deviceEntryBiometricAuthInteractor: DeviceEntryBiometricAuthInteractor,
fingerprintPropertyRepository: FingerprintPropertyRepository,
biometricSettingsRepository: BiometricSettingsRepository,
keyEventInteractor: KeyEventInteractor,
@@ -77,9 +79,8 @@
emit(recentPowerButtonPressThresholdMs * -1L - 1L)
}
- val playSuccessHaptic: Flow<Boolean> =
- repository.successHapticRequest
- .filter { it }
+ val playSuccessHaptic: Flow<Unit> =
+ deviceEntryInteractor.enteringDeviceFromBiometricUnlock
.sample(
combine(
powerButtonSideFpsEnrolled,
@@ -88,7 +89,7 @@
::Triple
)
)
- .map { (sideFpsEnrolled, powerButtonDown, lastPowerButtonWakeup) ->
+ .filter { (sideFpsEnrolled, powerButtonDown, lastPowerButtonWakeup) ->
val sideFpsAllowsHaptic =
!powerButtonDown &&
systemClock.uptimeMillis() - lastPowerButtonWakeup >
@@ -96,38 +97,28 @@
val allowHaptic = !sideFpsEnrolled || sideFpsAllowsHaptic
if (!allowHaptic) {
logger.d("Skip success haptic. Recent power button press or button is down.")
- handleSuccessHaptic() // immediately handle, don't vibrate
}
allowHaptic
}
- val playErrorHaptic: Flow<Boolean> =
- repository.errorHapticRequest
- .filter { it }
+ .map {} // map to Unit
+
+ private val playErrorHapticForBiometricFailure: Flow<Unit> =
+ merge(
+ deviceEntryFingerprintAuthInteractor.fingerprintFailure,
+ deviceEntryBiometricAuthInteractor.faceOnlyFaceFailure,
+ )
+ .map {} // map to Unit
+ val playErrorHaptic: Flow<Unit> =
+ playErrorHapticForBiometricFailure
.sample(combine(powerButtonSideFpsEnrolled, powerButtonDown, ::Pair))
- .map { (sideFpsEnrolled, powerButtonDown) ->
+ .filter { (sideFpsEnrolled, powerButtonDown) ->
val allowHaptic = !sideFpsEnrolled || !powerButtonDown
if (!allowHaptic) {
logger.d("Skip error haptic. Power button is down.")
- handleErrorHaptic() // immediately handle, don't vibrate
}
allowHaptic
}
-
- fun vibrateSuccess() {
- repository.requestSuccessHaptic()
- }
-
- fun vibrateError() {
- repository.requestErrorHaptic()
- }
-
- fun handleSuccessHaptic() {
- repository.handleSuccessHaptic()
- }
-
- fun handleErrorHaptic() {
- repository.handleErrorHaptic()
- }
+ .map {} // map to Unit
private val recentPowerButtonPressThresholdMs = 400L
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
index 4cddb9c..47be8ab 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
@@ -23,6 +23,7 @@
import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.TrustRepository
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.scene.shared.model.SceneKey
@@ -30,6 +31,7 @@
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collectLatest
@@ -60,6 +62,9 @@
trustRepository: TrustRepository,
flags: SceneContainerFlags,
) {
+ val enteringDeviceFromBiometricUnlock: Flow<BiometricUnlockSource> =
+ repository.enteringDeviceFromBiometricUnlock
+
/**
* Whether the device is unlocked.
*
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryBiometricMode.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryBiometricMode.kt
new file mode 100644
index 0000000..6d885b3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryBiometricMode.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.deviceentry.shared
+
+/** Models the biometrics that can be used to enter the device. */
+enum class DeviceEntryBiometricMode {
+ /** No biometrics can be used to enter the device from the lockscreen. */
+ NONE,
+ /** Only face can be used to enter the device from the lockscreen. */
+ FACE_ONLY,
+ /** Only fingerprint can be used to enter the device from the lockscreen. */
+ FINGERPRINT_ONLY,
+ /** Both face and fingerprint can be used to enter the device from the lockscreen. */
+ CO_EXPERIENCE,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
index 87c12b4..72b0891 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
@@ -25,7 +25,6 @@
import static com.android.systemui.flags.FlagManager.EXTRA_VALUE;
import static com.android.systemui.flags.FlagsCommonModule.ALL_FLAGS;
import static com.android.systemui.shared.Flags.exampleSharedFlag;
-
import static java.util.Objects.requireNonNull;
import android.content.BroadcastReceiver;
@@ -508,9 +507,7 @@
enabled = isEnabled((ResourceBooleanFlag) f);
overridden = readBooleanFlagOverride(f.getName()) != null;
} else if (f instanceof SysPropBooleanFlag) {
- // TODO(b/223379190): Teamfood not supported for sysprop flags yet.
enabled = isEnabled((SysPropBooleanFlag) f);
- teamfood = false;
overridden = !mSystemProperties.get(f.getName()).isEmpty();
} else {
// TODO: add support for other flag types.
@@ -519,7 +516,7 @@
}
if (enabled) {
- return new ReleasedFlag(f.getName(), f.getNamespace(), teamfood, overridden);
+ return new ReleasedFlag(f.getName(), f.getNamespace(), overridden);
} else {
return new UnreleasedFlag(f.getName(), f.getNamespace(), teamfood, overridden);
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
index 6a0e882..d5b95d67 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
@@ -16,7 +16,10 @@
package com.android.systemui.flags
+import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
+import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
@@ -29,5 +32,9 @@
override fun defineDependencies() {
NotificationsLiveDataStoreRefactor.token dependsOn NotificationIconContainerRefactor.token
FooterViewRefactor.token dependsOn NotificationIconContainerRefactor.token
+
+ val keyguardBottomAreaRefactor = FlagToken(
+ FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR, keyguardBottomAreaRefactor())
+ KeyguardShadeMigrationNssl.token dependsOn keyguardBottomAreaRefactor
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index cbfd17ff..9fe5c3f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -214,7 +214,7 @@
private fun listenForLockscreenToPrimaryBouncerDragging() {
var transitionId: UUID? = null
scope.launch("$TAG#listenForLockscreenToPrimaryBouncerDragging") {
- shadeRepository.shadeModel
+ shadeRepository.legacyShadeExpansion
.sample(
combine(
transitionInteractor.startedKeyguardTransitionStep,
@@ -224,23 +224,23 @@
),
::toQuad
)
- .collect { (shadeModel, keyguardState, statusBarState, isKeyguardUnlocked) ->
+ .collect { (shadeExpansion, keyguardState, statusBarState, isKeyguardUnlocked) ->
val id = transitionId
if (id != null) {
if (keyguardState.to == KeyguardState.PRIMARY_BOUNCER) {
// An existing `id` means a transition is started, and calls to
// `updateTransition` will control it until FINISHED or CANCELED
var nextState =
- if (shadeModel.expansionAmount == 0f) {
+ if (shadeExpansion == 0f) {
TransitionState.FINISHED
- } else if (shadeModel.expansionAmount == 1f) {
+ } else if (shadeExpansion == 1f) {
TransitionState.CANCELED
} else {
TransitionState.RUNNING
}
transitionRepository.updateTransition(
id,
- 1f - shadeModel.expansionAmount,
+ 1f - shadeExpansion,
nextState,
)
@@ -274,7 +274,7 @@
// integrated into KeyguardTransitionRepository
if (
keyguardState.to == KeyguardState.LOCKSCREEN &&
- shadeModel.isUserDragging &&
+ shadeRepository.legacyShadeTracking.value &&
!isKeyguardUnlocked &&
statusBarState == KEYGUARD
) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 702386d..c12efe8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -224,8 +224,8 @@
configurationInteractor
.dimensionPixelSize(R.dimen.keyguard_translate_distance_on_swipe_up)
.flatMapLatest { translationDistance ->
- shadeRepository.shadeModel.map {
- if (it.expansionAmount == 0f) {
+ shadeRepository.legacyShadeExpansion.map {
+ if (it == 0f) {
// Reset the translation value
0f
} else {
@@ -233,7 +233,7 @@
MathUtils.lerp(
translationDistance,
0,
- Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(it.expansionAmount)
+ Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(it)
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockModel.kt
index 8fe6309f..2ae5ce1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockModel.kt
@@ -51,9 +51,21 @@
companion object {
private val wakeAndUnlockModes =
setOf(WAKE_AND_UNLOCK, WAKE_AND_UNLOCK_FROM_DREAM, WAKE_AND_UNLOCK_PULSING)
+ private val dismissesKeyguardModes =
+ setOf(
+ WAKE_AND_UNLOCK,
+ WAKE_AND_UNLOCK_PULSING,
+ UNLOCK_COLLAPSING,
+ WAKE_AND_UNLOCK_FROM_DREAM,
+ DISMISS_BOUNCER
+ )
fun isWakeAndUnlock(model: BiometricUnlockModel): Boolean {
return wakeAndUnlockModes.contains(model)
}
+
+ fun dismissesKeyguard(model: BiometricUnlockModel): Boolean {
+ return dismissesKeyguardModes.contains(model)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt
index 3c143fe..cc385a8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt
@@ -49,7 +49,7 @@
}
/** Fingerprint authentication failed message. */
-object FailFingerprintAuthenticationStatus : FingerprintAuthenticationStatus()
+data object FailFingerprintAuthenticationStatus : FingerprintAuthenticationStatus()
/** Fingerprint authentication error message */
data class ErrorFingerprintAuthenticationStatus(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
index 4b4a19e..dcf4284 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
@@ -19,6 +19,7 @@
import android.annotation.SuppressLint
import android.content.res.ColorStateList
+import android.view.HapticFeedbackConstants
import android.view.View
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
@@ -30,6 +31,7 @@
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.statusbar.VibratorHelper
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
@@ -51,6 +53,7 @@
fgViewModel: DeviceEntryForegroundViewModel,
bgViewModel: DeviceEntryBackgroundViewModel,
falsingManager: FalsingManager,
+ vibratorHelper: VibratorHelper,
) {
DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()
val longPressHandlingView = view.longPressHandlingView
@@ -62,6 +65,10 @@
if (falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) {
return
}
+ vibratorHelper.performHapticFeedback(
+ view,
+ HapticFeedbackConstants.CONFIRM,
+ )
viewModel.onLongPress()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index e603ead..01a1ca3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -68,7 +68,6 @@
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -253,27 +252,21 @@
if (deviceEntryHapticsInteractor != null && vibratorHelper != null) {
launch {
- deviceEntryHapticsInteractor.playSuccessHaptic
- .filter { it }
- .collect {
- vibratorHelper.performHapticFeedback(
- view,
- HapticFeedbackConstants.CONFIRM,
- )
- deviceEntryHapticsInteractor.handleSuccessHaptic()
- }
+ deviceEntryHapticsInteractor.playSuccessHaptic.collect {
+ vibratorHelper.performHapticFeedback(
+ view,
+ HapticFeedbackConstants.CONFIRM,
+ )
+ }
}
launch {
- deviceEntryHapticsInteractor.playErrorHaptic
- .filter { it }
- .collect {
- vibratorHelper.performHapticFeedback(
- view,
- HapticFeedbackConstants.REJECT,
- )
- deviceEntryHapticsInteractor.handleErrorHaptic()
- }
+ deviceEntryHapticsInteractor.playErrorHaptic.collect {
+ vibratorHelper.performHapticFeedback(
+ view,
+ HapticFeedbackConstants.REJECT,
+ )
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
index 96efb23..39a0547 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
@@ -40,7 +40,7 @@
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import com.android.systemui.statusbar.phone.NotificationIconAreaController
import com.android.systemui.statusbar.phone.NotificationIconContainer
-import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.ui.SystemBarUtilsState
import javax.inject.Inject
import kotlinx.coroutines.DisposableHandle
@@ -49,13 +49,13 @@
constructor(
private val context: Context,
private val configurationState: ConfigurationState,
- private val configurationController: ConfigurationController,
private val featureFlags: FeatureFlagsClassic,
private val iconBindingFailureTracker: StatusBarIconViewBindingFailureTracker,
private val nicAodViewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
private val nicAodIconViewStore: AlwaysOnDisplayNotificationIconViewStore,
private val notificationIconAreaController: NotificationIconAreaController,
private val smartspaceViewModel: KeyguardSmartspaceViewModel,
+ private val systemBarUtilsState: SystemBarUtilsState,
) : KeyguardSection() {
private var nicBindingDisposable: DisposableHandle? = null
@@ -89,11 +89,11 @@
if (NotificationIconContainerRefactor.isEnabled) {
nicBindingDisposable?.dispose()
nicBindingDisposable =
- NotificationIconContainerViewBinder.bind(
+ NotificationIconContainerViewBinder.bindWhileAttached(
nic,
nicAodViewModel,
configurationState,
- configurationController,
+ systemBarUtilsState,
iconBindingFailureTracker,
nicAodIconViewStore,
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
index 77ab9f4..a693ec9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
@@ -49,6 +49,7 @@
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
import com.android.systemui.statusbar.NotificationShadeWindowController
+import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.gesture.TapGestureDetector
import dagger.Lazy
import javax.inject.Inject
@@ -76,6 +77,7 @@
@Application private val scope: CoroutineScope,
private val swipeUpAnywhereGestureHandler: Lazy<SwipeUpAnywhereGestureHandler>,
private val tapGestureDetector: Lazy<TapGestureDetector>,
+ private val vibratorHelper: Lazy<VibratorHelper>,
) : KeyguardSection() {
private val deviceEntryIconViewId = R.id.device_entry_icon_view
private val alternateBouncerViewId = R.id.alternate_bouncer
@@ -114,6 +116,7 @@
deviceEntryForegroundViewModel.get(),
deviceEntryBackgroundViewModel.get(),
falsingManager.get(),
+ vibratorHelper.get(),
)
}
constraintLayout.findViewById<FrameLayout?>(alternateBouncerViewId)?.let {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
index bd6aae8..f95713b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
@@ -19,7 +19,6 @@
import android.animation.FloatEvaluator
import android.animation.IntEvaluator
import com.android.keyguard.KeyguardViewController
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
@@ -56,7 +55,6 @@
val shadeDependentFlows: ShadeDependentFlows,
private val sceneContainerFlags: SceneContainerFlags,
private val keyguardViewController: Lazy<KeyguardViewController>,
- private val deviceEntryHapticsInteractor: DeviceEntryHapticsInteractor,
private val deviceEntryInteractor: DeviceEntryInteractor,
) {
private val intEvaluator = IntEvaluator()
@@ -182,8 +180,6 @@
}
fun onLongPress() {
- deviceEntryHapticsInteractor.vibrateSuccess()
-
// TODO (b/309804148): play auth ripple via an interactor
if (sceneContainerFlags.isEnabled()) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
index e94a3eb..2445bdb 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
@@ -15,25 +15,14 @@
*/
package com.android.systemui.shade.data.repository
-import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.shade.ShadeExpansionChangeEvent
-import com.android.systemui.shade.ShadeExpansionListener
-import com.android.systemui.shade.ShadeExpansionStateManager
-import com.android.systemui.shade.domain.model.ShadeModel
import javax.inject.Inject
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.distinctUntilChanged
+/** Data for the shade, mostly related to expansion of the shade and quick settings. */
interface ShadeRepository {
- /** ShadeModel information regarding shade expansion events */
- val shadeModel: Flow<ShadeModel>
-
/**
* Amount qs has expanded, [0-1]. 0 means fully collapsed, 1 means fully expanded. Quick
* Settings can be expanded without the full shade expansion.
@@ -167,34 +156,7 @@
/** Business logic for shade interactions */
@SysUISingleton
-class ShadeRepositoryImpl
-@Inject
-constructor(shadeExpansionStateManager: ShadeExpansionStateManager) : ShadeRepository {
- override val shadeModel: Flow<ShadeModel> =
- conflatedCallbackFlow {
- val callback =
- object : ShadeExpansionListener {
- override fun onPanelExpansionChanged(event: ShadeExpansionChangeEvent) {
- // Don't propagate ShadeExpansionChangeEvent.dragDownPxAmount field.
- // It is too noisy and produces extra events that consumers won't care
- // about
- val info =
- ShadeModel(
- expansionAmount = event.fraction,
- isExpanded = event.expanded,
- isUserDragging = event.tracking
- )
- trySendWithFailureLogging(info, TAG, "updated shade expansion info")
- }
- }
-
- val currentState = shadeExpansionStateManager.addExpansionListener(callback)
- callback.onPanelExpansionChanged(currentState)
-
- awaitClose { shadeExpansionStateManager.removeExpansionListener(callback) }
- }
- .distinctUntilChanged()
-
+class ShadeRepositoryImpl @Inject constructor() : ShadeRepository {
private val _qsExpansion = MutableStateFlow(0f)
override val qsExpansion: StateFlow<Float> = _qsExpansion.asStateFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/model/ShadeModel.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/model/ShadeModel.kt
deleted file mode 100644
index ce0f4283..0000000
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/model/ShadeModel.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-package com.android.systemui.shade.domain.model
-
-import android.annotation.FloatRange
-
-/** Information about shade (NotificationPanel) expansion */
-data class ShadeModel(
- /** 0 when collapsed, 1 when fully expanded. */
- @FloatRange(from = 0.0, to = 1.0) val expansionAmount: Float = 0f,
- /** Whether the panel should be considered expanded */
- val isExpanded: Boolean = false,
- /** Whether the user is actively dragging the panel. */
- val isUserDragging: Boolean = false,
-)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index faffb3e..d23c85a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Handler;
-import android.os.SystemClock;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.view.accessibility.AccessibilityEvent;
@@ -29,6 +28,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
+import com.android.systemui.util.time.SystemClock;
import java.util.stream.Stream;
@@ -39,21 +39,23 @@
*/
public abstract class AlertingNotificationManager {
private static final String TAG = "AlertNotifManager";
- protected final Clock mClock = new Clock();
+ protected final SystemClock mSystemClock;
protected final ArrayMap<String, AlertEntry> mAlertEntries = new ArrayMap<>();
protected final HeadsUpManagerLogger mLogger;
- public AlertingNotificationManager(HeadsUpManagerLogger logger, @Main Handler handler) {
- mLogger = logger;
- mHandler = handler;
- }
-
protected int mMinimumDisplayTime;
- protected int mStickyDisplayTime;
- protected int mAutoDismissNotificationDecay;
+ protected int mStickyForSomeTimeAutoDismissTime;
+ protected int mAutoDismissTime;
@VisibleForTesting
public Handler mHandler;
+ public AlertingNotificationManager(HeadsUpManagerLogger logger, @Main Handler handler,
+ SystemClock systemClock) {
+ mLogger = logger;
+ mHandler = handler;
+ mSystemClock = systemClock;
+ }
+
/**
* Called when posting a new notification that should alert the user and appear on screen.
* Adds the notification to be managed.
@@ -251,7 +253,7 @@
public long getEarliestRemovalTime(String key) {
AlertEntry alerting = mAlertEntries.get(key);
if (alerting != null) {
- return Math.max(0, alerting.mEarliestRemovaltime - mClock.currentTimeMillis());
+ return Math.max(0, alerting.mEarliestRemovalTime - mSystemClock.elapsedRealtime());
}
return 0;
}
@@ -259,7 +261,7 @@
protected class AlertEntry implements Comparable<AlertEntry> {
@Nullable public NotificationEntry mEntry;
public long mPostTime;
- public long mEarliestRemovaltime;
+ public long mEarliestRemovalTime;
@Nullable protected Runnable mRemoveAlertRunnable;
@@ -283,8 +285,8 @@
public void updateEntry(boolean updatePostTime, @Nullable String reason) {
mLogger.logUpdateEntry(mEntry, updatePostTime, reason);
- final long now = mClock.currentTimeMillis();
- mEarliestRemovaltime = now + mMinimumDisplayTime;
+ final long now = mSystemClock.elapsedRealtime();
+ mEarliestRemovalTime = now + mMinimumDisplayTime;
if (updatePostTime) {
mPostTime = Math.max(mPostTime, now);
@@ -318,7 +320,7 @@
* @return true if the notification has been on screen long enough
*/
public boolean wasShownLongEnough() {
- return mEarliestRemovaltime < mClock.currentTimeMillis();
+ return mEarliestRemovalTime < mSystemClock.elapsedRealtime();
}
@Override
@@ -351,7 +353,7 @@
if (mRemoveAlertRunnable != null) {
removeAutoRemovalCallbacks("removeAsSoonAsPossible (will be rescheduled)");
- final long timeLeft = mEarliestRemovaltime - mClock.currentTimeMillis();
+ final long timeLeft = mEarliestRemovalTime - mSystemClock.elapsedRealtime();
mHandler.postDelayed(mRemoveAlertRunnable, timeLeft);
}
}
@@ -361,22 +363,16 @@
* @return the post time
*/
protected long calculatePostTime() {
- return mClock.currentTimeMillis();
+ return mSystemClock.elapsedRealtime();
}
/**
* @return When the notification should auto-dismiss itself, based on
- * {@link SystemClock#elapsedRealTime()}
+ * {@link SystemClock#elapsedRealtime()}
*/
protected long calculateFinishTime() {
// Overridden by HeadsUpManager HeadsUpEntry #calculateFinishTime
return 0;
}
}
-
- protected final static class Clock {
- public long currentTimeMillis() {
- return SystemClock.elapsedRealtime();
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
index 22912df..85f4c36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
@@ -20,6 +20,7 @@
import com.android.systemui.statusbar.data.StatusBarDataLayerModule
import com.android.systemui.statusbar.phone.LightBarController
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController
+import com.android.systemui.statusbar.ui.SystemBarUtilsProxyImpl
import dagger.Binds
import dagger.Module
import dagger.multibindings.ClassKey
@@ -33,7 +34,7 @@
* ([com.android.systemui.statusbar.pipeline.dagger.StatusBarPipelineModule],
* [com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule], etc.).
*/
-@Module(includes = [StatusBarDataLayerModule::class])
+@Module(includes = [StatusBarDataLayerModule::class, SystemBarUtilsProxyImpl.Module::class])
abstract class StatusBarModule {
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
index ecca973..92391e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
@@ -22,7 +22,6 @@
import androidx.annotation.ColorInt
import androidx.collection.ArrayMap
import androidx.lifecycle.lifecycleScope
-import com.android.internal.policy.SystemBarUtils
import com.android.internal.statusbar.StatusBarIcon
import com.android.internal.util.ContrastColorUtil
import com.android.systemui.common.ui.ConfigurationState
@@ -39,10 +38,8 @@
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconsViewData
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconsViewData.LimitType
import com.android.systemui.statusbar.phone.NotificationIconContainer
-import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.statusbar.policy.onConfigChanged
+import com.android.systemui.statusbar.ui.SystemBarUtilsState
import com.android.systemui.util.kotlin.mapValuesNotNullTo
-import com.android.systemui.util.kotlin.stateFlow
import com.android.systemui.util.ui.isAnimating
import com.android.systemui.util.ui.stopAnimating
import com.android.systemui.util.ui.value
@@ -51,7 +48,6 @@
import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.launch
@@ -59,20 +55,20 @@
/** Binds a view-model to a [NotificationIconContainer]. */
object NotificationIconContainerViewBinder {
@JvmStatic
- fun bind(
+ fun bindWhileAttached(
view: NotificationIconContainer,
viewModel: NotificationIconContainerShelfViewModel,
configuration: ConfigurationState,
- configurationController: ConfigurationController,
+ systemBarUtilsState: SystemBarUtilsState,
failureTracker: StatusBarIconViewBindingFailureTracker,
- viewStore: ShelfNotificationIconViewStore,
+ viewStore: IconViewStore,
): DisposableHandle {
return view.repeatWhenAttached {
lifecycleScope.launch {
viewModel.icons.bindIcons(
view,
configuration,
- configurationController,
+ systemBarUtilsState,
notifyBindingFailures = { failureTracker.shelfFailures = it },
viewStore,
)
@@ -81,68 +77,89 @@
}
@JvmStatic
- fun bind(
+ fun bindWhileAttached(
view: NotificationIconContainer,
viewModel: NotificationIconContainerStatusBarViewModel,
configuration: ConfigurationState,
- configurationController: ConfigurationController,
+ systemBarUtilsState: SystemBarUtilsState,
failureTracker: StatusBarIconViewBindingFailureTracker,
- viewStore: StatusBarNotificationIconViewStore,
- ): DisposableHandle {
- val contrastColorUtil = ContrastColorUtil.getInstance(view.context)
- return view.repeatWhenAttached {
- lifecycleScope.run {
- launch {
- val iconColors: Flow<NotificationIconColors> =
- viewModel.iconColors.mapNotNull { it.iconColors(view.viewBounds) }
- viewModel.icons.bindIcons(
- view,
- configuration,
- configurationController,
- notifyBindingFailures = { failureTracker.statusBarFailures = it },
- viewStore,
- ) { _, sbiv ->
- StatusBarIconViewBinder.bindIconColors(
- sbiv,
- iconColors,
- contrastColorUtil,
- )
- }
- }
- launch { viewModel.bindIsolatedIcon(view, viewStore) }
- launch { viewModel.animationsEnabled.bindAnimationsEnabled(view) }
+ viewStore: IconViewStore,
+ ): DisposableHandle =
+ view.repeatWhenAttached {
+ lifecycleScope.launch {
+ bind(view, viewModel, configuration, systemBarUtilsState, failureTracker, viewStore)
}
}
+
+ suspend fun bind(
+ view: NotificationIconContainer,
+ viewModel: NotificationIconContainerStatusBarViewModel,
+ configuration: ConfigurationState,
+ systemBarUtilsState: SystemBarUtilsState,
+ failureTracker: StatusBarIconViewBindingFailureTracker,
+ viewStore: IconViewStore,
+ ): Unit = coroutineScope {
+ launch {
+ val contrastColorUtil = ContrastColorUtil.getInstance(view.context)
+ val iconColors: Flow<NotificationIconColors> =
+ viewModel.iconColors.mapNotNull { it.iconColors(view.viewBounds) }
+ viewModel.icons.bindIcons(
+ view,
+ configuration,
+ systemBarUtilsState,
+ notifyBindingFailures = { failureTracker.statusBarFailures = it },
+ viewStore,
+ ) { _, sbiv ->
+ StatusBarIconViewBinder.bindIconColors(
+ sbiv,
+ iconColors,
+ contrastColorUtil,
+ )
+ }
+ }
+ launch { viewModel.bindIsolatedIcon(view, viewStore) }
+ launch { viewModel.animationsEnabled.bindAnimationsEnabled(view) }
}
@JvmStatic
- fun bind(
+ fun bindWhileAttached(
view: NotificationIconContainer,
viewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
configuration: ConfigurationState,
- configurationController: ConfigurationController,
+ systemBarUtilsState: SystemBarUtilsState,
failureTracker: StatusBarIconViewBindingFailureTracker,
viewStore: IconViewStore,
): DisposableHandle {
return view.repeatWhenAttached {
lifecycleScope.launch {
- view.setUseIncreasedIconScale(true)
- launch {
- viewModel.icons.bindIcons(
- view,
- configuration,
- configurationController,
- notifyBindingFailures = { failureTracker.aodFailures = it },
- viewStore,
- ) { _, sbiv ->
- viewModel.bindAodStatusBarIconView(sbiv, configuration)
- }
- }
- launch { viewModel.areContainerChangesAnimated.bindAnimationsEnabled(view) }
+ bind(view, viewModel, configuration, systemBarUtilsState, failureTracker, viewStore)
}
}
}
+ suspend fun bind(
+ view: NotificationIconContainer,
+ viewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
+ configuration: ConfigurationState,
+ systemBarUtilsState: SystemBarUtilsState,
+ failureTracker: StatusBarIconViewBindingFailureTracker,
+ viewStore: IconViewStore,
+ ): Unit = coroutineScope {
+ view.setUseIncreasedIconScale(true)
+ launch {
+ viewModel.icons.bindIcons(
+ view,
+ configuration,
+ systemBarUtilsState,
+ notifyBindingFailures = { failureTracker.aodFailures = it },
+ viewStore,
+ ) { _, sbiv ->
+ viewModel.bindAodStatusBarIconView(sbiv, configuration)
+ }
+ }
+ launch { viewModel.areContainerChangesAnimated.bindAnimationsEnabled(view) }
+ }
+
private suspend fun NotificationIconContainerAlwaysOnDisplayViewModel.bindAodStatusBarIconView(
sbiv: StatusBarIconView,
configuration: ConfigurationState,
@@ -199,7 +216,7 @@
private suspend fun Flow<NotificationIconsViewData>.bindIcons(
view: NotificationIconContainer,
configuration: ConfigurationState,
- configurationController: ConfigurationController,
+ systemBarUtilsState: SystemBarUtilsState,
notifyBindingFailures: (Collection<String>) -> Unit,
viewStore: IconViewStore,
bindIcon: suspend (iconKey: String, view: StatusBarIconView) -> Unit = { _, _ -> },
@@ -210,12 +227,8 @@
)
val iconHorizontalPaddingFlow: Flow<Int> =
configuration.getDimensionPixelSize(R.dimen.status_bar_icon_horizontal_margin)
- val statusBarHeightFlow: StateFlow<Int> =
- stateFlow(changedSignals = configurationController.onConfigChanged) {
- SystemBarUtils.getStatusBarHeight(view.context)
- }
val layoutParams: Flow<FrameLayout.LayoutParams> =
- combine(iconSizeFlow, iconHorizontalPaddingFlow, statusBarHeightFlow) {
+ combine(iconSizeFlow, iconHorizontalPaddingFlow, systemBarUtilsState.statusBarHeight) {
iconSize,
iconHPadding,
statusBarHeight,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
index 5cdead4..699e140 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
@@ -29,7 +29,7 @@
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel
import com.android.systemui.statusbar.phone.NotificationIconAreaController
-import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.ui.SystemBarUtilsState
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.launch
@@ -39,7 +39,7 @@
shelf: NotificationShelf,
viewModel: NotificationShelfViewModel,
configuration: ConfigurationState,
- configurationController: ConfigurationController,
+ systemBarUtilsState: SystemBarUtilsState,
falsingManager: FalsingManager,
iconViewBindingFailureTracker: StatusBarIconViewBindingFailureTracker,
notificationIconAreaController: NotificationIconAreaController,
@@ -48,11 +48,11 @@
ActivatableNotificationViewBinder.bind(viewModel, shelf, falsingManager)
shelf.apply {
if (NotificationIconContainerRefactor.isEnabled) {
- NotificationIconContainerViewBinder.bind(
+ NotificationIconContainerViewBinder.bindWhileAttached(
shelfIcons,
viewModel.icons,
configuration,
- configurationController,
+ systemBarUtilsState,
iconViewBindingFailureTracker,
shelfIconViewStore,
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
index a4e1a9c..9373d49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
@@ -40,7 +40,7 @@
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.HideNotificationsBinder.bindHideList
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel
import com.android.systemui.statusbar.phone.NotificationIconAreaController
-import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.ui.SystemBarUtilsState
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.combine
@@ -53,12 +53,12 @@
private val viewModel: NotificationListViewModel,
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val configuration: ConfigurationState,
- private val configurationController: ConfigurationController,
private val falsingManager: FalsingManager,
private val iconAreaController: NotificationIconAreaController,
private val iconViewBindingFailureTracker: StatusBarIconViewBindingFailureTracker,
private val metricsLogger: MetricsLogger,
private val shelfIconViewStore: ShelfNotificationIconViewStore,
+ private val systemBarUtilsState: SystemBarUtilsState,
) {
fun bind(
@@ -91,7 +91,7 @@
shelf,
viewModel.shelf,
configuration,
- configurationController,
+ systemBarUtilsState,
falsingManager,
iconViewBindingFailureTracker,
iconAreaController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 4e77801..97fc35a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -47,7 +47,6 @@
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.WakefulnessLifecycle;
@@ -180,8 +179,6 @@
private final SystemClock mSystemClock;
private final boolean mOrderUnlockAndWake;
private final Lazy<SelectedUserInteractor> mSelectedUserInteractor;
- private final DeviceEntryHapticsInteractor mHapticsInteractor;
-
private long mLastFpFailureUptimeMillis;
private int mNumConsecutiveFpFailures;
@@ -288,7 +285,6 @@
ScreenOffAnimationController screenOffAnimationController,
VibratorHelper vibrator,
SystemClock systemClock,
- DeviceEntryHapticsInteractor hapticsInteractor,
Lazy<SelectedUserInteractor> selectedUserInteractor,
BiometricUnlockInteractor biometricUnlockInteractor
) {
@@ -320,7 +316,6 @@
mSystemClock = systemClock;
mOrderUnlockAndWake = resources.getBoolean(
com.android.internal.R.bool.config_orderUnlockAndWake);
- mHapticsInteractor = hapticsInteractor;
mSelectedUserInteractor = selectedUserInteractor;
dumpManager.registerDumpable(this);
@@ -442,7 +437,6 @@
if (mode == MODE_WAKE_AND_UNLOCK
|| mode == MODE_WAKE_AND_UNLOCK_PULSING || mode == MODE_UNLOCK_COLLAPSING
|| mode == MODE_WAKE_AND_UNLOCK_FROM_DREAM || mode == MODE_DISMISS_BOUNCER) {
- mHapticsInteractor.vibrateSuccess();
onBiometricUnlockedWithKeyguardDismissal(biometricSourceType);
}
startWakeAndUnlock(mode);
@@ -726,14 +720,6 @@
}
}
- // Suppress all face auth errors if fingerprint can be used to authenticate
- if ((biometricSourceType == BiometricSourceType.FACE
- && !mUpdateMonitor.isUnlockWithFingerprintPossible(
- mSelectedUserInteractor.get().getSelectedUserId()))
- || (biometricSourceType == BiometricSourceType.FINGERPRINT)) {
- mHapticsInteractor.vibrateError();
- }
-
cleanup();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 3a95e6d..644c896 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -49,6 +49,8 @@
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.policy.OnHeadsUpPhoneListenerChange;
import com.android.systemui.util.kotlin.JavaAdapter;
+import com.android.systemui.util.settings.GlobalSettings;
+import com.android.systemui.util.time.SystemClock;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -115,11 +117,14 @@
VisualStabilityProvider visualStabilityProvider,
ConfigurationController configurationController,
@Main Handler handler,
+ GlobalSettings globalSettings,
+ SystemClock systemClock,
AccessibilityManagerWrapper accessibilityManagerWrapper,
UiEventLogger uiEventLogger,
JavaAdapter javaAdapter,
ShadeInteractor shadeInteractor) {
- super(context, logger, handler, accessibilityManagerWrapper, uiEventLogger);
+ super(context, logger, handler, globalSettings, systemClock, accessibilityManagerWrapper,
+ uiEventLogger);
Resources resources = mContext.getResources();
mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time);
statusBarStateController.addCallback(mStatusBarStateListener);
@@ -206,7 +211,7 @@
@Override
public boolean shouldSwallowClick(@NonNull String key) {
BaseHeadsUpManager.HeadsUpEntry entry = getHeadsUpEntry(key);
- return entry != null && mClock.currentTimeMillis() < entry.mPostTime;
+ return entry != null && mSystemClock.elapsedRealtime() < entry.mPostTime;
}
public void onExpandingFinished() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 49880d4..cd99934 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -74,8 +74,8 @@
import com.android.systemui.statusbar.pipeline.shared.ui.binder.CollapsedStatusBarViewBinder;
import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarVisibilityChangeListener;
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel;
-import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.ui.SystemBarUtilsState;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.statusbar.window.StatusBarWindowStateListener;
import com.android.systemui.util.CarrierConfigTracker;
@@ -83,8 +83,6 @@
import com.android.systemui.util.CarrierConfigTracker.DefaultDataSubscriptionChangedListener;
import com.android.systemui.util.settings.SecureSettings;
-import kotlin.Unit;
-
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -95,6 +93,8 @@
import javax.inject.Inject;
+import kotlin.Unit;
+
/**
* Contains the collapsed status bar and handles hiding/showing based on disable flags
* and keyguard state. Also manages lifecycle to make sure the views it contains are being
@@ -153,7 +153,7 @@
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final NotificationIconContainerStatusBarViewModel mStatusBarIconsViewModel;
private final ConfigurationState mConfigurationState;
- private final ConfigurationController mConfigurationController;
+ private final SystemBarUtilsState mSystemBarUtilsState;
private final StatusBarNotificationIconViewStore mStatusBarIconViewStore;
private final DemoModeController mDemoModeController;
@@ -246,7 +246,7 @@
KeyguardUpdateMonitor keyguardUpdateMonitor,
NotificationIconContainerStatusBarViewModel statusBarIconsViewModel,
ConfigurationState configurationState,
- ConfigurationController configurationController,
+ SystemBarUtilsState systemBarUtilsState,
StatusBarNotificationIconViewStore statusBarIconViewStore,
DemoModeController demoModeController) {
mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory;
@@ -275,7 +275,7 @@
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mStatusBarIconsViewModel = statusBarIconsViewModel;
mConfigurationState = configurationState;
- mConfigurationController = configurationController;
+ mSystemBarUtilsState = systemBarUtilsState;
mStatusBarIconViewStore = statusBarIconViewStore;
mDemoModeController = demoModeController;
}
@@ -466,11 +466,11 @@
.inflate(R.layout.notification_icon_area, notificationIconArea, true);
NotificationIconContainer notificationIcons =
notificationIconArea.requireViewById(R.id.notificationIcons);
- NotificationIconContainerViewBinder.bind(
+ NotificationIconContainerViewBinder.bindWhileAttached(
notificationIcons,
mStatusBarIconsViewModel,
mConfigurationState,
- mConfigurationController,
+ mSystemBarUtilsState,
mIconViewBindingFailureTracker,
mStatusBarIconViewStore);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
index cec76f3..8054b04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
@@ -25,8 +25,6 @@
import android.content.res.Resources;
import android.database.ContentObserver;
import android.os.Handler;
-import android.os.SystemClock;
-import android.provider.Settings;
import android.util.ArrayMap;
import android.view.accessibility.AccessibilityManager;
@@ -40,6 +38,8 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.util.ListenerSet;
+import com.android.systemui.util.settings.GlobalSettings;
+import com.android.systemui.util.time.SystemClock;
import java.io.PrintWriter;
@@ -85,36 +85,40 @@
public BaseHeadsUpManager(@NonNull final Context context,
HeadsUpManagerLogger logger,
@Main Handler handler,
+ GlobalSettings globalSettings,
+ SystemClock systemClock,
AccessibilityManagerWrapper accessibilityManagerWrapper,
UiEventLogger uiEventLogger) {
- super(logger, handler);
+ super(logger, handler, systemClock);
mContext = context;
mAccessibilityMgr = accessibilityManagerWrapper;
mUiEventLogger = uiEventLogger;
Resources resources = context.getResources();
mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time);
- mStickyDisplayTime = resources.getInteger(R.integer.sticky_heads_up_notification_time);
- mAutoDismissNotificationDecay = resources.getInteger(R.integer.heads_up_notification_decay);
+ mStickyForSomeTimeAutoDismissTime = resources.getInteger(
+ R.integer.sticky_heads_up_notification_time);
+ mAutoDismissTime = resources.getInteger(R.integer.heads_up_notification_decay);
mTouchAcceptanceDelay = resources.getInteger(R.integer.touch_acceptance_delay);
mSnoozedPackages = new ArrayMap<>();
int defaultSnoozeLengthMs =
resources.getInteger(R.integer.heads_up_default_snooze_length_ms);
- mSnoozeLengthMs = Settings.Global.getInt(context.getContentResolver(),
- SETTING_HEADS_UP_SNOOZE_LENGTH_MS, defaultSnoozeLengthMs);
+ mSnoozeLengthMs = globalSettings.getInt(SETTING_HEADS_UP_SNOOZE_LENGTH_MS,
+ defaultSnoozeLengthMs);
ContentObserver settingsObserver = new ContentObserver(handler) {
@Override
public void onChange(boolean selfChange) {
- final int packageSnoozeLengthMs = Settings.Global.getInt(
- context.getContentResolver(), SETTING_HEADS_UP_SNOOZE_LENGTH_MS, -1);
+ final int packageSnoozeLengthMs = globalSettings.getInt(
+ SETTING_HEADS_UP_SNOOZE_LENGTH_MS, -1);
if (packageSnoozeLengthMs > -1 && packageSnoozeLengthMs != mSnoozeLengthMs) {
mSnoozeLengthMs = packageSnoozeLengthMs;
mLogger.logSnoozeLengthChange(packageSnoozeLengthMs);
}
}
};
- context.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false,
+ globalSettings.registerContentObserver(
+ globalSettings.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS),
+ /* notifyForDescendants = */ false,
settingsObserver);
}
@@ -231,7 +235,7 @@
final String key = snoozeKey(packageName, mUser);
Long snoozedUntil = mSnoozedPackages.get(key);
if (snoozedUntil != null) {
- if (snoozedUntil > mClock.currentTimeMillis()) {
+ if (snoozedUntil > mSystemClock.elapsedRealtime()) {
mLogger.logIsSnoozedReturned(key);
return true;
}
@@ -250,7 +254,7 @@
String packageName = entry.mEntry.getSbn().getPackageName();
String snoozeKey = snoozeKey(packageName, mUser);
mLogger.logPackageSnoozed(snoozeKey);
- mSnoozedPackages.put(snoozeKey, mClock.currentTimeMillis() + mSnoozeLengthMs);
+ mSnoozedPackages.put(snoozeKey, mSystemClock.elapsedRealtime() + mSnoozeLengthMs);
}
}
@@ -308,7 +312,7 @@
protected void dumpInternal(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.print(" mTouchAcceptanceDelay="); pw.println(mTouchAcceptanceDelay);
pw.print(" mSnoozeLengthMs="); pw.println(mSnoozeLengthMs);
- pw.print(" now="); pw.println(mClock.currentTimeMillis());
+ pw.print(" now="); pw.println(mSystemClock.elapsedRealtime());
pw.print(" mUser="); pw.println(mUser);
for (AlertEntry entry: mAlertEntries.values()) {
pw.print(" HeadsUpEntry="); pw.println(entry.mEntry);
@@ -519,12 +523,12 @@
/**
* @return When the notification should auto-dismiss itself, based on
- * {@link SystemClock#elapsedRealTime()}
+ * {@link SystemClock#elapsedRealtime()}
*/
@Override
protected long calculateFinishTime() {
final long duration = getRecommendedHeadsUpTimeoutMs(
- isStickyForSomeTime() ? mStickyDisplayTime : mAutoDismissNotificationDecay);
+ isStickyForSomeTime() ? mStickyForSomeTimeAutoDismissTime : mAutoDismissTime);
return mPostTime + duration;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ui/SystemBarUtilsProxy.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ui/SystemBarUtilsProxy.kt
new file mode 100644
index 0000000..2b3fb70
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ui/SystemBarUtilsProxy.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.ui
+
+import android.content.Context
+import com.android.internal.policy.SystemBarUtils
+import com.android.systemui.dagger.qualifiers.Application
+import dagger.Binds
+import javax.inject.Inject
+
+/**
+ * Proxy interface to [SystemBarUtils], allowing injection of different logic for testing.
+ *
+ * Developers should almost always prefer [SystemBarUtilsState] instead.
+ */
+interface SystemBarUtilsProxy {
+ fun getStatusBarHeight(): Int
+}
+
+class SystemBarUtilsProxyImpl
+@Inject
+constructor(
+ @Application private val context: Context,
+) : SystemBarUtilsProxy {
+ override fun getStatusBarHeight(): Int = SystemBarUtils.getStatusBarHeight(context)
+
+ @dagger.Module
+ interface Module {
+ @Binds fun bindImpl(impl: SystemBarUtilsProxyImpl): SystemBarUtilsProxy
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ui/SystemBarUtilsState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ui/SystemBarUtilsState.kt
new file mode 100644
index 0000000..ce811e2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ui/SystemBarUtilsState.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.ui
+
+import com.android.internal.policy.SystemBarUtils
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.onConfigChanged
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+
+/**
+ * Tracks state from [SystemBarUtils]. Using this is both more efficient and more testable than
+ * using [SystemBarUtils] directly.
+ */
+class SystemBarUtilsState
+@Inject
+constructor(
+ configurationController: ConfigurationController,
+ proxy: SystemBarUtilsProxy,
+) {
+ /** @see SystemBarUtils.getStatusBarHeight */
+ val statusBarHeight: Flow<Int> =
+ configurationController.onConfigChanged
+ .onStart<Any> { emit(Unit) }
+ .map { proxy.getStatusBarHeight() }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
index 24917b3..88f63ad 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
@@ -63,7 +63,7 @@
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.ui.SystemBarUtilsState;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;
@@ -185,7 +185,7 @@
mKeyguardSliceViewController,
mNotificationIconAreaController,
mSmartspaceController,
- mock(ConfigurationController.class),
+ mock(SystemBarUtilsState.class),
mock(ScreenOffAnimationController.class),
mock(StatusBarIconViewBindingFailureTracker.class),
mKeyguardUnlockAnimationController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
index 2a1cfd1..215f93d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
@@ -17,7 +17,6 @@
package com.android.systemui.accessibility.floatingmenu;
import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -26,9 +25,7 @@
import static org.mockito.Mockito.verifyZeroInteractions;
import android.graphics.PointF;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.annotations.EnableFlags;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
@@ -74,10 +71,6 @@
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
- @Rule
- public final CheckFlagsRule mCheckFlagsRule =
- DeviceFlagsValueProvider.createCheckFlagsRule();
-
@Mock
private AccessibilityManager mAccessibilityManager;
@@ -233,7 +226,7 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FLOATING_MENU_ANIMATED_TUCK)
+ @EnableFlags(Flags.FLAG_FLOATING_MENU_ANIMATED_TUCK)
public void tuck_animates() {
mMenuAnimationController.cancelAnimations();
mMenuAnimationController.moveToEdgeAndHide();
@@ -242,7 +235,7 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FLOATING_MENU_ANIMATED_TUCK)
+ @EnableFlags(Flags.FLAG_FLOATING_MENU_ANIMATED_TUCK)
public void untuck_animates() {
mMenuAnimationController.cancelAnimations();
mMenuAnimationController.moveOutEdgeAndShow();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
index 0f1364d..be6f3ff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
@@ -21,11 +21,8 @@
import static android.view.WindowInsets.Type.displayCutout;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.systemBars;
-
import static com.android.systemui.accessibility.floatingmenu.MenuViewLayer.LayerIndex;
-
import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -41,10 +38,8 @@
import android.graphics.Rect;
import android.os.Build;
import android.os.UserHandle;
-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;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -103,10 +98,6 @@
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
- @Rule
- public final CheckFlagsRule mCheckFlagsRule =
- DeviceFlagsValueProvider.createCheckFlagsRule();
-
@Mock
private IAccessibilityFloatingMenu mFloatingMenu;
@@ -230,7 +221,7 @@
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_FLOATING_MENU_IME_DISPLACEMENT_ANIMATION)
+ @DisableFlags(Flags.FLAG_FLOATING_MENU_IME_DISPLACEMENT_ANIMATION)
public void showingImeInsetsChange_overlapOnIme_menuShownAboveIme_old() {
mMenuAnimationController.moveAndPersistPosition(new PointF(0, IME_TOP + 100));
final PointF beforePosition = mMenuView.getMenuPosition();
@@ -243,7 +234,7 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FLOATING_MENU_IME_DISPLACEMENT_ANIMATION)
+ @EnableFlags(Flags.FLAG_FLOATING_MENU_IME_DISPLACEMENT_ANIMATION)
public void showingImeInsetsChange_overlapOnIme_menuShownAboveIme() {
mMenuAnimationController.moveAndPersistPosition(new PointF(0, IME_TOP + 100));
final PointF beforePosition = mMenuView.getMenuPosition();
@@ -259,7 +250,7 @@
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_FLOATING_MENU_IME_DISPLACEMENT_ANIMATION)
+ @DisableFlags(Flags.FLAG_FLOATING_MENU_IME_DISPLACEMENT_ANIMATION)
public void hidingImeInsetsChange_overlapOnIme_menuBackToOriginalPosition_old() {
mMenuAnimationController.moveAndPersistPosition(new PointF(0, IME_TOP + 200));
final PointF beforePosition = mMenuView.getMenuPosition();
@@ -271,7 +262,7 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FLOATING_MENU_IME_DISPLACEMENT_ANIMATION)
+ @EnableFlags(Flags.FLAG_FLOATING_MENU_IME_DISPLACEMENT_ANIMATION)
public void hidingImeInsetsChange_overlapOnIme_menuBackToOriginalPosition() {
mMenuAnimationController.moveAndPersistPosition(new PointF(0, IME_TOP + 200));
final PointF beforePosition = mMenuView.getMenuPosition();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
index 8f0a97c..8da6cf9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
@@ -17,9 +17,7 @@
package com.android.systemui.accessibility.floatingmenu;
import static android.app.UiModeManager.MODE_NIGHT_YES;
-
import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -27,9 +25,7 @@
import android.app.UiModeManager;
import android.graphics.Rect;
import android.graphics.drawable.GradientDrawable;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.annotations.EnableFlags;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.WindowManager;
@@ -66,10 +62,6 @@
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
- @Rule
- public final CheckFlagsRule mCheckFlagsRule =
- DeviceFlagsValueProvider.createCheckFlagsRule();
-
@Mock
private AccessibilityManager mAccessibilityManager;
@@ -147,7 +139,7 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FLOATING_MENU_RADII_ANIMATION)
+ @EnableFlags(Flags.FLAG_FLOATING_MENU_RADII_ANIMATION)
public void onEdgeChanged_startsRadiiAnimation() {
final RadiiAnimator radiiAnimator = getRadiiAnimator();
mMenuView.onEdgeChanged();
@@ -155,7 +147,7 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FLOATING_MENU_RADII_ANIMATION)
+ @EnableFlags(Flags.FLAG_FLOATING_MENU_RADII_ANIMATION)
public void onDraggingStart_startsRadiiAnimation() {
final RadiiAnimator radiiAnimator = getRadiiAnimator();
mMenuView.onDraggingStart();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DefaultUdfpsTouchOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DefaultUdfpsTouchOverlayViewModelTest.kt
index 575d8bf..fa17672 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DefaultUdfpsTouchOverlayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DefaultUdfpsTouchOverlayViewModelTest.kt
@@ -30,7 +30,6 @@
import com.android.systemui.runCurrent
import com.android.systemui.runTest
import com.android.systemui.shade.data.repository.FakeShadeRepository
-import com.android.systemui.shade.domain.model.ShadeModel
import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.user.domain.UserDomainLayerModule
import com.android.systemui.util.mockito.mock
@@ -83,23 +82,13 @@
private fun TestComponent.shadeExpanded(expanded: Boolean) {
if (expanded) {
- shadeRepository.setShadeModel(
- ShadeModel(
- expansionAmount = 1f,
- isExpanded = true,
- isUserDragging = false,
- )
- )
+ shadeRepository.setLegacyShadeExpansion(1f)
+ shadeRepository.setLegacyShadeTracking(false)
shadeRepository.setLegacyExpandedOrAwaitingInputTransfer(true)
} else {
keyguardRepository.setStatusBarState(StatusBarState.SHADE)
- shadeRepository.setShadeModel(
- ShadeModel(
- expansionAmount = 0f,
- isExpanded = false,
- isUserDragging = false,
- )
- )
+ shadeRepository.setLegacyShadeExpansion(0f)
+ shadeRepository.setLegacyShadeTracking(false)
shadeRepository.setLegacyExpandedOrAwaitingInputTransfer(false)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt
index f5f1622..863d9eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt
@@ -20,7 +20,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
-import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import com.android.systemui.keyguard.ui.viewmodel.deviceEntryIconViewModelTransitionsMock
import com.android.systemui.kosmos.testScope
@@ -49,7 +49,7 @@
class DeviceEntryUdfpsTouchOverlayViewModelTest : SysuiTestCase() {
val kosmos =
testKosmos().apply {
- featureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, true) }
+ fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, true) }
}
val testScope = kosmos.testScope
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
index 094616f..aa0d7b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
@@ -35,7 +35,7 @@
import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.flags.SystemPropertiesHelper
import com.android.systemui.keyguard.DismissCallbackRegistry
@@ -62,7 +62,6 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
-import org.mockito.Mockito
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -108,17 +107,18 @@
suspend fun TestScope.init() {
userRepository.setSelectedUserInfo(PRIMARY_USER)
- val featureFlags = FakeFeatureFlags().apply { set(Flags.REVAMPED_BOUNCER_MESSAGES, true) }
+ val featureFlags =
+ FakeFeatureFlagsClassic().apply { set(Flags.REVAMPED_BOUNCER_MESSAGES, true) }
primaryBouncerInteractor =
PrimaryBouncerInteractor(
bouncerRepository,
- Mockito.mock(BouncerView::class.java),
- Mockito.mock(Handler::class.java),
- Mockito.mock(KeyguardStateController::class.java),
- Mockito.mock(KeyguardSecurityModel::class.java),
- Mockito.mock(PrimaryBouncerCallbackInteractor::class.java),
- Mockito.mock(FalsingCollector::class.java),
- Mockito.mock(DismissCallbackRegistry::class.java),
+ mock(BouncerView::class.java),
+ mock(Handler::class.java),
+ mock(KeyguardStateController::class.java),
+ mock(KeyguardSecurityModel::class.java),
+ mock(PrimaryBouncerCallbackInteractor::class.java),
+ mock(FalsingCollector::class.java),
+ mock(DismissCallbackRegistry::class.java),
context,
keyguardUpdateMonitor,
fakeTrustRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt
index 9b8e581..59bcf01 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt
@@ -18,157 +18,165 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.keyguard.logging.BiometricUnlockLogger
import com.android.systemui.SysuiTestCase
-import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.SensorStrength
-import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
-import com.android.systemui.keyevent.data.repository.FakeKeyEventRepository
-import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor
-import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.power.data.repository.FakePowerRepository
-import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryHapticsInteractor
+import com.android.systemui.keyevent.data.repository.fakeKeyEventRepository
+import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
+import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.data.repository.powerRepository
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessState
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController
-import com.android.systemui.util.time.FakeSystemClock
+import com.android.systemui.testKosmos
+import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mockito.mock
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class DeviceEntryHapticsInteractorTest : SysuiTestCase() {
-
- private lateinit var repository: DeviceEntryHapticsRepository
- private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
- private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
- private lateinit var keyEventRepository: FakeKeyEventRepository
- private lateinit var powerRepository: FakePowerRepository
- private lateinit var systemClock: FakeSystemClock
- private lateinit var underTest: DeviceEntryHapticsInteractor
-
- @Before
- fun setUp() {
- repository = DeviceEntryHapticsRepositoryImpl()
- fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
- biometricSettingsRepository = FakeBiometricSettingsRepository()
- keyEventRepository = FakeKeyEventRepository()
- powerRepository = FakePowerRepository()
- systemClock = FakeSystemClock()
- underTest =
- DeviceEntryHapticsInteractor(
- repository = repository,
- fingerprintPropertyRepository = fingerprintPropertyRepository,
- biometricSettingsRepository = biometricSettingsRepository,
- keyEventInteractor = KeyEventInteractor(keyEventRepository),
- powerInteractor =
- PowerInteractor(
- powerRepository,
- mock(FalsingCollector::class.java),
- mock(ScreenOffAnimationController::class.java),
- mock(StatusBarStateController::class.java),
- ),
- systemClock = systemClock,
- logger = mock(BiometricUnlockLogger::class.java),
- )
- }
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val underTest = kosmos.deviceEntryHapticsInteractor
@Test
- fun nonPowerButtonFPS_vibrateSuccess() = runTest {
- val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
- setFingerprintSensorType(FingerprintSensorType.UDFPS_ULTRASONIC)
- underTest.vibrateSuccess()
- assertThat(playSuccessHaptic).isTrue()
- }
+ fun nonPowerButtonFPS_vibrateSuccess() =
+ testScope.runTest {
+ val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+ setFingerprintSensorType(FingerprintSensorType.UDFPS_ULTRASONIC)
+ runCurrent()
+ enterDeviceFromBiometricUnlock()
+ assertThat(playSuccessHaptic).isNotNull()
+ }
@Test
- fun powerButtonFPS_vibrateSuccess() = runTest {
- val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
- setPowerButtonFingerprintProperty()
- setFingerprintEnrolled()
- keyEventRepository.setPowerButtonDown(false)
+ fun powerButtonFPS_vibrateSuccess() =
+ testScope.runTest {
+ val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+ setPowerButtonFingerprintProperty()
+ setFingerprintEnrolled()
+ kosmos.fakeKeyEventRepository.setPowerButtonDown(false)
- // It's been 10 seconds since the last power button wakeup
- setAwakeFromPowerButton()
- runCurrent()
- systemClock.setUptimeMillis(systemClock.uptimeMillis() + 10000)
+ // It's been 10 seconds since the last power button wakeup
+ setAwakeFromPowerButton()
+ runCurrent()
+ kosmos.fakeSystemClock.setUptimeMillis(kosmos.fakeSystemClock.uptimeMillis() + 10000)
- underTest.vibrateSuccess()
- assertThat(playSuccessHaptic).isTrue()
- }
+ enterDeviceFromBiometricUnlock()
+ assertThat(playSuccessHaptic).isNotNull()
+ }
@Test
- fun powerButtonFPS_powerDown_doNotVibrateSuccess() = runTest {
- val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
- setPowerButtonFingerprintProperty()
- setFingerprintEnrolled()
- keyEventRepository.setPowerButtonDown(true) // power button is currently DOWN
+ fun powerButtonFPS_powerDown_doNotVibrateSuccess() =
+ testScope.runTest {
+ val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+ setPowerButtonFingerprintProperty()
+ setFingerprintEnrolled()
+ kosmos.fakeKeyEventRepository.setPowerButtonDown(true) // power button is currently DOWN
- // It's been 10 seconds since the last power button wakeup
- setAwakeFromPowerButton()
- runCurrent()
- systemClock.setUptimeMillis(systemClock.uptimeMillis() + 10000)
+ // It's been 10 seconds since the last power button wakeup
+ setAwakeFromPowerButton()
+ runCurrent()
+ kosmos.fakeSystemClock.setUptimeMillis(kosmos.fakeSystemClock.uptimeMillis() + 10000)
- underTest.vibrateSuccess()
- assertThat(playSuccessHaptic).isFalse()
- }
+ enterDeviceFromBiometricUnlock()
+ assertThat(playSuccessHaptic).isNull()
+ }
@Test
- fun powerButtonFPS_powerButtonRecentlyPressed_doNotVibrateSuccess() = runTest {
- val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
- setPowerButtonFingerprintProperty()
- setFingerprintEnrolled()
- keyEventRepository.setPowerButtonDown(false)
+ fun powerButtonFPS_powerButtonRecentlyPressed_doNotVibrateSuccess() =
+ testScope.runTest {
+ val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+ setPowerButtonFingerprintProperty()
+ setFingerprintEnrolled()
+ kosmos.fakeKeyEventRepository.setPowerButtonDown(false)
- // It's only been 50ms since the last power button wakeup
- setAwakeFromPowerButton()
- runCurrent()
- systemClock.setUptimeMillis(systemClock.uptimeMillis() + 50)
+ // It's only been 50ms since the last power button wakeup
+ setAwakeFromPowerButton()
+ runCurrent()
+ kosmos.fakeSystemClock.setUptimeMillis(kosmos.fakeSystemClock.uptimeMillis() + 50)
- underTest.vibrateSuccess()
- assertThat(playSuccessHaptic).isFalse()
- }
+ enterDeviceFromBiometricUnlock()
+ assertThat(playSuccessHaptic).isNull()
+ }
@Test
- fun nonPowerButtonFPS_vibrateError() = runTest {
- val playErrorHaptic by collectLastValue(underTest.playErrorHaptic)
- setFingerprintSensorType(FingerprintSensorType.UDFPS_ULTRASONIC)
- underTest.vibrateError()
- assertThat(playErrorHaptic).isTrue()
- }
+ fun nonPowerButtonFPS_vibrateError() =
+ testScope.runTest {
+ val playErrorHaptic by collectLastValue(underTest.playErrorHaptic)
+ setFingerprintSensorType(FingerprintSensorType.UDFPS_ULTRASONIC)
+ runCurrent()
+ fingerprintFailure()
+ assertThat(playErrorHaptic).isNotNull()
+ }
@Test
- fun powerButtonFPS_vibrateError() = runTest {
- val playErrorHaptic by collectLastValue(underTest.playErrorHaptic)
- setPowerButtonFingerprintProperty()
- setFingerprintEnrolled()
- underTest.vibrateError()
- assertThat(playErrorHaptic).isTrue()
- }
+ fun nonPowerButtonFPS_coExFaceFailure_doNotVibrateError() =
+ testScope.runTest {
+ val playErrorHaptic by collectLastValue(underTest.playErrorHaptic)
+ setFingerprintSensorType(FingerprintSensorType.UDFPS_ULTRASONIC)
+ coExEnrolledAndEnabled()
+ runCurrent()
+ faceFailure()
+ assertThat(playErrorHaptic).isNull()
+ }
@Test
- fun powerButtonFPS_powerDown_doNotVibrateError() = runTest {
- val playErrorHaptic by collectLastValue(underTest.playErrorHaptic)
- setPowerButtonFingerprintProperty()
- setFingerprintEnrolled()
- keyEventRepository.setPowerButtonDown(true)
- underTest.vibrateError()
- assertThat(playErrorHaptic).isFalse()
+ fun powerButtonFPS_vibrateError() =
+ testScope.runTest {
+ val playErrorHaptic by collectLastValue(underTest.playErrorHaptic)
+ setPowerButtonFingerprintProperty()
+ setFingerprintEnrolled()
+ runCurrent()
+ fingerprintFailure()
+ assertThat(playErrorHaptic).isNotNull()
+ }
+
+ @Test
+ fun powerButtonFPS_powerDown_doNotVibrateError() =
+ testScope.runTest {
+ val playErrorHaptic by collectLastValue(underTest.playErrorHaptic)
+ setPowerButtonFingerprintProperty()
+ setFingerprintEnrolled()
+ kosmos.fakeKeyEventRepository.setPowerButtonDown(true)
+ runCurrent()
+ fingerprintFailure()
+ assertThat(playErrorHaptic).isNull()
+ }
+
+ private suspend fun enterDeviceFromBiometricUnlock() {
+ kosmos.fakeDeviceEntryRepository.enteringDeviceFromBiometricUnlock(
+ BiometricUnlockSource.FINGERPRINT_SENSOR
+ )
+ }
+
+ private fun fingerprintFailure() {
+ kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+ FailFingerprintAuthenticationStatus
+ )
+ }
+
+ private fun faceFailure() {
+ kosmos.fakeDeviceEntryFaceAuthRepository.setAuthenticationStatus(
+ FailedFaceAuthenticationStatus()
+ )
}
private fun setFingerprintSensorType(fingerprintSensorType: FingerprintSensorType) {
- fingerprintPropertyRepository.setProperties(
+ kosmos.fingerprintPropertyRepository.setProperties(
sensorId = 0,
strength = SensorStrength.STRONG,
sensorType = fingerprintSensorType,
@@ -181,15 +189,20 @@
}
private fun setFingerprintEnrolled() {
- biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ kosmos.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
}
private fun setAwakeFromPowerButton() {
- powerRepository.updateWakefulness(
+ kosmos.powerRepository.updateWakefulness(
WakefulnessState.AWAKE,
WakeSleepReason.POWER_BUTTON,
WakeSleepReason.POWER_BUTTON,
powerButtonLaunchGestureTriggered = false,
)
}
+
+ private fun coExEnrolledAndEnabled() {
+ setFingerprintEnrolled()
+ kosmos.biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt
index 42b0f50..37c7409 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt
@@ -18,6 +18,7 @@
import android.companion.virtual.VirtualDeviceManager
import android.companion.virtual.flags.Flags.FLAG_INTERACTIVE_SCREEN_MIRROR
+import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.Display
@@ -75,7 +76,6 @@
@Before
fun setup() {
- mSetFlagsRule.disableFlags(FLAG_INTERACTIVE_SCREEN_MIRROR)
whenever(virtualDeviceManager.isVirtualDeviceOwnedMirrorDisplay(anyInt())).thenReturn(false)
fakeKeyguardRepository.setKeyguardShowing(false)
}
@@ -160,9 +160,9 @@
}
@Test
+ @EnableFlags(FLAG_INTERACTIVE_SCREEN_MIRROR)
fun displayState_virtualDeviceOwnedMirrorVirtualDisplay_connected() =
testScope.runTest {
- mSetFlagsRule.enableFlags(FLAG_INTERACTIVE_SCREEN_MIRROR)
whenever(virtualDeviceManager.isVirtualDeviceOwnedMirrorDisplay(anyInt()))
.thenReturn(true)
val value by lastValue()
@@ -183,9 +183,9 @@
}
@Test
+ @EnableFlags(FLAG_INTERACTIVE_SCREEN_MIRROR)
fun virtualDeviceOwnedMirrorVirtualDisplay_emitsConnectedDisplayAddition() =
testScope.runTest {
- mSetFlagsRule.enableFlags(FLAG_INTERACTIVE_SCREEN_MIRROR)
whenever(virtualDeviceManager.isVirtualDeviceOwnedMirrorDisplay(anyInt()))
.thenReturn(true)
var count = 0
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
index a903d25..523127e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
@@ -21,6 +21,8 @@
import android.content.pm.PackageManager.NameNotFoundException
import android.content.res.Resources
import android.content.res.Resources.NotFoundException
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.test.suitebuilder.annotation.SmallTest
import com.android.systemui.Flags.FLAG_SYSUI_TEAMFOOD
import com.android.systemui.SysuiTestCase
@@ -68,15 +70,14 @@
private val serverFlagReader = ServerFlagReaderFake()
private val teamfoodableFlagA = UnreleasedFlag(name = "a", namespace = "test", teamfood = true)
- private val teamfoodableFlagB = ReleasedFlag(name = "b", namespace = "test", teamfood = true)
+ private val releasedFlagB = ReleasedFlag(name = "b", namespace = "test")
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- mSetFlagsRule.disableFlags(FLAG_SYSUI_TEAMFOOD)
flagMap.put(teamfoodableFlagA.name, teamfoodableFlagA)
- flagMap.put(teamfoodableFlagB.name, teamfoodableFlagB)
+ flagMap.put(releasedFlagB.name, releasedFlagB)
mFeatureFlagsClassicDebug =
FeatureFlagsClassicDebug(
flagManager,
@@ -99,7 +100,6 @@
@Test
fun readBooleanFlag() {
- // Remember that the TEAMFOOD flag is id#1 and has special behavior.
whenever(flagManager.readFlagValue<Boolean>(eq("3"), any())).thenReturn(true)
whenever(flagManager.readFlagValue<Boolean>(eq("4"), any())).thenReturn(false)
@@ -122,9 +122,10 @@
}
@Test
+ @DisableFlags(FLAG_SYSUI_TEAMFOOD)
fun teamFoodFlag_False() {
assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagA)).isFalse()
- assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagB)).isTrue()
+ assertThat(mFeatureFlagsClassicDebug.isEnabled(releasedFlagB)).isTrue()
// Regular boolean flags should still test the same.
// Only our teamfoodableFlag should change.
@@ -132,10 +133,10 @@
}
@Test
+ @EnableFlags(FLAG_SYSUI_TEAMFOOD)
fun teamFoodFlag_True() {
- mSetFlagsRule.enableFlags(FLAG_SYSUI_TEAMFOOD)
assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagA)).isTrue()
- assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagB)).isTrue()
+ assertThat(mFeatureFlagsClassicDebug.isEnabled(releasedFlagB)).isTrue()
// Regular boolean flags should still test the same.
// Only our teamfoodableFlag should change.
@@ -143,14 +144,14 @@
}
@Test
+ @EnableFlags(FLAG_SYSUI_TEAMFOOD)
fun teamFoodFlag_Overridden() {
whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagA.name), any()))
.thenReturn(true)
- whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagB.name), any()))
+ whenever(flagManager.readFlagValue<Boolean>(eq(releasedFlagB.name), any()))
.thenReturn(false)
- mSetFlagsRule.enableFlags(FLAG_SYSUI_TEAMFOOD)
assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagA)).isTrue()
- assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagB)).isFalse()
+ assertThat(mFeatureFlagsClassicDebug.isEnabled(releasedFlagB)).isFalse()
// Regular boolean flags should still test the same.
// Only our teamfoodableFlag should change.
@@ -400,6 +401,7 @@
}
@Test
+ @DisableFlags(FLAG_SYSUI_TEAMFOOD)
fun serverSide_OverrideUncached_NoRestart() {
// No one has read the flag, so it's not in the cache.
serverFlagReader.setFlagValue(
@@ -411,6 +413,7 @@
}
@Test
+ @DisableFlags(FLAG_SYSUI_TEAMFOOD)
fun serverSide_Override_Restarts() {
// Read it to put it in the cache.
mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagA)
@@ -423,6 +426,7 @@
}
@Test
+ @DisableFlags(FLAG_SYSUI_TEAMFOOD)
fun serverSide_RedundantOverride_NoRestart() {
// Read it to put it in the cache.
mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagA)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index bf6d5c4..976dc5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -43,7 +43,6 @@
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
import com.android.systemui.shade.data.repository.FakeShadeRepository
-import com.android.systemui.shade.domain.model.ShadeModel
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
@@ -1329,12 +1328,8 @@
// GIVEN the keyguard is showing locked
keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
runCurrent()
- shadeRepository.setShadeModel(
- ShadeModel(
- expansionAmount = .9f,
- isUserDragging = true,
- )
- )
+ shadeRepository.setLegacyShadeTracking(true)
+ shadeRepository.setLegacyShadeExpansion(.9f)
runCurrent()
// THEN a transition from LOCKSCREEN => PRIMARY_BOUNCER should occur
@@ -1350,12 +1345,8 @@
// WHEN the user stops dragging and shade is back to expanded
clearInvocations(transitionRepository)
runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER)
- shadeRepository.setShadeModel(
- ShadeModel(
- expansionAmount = 1f,
- isUserDragging = false,
- )
- )
+ shadeRepository.setLegacyShadeTracking(false)
+ shadeRepository.setLegacyShadeExpansion(1f)
runCurrent()
// THEN a transition from PRIMARY_BOUNCER => LOCKSCREEN should occur
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
index 67fba42..22569e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
@@ -39,6 +39,7 @@
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
import com.android.systemui.statusbar.NotificationShadeWindowController
+import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.gesture.TapGestureDetector
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -91,6 +92,7 @@
TestScope().backgroundScope,
{ mock(SwipeUpAnywhereGestureHandler::class.java) },
{ mock(TapGestureDetector::class.java) },
+ { mock(VibratorHelper::class.java) },
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
index c9b14a4..daafe12 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
@@ -22,7 +22,7 @@
import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
import com.android.systemui.coroutines.collectValues
import com.android.systemui.flags.Flags
-import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
@@ -55,7 +55,7 @@
private val kosmos =
testKosmos().apply {
- featureFlagsClassic.apply {
+ fakeFeatureFlagsClassic.apply {
set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false)
set(Flags.FULL_SCREEN_USER_SWITCHER, false)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
index c15a2c6..e139466 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
@@ -23,7 +23,7 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.flags.Flags.FULL_SCREEN_USER_SWITCHER
-import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -47,7 +47,9 @@
@RunWith(AndroidJUnit4::class)
class LockscreenToAodTransitionViewModelTest : SysuiTestCase() {
private val kosmos =
- testKosmos().apply { featureFlagsClassic.apply { set(FULL_SCREEN_USER_SWITCHER, false) } }
+ testKosmos().apply {
+ fakeFeatureFlagsClassic.apply { set(FULL_SCREEN_USER_SWITCHER, false) }
+ }
private val testScope = kosmos.testScope
private val repository = kosmos.fakeKeyguardTransitionRepository
private val shadeRepository = kosmos.shadeRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
index b31968c..7a564ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
@@ -21,7 +21,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
-import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -45,7 +45,7 @@
class LockscreenToPrimaryBouncerTransitionViewModelTest : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
- featureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
+ fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
}
private val testScope = kosmos.testScope
private val repository = kosmos.fakeKeyguardTransitionRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
index f8aa359..750693c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
@@ -19,33 +19,20 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.shade.ShadeExpansionChangeEvent
-import com.android.systemui.shade.ShadeExpansionStateManager
-import com.android.systemui.shade.domain.model.ShadeModel
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
-import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class ShadeRepositoryImplTest : SysuiTestCase() {
- @Mock private lateinit var shadeExpansionStateManager: ShadeExpansionStateManager
private val testDispatcher = StandardTestDispatcher()
private val testScope = TestScope(testDispatcher)
@@ -53,57 +40,10 @@
@Before
fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- underTest = ShadeRepositoryImpl(shadeExpansionStateManager)
- `when`(shadeExpansionStateManager.addExpansionListener(any()))
- .thenReturn(ShadeExpansionChangeEvent(0f, false, false, 0f))
+ underTest = ShadeRepositoryImpl()
}
@Test
- fun shadeExpansionChangeEvent() =
- testScope.runTest {
- var latest: ShadeModel? = null
- val job = underTest.shadeModel.onEach { latest = it }.launchIn(this)
- runCurrent()
- assertThat(latest?.expansionAmount).isEqualTo(0f)
- assertThat(latest?.isExpanded).isEqualTo(false)
- assertThat(latest?.isUserDragging).isEqualTo(false)
-
- val captor = withArgCaptor {
- verify(shadeExpansionStateManager).addExpansionListener(capture())
- }
-
- captor.onPanelExpansionChanged(
- ShadeExpansionChangeEvent(
- fraction = 1f,
- expanded = true,
- tracking = false,
- dragDownPxAmount = 0f,
- )
- )
- runCurrent()
- assertThat(latest?.expansionAmount).isEqualTo(1f)
- assertThat(latest?.isExpanded).isEqualTo(true)
- assertThat(latest?.isUserDragging).isEqualTo(false)
-
- captor.onPanelExpansionChanged(
- ShadeExpansionChangeEvent(
- fraction = .67f,
- expanded = false,
- tracking = true,
- dragDownPxAmount = 0f,
- )
- )
- runCurrent()
- assertThat(latest?.expansionAmount).isEqualTo(.67f)
- assertThat(latest?.isExpanded).isEqualTo(false)
- assertThat(latest?.isUserDragging).isEqualTo(true)
-
- job.cancel()
- }
-
- @Test
fun updateQsExpansion() =
testScope.runTest {
assertThat(underTest.qsExpansion.value).isEqualTo(0f)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index b98dc00..a3cff87e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -39,12 +39,15 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
+import com.android.systemui.util.settings.FakeGlobalSettings;
+import com.android.systemui.util.time.SystemClock;
+import com.android.systemui.util.time.SystemClockImpl;
import org.junit.After;
import org.junit.Before;
@@ -74,18 +77,26 @@
protected final Runnable mTestTimeoutRunnable = () -> mTimedOut = true;
protected Handler mTestHandler;
+ protected final FakeGlobalSettings mGlobalSettings = new FakeGlobalSettings();
+ protected final SystemClock mSystemClock = new SystemClockImpl();
protected boolean mTimedOut = false;
@Mock protected ExpandableNotificationRow mRow;
+ static {
+ assertThat(TEST_MINIMUM_DISPLAY_TIME).isLessThan(TEST_AUTO_DISMISS_TIME);
+ assertThat(TEST_AUTO_DISMISS_TIME).isLessThan(TEST_STICKY_AUTO_DISMISS_TIME);
+ assertThat(TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(TEST_TIMEOUT_TIME);
+ }
+
private static class TestableAlertingNotificationManager extends AlertingNotificationManager {
private AlertEntry mLastCreatedEntry;
- private TestableAlertingNotificationManager(Handler handler) {
- super(new HeadsUpManagerLogger(logcatLogBuffer()), handler);
+ private TestableAlertingNotificationManager(Handler handler, SystemClock systemClock) {
+ super(new HeadsUpManagerLogger(logcatLogBuffer()), handler, systemClock);
mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
- mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
- mStickyDisplayTime = TEST_STICKY_AUTO_DISMISS_TIME;
+ mAutoDismissTime = TEST_AUTO_DISMISS_TIME;
+ mStickyForSomeTimeAutoDismissTime = TEST_STICKY_AUTO_DISMISS_TIME;
}
@Override
@@ -107,7 +118,7 @@
}
protected AlertingNotificationManager createAlertingNotificationManager() {
- return new TestableAlertingNotificationManager(mTestHandler);
+ return new TestableAlertingNotificationManager(mTestHandler, mSystemClock);
}
protected StatusBarNotification createSbn(int id, Notification n) {
@@ -167,10 +178,6 @@
@Before
public void setUp() {
mTestHandler = Handler.createAsync(Looper.myLooper());
-
- assertThat(TEST_MINIMUM_DISPLAY_TIME).isLessThan(TEST_AUTO_DISMISS_TIME);
- assertThat(TEST_AUTO_DISMISS_TIME).isLessThan(TEST_STICKY_AUTO_DISMISS_TIME);
- assertThat(TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(TEST_TIMEOUT_TIME);
}
@After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 8bc5e70..34c7b09 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -55,8 +55,10 @@
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.SparseArray;
@@ -96,12 +98,26 @@
import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
import java.util.concurrent.Executor;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper
public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
+
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return FlagsParameterization.allCombinationsOf(FLAG_ALLOW_PRIVATE_PROFILE);
+ }
+
+ public NotificationLockscreenUserManagerTest(FlagsParameterization flags) {
+ mSetFlagsRule.setFlagsParameterization(flags);
+ }
+
private static final int TEST_PROFILE_USERHANDLE = 12;
@Mock
private NotificationPresenter mPresenter;
@@ -762,8 +778,8 @@
}
@Test
+ @EnableFlags(FLAG_ALLOW_PRIVATE_PROFILE)
public void testProfileAvailabilityIntent() {
- mSetFlagsRule.enableFlags(FLAG_ALLOW_PRIVATE_PROFILE);
mLockscreenUserManager.mCurrentProfiles.clear();
assertEquals(0, mLockscreenUserManager.mCurrentProfiles.size());
mLockscreenUserManager.mCurrentProfiles.append(0, mock(UserInfo.class));
@@ -773,8 +789,8 @@
}
@Test
+ @EnableFlags(FLAG_ALLOW_PRIVATE_PROFILE)
public void testProfileUnAvailabilityIntent() {
- mSetFlagsRule.enableFlags(FLAG_ALLOW_PRIVATE_PROFILE);
mLockscreenUserManager.mCurrentProfiles.clear();
assertEquals(0, mLockscreenUserManager.mCurrentProfiles.size());
mLockscreenUserManager.mCurrentProfiles.append(0, mock(UserInfo.class));
@@ -784,8 +800,8 @@
}
@Test
+ @DisableFlags(FLAG_ALLOW_PRIVATE_PROFILE)
public void testManagedProfileAvailabilityIntent() {
- mSetFlagsRule.disableFlags(FLAG_ALLOW_PRIVATE_PROFILE);
mLockscreenUserManager.mCurrentProfiles.clear();
mLockscreenUserManager.mCurrentManagedProfiles.clear();
assertEquals(0, mLockscreenUserManager.mCurrentProfiles.size());
@@ -798,8 +814,8 @@
}
@Test
+ @DisableFlags(FLAG_ALLOW_PRIVATE_PROFILE)
public void testManagedProfileUnAvailabilityIntent() {
- mSetFlagsRule.disableFlags(FLAG_ALLOW_PRIVATE_PROFILE);
mLockscreenUserManager.mCurrentProfiles.clear();
mLockscreenUserManager.mCurrentManagedProfiles.clear();
assertEquals(0, mLockscreenUserManager.mCurrentProfiles.size());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
index fa5fad0..255cf6f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
@@ -15,11 +15,12 @@
*/
package com.android.systemui.statusbar.notification.collection.coordinator
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.setFlagValue
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
@@ -40,10 +41,9 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations.initMocks
+import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -66,12 +66,6 @@
fun setUp() {
initMocks(this)
entry = NotificationEntryBuilder().setSection(section).build()
- setUpWithFlags()
- }
-
- private fun setUpWithFlags(vararg flags: Pair<String, Boolean>) {
- flags.forEach { (name, value) -> mSetFlagsRule.setFlagValue(name, value) }
- reset(pipeline)
coordinator =
StackCoordinator(
groupExpansionManagerImpl,
@@ -86,15 +80,15 @@
}
@Test
+ @DisableFlags(NotificationIconContainerRefactor.FLAG_NAME)
fun testUpdateNotificationIcons() {
- setUpWithFlags(NotificationIconContainerRefactor.FLAG_NAME to false)
afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
verify(notificationIconAreaController).updateNotificationIcons(eq(listOf(entry)))
}
@Test
+ @EnableFlags(NotificationIconContainerRefactor.FLAG_NAME)
fun testSetRenderedListOnInteractor() {
- setUpWithFlags(NotificationIconContainerRefactor.FLAG_NAME to true)
afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
verify(renderListInteractor).setRenderedList(eq(listOf(entry)))
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java
index 22c5bae..57dac3a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java
@@ -16,12 +16,11 @@
package com.android.systemui.statusbar.notification.footer.ui.view;
+import static com.android.systemui.log.LogAssertKt.assertLogsWtf;
import static com.google.common.truth.Truth.assertThat;
-
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
-
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
@@ -31,33 +30,47 @@
import static org.mockito.Mockito.verify;
import android.content.Context;
-import android.testing.AndroidTestingRunner;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import androidx.test.filters.SmallTest;
-import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.res.R;
+import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.List;
+
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(ParameterizedAndroidJunit4.class)
public class FooterViewTest extends SysuiTestCase {
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getFlags() {
+ return FlagsParameterization.allCombinationsOf(FooterViewRefactor.FLAG_NAME);
+ }
+
+ public FooterViewTest(FlagsParameterization flags) {
+ mSetFlagsRule.setFlagsParameterization(flags);
+ }
+
FooterView mView;
Context mSpyContext = spy(mContext);
@Before
public void setUp() {
- mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATIONS_FOOTER_VIEW_REFACTOR);
-
mView = (FooterView) LayoutInflater.from(mSpyContext).inflate(
R.layout.status_bar_notification_footer, null, false);
mView.setAnimationDuration(0);
@@ -114,6 +127,7 @@
}
@Test
+ @EnableFlags(FooterViewRefactor.FLAG_NAME)
public void testSetClearAllButtonText_resourceOnlyFetchedOnce() {
int resId = R.string.clear_all_notifications_text;
mView.setClearAllButtonText(resId);
@@ -132,6 +146,16 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
+ public void testSetClearAllButtonText_expectsFlagEnabled() {
+ clearInvocations(mSpyContext);
+ int resId = R.string.clear_all_notifications_text;
+ assertLogsWtf(()-> mView.setClearAllButtonText(resId));
+ verify(mSpyContext, never()).getString(anyInt());
+ }
+
+ @Test
+ @EnableFlags(FooterViewRefactor.FLAG_NAME)
public void testSetClearAllButtonDescription_resourceOnlyFetchedOnce() {
int resId = R.string.accessibility_clear_all;
mView.setClearAllButtonDescription(resId);
@@ -150,6 +174,16 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
+ public void testSetClearAllButtonDescription_expectsFlagEnabled() {
+ clearInvocations(mSpyContext);
+ int resId = R.string.accessibility_clear_all;
+ assertLogsWtf(()-> mView.setClearAllButtonDescription(resId));
+ verify(mSpyContext, never()).getString(anyInt());
+ }
+
+ @Test
+ @EnableFlags(FooterViewRefactor.FLAG_NAME)
public void testSetMessageString_resourceOnlyFetchedOnce() {
int resId = R.string.unlock_to_see_notif_text;
mView.setMessageString(resId);
@@ -168,6 +202,16 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
+ public void testSetMessageString_expectsFlagEnabled() {
+ clearInvocations(mSpyContext);
+ int resId = R.string.unlock_to_see_notif_text;
+ assertLogsWtf(()-> mView.setMessageString(resId));
+ verify(mSpyContext, never()).getString(anyInt());
+ }
+
+ @Test
+ @EnableFlags(FooterViewRefactor.FLAG_NAME)
public void testSetMessageIcon_resourceOnlyFetchedOnce() {
int resId = R.drawable.ic_friction_lock_closed;
mView.setMessageIcon(resId);
@@ -183,6 +227,15 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
+ public void testSetMessageIcon_expectsFlagEnabled() {
+ clearInvocations(mSpyContext);
+ int resId = R.drawable.ic_friction_lock_closed;
+ assertLogsWtf(()-> mView.setMessageIcon(resId));
+ verify(mSpyContext, never()).getDrawable(anyInt());
+ }
+
+ @Test
public void testSetFooterLabelVisible() {
mView.setFooterLabelVisible(true);
assertThat(mView.findViewById(R.id.manage_text).getVisibility()).isEqualTo(View.GONE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
index 0ba820f..8ab13f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
@@ -16,9 +16,9 @@
package com.android.systemui.statusbar.notification.footer.ui.viewmodel
+import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags
import com.android.systemui.SysUITestComponent
import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
@@ -38,6 +38,7 @@
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.statusbar.notification.collection.render.NotifStats
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
+import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModelModule
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.user.domain.interactor.HeadlessSystemUserModeModule
@@ -55,6 +56,7 @@
@RunWith(AndroidTestingRunner::class)
@SmallTest
+@EnableFlags(FooterViewRefactor.FLAG_NAME)
class FooterViewModelTest : SysuiTestCase() {
private lateinit var footerViewModel: FooterViewModel
@@ -106,8 +108,6 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
- mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATIONS_FOOTER_VIEW_REFACTOR)
-
// The underTest in the component is Optional, because that matches the provider we
// currently have for the footer view model.
footerViewModel = testComponent.underTest.get()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt
index 7361f6b..7ed3312 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.interruption
+import android.platform.test.annotations.DisableFlags
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
@@ -34,11 +35,8 @@
@SmallTest
@RunWith(AndroidTestingRunner::class)
+@DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
class NotificationInterruptStateProviderWrapperTest : VisualInterruptionDecisionProviderTestBase() {
- init {
- mSetFlagsRule.disableFlags(VisualInterruptionRefactor.FLAG_NAME)
- }
-
override val provider by lazy {
NotificationInterruptStateProviderWrapper(
NotificationInterruptStateProviderImpl(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
index d2c046c..da68d9c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.interruption
+import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -27,11 +28,8 @@
@SmallTest
@RunWith(AndroidTestingRunner::class)
+@EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionProviderTestBase() {
- init {
- mSetFlagsRule.enableFlags(VisualInterruptionRefactor.FLAG_NAME)
- }
-
override val provider by lazy {
VisualInterruptionDecisionProviderImpl(
ambientDisplayConfiguration,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
index f00abc9..21774aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
@@ -19,10 +19,10 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
import android.app.NotificationManager.Policy
+import android.platform.test.annotations.EnableFlags
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags
import com.android.systemui.SysUITestComponent
import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
@@ -41,6 +41,7 @@
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs
+import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModelModule
import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModelModule
import com.android.systemui.statusbar.policy.FakeConfigurationController
@@ -59,6 +60,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
+@EnableFlags(FooterViewRefactor.FLAG_NAME)
class NotificationListViewModelTest : SysuiTestCase() {
@SysUISingleton
@@ -104,8 +106,6 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
-
- mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATIONS_FOOTER_VIEW_REFACTOR)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 2209e5e..85b9392 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -26,7 +26,7 @@
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
-import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
@@ -55,7 +55,7 @@
val kosmos =
testKosmos().apply {
- featureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
+ fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
}
val testScope = kosmos.testScope
val configurationRepository = kosmos.fakeConfigurationRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 051a4c1..6f65eb4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -47,7 +47,6 @@
import com.android.keyguard.logging.BiometricUnlockLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.WakefulnessLifecycle;
@@ -122,8 +121,6 @@
@Mock
private ViewRootImpl mViewRootImpl;
@Mock
- private DeviceEntryHapticsInteractor mDeviceEntryHapticsInteractor;
- @Mock
private SelectedUserInteractor mSelectedUserInteractor;
@Mock
private BiometricUnlockInteractor mBiometricUnlockInteractor;
@@ -160,7 +157,6 @@
mAuthController, mStatusBarStateController,
mSessionTracker, mLatencyTracker, mScreenOffAnimationController, mVibratorHelper,
mSystemClock,
- mDeviceEntryHapticsInteractor,
() -> mSelectedUserInteractor,
mBiometricUnlockInteractor
);
@@ -466,26 +462,6 @@
}
@Test
- public void onFingerprintSuccess_requestSuccessHaptic() {
- // WHEN biometric fingerprint succeeds
- givenFingerprintModeUnlockCollapsing();
- mBiometricUnlockController.startWakeAndUnlock(BiometricSourceType.FINGERPRINT,
- true);
-
- // THEN always vibrate the device
- verify(mDeviceEntryHapticsInteractor).vibrateSuccess();
- }
-
- @Test
- public void onFingerprintFail_requestErrorHaptic() {
- // WHEN biometric fingerprint fails
- mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
-
- // THEN always vibrate the device
- verify(mDeviceEntryHapticsInteractor).vibrateError();
- }
-
- @Test
public void onFingerprintDetect_showBouncer() {
// WHEN fingerprint detect occurs
mBiometricUnlockController.onBiometricDetected(UserHandle.USER_CURRENT,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index 48b95d4..37ee322 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -46,6 +46,8 @@
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
import com.android.systemui.util.kotlin.JavaAdapter;
+import com.android.systemui.util.settings.GlobalSettings;
+import com.android.systemui.util.time.SystemClock;
import org.junit.After;
import org.junit.Before;
@@ -87,6 +89,8 @@
KeyguardBypassController keyguardBypassController,
ConfigurationController configurationController,
Handler handler,
+ GlobalSettings globalSettings,
+ SystemClock systemClock,
AccessibilityManagerWrapper accessibilityManagerWrapper,
UiEventLogger uiEventLogger,
JavaAdapter javaAdapter,
@@ -101,13 +105,15 @@
visualStabilityProvider,
configurationController,
handler,
+ globalSettings,
+ systemClock,
accessibilityManagerWrapper,
uiEventLogger,
javaAdapter,
shadeInteractor
);
mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
- mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
+ mAutoDismissTime = TEST_AUTO_DISMISS_TIME;
}
}
@@ -121,6 +127,8 @@
mBypassController,
mConfigurationController,
mTestHandler,
+ mGlobalSettings,
+ mSystemClock,
mAccessibilityManagerWrapper,
mUiEventLogger,
mJavaAdapter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java
index c24d9ad..b3708ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java
@@ -20,6 +20,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.platform.test.annotations.DisableFlags;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -49,6 +50,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
+@DisableFlags(NotificationIconContainerRefactor.FLAG_NAME)
public class LegacyNotificationIconAreaControllerImplTest extends SysuiTestCase {
@Mock
@@ -83,7 +85,6 @@
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mSetFlagsRule.disableFlags(NotificationIconContainerRefactor.FLAG_NAME);
mController = new LegacyNotificationIconAreaControllerImpl(
mContext,
mStatusBarStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index bbdc9ce..1dafcc4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -15,12 +15,13 @@
package com.android.systemui.statusbar.phone;
import static android.view.Display.DEFAULT_DISPLAY;
-
import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE;
import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK;
import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE;
-
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@@ -30,14 +31,14 @@
import android.app.Notification;
import android.app.PendingIntent;
import android.app.StatusBarManager;
-import android.platform.test.flag.junit.SetFlagsRule;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import androidx.test.filters.SmallTest;
-import com.android.systemui.Flags;
import com.android.systemui.InitController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ActivityStarter;
@@ -64,6 +65,7 @@
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionCondition;
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider;
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionFilter;
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor;
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -73,7 +75,6 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -100,10 +101,6 @@
private final KeyguardStateController mKeyguardStateController =
mock(KeyguardStateController.class);
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(
- SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
-
@Before
public void setup() {
mCommandQueue = new CommandQueue(mContext, new FakeDisplayTracker(mContext));
@@ -114,29 +111,46 @@
mDependency.injectMockDependency(NotificationShadeWindowController.class);
when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(true);
+
+ createPresenter();
+ if (VisualInterruptionRefactor.isEnabled()) {
+ verifyAndCaptureSuppressors();
+ } else {
+ verifyAndCaptureLegacySuppressor();
+ }
}
@Test
+ @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
public void testInit_refactorDisabled() {
- ensureRefactorDisabledState();
+ assertFalse(VisualInterruptionRefactor.isEnabled());
+ assertNull(mAlertsDisabledCondition);
+ assertNull(mVrModeCondition);
+ assertNull(mNeedsRedactionFilter);
+ assertNull(mPanelsDisabledCondition);
+ assertNotNull(mInterruptSuppressor);
}
@Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
public void testInit_refactorEnabled() {
- ensureRefactorEnabledState();
+ assertTrue(VisualInterruptionRefactor.isEnabled());
+ assertNotNull(mAlertsDisabledCondition);
+ assertNotNull(mVrModeCondition);
+ assertNotNull(mNeedsRedactionFilter);
+ assertNotNull(mPanelsDisabledCondition);
+ assertNull(mInterruptSuppressor);
}
@Test
+ @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
public void testNoSuppressHeadsUp_default_refactorDisabled() {
- ensureRefactorDisabledState();
-
assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(createNotificationEntry()));
}
@Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
public void testNoSuppressHeadsUp_default_refactorEnabled() {
- ensureRefactorEnabledState();
-
assertFalse(mAlertsDisabledCondition.shouldSuppress());
assertFalse(mVrModeCondition.shouldSuppress());
assertFalse(mNeedsRedactionFilter.shouldSuppress(createNotificationEntry()));
@@ -144,9 +158,8 @@
}
@Test
+ @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
public void testSuppressHeadsUp_disabledStatusBar_refactorDisabled() {
- ensureRefactorDisabledState();
-
mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0,
false /* animate */);
TestableLooper.get(this).processAllMessages();
@@ -156,9 +169,8 @@
}
@Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
public void testSuppressHeadsUp_disabledStatusBar_refactorEnabled() {
- ensureRefactorEnabledState();
-
mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0,
false /* animate */);
TestableLooper.get(this).processAllMessages();
@@ -168,9 +180,8 @@
}
@Test
+ @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
public void testSuppressHeadsUp_disabledNotificationShade_refactorDisabled() {
- ensureRefactorDisabledState();
-
mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
false /* animate */);
TestableLooper.get(this).processAllMessages();
@@ -180,9 +191,8 @@
}
@Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
public void testSuppressHeadsUp_disabledNotificationShade_refactorEnabled() {
- ensureRefactorEnabledState();
-
mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
false /* animate */);
TestableLooper.get(this).processAllMessages();
@@ -192,9 +202,8 @@
}
@Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
public void testPanelsDisabledConditionSuppressesPeek() {
- ensureRefactorEnabledState();
-
final Set<VisualInterruptionType> types = mPanelsDisabledCondition.getTypes();
assertTrue(types.contains(PEEK));
assertFalse(types.contains(PULSE));
@@ -202,9 +211,8 @@
}
@Test
+ @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
public void testNoSuppressHeadsUp_FSI_nonOccludedKeyguard_refactorDisabled() {
- ensureRefactorDisabledState();
-
when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mKeyguardStateController.isOccluded()).thenReturn(false);
@@ -212,9 +220,8 @@
}
@Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
public void testNoSuppressHeadsUp_FSI_nonOccludedKeyguard_refactorEnabled() {
- ensureRefactorEnabledState();
-
when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mKeyguardStateController.isOccluded()).thenReturn(false);
@@ -227,9 +234,8 @@
}
@Test
+ @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
public void testSuppressInterruptions_vrMode_refactorDisabled() {
- ensureRefactorDisabledState();
-
mStatusBarNotificationPresenter.mVrMode = true;
assertTrue("Vr mode should suppress interruptions",
@@ -237,9 +243,8 @@
}
@Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
public void testSuppressInterruptions_vrMode_refactorEnabled() {
- ensureRefactorEnabledState();
-
mStatusBarNotificationPresenter.mVrMode = true;
assertTrue("Vr mode should suppress interruptions", mVrModeCondition.shouldSuppress());
@@ -251,9 +256,8 @@
}
@Test
+ @DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
public void testSuppressInterruptions_statusBarAlertsDisabled_refactorDisabled() {
- ensureRefactorDisabledState();
-
when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false);
assertTrue("When alerts aren't enabled, interruptions are suppressed",
@@ -261,9 +265,8 @@
}
@Test
+ @EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
public void testSuppressInterruptions_statusBarAlertsDisabled_refactorEnabled() {
- ensureRefactorEnabledState();
-
when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false);
assertTrue("When alerts aren't enabled, interruptions are suppressed",
@@ -349,18 +352,6 @@
mInterruptSuppressor = suppressorCaptor.getValue();
}
- private void ensureRefactorDisabledState() {
- mSetFlagsRule.disableFlags(Flags.FLAG_VISUAL_INTERRUPTIONS_REFACTOR);
- createPresenter();
- verifyAndCaptureLegacySuppressor();
- }
-
- private void ensureRefactorEnabledState() {
- mSetFlagsRule.enableFlags(Flags.FLAG_VISUAL_INTERRUPTIONS_REFACTOR);
- createPresenter();
- verifyAndCaptureSuppressors();
- }
-
private NotificationEntry createNotificationEntry() {
return new NotificationEntryBuilder()
.setPkg("a")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 17c2938..1cc611c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -69,8 +69,8 @@
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeCollapsedStatusBarViewBinder;
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeCollapsedStatusBarViewModel;
-import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.ui.SystemBarUtilsState;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.statusbar.window.StatusBarWindowStateListener;
import com.android.systemui.util.CarrierConfigTracker;
@@ -717,7 +717,7 @@
mKeyguardUpdateMonitor,
mock(NotificationIconContainerStatusBarViewModel.class),
mock(ConfigurationState.class),
- mock(ConfigurationController.class),
+ mock(SystemBarUtilsState.class),
mock(StatusBarNotificationIconViewStore.class),
mock(DemoModeController.class));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
index 09dc1e5..89842d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
@@ -16,9 +16,10 @@
package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import androidx.test.filters.SmallTest
import com.android.systemui.CoroutineTestScopeModule
-import com.android.systemui.Flags
import com.android.systemui.SysUITestComponent
import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
@@ -29,6 +30,7 @@
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.log.assertLogsWtf
import com.android.systemui.runTest
import com.android.systemui.statusbar.data.model.StatusBarMode
import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository
@@ -37,14 +39,15 @@
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
+import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
import com.google.common.truth.Truth.assertThat
import dagger.BindsInstance
import dagger.Component
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import org.junit.Before
import org.junit.Test
@SmallTest
@@ -79,11 +82,6 @@
testScope = CoroutineTestScopeModule(TestScope(UnconfinedTestDispatcher())),
)
- @Before
- fun setUp() {
- mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATIONS_LIVE_DATA_STORE_REFACTOR)
- }
-
@Test
fun isTransitioningFromLockscreenToOccluded_started_isTrue() =
testComponent.runTest {
@@ -347,6 +345,7 @@
}
@Test
+ @EnableFlags(NotificationsLiveDataStoreRefactor.FLAG_NAME)
fun areNotificationsLightsOut_lowProfileWithNotifications_true() =
testComponent.runTest {
statusBarModeRepository.defaultDisplay.statusBarMode.value =
@@ -360,6 +359,7 @@
}
@Test
+ @EnableFlags(NotificationsLiveDataStoreRefactor.FLAG_NAME)
fun areNotificationsLightsOut_lowProfileWithoutNotifications_false() =
testComponent.runTest {
statusBarModeRepository.defaultDisplay.statusBarMode.value =
@@ -373,6 +373,7 @@
}
@Test
+ @EnableFlags(NotificationsLiveDataStoreRefactor.FLAG_NAME)
fun areNotificationsLightsOut_defaultStatusBarModeWithoutNotifications_false() =
testComponent.runTest {
statusBarModeRepository.defaultDisplay.statusBarMode.value = StatusBarMode.TRANSPARENT
@@ -385,6 +386,7 @@
}
@Test
+ @EnableFlags(NotificationsLiveDataStoreRefactor.FLAG_NAME)
fun areNotificationsLightsOut_defaultStatusBarModeWithNotifications_false() =
testComponent.runTest {
statusBarModeRepository.defaultDisplay.statusBarMode.value = StatusBarMode.TRANSPARENT
@@ -396,6 +398,16 @@
assertThat(actual).isFalse()
}
+ @Test
+ @DisableFlags(NotificationsLiveDataStoreRefactor.FLAG_NAME)
+ fun areNotificationsLightsOut_requiresFlagEnabled() =
+ testComponent.runTest {
+ assertLogsWtf {
+ val flow = underTest.areNotificationsLightsOut(DISPLAY_ID)
+ assertThat(flow).isEqualTo(emptyFlow<Boolean>())
+ }
+ }
+
private fun activeNotificationsStore(notifications: List<ActiveNotificationModel>) =
ActiveNotificationsStore.Builder()
.apply { notifications.forEach(::addIndividualNotif) }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
index 4f3f564..2940c39 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
@@ -34,7 +34,6 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
import android.app.Notification;
import android.app.PendingIntent;
@@ -57,17 +56,25 @@
import com.android.systemui.statusbar.AlertingNotificationManagerTest;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.util.settings.GlobalSettings;
+import com.android.systemui.util.time.SystemClock;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class HeadsUpManagerTest extends AlertingNotificationManagerTest {
+public class BaseHeadsUpManagerTest extends AlertingNotificationManagerTest {
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
private static final int TEST_TOUCH_ACCEPTANCE_TIME = 200;
private static final int TEST_A11Y_AUTO_DISMISS_TIME = 1_000;
private static final int TEST_A11Y_TIMEOUT_TIME = 3_000;
@@ -76,17 +83,33 @@
private final HeadsUpManagerLogger mLogger = spy(new HeadsUpManagerLogger(logcatLogBuffer()));
@Mock private AccessibilityManagerWrapper mAccessibilityMgr;
+ static {
+ assertThat(TEST_MINIMUM_DISPLAY_TIME).isLessThan(TEST_AUTO_DISMISS_TIME);
+ assertThat(TEST_AUTO_DISMISS_TIME).isLessThan(TEST_STICKY_AUTO_DISMISS_TIME);
+ assertThat(TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(TEST_A11Y_AUTO_DISMISS_TIME);
+
+ assertThat(TEST_TOUCH_ACCEPTANCE_TIME + TEST_AUTO_DISMISS_TIME).isLessThan(
+ TEST_TIMEOUT_TIME);
+ assertThat(TEST_TOUCH_ACCEPTANCE_TIME + TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(
+ TEST_TIMEOUT_TIME);
+ assertThat(TEST_TOUCH_ACCEPTANCE_TIME + TEST_A11Y_AUTO_DISMISS_TIME).isLessThan(
+ TEST_A11Y_TIMEOUT_TIME);
+ }
+
private final class TestableHeadsUpManager extends BaseHeadsUpManager {
TestableHeadsUpManager(Context context,
HeadsUpManagerLogger logger,
Handler handler,
+ GlobalSettings globalSettings,
+ SystemClock systemClock,
AccessibilityManagerWrapper accessibilityManagerWrapper,
UiEventLogger uiEventLogger) {
- super(context, logger, handler, accessibilityManagerWrapper, uiEventLogger);
+ super(context, logger, handler, globalSettings, systemClock,
+ accessibilityManagerWrapper, uiEventLogger);
mTouchAcceptanceDelay = TEST_TOUCH_ACCEPTANCE_TIME;
mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
- mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
- mStickyDisplayTime = TEST_STICKY_AUTO_DISMISS_TIME;
+ mAutoDismissTime = TEST_AUTO_DISMISS_TIME;
+ mStickyForSomeTimeAutoDismissTime = TEST_STICKY_AUTO_DISMISS_TIME;
}
// The following are only implemented by HeadsUpManagerPhone. If you need them, use that.
@@ -160,8 +183,8 @@
}
private BaseHeadsUpManager createHeadsUpManager() {
- return new TestableHeadsUpManager(mContext, mLogger, mTestHandler, mAccessibilityMgr,
- mUiEventLoggerFake);
+ return new TestableHeadsUpManager(mContext, mLogger, mTestHandler, mGlobalSettings,
+ mSystemClock, mAccessibilityMgr, mUiEventLoggerFake);
}
@Override
@@ -214,19 +237,7 @@
@Before
@Override
public void setUp() {
- initMocks(this);
super.setUp();
-
- assertThat(TEST_MINIMUM_DISPLAY_TIME).isLessThan(TEST_AUTO_DISMISS_TIME);
- assertThat(TEST_AUTO_DISMISS_TIME).isLessThan(TEST_STICKY_AUTO_DISMISS_TIME);
- assertThat(TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(TEST_A11Y_AUTO_DISMISS_TIME);
-
- assertThat(TEST_TOUCH_ACCEPTANCE_TIME + TEST_AUTO_DISMISS_TIME).isLessThan(
- TEST_TIMEOUT_TIME);
- assertThat(TEST_TOUCH_ACCEPTANCE_TIME + TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(
- TEST_TIMEOUT_TIME);
- assertThat(TEST_TOUCH_ACCEPTANCE_TIME + TEST_A11Y_AUTO_DISMISS_TIME).isLessThan(
- TEST_A11Y_TIMEOUT_TIME);
}
@After
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/android/content/pm/LauncherAppsKosmos.kt
similarity index 69%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/android/content/pm/LauncherAppsKosmos.kt
index 8bb07d9..94fc1fc 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/android/content/pm/LauncherAppsKosmos.kt
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package com.android.systemui.deviceentry.data.repository
+package android.content.pm
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
-var Kosmos.deviceEntryHapticsRepository: DeviceEntryHapticsRepository by
- Kosmos.Fixture { fakeDeviceEntryHapticsRepository }
-val Kosmos.fakeDeviceEntryHapticsRepository by Kosmos.Fixture { FakeDeviceEntryHapticsRepository() }
+val Kosmos.launcherApps by Kosmos.Fixture { mock<LauncherApps>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
index 4fdea97..4642b47 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
@@ -40,9 +40,6 @@
private val currentTime: () -> Long,
) : AuthenticationRepository {
- private val _isAutoConfirmFeatureEnabled = MutableStateFlow(false)
- override val isAutoConfirmFeatureEnabled: StateFlow<Boolean> =
- _isAutoConfirmFeatureEnabled.asStateFlow()
override val authenticationChallengeResult = MutableSharedFlow<Boolean>()
override val hintedPinLength: Int = HINTING_PIN_LENGTH
@@ -53,6 +50,12 @@
override val throttling: MutableStateFlow<AuthenticationThrottlingModel?> =
MutableStateFlow(null)
+ override val hasThrottlingOccurred = MutableStateFlow(false)
+
+ private val _isAutoConfirmFeatureEnabled = MutableStateFlow(false)
+ override val isAutoConfirmFeatureEnabled: StateFlow<Boolean> =
+ _isAutoConfirmFeatureEnabled.asStateFlow()
+
private val _authenticationMethod =
MutableStateFlow<AuthenticationMethodModel>(DEFAULT_AUTHENTICATION_METHOD)
override val authenticationMethod: StateFlow<AuthenticationMethodModel> =
@@ -107,6 +110,9 @@
override suspend fun setThrottleDuration(durationMs: Int) {
throttlingEndTimestamp = if (durationMs > 0) currentTime() + durationMs else 0
+ if (durationMs > 0) {
+ hasThrottlingOccurred.value = true
+ }
}
override suspend fun checkCredential(
@@ -128,6 +134,7 @@
return if (
isSuccessful || failedAttemptCount < MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING - 1
) {
+ hasThrottlingOccurred.value = false
AuthenticationResultModel(
isSuccessful = isSuccessful,
throttleDurationMs = 0,
@@ -166,7 +173,8 @@
AuthenticationPatternCoordinate(0, 2),
)
const val MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING = 5
- const val THROTTLE_DURATION_MS = 30000
+ const val THROTTLE_DURATION_SECONDS = 30
+ const val THROTTLE_DURATION_MS = THROTTLE_DURATION_SECONDS * 1000
const val HINTING_PIN_LENGTH = 6
val DEFAULT_PIN = buildList { repeat(HINTING_PIN_LENGTH) { add(it + 1) } }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt
index d97cb56..8ff04a63 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt
@@ -16,7 +16,6 @@
package com.android.systemui.deviceentry.data
import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepositoryModule
-import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryHapticsRepositoryModule
import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepositoryModule
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepositoryModule
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepositoryModule
@@ -29,7 +28,6 @@
[
FakeBiometricSettingsRepositoryModule::class,
FakeDeviceEntryRepositoryModule::class,
- FakeDeviceEntryHapticsRepositoryModule::class,
FakeDeviceEntryFaceAuthRepositoryModule::class,
FakeDeviceEntryFingerprintAuthRepositoryModule::class,
FakeFingerprintPropertyRepositoryModule::class,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryHapticsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryHapticsRepository.kt
deleted file mode 100644
index b29b67d..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryHapticsRepository.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.deviceentry.data.repository
-
-import com.android.systemui.dagger.SysUISingleton
-import dagger.Binds
-import dagger.Module
-import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.asStateFlow
-
-/** Fake implementation of [DeviceEntryHapticsRepository] */
-@SysUISingleton
-class FakeDeviceEntryHapticsRepository @Inject constructor() : DeviceEntryHapticsRepository {
- private var _successHapticRequest: MutableStateFlow<Boolean> = MutableStateFlow(false)
- override val successHapticRequest: Flow<Boolean> = _successHapticRequest.asStateFlow()
-
- private var _errorHapticRequest: MutableStateFlow<Boolean> = MutableStateFlow(false)
- override val errorHapticRequest: Flow<Boolean> = _errorHapticRequest.asStateFlow()
-
- override fun requestSuccessHaptic() {
- _successHapticRequest.value = true
- }
-
- override fun handleSuccessHaptic() {
- _successHapticRequest.value = false
- }
-
- override fun requestErrorHaptic() {
- _errorHapticRequest.value = true
- }
-
- override fun handleErrorHaptic() {
- _errorHapticRequest.value = false
- }
-}
-
-@Module
-interface FakeDeviceEntryHapticsRepositoryModule {
- @Binds fun bindFake(fake: FakeDeviceEntryHapticsRepository): DeviceEntryHapticsRepository
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt
index ba70d46..6436a38 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt
@@ -16,16 +16,24 @@
package com.android.systemui.deviceentry.data.repository
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import dagger.Binds
import dagger.Module
import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
/** Fake implementation of [DeviceEntryRepository] */
@SysUISingleton
class FakeDeviceEntryRepository @Inject constructor() : DeviceEntryRepository {
+ private val _enteringDeviceFromBiometricUnlock: MutableSharedFlow<BiometricUnlockSource> =
+ MutableSharedFlow()
+ override val enteringDeviceFromBiometricUnlock: Flow<BiometricUnlockSource> =
+ _enteringDeviceFromBiometricUnlock.asSharedFlow()
private var isLockscreenEnabled = true
@@ -54,6 +62,10 @@
fun setBypassEnabled(isBypassEnabled: Boolean) {
_isBypassEnabled.value = isBypassEnabled
}
+
+ suspend fun enteringDeviceFromBiometricUnlock(sourceType: BiometricUnlockSource) {
+ _enteringDeviceFromBiometricUnlock.emit(sourceType)
+ }
}
@Module
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractorKosmos.kt
new file mode 100644
index 0000000..1bd1056
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractorKosmos.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.deviceentry.domain.interactor
+
+import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
+import com.android.systemui.kosmos.Kosmos
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+val Kosmos.deviceEntryBiometricAuthInteractor by
+ Kosmos.Fixture {
+ DeviceEntryBiometricAuthInteractor(
+ biometricSettingsRepository = biometricSettingsRepository,
+ deviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt
similarity index 61%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt
index 8bb07d9..d2dff78 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt
@@ -14,10 +14,17 @@
* limitations under the License.
*/
-package com.android.systemui.deviceentry.data.repository
+@file:OptIn(ExperimentalCoroutinesApi::class)
+package com.android.systemui.deviceentry.domain.interactor
+
+import com.android.systemui.keyguard.data.repository.deviceEntryFaceAuthRepository
import com.android.systemui.kosmos.Kosmos
+import kotlinx.coroutines.ExperimentalCoroutinesApi
-var Kosmos.deviceEntryHapticsRepository: DeviceEntryHapticsRepository by
- Kosmos.Fixture { fakeDeviceEntryHapticsRepository }
-val Kosmos.fakeDeviceEntryHapticsRepository by Kosmos.Fixture { FakeDeviceEntryHapticsRepository() }
+val Kosmos.deviceEntryFaceAuthInteractor by
+ Kosmos.Fixture {
+ DeviceEntryFaceAuthInteractor(
+ repository = deviceEntryFaceAuthRepository,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractorKosmos.kt
new file mode 100644
index 0000000..66c6f86
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractorKosmos.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.deviceentry.domain.interactor
+
+import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
+import com.android.systemui.kosmos.Kosmos
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+val Kosmos.deviceEntryFingerprintAuthInteractor by
+ Kosmos.Fixture {
+ DeviceEntryFingerprintAuthInteractor(
+ repository = deviceEntryFingerprintAuthRepository,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt
index de6cacb..6bf527d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt
@@ -20,7 +20,6 @@
import com.android.keyguard.logging.biometricUnlockLogger
import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
-import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryHapticsRepository
import com.android.systemui.keyevent.domain.interactor.keyEventInteractor
import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
import com.android.systemui.kosmos.Kosmos
@@ -31,7 +30,9 @@
val Kosmos.deviceEntryHapticsInteractor by
Kosmos.Fixture {
DeviceEntryHapticsInteractor(
- repository = fakeDeviceEntryHapticsRepository,
+ deviceEntryInteractor = deviceEntryInteractor,
+ deviceEntryFingerprintAuthInteractor = deviceEntryFingerprintAuthInteractor,
+ deviceEntryBiometricAuthInteractor = deviceEntryBiometricAuthInteractor,
fingerprintPropertyRepository = fingerprintPropertyRepository,
biometricSettingsRepository = biometricSettingsRepository,
keyEventInteractor = keyEventInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt
index e6b7f62..abadaf7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt
@@ -16,6 +16,45 @@
package com.android.systemui.flags
+import android.content.res.mainResources
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
-val Kosmos.featureFlagsClassic by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+/**
+ * Main fixture for supplying a [FeatureFlagsClassic]. Should be used by other fixtures. Unless
+ * overridden in the test, this by default uses [fakeFeatureFlagsClassic].
+ */
+var Kosmos.featureFlagsClassic: FeatureFlagsClassic by Kosmos.Fixture { fakeFeatureFlagsClassic }
+
+/**
+ * Fixture supplying a shared [FakeFeatureFlagsClassic] instance. Can be accessed in tests in order
+ * to override flag values.
+ */
+val Kosmos.fakeFeatureFlagsClassic by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+
+/**
+ * Fixture supplying a real [FeatureFlagsClassicRelease] instance, for use by tests that want to
+ * reflect the current state of the device in release builds (example: screenshot tests).
+ *
+ * By default, this fixture is unused; tests should override [featureFlagsClassic] in order to
+ * utilize this fixture:
+ * ```kotlin
+ * val kosmos = Kosmos()
+ * kosmos.featureFlagsClassic = kosmos.featureFlagsClassicRelease
+ * ```
+ */
+val Kosmos.featureFlagsClassicRelease by
+ Kosmos.Fixture {
+ FeatureFlagsClassicRelease(
+ /* resources = */ mainResources,
+ /* systemProperties = */ systemPropertiesHelper,
+ /* serverFlagReader = */ serverFlagReader,
+ /* allFlags = */ FlagsCommonModule.providesAllFlags(),
+ /* restarter = */ restarter,
+ )
+ }
+
+val Kosmos.systemPropertiesHelper by Kosmos.Fixture { SystemPropertiesHelper() }
+var Kosmos.serverFlagReader: ServerFlagReader by Kosmos.Fixture { serverFlagReaderFake }
+val Kosmos.serverFlagReaderFake by Kosmos.Fixture { ServerFlagReaderFake() }
+var Kosmos.restarter: Restarter by Kosmos.Fixture { mock() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt
index 299262b..6557bcf 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt
@@ -16,7 +16,6 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.deviceentry.domain.interactor.deviceEntryHapticsInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.burnInInteractor
@@ -45,7 +44,6 @@
shadeDependentFlows = shadeDependentFlows,
sceneContainerFlags = sceneContainerFlags,
keyguardViewController = { statusBarKeyguardViewManager },
- deviceEntryHapticsInteractor = deviceEntryHapticsInteractor,
deviceEntryInteractor = deviceEntryInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogAssert.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogAssert.kt
new file mode 100644
index 0000000..10f9346
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogAssert.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log
+
+import android.util.Log
+import android.util.Log.TerribleFailureHandler
+import junit.framework.Assert
+
+/**
+ * Assert that the given block makes a call to Log.wtf
+ *
+ * @return the details of the log
+ */
+fun assertLogsWtf(
+ message: String = "Expected Log.wtf to be called",
+ allowMultiple: Boolean = false,
+ loggingBlock: () -> Unit,
+): TerribleFailureLog {
+ var caught: TerribleFailureLog? = null
+ var count = 0
+ val newHandler = TerribleFailureHandler { tag, failure, system ->
+ if (caught == null) {
+ caught = TerribleFailureLog(tag, failure, system)
+ }
+ count++
+ }
+ val oldHandler = Log.setWtfHandler(newHandler)
+ try {
+ loggingBlock()
+ } finally {
+ Log.setWtfHandler(oldHandler)
+ }
+ Assert.assertNotNull(message, caught)
+ if (!allowMultiple && count != 1) {
+ Assert.fail("Unexpectedly caught Log.Wtf $count times; expected only 1. First: $caught")
+ }
+ return caught!!
+}
+
+@JvmOverloads
+fun assertLogsWtf(
+ message: String = "Expected Log.wtf to be called",
+ allowMultiple: Boolean = false,
+ loggingRunnable: Runnable,
+): TerribleFailureLog =
+ assertLogsWtf(message = message, allowMultiple = allowMultiple) { loggingRunnable.run() }
+
+/** The data passed to [TerribleFailureHandler.onTerribleFailure] */
+data class TerribleFailureLog(
+ val tag: String,
+ val failure: Log.TerribleFailure,
+ val system: Boolean
+)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
index 9c10848..f7005ab 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
@@ -18,21 +18,14 @@
package com.android.systemui.shade.data.repository
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.shade.domain.model.ShadeModel
import dagger.Binds
import dagger.Module
import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
/** Fake implementation of [ShadeRepository] */
@SysUISingleton
class FakeShadeRepository @Inject constructor() : ShadeRepository {
-
- private val _shadeModel = MutableStateFlow(ShadeModel())
- override val shadeModel: Flow<ShadeModel> = _shadeModel
-
private val _qsExpansion = MutableStateFlow(0f)
override val qsExpansion = _qsExpansion
@@ -114,10 +107,6 @@
_legacyIsClosing.value = isClosing
}
- fun setShadeModel(model: ShadeModel) {
- _shadeModel.value = model
- }
-
override fun setQsExpansion(qsExpansion: Float) {
_qsExpansion.value = qsExpansion
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotifPipelineKosmos.kt
similarity index 69%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotifPipelineKosmos.kt
index 8bb07d9..a48b270 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotifPipelineKosmos.kt
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package com.android.systemui.deviceentry.data.repository
+package com.android.systemui.statusbar.notification.collection
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
-var Kosmos.deviceEntryHapticsRepository: DeviceEntryHapticsRepository by
- Kosmos.Fixture { fakeDeviceEntryHapticsRepository }
-val Kosmos.fakeDeviceEntryHapticsRepository by Kosmos.Fixture { FakeDeviceEntryHapticsRepository() }
+var Kosmos.notifPipeline by Kosmos.Fixture { mock<NotifPipeline>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollectionKosmos.kt
similarity index 69%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollectionKosmos.kt
index 8bb07d9..f00538e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollectionKosmos.kt
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package com.android.systemui.deviceentry.data.repository
+package com.android.systemui.statusbar.notification.collection.notifcollection
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.notification.collection.notifPipeline
-var Kosmos.deviceEntryHapticsRepository: DeviceEntryHapticsRepository by
- Kosmos.Fixture { fakeDeviceEntryHapticsRepository }
-val Kosmos.fakeDeviceEntryHapticsRepository by Kosmos.Fixture { FakeDeviceEntryHapticsRepository() }
+var Kosmos.commonNotifCollection by Kosmos.Fixture { notifPipeline }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProviderKosmos.kt
similarity index 69%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProviderKosmos.kt
index 8bb07d9..e7c4085 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProviderKosmos.kt
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package com.android.systemui.deviceentry.data.repository
+package com.android.systemui.statusbar.notification.collection.provider
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
-var Kosmos.deviceEntryHapticsRepository: DeviceEntryHapticsRepository by
- Kosmos.Fixture { fakeDeviceEntryHapticsRepository }
-val Kosmos.fakeDeviceEntryHapticsRepository by Kosmos.Fixture { FakeDeviceEntryHapticsRepository() }
+var Kosmos.sectionStyleProvider: SectionStyleProvider by Kosmos.Fixture { mock() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractorKosmos.kt
similarity index 60%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractorKosmos.kt
index 8bb07d9..f7acae9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractorKosmos.kt
@@ -14,10 +14,13 @@
* limitations under the License.
*/
-package com.android.systemui.deviceentry.data.repository
+package com.android.systemui.statusbar.notification.domain.interactor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.notification.collection.provider.sectionStyleProvider
+import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
-var Kosmos.deviceEntryHapticsRepository: DeviceEntryHapticsRepository by
- Kosmos.Fixture { fakeDeviceEntryHapticsRepository }
-val Kosmos.fakeDeviceEntryHapticsRepository by Kosmos.Fixture { FakeDeviceEntryHapticsRepository() }
+val Kosmos.renderNotificationListInteractor by
+ Kosmos.Fixture {
+ RenderNotificationListInteractor(activeNotificationListRepository, sectionStyleProvider)
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/IconBuilderKosmos.kt
similarity index 69%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/IconBuilderKosmos.kt
index 8bb07d9..4535652 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/IconBuilderKosmos.kt
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package com.android.systemui.deviceentry.data.repository
+package com.android.systemui.statusbar.notification.icon
+import android.content.applicationContext
import com.android.systemui.kosmos.Kosmos
-var Kosmos.deviceEntryHapticsRepository: DeviceEntryHapticsRepository by
- Kosmos.Fixture { fakeDeviceEntryHapticsRepository }
-val Kosmos.fakeDeviceEntryHapticsRepository by Kosmos.Fixture { FakeDeviceEntryHapticsRepository() }
+val Kosmos.iconBuilder by Kosmos.Fixture { IconBuilder(applicationContext) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/IconManagerKosmos.kt
similarity index 68%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/IconManagerKosmos.kt
index 8bb07d9..d3a8e0c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/IconManagerKosmos.kt
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.deviceentry.data.repository
+package com.android.systemui.statusbar.notification.icon
+import android.content.pm.launcherApps
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.notification.collection.notifcollection.commonNotifCollection
-var Kosmos.deviceEntryHapticsRepository: DeviceEntryHapticsRepository by
- Kosmos.Fixture { fakeDeviceEntryHapticsRepository }
-val Kosmos.fakeDeviceEntryHapticsRepository by Kosmos.Fixture { FakeDeviceEntryHapticsRepository() }
+val Kosmos.iconManager by
+ Kosmos.Fixture { IconManager(commonNotifCollection, launcherApps, iconBuilder) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinderKosmos.kt
index 75e5aeaf..ca5b401 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinderKosmos.kt
@@ -26,18 +26,18 @@
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.statusBarIconViewBindingFailureTracker
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationListViewModel
import com.android.systemui.statusbar.phone.notificationIconAreaController
-import com.android.systemui.statusbar.policy.configurationController
+import com.android.systemui.statusbar.ui.systemBarUtilsState
val Kosmos.notificationListViewBinder by Fixture {
NotificationListViewBinder(
viewModel = notificationListViewModel,
backgroundDispatcher = testDispatcher,
configuration = configurationState,
- configurationController = configurationController,
falsingManager = falsingManager,
iconAreaController = notificationIconAreaController,
iconViewBindingFailureTracker = statusBarIconViewBindingFailureTracker,
metricsLogger = metricsLogger,
shelfIconViewStore = shelfNotificationIconViewStore,
+ systemBarUtilsState = systemBarUtilsState,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/FakeSystemBarUtilsProxy.kt
similarity index 64%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/FakeSystemBarUtilsProxy.kt
index 8bb07d9..d38baba 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/FakeSystemBarUtilsProxy.kt
@@ -14,10 +14,8 @@
* limitations under the License.
*/
-package com.android.systemui.deviceentry.data.repository
+package com.android.systemui.statusbar.ui
-import com.android.systemui.kosmos.Kosmos
-
-var Kosmos.deviceEntryHapticsRepository: DeviceEntryHapticsRepository by
- Kosmos.Fixture { fakeDeviceEntryHapticsRepository }
-val Kosmos.fakeDeviceEntryHapticsRepository by Kosmos.Fixture { FakeDeviceEntryHapticsRepository() }
+class FakeSystemBarUtilsProxy(private var statusBarHeight: Int) : SystemBarUtilsProxy {
+ override fun getStatusBarHeight(): Int = statusBarHeight
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/SystemBarUtilsProxyKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/SystemBarUtilsProxyKosmos.kt
new file mode 100644
index 0000000..f24037d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/SystemBarUtilsProxyKosmos.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.ui
+
+import android.content.applicationContext
+import android.content.res.mainResources
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.res.R
+
+/**
+ * Main fixture for supplying a [SystemBarUtilsProxy]. Should be used by other fixtures. Unless
+ * overridden in the test, this by default uses [fakeSystemBarUtilsProxy].
+ */
+var Kosmos.systemBarUtilsProxy: SystemBarUtilsProxy by Fixture { fakeSystemBarUtilsProxy }
+
+/**
+ * Fixture supplying a real [SystemBarUtilsProxyImpl] instance, for use by tests that want to use
+ * the real device logic to determine system bar properties. Note this this real instance does *not*
+ * support Robolectric tests; by opting in, you are explicitly opting-out of using Robolectric.
+ *
+ * By default, this fixture is unused; tests should override [systemBarUtilsProxy] in order to
+ * utilize this fixture:
+ * ```kotlin
+ * val kosmos = Kosmos()
+ * kosmos.systemBarUtilsProxy = kosmos.systemBarUtilsProxyImpl
+ * ```
+ */
+val Kosmos.systemBarUtilsProxyImpl by Fixture { SystemBarUtilsProxyImpl(applicationContext) }
+
+/**
+ * Fixture supplying a shared [FakeSystemBarUtilsProxy] instance. Can be accessed or overridden in
+ * tests in order to provide custom results.
+ */
+var Kosmos.fakeSystemBarUtilsProxy by Fixture {
+ FakeSystemBarUtilsProxy(mainResources.getDimensionPixelSize(R.dimen.status_bar_height))
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/SystemBarUtilsStateKosmos.kt
similarity index 69%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/SystemBarUtilsStateKosmos.kt
index 8bb07d9..e208add 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/SystemBarUtilsStateKosmos.kt
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.deviceentry.data.repository
+package com.android.systemui.statusbar.ui
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.policy.configurationController
-var Kosmos.deviceEntryHapticsRepository: DeviceEntryHapticsRepository by
- Kosmos.Fixture { fakeDeviceEntryHapticsRepository }
-val Kosmos.fakeDeviceEntryHapticsRepository by Kosmos.Fixture { FakeDeviceEntryHapticsRepository() }
+val Kosmos.systemBarUtilsState by
+ Kosmos.Fixture { SystemBarUtilsState(configurationController, systemBarUtilsProxy) }
diff --git a/ravenwood/framework-minus-apex-ravenwood-policies.txt b/ravenwood/framework-minus-apex-ravenwood-policies.txt
index c9069e5..f5e4af5 100644
--- a/ravenwood/framework-minus-apex-ravenwood-policies.txt
+++ b/ravenwood/framework-minus-apex-ravenwood-policies.txt
@@ -3,6 +3,9 @@
# Keep all AIDL interfaces
class :aidl stubclass
+# Keep all feature flag implementations
+class :feature_flags stubclass
+
# Collections
class android.util.ArrayMap stubclass
class android.util.ArraySet stubclass
diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt
index 831fce1..e3f1932 100644
--- a/ravenwood/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/ravenwood-annotation-allowed-classes.txt
@@ -88,6 +88,8 @@
android.graphics.Rect
android.graphics.RectF
+android.content.ContentProvider
+
com.android.server.LocalServices
com.android.internal.os.SomeArgs
diff --git a/ravenwood/test-authors.md b/ravenwood/test-authors.md
index 5adef53..de05777 100644
--- a/ravenwood/test-authors.md
+++ b/ravenwood/test-authors.md
@@ -71,10 +71,10 @@
Once you’ve defined your test, you can use typical commands to execute it locally:
```
-$ atest MyTestsRavenwood
+$ atest --host MyTestsRavenwood
```
-> **Note:** There's a known bug where `atest` currently requires a connected device to run Ravenwood tests, but that device isn't used for testing.
+> **Note:** There's a known bug where `atest` currently requires a connected device to run Ravenwood tests, but that device isn't used for testing. Using the `--host` argument above is a way to bypass this requirement until bug #312525698 is fixed.
You can also run your new tests automatically via `TEST_MAPPING` rules like this:
@@ -89,6 +89,27 @@
}
```
+## Strategies for feature flags
+
+Ravenwood supports writing tests against logic that uses feature flags through the existing `SetFlagsRule` infrastructure maintained by the feature flagging team:
+
+```
+import android.platform.test.flag.junit.SetFlagsRule;
+
+@RunWith(AndroidJUnit4.class)
+public class MyCodeTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ @Test
+ public void testEnabled() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_MY_FLAG);
+ // verify test logic that depends on flag being enabled
+ }
+```
+
+This naturally composes together well with any `RavenwoodRule` that your test may need.
+
## Strategies for migration/bivalent tests
Ravenwood aims to support tests that are written in a “bivalent” way, where the same test code can run on both a real Android device and under a Ravenwood environment.
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index f55ecb0..0a2a780 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -981,17 +981,22 @@
transitionToDelegatingStateAndClear();
}
transitToSinglePanningStateAndClear();
- } else {
+ } else if (!mIsTwoFingerCountReached) {
+ // If it is a two-finger gesture, do not transition to the
+ // delegating state to ensure the reachability of
+ // the two-finger triple tap (triggerable with ACTION_UP)
transitionToDelegatingStateAndClear();
}
} else if (isActivated() && pointerDownValid(mSecondPointerDownLocation)
&& distanceClosestPointerToPoint(
- mSecondPointerDownLocation, /* move */ event) > mSwipeMinDistance
- // If mCompleteTapCount is not zero, it means that it is a multi tap
- // gesture. So, we should not transit to the PanningScalingState.
- && mCompletedTapCount == 0) {
+ mSecondPointerDownLocation, /* move */ event) > mSwipeMinDistance) {
// Second pointer is swiping, so transit to PanningScalingState
- transitToPanningScalingStateAndClear();
+ // Delay an ACTION_MOVE for tap timeout to ensure it is not trigger from
+ // multi finger multi tap
+ storePointerDownLocation(mSecondPointerDownLocation, event);
+ mHandler.sendEmptyMessageDelayed(
+ MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE,
+ ViewConfiguration.getTapTimeout());
}
}
break;
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 9b78ed4..923728f 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -859,6 +859,11 @@
}
@Override
+ public boolean isValidVirtualDeviceId(int deviceId) {
+ return mImpl.isValidVirtualDeviceId(deviceId);
+ }
+
+ @Override
public @Nullable String getPersistentIdForDevice(int deviceId) {
if (deviceId == Context.DEVICE_ID_DEFAULT) {
return VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
diff --git a/services/core/Android.bp b/services/core/Android.bp
index b4cf34e..20a3b9a 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -154,7 +154,6 @@
static_libs: [
"android.frameworks.location.altitude-V1-java", // AIDL
- "android.frameworks.vibrator-V1-java", // AIDL
"android.hardware.authsecret-V1.0-java",
"android.hardware.authsecret-V1-java",
"android.hardware.boot-V1.0-java", // HIDL
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 4bb9f4f..c436c72 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -127,6 +127,7 @@
"/system/bin/mediaserver",
"/system/bin/netd",
"/system/bin/sdcard",
+ "/system/bin/servicemanager",
"/system/bin/surfaceflinger",
"/system/bin/vold",
"media.extractor", // system/bin/mediaextractor
diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java
index e91b7e8..b1a12f7 100644
--- a/services/core/java/com/android/server/appop/DiscreteRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java
@@ -37,6 +37,7 @@
import static android.app.AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO;
import static android.app.AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
+import static android.app.AppOpsManager.OP_RESERVED_FOR_TESTING;
import static android.app.AppOpsManager.flagsToString;
import static android.app.AppOpsManager.getUidStateName;
@@ -136,7 +137,7 @@
private static final String DEFAULT_DISCRETE_OPS = OP_FINE_LOCATION + "," + OP_COARSE_LOCATION
+ "," + OP_CAMERA + "," + OP_RECORD_AUDIO + "," + OP_PHONE_CALL_MICROPHONE + ","
+ OP_PHONE_CALL_CAMERA + "," + OP_RECEIVE_AMBIENT_TRIGGER_AUDIO + ","
- + OP_RECEIVE_SANDBOX_TRIGGER_AUDIO;
+ + OP_RECEIVE_SANDBOX_TRIGGER_AUDIO + "," + OP_RESERVED_FOR_TESTING;
private static final long DEFAULT_DISCRETE_HISTORY_CUTOFF = Duration.ofDays(7).toMillis();
private static final long MAXIMUM_DISCRETE_HISTORY_CUTOFF = Duration.ofDays(30).toMillis();
private static final long DEFAULT_DISCRETE_HISTORY_QUANTIZATION =
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index e54bf64..d077ebc 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -1501,7 +1501,7 @@
} else {
status = addOp.deviceRoleAction(useCase, role, devices);
if (status == AudioSystem.SUCCESS) {
- rolesMap.put(key, devices);
+ rolesMap.put(key, new ArrayList(devices));
}
}
return status;
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
index cecde55..823788f 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -21,6 +21,7 @@
import android.companion.virtual.IVirtualDevice;
import android.companion.virtual.VirtualDevice;
import android.companion.virtual.sensor.VirtualSensor;
+import android.content.Context;
import android.os.LocaleList;
import android.util.ArraySet;
@@ -149,6 +150,14 @@
public abstract @NonNull ArraySet<Integer> getDisplayIdsForDevice(int deviceId);
/**
+ * Checks whether the passed {@code deviceId} is a valid virtual device ID or not.
+ *
+ * <p>{@link Context#DEVICE_ID_DEFAULT} is not valid as it is the ID of the default
+ * device which is not a virtual device.</p>
+ */
+ public abstract boolean isValidVirtualDeviceId(int deviceId);
+
+ /**
* Returns the ID of the device which owns the display with the given ID.
*
* <p>In case the virtual display ID is invalid or doesn't belong to a virtual device, then
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 49095ce..42c2548 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -1432,7 +1432,7 @@
}
private void unlockKeystore(int userId, SyntheticPassword sp) {
- Authorization.onLockScreenEvent(false, userId, sp.deriveKeyStorePassword(), null);
+ Authorization.onDeviceUnlocked(userId, sp.deriveKeyStorePassword());
}
@VisibleForTesting /** Note: this method is overridden in unit tests */
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 4ef8cb7..7f013b8 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -160,7 +160,9 @@
UserProperties.SHOW_IN_SHARING_SURFACES_WITH_PARENT)
.setMediaSharedWithParent(true)
.setCredentialShareableWithParent(true)
- .setDeleteAppWithParent(true));
+ .setDeleteAppWithParent(true)
+ .setCrossProfileContentSharingStrategy(UserProperties
+ .CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT));
}
/**
@@ -318,7 +320,9 @@
UserProperties.SHOW_IN_SHARING_SURFACES_SEPARATE)
.setCrossProfileIntentFilterAccessControl(
UserProperties.CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM)
- .setInheritDevicePolicy(UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT));
+ .setInheritDevicePolicy(UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT)
+ .setCrossProfileContentSharingStrategy(
+ UserProperties.CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT));
}
/**
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index d172d3f..eac4fc0 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -865,21 +865,19 @@
mDeviceLockedForUser.put(userId, locked);
}
if (changed) {
- dispatchDeviceLocked(userId, locked);
- Authorization.onLockScreenEvent(locked, userId, null,
- getBiometricSids(userId));
+ notifyTrustAgentsOfDeviceLockState(userId, locked);
+ notifyKeystoreOfDeviceLockState(userId, locked);
// Also update the user's profiles who have unified challenge, since they
// share the same unlocked state (see {@link #isDeviceLocked(int)})
for (int profileHandle : mUserManager.getEnabledProfileIds(userId)) {
if (mLockPatternUtils.isManagedProfileWithUnifiedChallenge(profileHandle)) {
- Authorization.onLockScreenEvent(locked, profileHandle, null,
- getBiometricSids(profileHandle));
+ notifyKeystoreOfDeviceLockState(profileHandle, locked);
}
}
}
}
- private void dispatchDeviceLocked(int userId, boolean isLocked) {
+ private void notifyTrustAgentsOfDeviceLockState(int userId, boolean isLocked) {
for (int i = 0; i < mActiveAgents.size(); i++) {
AgentInfo agent = mActiveAgents.valueAt(i);
if (agent.userId == userId) {
@@ -892,6 +890,17 @@
}
}
+ private void notifyKeystoreOfDeviceLockState(int userId, boolean isLocked) {
+ if (isLocked) {
+ Authorization.onDeviceLocked(userId, getBiometricSids(userId));
+ } else {
+ // Notify Keystore that the device is now unlocked for the user. Note that for unlocks
+ // with LSKF, this is redundant with the call from LockSettingsService which provides
+ // the password. However, for unlocks with biometric or trust agent, this is required.
+ Authorization.onDeviceUnlocked(userId, /* password= */ null);
+ }
+ }
+
private void dispatchEscrowTokenActivatedLocked(long handle, int userId) {
for (int i = 0; i < mActiveAgents.size(); i++) {
AgentInfo agent = mActiveAgents.valueAt(i);
@@ -1425,10 +1434,10 @@
}
}
- private long[] getBiometricSids(int userId) {
+ private @NonNull long[] getBiometricSids(int userId) {
BiometricManager biometricManager = mContext.getSystemService(BiometricManager.class);
if (biometricManager == null) {
- return null;
+ return new long[0];
}
return biometricManager.getAuthenticatorIds(userId);
}
@@ -1680,8 +1689,7 @@
mDeviceLockedForUser.put(userId, locked);
}
- Authorization.onLockScreenEvent(locked, userId, null,
- getBiometricSids(userId));
+ notifyKeystoreOfDeviceLockState(userId, locked);
if (locked) {
try {
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index 7862f58..e501b9d 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -146,17 +146,31 @@
mGrantedUriPermissions = new SparseArray<>();
private UriGrantsManagerService() {
- this(SystemServiceManager.ensureSystemDir());
+ this(SystemServiceManager.ensureSystemDir(), "uri-grants");
}
- private UriGrantsManagerService(File systemDir) {
+ private UriGrantsManagerService(File systemDir, String commitTag) {
mH = new H(IoThread.get().getLooper());
- mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml"), "uri-grants");
+ final File file = new File(systemDir, "urigrants.xml");
+ mGrantFile = (commitTag != null) ? new AtomicFile(file, commitTag) : new AtomicFile(file);
}
@VisibleForTesting
static UriGrantsManagerService createForTest(File systemDir) {
- final UriGrantsManagerService service = new UriGrantsManagerService(systemDir);
+ final UriGrantsManagerService service = new UriGrantsManagerService(systemDir, null) {
+ @VisibleForTesting
+ protected int checkUidPermission(String permission, int uid) {
+ // Tests have no permission granted
+ return PackageManager.PERMISSION_DENIED;
+ }
+
+ @VisibleForTesting
+ protected int checkComponentPermission(String permission, int uid, int owningUid,
+ boolean exported) {
+ // Tests have no permission granted
+ return PackageManager.PERMISSION_DENIED;
+ }
+ };
service.mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
service.mPmInternal = LocalServices.getService(PackageManagerInternal.class);
return service;
@@ -202,7 +216,8 @@
}
}
- private int checkUidPermission(String permission, int uid) {
+ @VisibleForTesting
+ protected int checkUidPermission(String permission, int uid) {
try {
return AppGlobals.getPackageManager().checkUidPermission(permission, uid);
} catch (RemoteException e) {
@@ -210,6 +225,12 @@
}
}
+ @VisibleForTesting
+ protected int checkComponentPermission(String permission, int uid, int owningUid,
+ boolean exported) {
+ return ActivityManager.checkComponentPermission(permission, uid, owningUid, exported);
+ }
+
/**
* Grant uri permissions to the specified app.
*
@@ -916,7 +937,7 @@
ProviderInfo pi, GrantUri grantUri, int uid, final int modeFlags) {
if (DEBUG) Slog.v(TAG, "checkHoldingPermissions: uri=" + grantUri + " uid=" + uid);
if (UserHandle.getUserId(uid) != grantUri.sourceUserId) {
- if (ActivityManager.checkComponentPermission(INTERACT_ACROSS_USERS, uid, -1, true)
+ if (checkComponentPermission(INTERACT_ACROSS_USERS, uid, -1, true)
!= PERMISSION_GRANTED) {
return false;
}
@@ -1340,7 +1361,7 @@
if (uid == Process.SYSTEM_UID || uid == Process.ROOT_UID) {
return true;
}
- return ActivityManager.checkComponentPermission(
+ return checkComponentPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
uid, /* owningUid = */-1, /* exported = */ true)
== PackageManager.PERMISSION_GRANTED;
diff --git a/services/core/java/com/android/server/utils/OWNERS b/services/core/java/com/android/server/utils/OWNERS
index be91611d..fbc0b56 100644
--- a/services/core/java/com/android/server/utils/OWNERS
+++ b/services/core/java/com/android/server/utils/OWNERS
@@ -10,3 +10,8 @@
per-file Watcher.java = shombert@google.com
per-file EventLogger.java = file:/platform/frameworks/av:/media/janitors/media_solutions_OWNERS
per-file EventLogger.java = jmtrivi@google.com
+
+# Bug component : 158088 = per-file AnrTimer*.java
+per-file AnrTimer*.java = file:/PERFORMANCE_OWNERS
+
+per-file flags.aconfig = file:/PERFORMANCE_OWNERS
diff --git a/services/core/java/com/android/server/vibrator/VibratorControlService.java b/services/core/java/com/android/server/vibrator/VibratorControlService.java
deleted file mode 100644
index 2eeb903..0000000
--- a/services/core/java/com/android/server/vibrator/VibratorControlService.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.vibrator;
-
-import android.annotation.NonNull;
-import android.annotation.SuppressLint;
-import android.frameworks.vibrator.IVibratorControlService;
-import android.frameworks.vibrator.IVibratorController;
-import android.frameworks.vibrator.VibrationParam;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Slog;
-
-import java.util.Objects;
-
-/**
- * Implementation of {@link IVibratorControlService} which allows the registration of
- * {@link IVibratorController} to set and receive vibration params.
- *
- * @hide
- */
-public final class VibratorControlService extends IVibratorControlService.Stub {
- private static final String TAG = "VibratorControlService";
-
- private final VibratorControllerHolder mVibratorControllerHolder;
- private final Object mLock;
-
- public VibratorControlService(VibratorControllerHolder vibratorControllerHolder, Object lock) {
- mVibratorControllerHolder = vibratorControllerHolder;
- mLock = lock;
- }
-
- @Override
- public void registerVibratorController(IVibratorController controller)
- throws RemoteException {
- synchronized (mLock) {
- mVibratorControllerHolder.setVibratorController(controller);
- }
- }
-
- @Override
- public void unregisterVibratorController(@NonNull IVibratorController controller)
- throws RemoteException {
- Objects.requireNonNull(controller);
-
- synchronized (mLock) {
- if (mVibratorControllerHolder.getVibratorController() == null) {
- Slog.w(TAG, "Received request to unregister IVibratorController = "
- + controller + ", but no controller was previously registered. Request "
- + "Ignored.");
- return;
- }
- if (!Objects.equals(mVibratorControllerHolder.getVibratorController().asBinder(),
- controller.asBinder())) {
- Slog.wtf(TAG, "Failed to unregister IVibratorController. The provided "
- + "controller doesn't match the registered one. " + this);
- return;
- }
- mVibratorControllerHolder.setVibratorController(null);
- }
- }
-
- @Override
- public void setVibrationParams(
- @SuppressLint("ArrayReturn") VibrationParam[] params, IVibratorController token)
- throws RemoteException {
- // TODO(b/305939964): Add set vibration implementation.
- }
-
- @Override
- public void clearVibrationParams(int types, IVibratorController token) throws RemoteException {
- // TODO(b/305939964): Add clear vibration implementation.
- }
-
- @Override
- public void onRequestVibrationParamsComplete(
- IBinder requestToken, @SuppressLint("ArrayReturn") VibrationParam[] result)
- throws RemoteException {
- // TODO(305942827): Cache the vibration params in VibrationScaler
- }
-
- @Override
- public int getInterfaceVersion() throws RemoteException {
- return this.VERSION;
- }
-
- @Override
- public String getInterfaceHash() throws RemoteException {
- return this.HASH;
- }
-}
diff --git a/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java b/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java
deleted file mode 100644
index 63e69db..0000000
--- a/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.vibrator;
-
-import android.annotation.NonNull;
-import android.frameworks.vibrator.IVibratorController;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Slog;
-
-/**
- * Holder class for {@link IVibratorController}.
- *
- * @hide
- */
-public final class VibratorControllerHolder implements IBinder.DeathRecipient {
- private static final String TAG = "VibratorControllerHolder";
-
- private IVibratorController mVibratorController;
-
- public IVibratorController getVibratorController() {
- return mVibratorController;
- }
-
- /**
- * Sets the {@link IVibratorController} in {@link VibratorControllerHolder} to the new
- * controller. This will also take care of registering and unregistering death notifications
- * for the cached {@link IVibratorController}.
- */
- public void setVibratorController(IVibratorController controller) {
- try {
- if (mVibratorController != null) {
- mVibratorController.asBinder().unlinkToDeath(this, 0);
- }
- mVibratorController = controller;
- if (mVibratorController != null) {
- mVibratorController.asBinder().linkToDeath(this, 0);
- }
- } catch (RemoteException e) {
- Slog.wtf(TAG, "Failed to set IVibratorController: " + this, e);
- }
- }
-
- @Override
- public void binderDied(@NonNull IBinder deadBinder) {
- if (deadBinder == mVibratorController.asBinder()) {
- setVibratorController(null);
- }
- }
-
- @Override
- public void binderDied() {
- // Should not be used as binderDied(IBinder who) is overridden.
- Slog.wtf(TAG, "binderDied() called unexpectedly.");
- }
-}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index d5044d9..7d4bd3b 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -53,7 +53,6 @@
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.VibratorInfo;
-import android.os.vibrator.Flags;
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.VibrationEffectSegment;
import android.os.vibrator.VibratorInfoFactory;
@@ -88,13 +87,10 @@
import java.util.function.Consumer;
import java.util.function.Function;
-
/** System implementation of {@link IVibratorManagerService}. */
public class VibratorManagerService extends IVibratorManagerService.Stub {
private static final String TAG = "VibratorManagerService";
private static final String EXTERNAL_VIBRATOR_SERVICE = "external_vibrator_service";
- private static final String VIBRATOR_CONTROL_SERVICE =
- "android.frameworks.vibrator.IVibratorControlService/default";
private static final boolean DEBUG = false;
private static final VibrationAttributes DEFAULT_ATTRIBUTES =
new VibrationAttributes.Builder().build();
@@ -273,10 +269,6 @@
context.registerReceiver(mIntentReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService());
- if (Flags.adaptiveHapticsEnabled()) {
- injector.addService(VIBRATOR_CONTROL_SERVICE,
- new VibratorControlService(new VibratorControllerHolder(), mLock));
- }
}
/** Finish initialization at boot phase {@link SystemService#PHASE_SYSTEM_SERVICES_READY}. */
@@ -411,9 +403,13 @@
@Override // Binder call
public void performHapticFeedback(
- int uid, int deviceId, String opPkg, int constant, boolean always, String reason,
- IBinder token) {
- performHapticFeedbackInternal(uid, deviceId, opPkg, constant, always, reason, token);
+ int uid, int deviceId, String opPkg, int constant, boolean always, String reason) {
+ // Note that the `performHapticFeedback` method does not take a token argument from the
+ // caller, and instead, uses this service as the token. This is to mitigate performance
+ // impact that would otherwise be caused due to marshal latency. Haptic feedback effects are
+ // short-lived, so we don't need to cancel when the process dies.
+ performHapticFeedbackInternal(
+ uid, deviceId, opPkg, constant, always, reason, /* token= */ this);
}
/**
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index f8b22c9..07dac54 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -443,6 +443,8 @@
// indicates BAL would be blocked because only creator of the PI has the privilege to allow
// BAL, the sender does not have the privilege to allow BAL.
private boolean mOnlyCreatorAllows;
+ /** indicates that this verdict is based on the real calling UID and not the calling UID */
+ private boolean mBasedOnRealCaller;
BalVerdict(@BalCode int balCode, boolean background, String message) {
this.mBackground = background;
@@ -472,6 +474,15 @@
return mOnlyCreatorAllows;
}
+ private BalVerdict setBasedOnRealCaller() {
+ mBasedOnRealCaller = true;
+ return this;
+ }
+
+ private boolean isBasedOnRealCaller() {
+ return mBasedOnRealCaller;
+ }
+
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(balCodeToString(mCode));
@@ -495,7 +506,15 @@
return builder.toString();
}
+ public @BalCode int getRawCode() {
+ return mCode;
+ }
+
public @BalCode int getCode() {
+ if (mBasedOnRealCaller && mCode != BAL_BLOCK) {
+ // for compatibility always return BAL_ALLOW_PENDING_INTENT if based on real caller
+ return BAL_ALLOW_PENDING_INTENT;
+ }
return mCode;
}
}
@@ -580,7 +599,8 @@
// PendingIntents is null).
BalVerdict resultForRealCaller = state.callerIsRealCaller() && resultForCaller.allows()
? resultForCaller
- : checkBackgroundActivityStartAllowedBySender(state, checkedOptions);
+ : checkBackgroundActivityStartAllowedBySender(state, checkedOptions)
+ .setBasedOnRealCaller();
if (state.isPendingIntent()) {
resultForCaller.setOnlyCreatorAllows(
resultForCaller.allows() && resultForRealCaller.blocks());
@@ -828,7 +848,7 @@
&& ActivityManager.checkComponentPermission(
android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
state.mRealCallingUid, NO_PROCESS_UID, true) == PackageManager.PERMISSION_GRANTED) {
- return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
+ return new BalVerdict(BAL_ALLOW_PERMISSION,
/*background*/ false,
"realCallingUid has BAL permission.");
}
@@ -839,18 +859,18 @@
|| state.mAppSwitchState == APP_SWITCH_FG_ONLY;
if (Flags.balImproveRealCallerVisibilityCheck()) {
if (appSwitchAllowedOrFg && state.mRealCallingUidHasAnyVisibleWindow) {
- return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
+ return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW,
/*background*/ false, "realCallingUid has visible window");
}
if (mService.mActiveUids.hasNonAppVisibleWindow(state.mRealCallingUid)) {
- return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
+ return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW,
/*background*/ false, "realCallingUid has non-app visible window");
}
} else {
// don't abort if the realCallingUid has a visible window
// TODO(b/171459802): We should check appSwitchAllowed also
if (state.mRealCallingUidHasAnyVisibleWindow) {
- return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
+ return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW,
/*background*/ false,
"realCallingUid has visible (non-toast) window.");
}
@@ -860,7 +880,7 @@
// wasn't allowed to start an activity
if (state.mForcedBalByPiSender.allowsBackgroundActivityStarts()
&& state.mIsRealCallingUidPersistentSystemProcess) {
- return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
+ return new BalVerdict(BAL_ALLOW_ALLOWLISTED_UID,
/*background*/ false,
"realCallingUid is persistent system process AND intent "
+ "sender forced to allow.");
@@ -868,7 +888,7 @@
// don't abort if the realCallingUid is an associated companion app
if (mService.isAssociatedCompanionApp(
UserHandle.getUserId(state.mRealCallingUid), state.mRealCallingUid)) {
- return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
+ return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT,
/*background*/ false,
"realCallingUid is a companion app.");
}
@@ -1469,7 +1489,7 @@
intent != null ? intent.getComponent().flattenToShortString() : "";
FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED,
activityName,
- code,
+ BAL_ALLOW_PENDING_INTENT,
callingUid,
realCallingUid);
}
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index 0e45f61..061fe0f 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -30,3 +30,6 @@
per-file com_android_server_vibrator_* = file:/services/core/java/com/android/server/vibrator/OWNERS
per-file com_android_server_am_CachedAppOptimizer.cpp = timmurray@google.com, edgararriaga@google.com, dualli@google.com, carmenjackson@google.com, philipcuadra@google.com
per-file com_android_server_companion_virtual_InputController.cpp = file:/services/companion/java/com/android/server/companion/virtual/OWNERS
+
+# Bug component : 158088 = per-file com_android_server_utils_AnrTimer*.java
+per-file com_android_server_utils_AnrTimer*.java = file:/PERFORMANCE_OWNERS
diff --git a/services/manifest_services.xml b/services/manifest_services.xml
index e2fdfe9..7638915 100644
--- a/services/manifest_services.xml
+++ b/services/manifest_services.xml
@@ -4,14 +4,4 @@
<version>1</version>
<fqname>IAltitudeService/default</fqname>
</hal>
- <hal format="aidl">
- <name>android.frameworks.vibrator</name>
- <version>1</version>
- <fqname>IVibratorController/default</fqname>
- </hal>
- <hal format="aidl">
- <name>android.frameworks.vibrator</name>
- <version>1</version>
- <fqname>IVibratorControlService/default</fqname>
- </hal>
</manifest>
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index 39aaab2..a212812 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -31,6 +31,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.Property;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
@@ -1396,16 +1397,20 @@
XmlResourceParser parser = null;
try {
- parser = serviceInfo.loadXmlMetaData(mPackageManager,
- MidiDeviceService.SERVICE_INTERFACE);
- if (parser == null) return;
+ if (serviceInfo == null) {
+ Log.w(TAG, "Skipping null service info");
+ return;
+ }
// ignore virtual device servers that do not require the correct permission
if (!android.Manifest.permission.BIND_MIDI_DEVICE_SERVICE.equals(
serviceInfo.permission)) {
- Log.w(TAG, "Skipping MIDI device service " + serviceInfo.packageName
- + ": it does not require the permission "
- + android.Manifest.permission.BIND_MIDI_DEVICE_SERVICE);
+ return;
+ }
+ parser = serviceInfo.loadXmlMetaData(mPackageManager,
+ MidiDeviceService.SERVICE_INTERFACE);
+ if (parser == null) {
+ Log.w(TAG, "loading xml metadata failed");
return;
}
@@ -1533,21 +1538,14 @@
XmlResourceParser parser = null;
try {
- ComponentName componentName = new ComponentName(serviceInfo.packageName,
- serviceInfo.name);
- int resId = mPackageManager.getProperty(MidiUmpDeviceService.SERVICE_INTERFACE,
- componentName).getResourceId();
- Resources resources = mPackageManager.getResourcesForApplication(
- serviceInfo.packageName);
- parser = resources.getXml(resId);
- if (parser == null) return;
+ if (serviceInfo == null) {
+ Log.w(TAG, "Skipping null service info");
+ return;
+ }
// ignore virtual device servers that do not require the correct permission
if (!android.Manifest.permission.BIND_MIDI_DEVICE_SERVICE.equals(
serviceInfo.permission)) {
- Log.w(TAG, "Skipping MIDI device service " + serviceInfo.packageName
- + ": it does not require the permission "
- + android.Manifest.permission.BIND_MIDI_DEVICE_SERVICE);
return;
}
@@ -1557,6 +1555,31 @@
return;
}
+ ComponentName componentName = new ComponentName(serviceInfo.packageName,
+ serviceInfo.name);
+ Property property = mPackageManager.getProperty(MidiUmpDeviceService.SERVICE_INTERFACE,
+ componentName);
+ if (property == null) {
+ Log.w(TAG, "Getting MidiUmpDeviceService property failed");
+ return;
+ }
+ int resId = property.getResourceId();
+ if (resId == 0) {
+ Log.w(TAG, "Getting MidiUmpDeviceService resourceId failed");
+ return;
+ }
+ Resources resources = mPackageManager.getResourcesForApplication(
+ serviceInfo.packageName);
+ if (resources == null) {
+ Log.w(TAG, "Getting resource failed " + serviceInfo.packageName);
+ return;
+ }
+ parser = resources.getXml(resId);
+ if (parser == null) {
+ Log.w(TAG, "Getting XML failed " + resId);
+ return;
+ }
+
Bundle properties = null;
int numPorts = 0;
boolean isPrivate = false;
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/BackgroundJobsControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/BackgroundJobsControllerTest.java
index 25d331f..23886a1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/BackgroundJobsControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/BackgroundJobsControllerTest.java
@@ -167,6 +167,11 @@
}
private void setStoppedState(int uid, String pkgName, boolean stopped) {
+ doReturn(stopped).when(mPackageManagerInternal).isPackageStopped(pkgName, uid);
+ sendPackageStoppedBroadcast(uid, pkgName, stopped);
+ }
+
+ private void sendPackageStoppedBroadcast(int uid, String pkgName, boolean stopped) {
Intent intent = new Intent(
stopped ? Intent.ACTION_PACKAGE_RESTARTED : Intent.ACTION_PACKAGE_UNSTOPPED);
intent.putExtra(Intent.EXTRA_UID, uid);
@@ -174,14 +179,6 @@
mStoppedReceiver.onReceive(mContext, intent);
}
- private void setUidBias(int uid, int bias) {
- int prevBias = mJobSchedulerService.getUidBias(uid);
- doReturn(bias).when(mJobSchedulerService).getUidBias(uid);
- synchronized (mBackgroundJobsController.mLock) {
- mBackgroundJobsController.onUidBiasChangedLocked(uid, prevBias, bias);
- }
- }
-
private void trackJobs(JobStatus... jobs) {
for (JobStatus job : jobs) {
mJobStore.add(job);
@@ -208,6 +205,47 @@
}
@Test
+ public void testRestartedBroadcastWithoutStopping() {
+ mSetFlagsRule.enableFlags(android.content.pm.Flags.FLAG_STAY_STOPPED);
+ // Scheduled by SOURCE_UID:SOURCE_PACKAGE for itself.
+ JobStatus directJob1 = createJobStatus("testStopped", SOURCE_PACKAGE, SOURCE_UID,
+ createBaseJobInfoBuilder(SOURCE_PACKAGE, 1).build());
+ // Scheduled by ALTERNATE_UID:ALTERNATE_SOURCE_PACKAGE for itself.
+ JobStatus directJob2 = createJobStatus("testStopped",
+ ALTERNATE_SOURCE_PACKAGE, ALTERNATE_UID,
+ createBaseJobInfoBuilder(ALTERNATE_SOURCE_PACKAGE, 2).build());
+ // Scheduled by CALLING_PACKAGE for SOURCE_PACKAGE.
+ JobStatus proxyJob1 = createJobStatus("testStopped", SOURCE_PACKAGE, CALLING_UID,
+ createBaseJobInfoBuilder(CALLING_PACKAGE, 3).build());
+ // Scheduled by CALLING_PACKAGE for ALTERNATE_SOURCE_PACKAGE.
+ JobStatus proxyJob2 = createJobStatus("testStopped",
+ ALTERNATE_SOURCE_PACKAGE, CALLING_UID,
+ createBaseJobInfoBuilder(CALLING_PACKAGE, 4).build());
+
+ trackJobs(directJob1, directJob2, proxyJob1, proxyJob2);
+
+ sendPackageStoppedBroadcast(ALTERNATE_UID, ALTERNATE_SOURCE_PACKAGE, true);
+ assertTrue(directJob1.isConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED));
+ assertFalse(directJob1.isUserBgRestricted());
+ assertTrue(directJob2.isConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED));
+ assertFalse(directJob2.isUserBgRestricted());
+ assertTrue(proxyJob1.isConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED));
+ assertFalse(proxyJob1.isUserBgRestricted());
+ assertTrue(proxyJob2.isConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED));
+ assertFalse(proxyJob2.isUserBgRestricted());
+
+ sendPackageStoppedBroadcast(ALTERNATE_UID, ALTERNATE_SOURCE_PACKAGE, false);
+ assertTrue(directJob1.isConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED));
+ assertFalse(directJob1.isUserBgRestricted());
+ assertTrue(directJob2.isConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED));
+ assertFalse(directJob2.isUserBgRestricted());
+ assertTrue(proxyJob1.isConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED));
+ assertFalse(proxyJob1.isUserBgRestricted());
+ assertTrue(proxyJob2.isConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED));
+ assertFalse(proxyJob2.isUserBgRestricted());
+ }
+
+ @Test
public void testStopped_disabled() {
mSetFlagsRule.disableFlags(android.content.pm.Flags.FLAG_STAY_STOPPED);
// Scheduled by SOURCE_UID:SOURCE_PACKAGE for itself.
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 4fb9472..650c473 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
@@ -30,15 +30,16 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.server.job.controllers.FlexibilityController.FcConfig.DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS;
import static com.android.server.job.controllers.FlexibilityController.FcConfig.DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS;
+import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_APPLIED_CONSTRAINTS;
import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_DEADLINE_PROXIMITY_LIMIT;
import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_FALLBACK_FLEXIBILITY_DEADLINE;
-import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_FLEXIBILITY_ENABLED;
import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS;
-import static com.android.server.job.controllers.FlexibilityController.NUM_FLEXIBLE_CONSTRAINTS;
+import static com.android.server.job.controllers.FlexibilityController.FLEXIBLE_CONSTRAINTS;
import static com.android.server.job.controllers.FlexibilityController.SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BATTERY_NOT_LOW;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CHARGING;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CONNECTIVITY;
+import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CONTENT_TRIGGER;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_FLEXIBLE;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_IDLE;
import static com.android.server.job.controllers.JobStatus.MIN_WINDOW_FOR_FLEXIBILITY_MS;
@@ -66,6 +67,7 @@
import android.provider.DeviceConfig;
import android.util.ArraySet;
+import com.android.server.AppSchedulingModuleThread;
import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.JobStore;
@@ -129,6 +131,7 @@
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.hasSystemFeature(
PackageManager.FEATURE_AUTOMOTIVE)).thenReturn(false);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_EMBEDDED)).thenReturn(false);
// Used in FlexibilityController.FcConstants.
doAnswer((Answer<Void>) invocationOnMock -> null)
.when(() -> DeviceConfig.addOnPropertiesChangedListener(
@@ -161,7 +164,8 @@
setDeviceConfigString(KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS, "50,60,70,80");
setDeviceConfigLong(KEY_DEADLINE_PROXIMITY_LIMIT, 0L);
- setDeviceConfigBoolean(KEY_FLEXIBILITY_ENABLED, true);
+ setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, FLEXIBLE_CONSTRAINTS);
+ waitForQuietModuleThread();
}
@After
@@ -171,17 +175,22 @@
}
}
- private void setDeviceConfigBoolean(String key, boolean val) {
- mDeviceConfigPropertiesBuilder.setBoolean(key, val);
- synchronized (mFlexibilityController.mLock) {
- mFlexibilityController.prepareForUpdatedConstantsLocked();
- mFcConfig.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key);
- mFlexibilityController.onConstantsUpdatedLocked();
- }
+ private void setDeviceConfigInt(String key, int val) {
+ mDeviceConfigPropertiesBuilder.setInt(key, val);
+ updateDeviceConfig(key);
}
private void setDeviceConfigLong(String key, Long val) {
mDeviceConfigPropertiesBuilder.setLong(key, val);
+ updateDeviceConfig(key);
+ }
+
+ private void setDeviceConfigString(String key, String val) {
+ mDeviceConfigPropertiesBuilder.setString(key, val);
+ updateDeviceConfig(key);
+ }
+
+ private void updateDeviceConfig(String key) {
synchronized (mFlexibilityController.mLock) {
mFlexibilityController.prepareForUpdatedConstantsLocked();
mFcConfig.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key);
@@ -189,13 +198,9 @@
}
}
- private void setDeviceConfigString(String key, String val) {
- mDeviceConfigPropertiesBuilder.setString(key, val);
- synchronized (mFlexibilityController.mLock) {
- mFlexibilityController.prepareForUpdatedConstantsLocked();
- mFcConfig.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key);
- mFlexibilityController.onConstantsUpdatedLocked();
- }
+ private void waitForQuietModuleThread() {
+ assertTrue("Failed to wait for quiet module thread",
+ AppSchedulingModuleThread.getHandler().runWithScissors(() -> {}, 10_000L));
}
private static JobInfo.Builder createJob(int id) {
@@ -207,6 +212,10 @@
JobStatus js = JobStatus.createFromJobInfo(
jobInfo, 1000, SOURCE_PACKAGE, SOURCE_USER_ID, "FCTest", testTag);
js.enqueueTime = FROZEN_TIME;
+ if (js.hasFlexibilityConstraint()) {
+ js.setNumAppliedFlexibleConstraints(Integer.bitCount(
+ mFlexibilityController.getRelevantAppliedConstraintsLocked(js)));
+ }
return js;
}
@@ -215,18 +224,120 @@
*/
@Test
public void testDefaultVariableValues() {
- assertEquals(NUM_FLEXIBLE_CONSTRAINTS,
+ assertEquals(Integer.bitCount(FLEXIBLE_CONSTRAINTS),
mFlexibilityController.mFcConfig.DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS.length
);
}
@Test
- public void testOnConstantsUpdated_DefaultFlexibility() {
+ public void testAppliedConstraints() {
+ setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, FLEXIBLE_CONSTRAINTS);
+
+ // Add connectivity to require 4 constraints
+ JobStatus connJs = createJobStatus("testAppliedConstraints",
+ createJob(0).setRequiredNetworkType(NETWORK_TYPE_ANY));
+ JobStatus nonConnJs = createJobStatus("testAppliedConstraints",
+ createJob(1).setRequiredNetworkType(NETWORK_TYPE_NONE));
+
+ mFlexibilityController.maybeStartTrackingJobLocked(connJs, null);
+ mFlexibilityController.maybeStartTrackingJobLocked(nonConnJs, null);
+
+ assertEquals(4, connJs.getNumAppliedFlexibleConstraints());
+ assertEquals(3, nonConnJs.getNumAppliedFlexibleConstraints());
+
+ mFlexibilityController.setConstraintSatisfied(
+ CONSTRAINT_BATTERY_NOT_LOW, true,
+ JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS);
+ mFlexibilityController.setConstraintSatisfied(
+ CONSTRAINT_CHARGING, false,
+ JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS);
+ mFlexibilityController.setConstraintSatisfied(
+ CONSTRAINT_IDLE, false,
+ JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS);
+ mFlexibilityController.setConstraintSatisfied(
+ CONSTRAINT_CONNECTIVITY, true,
+ JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS);
+ connJs.setTransportAffinitiesSatisfied(true);
+
+ synchronized (mFlexibilityController.mLock) {
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs));
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs));
+ }
+
+ setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS,
+ CONSTRAINT_BATTERY_NOT_LOW | CONSTRAINT_CONNECTIVITY);
+ waitForQuietModuleThread();
+
+ // Only battery-not-low (which is satisfied) applies to the non-connectivity job, so it
+ // should be able to run.
+ assertEquals(2, connJs.getNumAppliedFlexibleConstraints());
+ assertEquals(1, nonConnJs.getNumAppliedFlexibleConstraints());
+ synchronized (mFlexibilityController.mLock) {
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs));
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs));
+ }
+
+ setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, CONSTRAINT_BATTERY_NOT_LOW);
+ waitForQuietModuleThread();
+
+ assertEquals(1, connJs.getNumAppliedFlexibleConstraints());
+ assertEquals(1, nonConnJs.getNumAppliedFlexibleConstraints());
+ synchronized (mFlexibilityController.mLock) {
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs));
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs));
+ }
+
+ setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, CONSTRAINT_CONNECTIVITY);
+ waitForQuietModuleThread();
+
+ // No constraints apply to the non-connectivity job, so it should be able to run.
+ assertEquals(1, connJs.getNumAppliedFlexibleConstraints());
+ assertEquals(0, nonConnJs.getNumAppliedFlexibleConstraints());
+ synchronized (mFlexibilityController.mLock) {
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs));
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs));
+ }
+
+ setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, CONSTRAINT_CHARGING);
+ waitForQuietModuleThread();
+
+ assertEquals(1, connJs.getNumAppliedFlexibleConstraints());
+ assertEquals(1, nonConnJs.getNumAppliedFlexibleConstraints());
+ synchronized (mFlexibilityController.mLock) {
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs));
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs));
+ }
+
+ setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, 0);
+ waitForQuietModuleThread();
+
+ // No constraints apply, so they should be able to run.
+ assertEquals(0, connJs.getNumAppliedFlexibleConstraints());
+ assertEquals(0, nonConnJs.getNumAppliedFlexibleConstraints());
+ synchronized (mFlexibilityController.mLock) {
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs));
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs));
+ }
+
+ // Invalid constraint to apply.
+ setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, CONSTRAINT_CONTENT_TRIGGER);
+ waitForQuietModuleThread();
+
+ // No constraints apply, so they should be able to run.
+ assertEquals(0, connJs.getNumAppliedFlexibleConstraints());
+ assertEquals(0, nonConnJs.getNumAppliedFlexibleConstraints());
+ synchronized (mFlexibilityController.mLock) {
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs));
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs));
+ }
+ }
+
+ @Test
+ public void testOnConstantsUpdated_AppliedConstraints() {
JobStatus js = createJobStatus("testDefaultFlexibilityConfig", createJob(0));
- assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(js));
- setDeviceConfigBoolean(KEY_FLEXIBILITY_ENABLED, false);
+ setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, 0);
assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(js));
- setDeviceConfigBoolean(KEY_FLEXIBILITY_ENABLED, true);
+ setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, FLEXIBLE_CONSTRAINTS);
assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(js));
}
@@ -261,10 +372,10 @@
new int[] {10, 20, 30, 40});
assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10,
mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js));
- js.adjustNumRequiredFlexibleConstraints(-1);
+ js.setNumDroppedFlexibleConstraints(1);
assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 2,
mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js));
- js.adjustNumRequiredFlexibleConstraints(-1);
+ js.setNumDroppedFlexibleConstraints(2);
assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 3,
mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js));
}
@@ -303,12 +414,12 @@
.getNextConstraintDropTimeElapsedLocked(js);
assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 5,
nextTimeToDropNumConstraints);
- js.adjustNumRequiredFlexibleConstraints(-1);
+ js.setNumDroppedFlexibleConstraints(1);
nextTimeToDropNumConstraints = mFlexibilityController
.getNextConstraintDropTimeElapsedLocked(js);
assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 6,
nextTimeToDropNumConstraints);
- js.adjustNumRequiredFlexibleConstraints(-1);
+ js.setNumDroppedFlexibleConstraints(2);
nextTimeToDropNumConstraints = mFlexibilityController
.getNextConstraintDropTimeElapsedLocked(js);
assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 7,
@@ -321,11 +432,11 @@
nextTimeToDropNumConstraints = mFlexibilityController
.getNextConstraintDropTimeElapsedLocked(js);
assertEquals(130400100, nextTimeToDropNumConstraints);
- js.adjustNumRequiredFlexibleConstraints(-1);
+ js.setNumDroppedFlexibleConstraints(1);
nextTimeToDropNumConstraints = mFlexibilityController
.getNextConstraintDropTimeElapsedLocked(js);
assertEquals(156320100L, nextTimeToDropNumConstraints);
- js.adjustNumRequiredFlexibleConstraints(-1);
+ js.setNumDroppedFlexibleConstraints(2);
nextTimeToDropNumConstraints = mFlexibilityController
.getNextConstraintDropTimeElapsedLocked(js);
assertEquals(182240100L, nextTimeToDropNumConstraints);
@@ -337,11 +448,11 @@
nextTimeToDropNumConstraints = mFlexibilityController
.getNextConstraintDropTimeElapsedLocked(js);
assertEquals(129600100, nextTimeToDropNumConstraints);
- js.adjustNumRequiredFlexibleConstraints(-1);
+ js.setNumDroppedFlexibleConstraints(1);
nextTimeToDropNumConstraints = mFlexibilityController
.getNextConstraintDropTimeElapsedLocked(js);
assertEquals(155520100L, nextTimeToDropNumConstraints);
- js.adjustNumRequiredFlexibleConstraints(-1);
+ js.setNumDroppedFlexibleConstraints(2);
nextTimeToDropNumConstraints = mFlexibilityController
.getNextConstraintDropTimeElapsedLocked(js);
assertEquals(181440100L, nextTimeToDropNumConstraints);
@@ -357,12 +468,12 @@
.getNextConstraintDropTimeElapsedLocked(js);
assertEquals(windowStart + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 5,
nextTimeToDropNumConstraints);
- js.adjustNumRequiredFlexibleConstraints(-1);
+ js.setNumDroppedFlexibleConstraints(1);
nextTimeToDropNumConstraints = mFlexibilityController
.getNextConstraintDropTimeElapsedLocked(js);
assertEquals(windowStart + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 6,
nextTimeToDropNumConstraints);
- js.adjustNumRequiredFlexibleConstraints(-1);
+ js.setNumDroppedFlexibleConstraints(2);
nextTimeToDropNumConstraints = mFlexibilityController
.getNextConstraintDropTimeElapsedLocked(js);
assertEquals(windowStart + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 7,
@@ -580,10 +691,10 @@
}
@Test
- public void testWontStopJobFromRunning() {
- JobStatus js = createJobStatus("testWontStopJobFromRunning", createJob(101));
+ public void testWontStopAlreadyRunningJob() {
+ JobStatus js = createJobStatus("testWontStopAlreadyRunningJob", createJob(101));
// Stop satisfied constraints from causing a false positive.
- js.adjustNumRequiredFlexibleConstraints(100);
+ js.setNumAppliedFlexibleConstraints(100);
synchronized (mFlexibilityController.mLock) {
when(mJobSchedulerService.isCurrentlyRunningLocked(js)).thenReturn(true);
assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(js));
@@ -593,10 +704,9 @@
@Test
public void testFlexibilityTracker() {
FlexibilityController.FlexibilityTracker flexTracker =
- mFlexibilityController.new
- FlexibilityTracker(NUM_FLEXIBLE_CONSTRAINTS);
+ mFlexibilityController.new FlexibilityTracker(4);
// Plus one for jobs with 0 required constraint.
- assertEquals(NUM_FLEXIBLE_CONSTRAINTS + 1, flexTracker.size());
+ assertEquals(4 + 1, flexTracker.size());
JobStatus[] jobs = new JobStatus[4];
JobInfo.Builder jb;
for (int i = 0; i < jobs.length; i++) {
@@ -622,21 +732,21 @@
assertEquals(3, trackedJobs.get(3).size());
assertEquals(0, trackedJobs.get(4).size());
- flexTracker.adjustJobsRequiredConstraints(jobs[0], -1, FROZEN_TIME);
+ flexTracker.setNumDroppedFlexibleConstraints(jobs[0], 1);
assertEquals(1, trackedJobs.get(0).size());
assertEquals(0, trackedJobs.get(1).size());
assertEquals(1, trackedJobs.get(2).size());
assertEquals(2, trackedJobs.get(3).size());
assertEquals(0, trackedJobs.get(4).size());
- flexTracker.adjustJobsRequiredConstraints(jobs[0], -1, FROZEN_TIME);
+ flexTracker.setNumDroppedFlexibleConstraints(jobs[0], 2);
assertEquals(1, trackedJobs.get(0).size());
assertEquals(1, trackedJobs.get(1).size());
assertEquals(0, trackedJobs.get(2).size());
assertEquals(2, trackedJobs.get(3).size());
assertEquals(0, trackedJobs.get(4).size());
- flexTracker.adjustJobsRequiredConstraints(jobs[0], -1, FROZEN_TIME);
+ flexTracker.setNumDroppedFlexibleConstraints(jobs[0], 3);
assertEquals(2, trackedJobs.get(0).size());
assertEquals(0, trackedJobs.get(1).size());
assertEquals(0, trackedJobs.get(2).size());
@@ -650,14 +760,14 @@
assertEquals(1, trackedJobs.get(3).size());
assertEquals(0, trackedJobs.get(4).size());
- flexTracker.resetJobNumDroppedConstraints(jobs[0], FROZEN_TIME);
+ flexTracker.calculateNumDroppedConstraints(jobs[0], FROZEN_TIME);
assertEquals(1, trackedJobs.get(0).size());
assertEquals(0, trackedJobs.get(1).size());
assertEquals(0, trackedJobs.get(2).size());
assertEquals(2, trackedJobs.get(3).size());
assertEquals(0, trackedJobs.get(4).size());
- flexTracker.adjustJobsRequiredConstraints(jobs[0], -2, FROZEN_TIME);
+ flexTracker.setNumDroppedFlexibleConstraints(jobs[0], 2);
assertEquals(1, trackedJobs.get(0).size());
assertEquals(1, trackedJobs.get(1).size());
assertEquals(0, trackedJobs.get(2).size());
@@ -669,7 +779,7 @@
JobSchedulerService.sElapsedRealtimeClock =
Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC);
- flexTracker.resetJobNumDroppedConstraints(jobs[0], nowElapsed);
+ flexTracker.calculateNumDroppedConstraints(jobs[0], nowElapsed);
assertEquals(1, trackedJobs.get(0).size());
assertEquals(0, trackedJobs.get(1).size());
assertEquals(1, trackedJobs.get(2).size());
@@ -779,9 +889,9 @@
mFlexibilityController.setConstraintSatisfied(
SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS, false, FROZEN_TIME);
// Require only a single constraint
- jsAny.adjustNumRequiredFlexibleConstraints(-3);
- jsCell.adjustNumRequiredFlexibleConstraints(-2);
- jsWifi.adjustNumRequiredFlexibleConstraints(-2);
+ jsAny.setNumAppliedFlexibleConstraints(1);
+ jsCell.setNumAppliedFlexibleConstraints(1);
+ jsWifi.setNumAppliedFlexibleConstraints(1);
synchronized (mFlexibilityController.mLock) {
jsAny.setTransportAffinitiesSatisfied(false);
jsCell.setTransportAffinitiesSatisfied(false);
@@ -1008,9 +1118,9 @@
}
@Test
- public void testResetJobNumDroppedConstraints() {
+ public void testCalculateNumDroppedConstraints() {
JobInfo.Builder jb = createJob(22);
- JobStatus js = createJobStatus("testResetJobNumDroppedConstraints", jb);
+ JobStatus js = createJobStatus("testCalculateNumDroppedConstraints", jb);
long nowElapsed = FROZEN_TIME;
mFlexibilityController.mFlexibilityTracker.add(js);
@@ -1025,14 +1135,14 @@
Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC);
mFlexibilityController.mFlexibilityTracker
- .adjustJobsRequiredConstraints(js, -1, nowElapsed);
+ .setNumDroppedFlexibleConstraints(js, 1);
assertEquals(2, js.getNumRequiredFlexibleConstraints());
assertEquals(1, js.getNumDroppedFlexibleConstraints());
assertEquals(1, mFlexibilityController
.mFlexibilityTracker.getJobsByNumRequiredConstraints(2).size());
- mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js, nowElapsed);
+ mFlexibilityController.mFlexibilityTracker.calculateNumDroppedConstraints(js, nowElapsed);
assertEquals(2, js.getNumRequiredFlexibleConstraints());
assertEquals(1, js.getNumDroppedFlexibleConstraints());
@@ -1043,7 +1153,7 @@
JobSchedulerService.sElapsedRealtimeClock =
Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC);
- mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js, nowElapsed);
+ mFlexibilityController.mFlexibilityTracker.calculateNumDroppedConstraints(js, nowElapsed);
assertEquals(3, js.getNumRequiredFlexibleConstraints());
assertEquals(0, js.getNumDroppedFlexibleConstraints());
@@ -1054,7 +1164,7 @@
JobSchedulerService.sElapsedRealtimeClock =
Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC);
- mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js, nowElapsed);
+ mFlexibilityController.mFlexibilityTracker.calculateNumDroppedConstraints(js, nowElapsed);
assertEquals(0, js.getNumRequiredFlexibleConstraints());
assertEquals(3, js.getNumDroppedFlexibleConstraints());
@@ -1063,7 +1173,7 @@
JobSchedulerService.sElapsedRealtimeClock =
Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC);
- mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js, nowElapsed);
+ mFlexibilityController.mFlexibilityTracker.calculateNumDroppedConstraints(js, nowElapsed);
assertEquals(1, js.getNumRequiredFlexibleConstraints());
assertEquals(2, js.getNumDroppedFlexibleConstraints());
@@ -1139,20 +1249,28 @@
}
@Test
- public void testDeviceDisabledFlexibility_Auto() {
- when(mPackageManager.hasSystemFeature(
- PackageManager.FEATURE_AUTOMOTIVE)).thenReturn(true);
+ public void testUnsupportedDevice_Auto() {
+ runTestUnsupportedDevice(PackageManager.FEATURE_AUTOMOTIVE);
+ }
+
+ @Test
+ public void testUnsupportedDevice_Embedded() {
+ runTestUnsupportedDevice(PackageManager.FEATURE_EMBEDDED);
+ }
+
+ private void runTestUnsupportedDevice(String feature) {
+ when(mPackageManager.hasSystemFeature(feature)).thenReturn(true);
mFlexibilityController =
new FlexibilityController(mJobSchedulerService, mPrefetchController);
- assertFalse(mFlexibilityController.mFlexibilityEnabled);
+ assertFalse(mFlexibilityController.isEnabled());
- JobStatus js = createJobStatus("testIsAuto", createJob(0));
+ JobStatus js = createJobStatus("testUnsupportedDevice", createJob(0));
mFlexibilityController.maybeStartTrackingJobLocked(js, null);
assertTrue(js.isConstraintSatisfied(CONSTRAINT_FLEXIBLE));
- setDeviceConfigBoolean(KEY_FLEXIBILITY_ENABLED, true);
- assertFalse(mFlexibilityController.mFlexibilityEnabled);
+ setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, FLEXIBLE_CONSTRAINTS);
+ assertFalse(mFlexibilityController.isEnabled());
ArrayList<ArraySet<JobStatus>> jobs =
mFlexibilityController.mFlexibilityTracker.getArrayList();
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 8397b877..293391f 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
@@ -232,6 +232,19 @@
}
@Test
+ public void testFlexibleConstraintCounts() {
+ JobStatus js = createJobStatus(new JobInfo.Builder(101, new ComponentName("foo", "bar"))
+ .setUserInitiated(false)
+ .build());
+
+ js.setNumAppliedFlexibleConstraints(3);
+ js.setNumDroppedFlexibleConstraints(2);
+ assertEquals(3, js.getNumAppliedFlexibleConstraints());
+ assertEquals(2, js.getNumDroppedFlexibleConstraints());
+ assertEquals(1, js.getNumRequiredFlexibleConstraints());
+ }
+
+ @Test
public void testIsUserVisibleJob() {
JobInfo jobInfo = new JobInfo.Builder(101, new ComponentName("foo", "bar"))
.setUserInitiated(false)
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 2ece8c7..9b84190 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -79,6 +79,7 @@
"coretests-aidl",
"securebox",
"flag-junit",
+ "ravenwood-junit",
],
libs: [
@@ -140,6 +141,23 @@
resource_zips: [":FrameworksServicesTests_apks_as_resources"],
}
+android_ravenwood_test {
+ name: "FrameworksServicesTestsRavenwood",
+ libs: [
+ "android.test.mock",
+ ],
+ static_libs: [
+ "androidx.annotation_annotation",
+ "androidx.test.rules",
+ "mockito_ravenwood",
+ "services.core",
+ ],
+ srcs: [
+ "src/com/android/server/uri/**/*.java",
+ ],
+ auto_gen_config: true,
+}
+
java_library {
name: "servicestests-core-utils",
srcs: [
diff --git a/services/tests/servicestests/res/xml/usertypes_test_profile.xml b/services/tests/servicestests/res/xml/usertypes_test_profile.xml
index e89199d..9b047f2 100644
--- a/services/tests/servicestests/res/xml/usertypes_test_profile.xml
+++ b/services/tests/servicestests/res/xml/usertypes_test_profile.xml
@@ -45,6 +45,7 @@
inheritDevicePolicy='450'
deleteAppWithParent='false'
alwaysVisible='true'
+ crossProfileContentSharingStrategy='0'
/>
</profile-type>
<profile-type name='custom.test.1' max-allowed-per-parent='14' />
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 9114027..a9967f6 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -717,6 +717,45 @@
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
+ public void testSecondFingerSwipe_twoPointerDownAndActivatedState_shouldInPanningState() {
+ goFromStateIdleTo(STATE_ACTIVATED);
+ PointF pointer1 = DEFAULT_POINT;
+ PointF pointer2 = new PointF(DEFAULT_X * 1.5f, DEFAULT_Y);
+
+ send(downEvent());
+ send(pointerEvent(ACTION_POINTER_DOWN, new PointF[] {pointer1, pointer2}, 1));
+ //The minimum movement to transit to panningState.
+ final float sWipeMinDistance = ViewConfiguration.get(mContext).getScaledTouchSlop();
+ pointer2.offset(sWipeMinDistance + 1, 0);
+ send(pointerEvent(ACTION_MOVE, new PointF[] {pointer1, pointer2}, 1));
+ fastForward(ViewConfiguration.getTapTimeout());
+ assertIn(STATE_PANNING);
+
+ returnToNormalFrom(STATE_PANNING);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
+ public void testTowFingerSwipe_twoPointerDownAndShortcutTriggeredState_shouldInPanningState() {
+ goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED);
+ PointF pointer1 = DEFAULT_POINT;
+ PointF pointer2 = new PointF(DEFAULT_X * 1.5f, DEFAULT_Y);
+
+ send(downEvent());
+ send(pointerEvent(ACTION_POINTER_DOWN, new PointF[] {pointer1, pointer2}, 1));
+ //The minimum movement to transit to panningState.
+ final float sWipeMinDistance = ViewConfiguration.get(mContext).getScaledTouchSlop();
+ pointer2.offset(sWipeMinDistance + 1, 0);
+ send(pointerEvent(ACTION_MOVE, new PointF[] {pointer1, pointer2}, 1));
+ fastForward(ViewConfiguration.getTapTimeout());
+ assertIn(STATE_PANNING);
+
+ returnToNormalFrom(STATE_PANNING);
+ }
+
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
public void testSecondFingerSwipe_twoPointerDownAndActivatedState_panningState() {
goFromStateIdleTo(STATE_ACTIVATED);
PointF pointer1 = DEFAULT_POINT;
@@ -734,6 +773,7 @@
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
public void testSecondFingerSwipe_twoPointerDownAndShortcutTriggeredState_panningState() {
goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED);
PointF pointer1 = DEFAULT_POINT;
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 9213601a..995d1f4 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -444,18 +444,27 @@
}
@Test
+ public void isDeviceIdValid_invalidDeviceId_returnsFalse() {
+ assertThat(mVdm.isValidVirtualDeviceId(DEVICE_ID_INVALID)).isFalse();
+ assertThat(mLocalService.isValidVirtualDeviceId(DEVICE_ID_INVALID)).isFalse();
+ }
+
+ @Test
public void isDeviceIdValid_defaultDeviceId_returnsFalse() {
assertThat(mVdm.isValidVirtualDeviceId(DEVICE_ID_DEFAULT)).isFalse();
+ assertThat(mLocalService.isValidVirtualDeviceId(DEVICE_ID_DEFAULT)).isFalse();
}
@Test
public void isDeviceIdValid_validVirtualDeviceId_returnsTrue() {
assertThat(mVdm.isValidVirtualDeviceId(mDeviceImpl.getDeviceId())).isTrue();
+ assertThat(mLocalService.isValidVirtualDeviceId(mDeviceImpl.getDeviceId())).isTrue();
}
@Test
public void isDeviceIdValid_nonExistentDeviceId_returnsFalse() {
assertThat(mVdm.isValidVirtualDeviceId(mDeviceImpl.getDeviceId() + 1)).isFalse();
+ assertThat(mLocalService.isValidVirtualDeviceId(mDeviceImpl.getDeviceId() + 1)).isFalse();
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
index d70a4fd..72cc969 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
@@ -71,6 +71,7 @@
.setAuthAlwaysRequiredToDisableQuietMode(false)
.setDeleteAppWithParent(false)
.setAlwaysVisible(false)
+ .setCrossProfileContentSharingStrategy(0)
.build();
final UserProperties actualProps = new UserProperties(defaultProps);
actualProps.setShowInLauncher(14);
@@ -86,6 +87,7 @@
actualProps.setAuthAlwaysRequiredToDisableQuietMode(true);
actualProps.setDeleteAppWithParent(true);
actualProps.setAlwaysVisible(true);
+ actualProps.setCrossProfileContentSharingStrategy(1);
// Write the properties to xml.
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -199,6 +201,8 @@
copy::isMediaSharedWithParent, true);
assertEqualGetterOrThrows(orig::isCredentialShareableWithParent,
copy::isCredentialShareableWithParent, true);
+ assertEqualGetterOrThrows(orig::getCrossProfileContentSharingStrategy,
+ copy::getCrossProfileContentSharingStrategy, true);
}
/**
@@ -256,5 +260,7 @@
.isEqualTo(actual.isAuthAlwaysRequiredToDisableQuietMode());
assertThat(expected.getDeleteAppWithParent()).isEqualTo(actual.getDeleteAppWithParent());
assertThat(expected.getAlwaysVisible()).isEqualTo(actual.getAlwaysVisible());
+ assertThat(expected.getCrossProfileContentSharingStrategy())
+ .isEqualTo(actual.getCrossProfileContentSharingStrategy());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
index 77f6939..d0ad573 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
@@ -29,7 +29,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
@@ -96,7 +95,8 @@
.setShowInQuietMode(30)
.setInheritDevicePolicy(340)
.setDeleteAppWithParent(true)
- .setAlwaysVisible(true);
+ .setAlwaysVisible(true)
+ .setCrossProfileContentSharingStrategy(1);
final UserTypeDetails type = new UserTypeDetails.Builder()
.setName("a.name")
@@ -175,6 +175,8 @@
.getInheritDevicePolicy());
assertTrue(type.getDefaultUserPropertiesReference().getDeleteAppWithParent());
assertTrue(type.getDefaultUserPropertiesReference().getAlwaysVisible());
+ assertEquals(1, type.getDefaultUserPropertiesReference()
+ .getCrossProfileContentSharingStrategy());
assertEquals(23, type.getBadgeLabel(0));
assertEquals(24, type.getBadgeLabel(1));
@@ -231,6 +233,8 @@
assertEquals(UserProperties.SHOW_IN_LAUNCHER_SEPARATE, props.getShowInSharingSurfaces());
assertEquals(UserProperties.SHOW_IN_QUIET_MODE_PAUSED,
props.getShowInQuietMode());
+ assertEquals(UserProperties.CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION,
+ props.getCrossProfileContentSharingStrategy());
assertFalse(type.hasBadge());
}
@@ -323,7 +327,8 @@
.setShowInSharingSurfaces(22)
.setShowInQuietMode(24)
.setDeleteAppWithParent(true)
- .setAlwaysVisible(false);
+ .setAlwaysVisible(false)
+ .setCrossProfileContentSharingStrategy(1);
final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>();
builders.put(userTypeAosp1, new UserTypeDetails.Builder()
@@ -370,6 +375,8 @@
aospType.getDefaultUserPropertiesReference().getShowInQuietMode());
assertTrue(aospType.getDefaultUserPropertiesReference().getDeleteAppWithParent());
assertFalse(aospType.getDefaultUserPropertiesReference().getAlwaysVisible());
+ assertEquals(1, aospType.getDefaultUserPropertiesReference()
+ .getCrossProfileContentSharingStrategy());
// userTypeAosp2 should be modified.
aospType = builders.get(userTypeAosp2).createUserTypeDetails();
@@ -422,6 +429,8 @@
.getInheritDevicePolicy());
assertFalse(aospType.getDefaultUserPropertiesReference().getDeleteAppWithParent());
assertTrue(aospType.getDefaultUserPropertiesReference().getAlwaysVisible());
+ assertEquals(0, aospType.getDefaultUserPropertiesReference()
+ .getCrossProfileContentSharingStrategy());
// userTypeOem1 should be created.
UserTypeDetails.Builder customType = builders.get(userTypeOem1);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 8933c6c..ced0bb5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -23,7 +23,6 @@
import static org.junit.Assume.assumeTrue;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertThrows;
-import static org.testng.Assert.assertTrue;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
@@ -219,6 +218,8 @@
.isEqualTo(cloneUserProperties.isMediaSharedWithParent());
assertThat(typeProps.isCredentialShareableWithParent())
.isEqualTo(cloneUserProperties.isCredentialShareableWithParent());
+ assertThat(typeProps.getCrossProfileContentSharingStrategy())
+ .isEqualTo(cloneUserProperties.getCrossProfileContentSharingStrategy());
assertThrows(SecurityException.class, cloneUserProperties::getDeleteAppWithParent);
assertThrows(SecurityException.class, cloneUserProperties::getAlwaysVisible);
compareDrawables(mUserManager.getUserBadge(),
@@ -338,6 +339,8 @@
assertThat(typeProps.isAuthAlwaysRequiredToDisableQuietMode())
.isEqualTo(privateProfileUserProperties
.isAuthAlwaysRequiredToDisableQuietMode());
+ assertThat(typeProps.getCrossProfileContentSharingStrategy())
+ .isEqualTo(privateProfileUserProperties.getCrossProfileContentSharingStrategy());
assertThrows(SecurityException.class, privateProfileUserProperties::getDeleteAppWithParent);
compareDrawables(mUserManager.getUserBadge(),
Resources.getSystem().getDrawable(userTypeDetails.getBadgePlain()));
diff --git a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
index e418d2f..769ec5f 100644
--- a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
@@ -57,17 +57,22 @@
import android.net.Uri;
import android.os.Process;
import android.os.UserHandle;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.util.ArraySet;
import androidx.test.InstrumentationRegistry;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import java.util.Arrays;
import java.util.Set;
public class UriGrantsManagerServiceTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
private UriGrantsMockContext mContext;
private UriGrantsManagerInternal mService;
@@ -79,7 +84,7 @@
@Before
public void setUp() throws Exception {
- mContext = new UriGrantsMockContext(InstrumentationRegistry.getContext());
+ mContext = new UriGrantsMockContext();
mService = UriGrantsManagerService.createForTest(mContext.getFilesDir()).getLocalService();
}
diff --git a/services/tests/servicestests/src/com/android/server/uri/UriGrantsMockContext.java b/services/tests/servicestests/src/com/android/server/uri/UriGrantsMockContext.java
index 7eb6c97..4c11de09 100644
--- a/services/tests/servicestests/src/com/android/server/uri/UriGrantsMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/uri/UriGrantsMockContext.java
@@ -21,11 +21,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-import android.annotation.NonNull;
import android.app.ActivityManagerInternal;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.ContextWrapper;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -33,18 +29,19 @@
import android.content.pm.PathPermission;
import android.content.pm.ProviderInfo;
import android.net.Uri;
-import android.os.FileUtils;
import android.os.PatternMatcher;
import android.os.Process;
import android.os.UserHandle;
-import android.test.mock.MockContentResolver;
+import android.test.mock.MockContext;
import android.test.mock.MockPackageManager;
import com.android.server.LocalServices;
import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
-public class UriGrantsMockContext extends ContextWrapper {
+public class UriGrantsMockContext extends MockContext {
static final String TAG = "UriGrants";
static final int FLAG_READ = Intent.FLAG_GRANT_READ_URI_PERMISSION;
@@ -98,19 +95,14 @@
private final File mDir;
private final MockPackageManager mPackage;
- private final MockContentResolver mResolver;
final ActivityManagerInternal mAmInternal;
final PackageManagerInternal mPmInternal;
- public UriGrantsMockContext(@NonNull Context base) {
- super(base);
- mDir = new File(base.getFilesDir(), TAG);
- mDir.mkdirs();
- FileUtils.deleteContents(mDir);
+ public UriGrantsMockContext() throws IOException {
+ mDir = Files.createTempDirectory(TAG).toFile();
mPackage = new MockPackageManager();
- mResolver = new MockContentResolver(this);
mAmInternal = mock(ActivityManagerInternal.class);
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
@@ -239,11 +231,6 @@
}
@Override
- public ContentResolver getContentResolver() {
- return mResolver;
- }
-
- @Override
public File getFilesDir() {
return mDir;
}
diff --git a/services/tests/servicestests/src/com/android/server/uri/UriPermissionTest.java b/services/tests/servicestests/src/com/android/server/uri/UriPermissionTest.java
index 07005a9..4d4f5ed 100644
--- a/services/tests/servicestests/src/com/android/server/uri/UriPermissionTest.java
+++ b/services/tests/servicestests/src/com/android/server/uri/UriPermissionTest.java
@@ -35,12 +35,18 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import android.platform.test.ravenwood.RavenwoodRule;
+
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
public class UriPermissionTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
@Mock
private UriGrantsManagerInternal mService;
diff --git a/services/tests/servicestests/src/com/android/server/utils/OWNERS b/services/tests/servicestests/src/com/android/server/utils/OWNERS
index 5e24828..f5b19a1 100644
--- a/services/tests/servicestests/src/com/android/server/utils/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/utils/OWNERS
@@ -1,2 +1,5 @@
per-file EventLoggerTest.java = file:/platform/frameworks/av:/media/janitors/media_solutions_OWNERS
per-file EventLoggerTest.java = jmtrivi@google.com
+
+# Bug component : 158088 = per-file AnrTimer*.java
+per-file AnrTimer*.java = file:/PERFORMANCE_OWNERS
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
deleted file mode 100644
index 49efd1b..0000000
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.vibrator;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.RemoteException;
-
-import org.junit.Before;
-import org.junit.Test;
-
-public class VibratorControlServiceTest {
-
- private VibratorControlService mVibratorControlService;
- private final Object mLock = new Object();
-
- @Before
- public void setUp() throws Exception {
- mVibratorControlService = new VibratorControlService(new VibratorControllerHolder(), mLock);
- }
-
- @Test
- public void testRegisterVibratorController() throws RemoteException {
- FakeVibratorController fakeController = new FakeVibratorController();
- mVibratorControlService.registerVibratorController(fakeController);
-
- assertThat(fakeController.isLinkedToDeath).isTrue();
- }
-
- @Test
- public void testUnregisterVibratorController_providingTheRegisteredController_performsRequest()
- throws RemoteException {
- FakeVibratorController fakeController = new FakeVibratorController();
- mVibratorControlService.registerVibratorController(fakeController);
- mVibratorControlService.unregisterVibratorController(fakeController);
- assertThat(fakeController.isLinkedToDeath).isFalse();
- }
-
- @Test
- public void testUnregisterVibratorController_providingAnInvalidController_ignoresRequest()
- throws RemoteException {
- FakeVibratorController fakeController1 = new FakeVibratorController();
- FakeVibratorController fakeController2 = new FakeVibratorController();
- mVibratorControlService.registerVibratorController(fakeController1);
-
- mVibratorControlService.unregisterVibratorController(fakeController2);
- assertThat(fakeController1.isLinkedToDeath).isTrue();
- }
-}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java
deleted file mode 100644
index 79abe21..0000000
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.vibrator;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.RemoteException;
-
-import org.junit.Before;
-import org.junit.Test;
-
-public class VibratorControllerHolderTest {
-
- private final FakeVibratorController mFakeVibratorController = new FakeVibratorController();
- private VibratorControllerHolder mVibratorControllerHolder;
-
- @Before
- public void setUp() throws Exception {
- mVibratorControllerHolder = new VibratorControllerHolder();
- }
-
- @Test
- public void testSetVibratorController_linksVibratorControllerToDeath() throws RemoteException {
- mVibratorControllerHolder.setVibratorController(mFakeVibratorController);
- assertThat(mVibratorControllerHolder.getVibratorController())
- .isEqualTo(mFakeVibratorController);
- assertThat(mFakeVibratorController.isLinkedToDeath).isTrue();
- }
-
- @Test
- public void testSetVibratorController_setControllerToNull_unlinksVibratorControllerToDeath()
- throws RemoteException {
- mVibratorControllerHolder.setVibratorController(mFakeVibratorController);
- mVibratorControllerHolder.setVibratorController(null);
- assertThat(mFakeVibratorController.isLinkedToDeath).isFalse();
- assertThat(mVibratorControllerHolder.getVibratorController()).isNull();
- }
-
- @Test
- public void testBinderDied_withValidController_unlinksVibratorControllerToDeath()
- throws RemoteException {
- mVibratorControllerHolder.setVibratorController(mFakeVibratorController);
- mVibratorControllerHolder.binderDied(mFakeVibratorController);
- assertThat(mFakeVibratorController.isLinkedToDeath).isFalse();
- assertThat(mVibratorControllerHolder.getVibratorController()).isNull();
- }
-
- @Test
- public void testBinderDied_withInvalidController_ignoresRequest()
- throws RemoteException {
- mVibratorControllerHolder.setVibratorController(mFakeVibratorController);
- FakeVibratorController imposterVibratorController = new FakeVibratorController();
- mVibratorControllerHolder.binderDied(imposterVibratorController);
- assertThat(mFakeVibratorController.isLinkedToDeath).isTrue();
- assertThat(mVibratorControllerHolder.getVibratorController())
- .isEqualTo(mFakeVibratorController);
- }
-}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index a105649..4e9bbe0 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -307,10 +307,9 @@
@Override
void addService(String name, IBinder service) {
- if (service instanceof VibratorManagerService.ExternalVibratorService) {
- mExternalVibratorService =
- (VibratorManagerService.ExternalVibratorService) service;
- }
+ Object serviceInstance = service;
+ mExternalVibratorService =
+ (VibratorManagerService.ExternalVibratorService) serviceInstance;
}
HapticFeedbackVibrationProvider createHapticFeedbackVibrationProvider(
@@ -1399,6 +1398,17 @@
}
@Test
+ public void performHapticFeedback_usesServiceAsToken() throws Exception {
+ VibratorManagerService service = createSystemReadyService();
+
+ HalVibration vibration =
+ performHapticFeedbackAndWaitUntilFinished(
+ service, HapticFeedbackConstants.SCROLL_TICK, /* always= */ true);
+
+ assertTrue(vibration.callerToken == service);
+ }
+
+ @Test
public void vibrate_withIntensitySettings_appliesSettingsToScaleVibrations() throws Exception {
int defaultNotificationIntensity =
mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_NOTIFICATION);
diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java
deleted file mode 100644
index 7e23587..0000000
--- a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.vibrator;
-
-import android.annotation.NonNull;
-import android.frameworks.vibrator.IVibratorController;
-import android.os.IBinder;
-import android.os.RemoteException;
-
-/**
- * Provides a fake implementation of {@link android.frameworks.vibrator.IVibratorController} for
- * testing.
- */
-public final class FakeVibratorController extends IVibratorController.Stub {
-
- public boolean isLinkedToDeath = false;
-
- @Override
- public void requestVibrationParams(int i, long l, IBinder iBinder) throws RemoteException {
-
- }
-
- @Override
- public int getInterfaceVersion() throws RemoteException {
- return 0;
- }
-
- @Override
- public String getInterfaceHash() throws RemoteException {
- return null;
- }
-
- @Override
- public void linkToDeath(@NonNull DeathRecipient recipient, int flags) {
- super.linkToDeath(recipient, flags);
- isLinkedToDeath = true;
- }
-
- @Override
- public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags) {
- isLinkedToDeath = false;
- return super.unlinkToDeath(recipient, flags);
- }
-}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
index 8354d98..8ca4732 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
@@ -23,12 +23,16 @@
class AndroidHeuristicsFilter(
private val classes: ClassNodes,
val aidlPolicy: FilterPolicyWithReason?,
+ val featureFlagsPolicy: FilterPolicyWithReason?,
fallback: OutputFilter
) : DelegatingFilter(fallback) {
override fun getPolicyForClass(className: String): FilterPolicyWithReason {
if (aidlPolicy != null && classes.isAidlClass(className)) {
return aidlPolicy
}
+ if (featureFlagsPolicy != null && classes.isFeatureFlagsClass(className)) {
+ return featureFlagsPolicy
+ }
return super.getPolicyForClass(className)
}
}
@@ -40,4 +44,16 @@
return hasClass(className) &&
hasClass("$className\$Stub") &&
hasClass("$className\$Stub\$Proxy")
-}
\ No newline at end of file
+}
+
+/**
+ * @return if a given class "seems like" an feature flags class.
+ */
+private fun ClassNodes.isFeatureFlagsClass(className: String): Boolean {
+ // Matches template classes defined here:
+ // https://cs.android.com/android/platform/superproject/+/master:build/make/tools/aconfig/templates/
+ return className.endsWith("/Flags")
+ || className.endsWith("/FeatureFlags")
+ || className.endsWith("/FeatureFlagsImpl")
+ || className.endsWith("/FakeFeatureFlagsImpl");
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
index b4354ba..d38a6e3 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
@@ -62,7 +62,8 @@
var lineNo = 0
- var aidlPolicy: FilterPolicy? = null
+ var aidlPolicy: FilterPolicyWithReason? = null
+ var featureFlagsPolicy: FilterPolicyWithReason? = null
try {
BufferedReader(FileReader(filename)).use { reader ->
@@ -130,7 +131,15 @@
throw ParseException(
"Policy for AIDL classes already defined")
}
- aidlPolicy = policy
+ aidlPolicy = policy.withReason("$FILTER_REASON (AIDL)")
+ }
+ SpecialClass.FeatureFlags -> {
+ if (featureFlagsPolicy != null) {
+ throw ParseException(
+ "Policy for feature flags already defined")
+ }
+ featureFlagsPolicy =
+ policy.withReason("$FILTER_REASON (feature flags)")
}
}
}
@@ -196,10 +205,10 @@
}
var ret: OutputFilter = imf
- aidlPolicy?.let { policy ->
+ if (aidlPolicy != null || featureFlagsPolicy != null) {
log.d("AndroidHeuristicsFilter enabled")
ret = AndroidHeuristicsFilter(
- classes, policy.withReason("$FILTER_REASON (AIDL)"), imf)
+ classes, aidlPolicy, featureFlagsPolicy, imf)
}
return ret
}
@@ -208,6 +217,7 @@
private enum class SpecialClass {
NotSpecial,
Aidl,
+ FeatureFlags,
}
private fun resolveSpecialClass(className: String): SpecialClass {
@@ -216,6 +226,7 @@
}
when (className.lowercase()) {
":aidl" -> return SpecialClass.Aidl
+ ":feature_flags" -> return SpecialClass.FeatureFlags
}
throw ParseException("Invalid special class name \"$className\"")
}