Merge "In SkSL, replace 'sample' with '.eval'"
diff --git a/apex/blobstore/README.md b/apex/blobstore/README.md
new file mode 100644
index 0000000..69af436
--- /dev/null
+++ b/apex/blobstore/README.md
@@ -0,0 +1,125 @@
+<!--
+ 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
+-->
+
+# BlobStore Manager
+
+## Introduction
+* BlobStoreManager is a system service added in Android R release that facilitates sharing of
+ data blobs among apps.
+* Apps that would like to share data blobs with other apps can do so by contributing those
+ data blobs with the System and can choose how they would like the System to share the data blobs
+ with other apps.
+* Apps can access data blobs shared by other apps from the System using checksum of the data blobs
+ (plus some other data attributes. More details [below](#blob-handle)).
+* The APIs provided by the BlobStoreManager are meant to reduce storage and network usage by
+ reusing the data available on the device instead of downloading the same data again and having
+ multiple copies of the same data on disk.
+* It is not meant to provide access to the data which apps otherwise would not be able to access.
+ In other words, if an app’s only means of obtaining access to certain data is through
+ BlobStoreManager, then that use case is not really intended or supported.
+* For example, if earlier an app was downloading certain shared data from a server, then by using
+ BlobStoreManager, it can first check whether or not the data is already available on the device
+ before downloading.
+
+## Concepts
+### Blob handle
+Blob handle is the identifier of the data and it is what apps need to use for referring to the
+data blobs. Currently, this is made of following bits of information:
+* SHA256 checksum of data
+* Data label: A user readable string that indicates what the data blob is.
+ This is meant to be used when surfacing a list of blobs to the user.
+* Data expiry time: A timestamp after which the data blob should be considered invalid and not
+ allowed to be accessed by any app.
+* Data tag: An opaque string associated with the blob. System does not interpret this in any way or
+ use it for any purposes other than when checking whether two Blob handle identifiers are referring
+ to the same data blob. This is meant to be used by the apps, either for categorization for
+ data blobs or for adding additional identifiers. For example, an app can add tags like
+ *machine_learning* or *media* depending on the data blob if necessary.
+
+When comparing two Blob handles, the System will compare all the pieces of information above and
+only when two Blob handles are equal, the data blobs corresponding to those identifiers are
+considered equal.
+
+### Blob sharing session
+Session is a way to allow apps to contribute data over multiple time intervals. Each session is
+associated with a unique Identifier that is created and obtained by the apps by calling
+[BlobStoreManager#createSession](https://developer.android.com/reference/android/app/blob/BlobStoreManager#createSession(android.app.blob.BlobHandle)).
+Apps can save the Identifier associated with a session and use it to open and close it
+multiple times for contributing the data. For example, if an app is downloading
+some content over the network, it can start a Session and start contributing this data to the
+System immediately and if the network connection is lost for any reason, the app can close this
+session. When the download resumes, the app can reopen the session and start contributing again.
+Note that once the entire data is contributed, the app has no reason to hold on to the Session Id.
+
+### Blob commit
+Since a data blob can be contributed in a session over multiple time intervals, an app closing a
+session does not imply that the contribution is completed. So, *commit* is added as an explicit
+event / signal for the app to indicate that the contribution of the data blob is completed.
+At this point, the System can verify the data blob does indeed correspond to the Blob handle used
+by the app and prevent the app from making any further modifications to the data blob. Once the
+data blob is committed and verified by the System, it is available for other applications to access.
+
+### Access modes
+When an application contributes a data blob to the System, it can choose to specify how it would
+like the System to share this data blob with other applications. Access modes refer to the type of
+access that apps specified when contributing a data blob. As of Android S release, there are
+four access modes:
+* Allow specific packages: Apps can specify a specific set of applications that are allowed to
+ access their data blob.
+* Allow packages with the same signature: Apps can specify that only the applications that are
+ signed with the same certificate as them can access their data blob.
+* Allow public access: Apps can specify that any other app on the device can access their data blob.
+* Allow private access: Apps can specify that no other app can access their data blob unless they
+ happen to contribute the same data blob.
+ * Note that in this case, two apps might download the same data blob and contribute to the System
+ in which case we are not saving anything in terms of bandwidth usage, but we would still be
+ saving disk usage since we would be keeping only one copy of data on disk.
+
+### Lease
+Leasing a blob is a way to specify that an application is interested in using a data blob
+and would like the System to not delete this data blob. Applications can also access a blob
+without holding a lease on it, in which case the System can choose to delete the data blob at any
+time. So, if an application wants to make sure a data blob is available for access for a certain
+period, it is recommended that the application acquire a lease on the data blob. Applications can
+either specify upfront how long they would like to hold the lease for (which is called the lease
+expiry time), or they can acquire a lease without specifying a time period and release the lease
+when they are done with the data blob.
+
+## Sharing data blobs across users
+By default, data blobs are only accessible to applications in the user in which the data blob was
+contributed, but if an application holds the permission
+[ACCESS_BLOBS_ACROSS_USERS](https://developer.android.com/reference/android/Manifest.permission#ACCESS_BLOBS_ACROSS_USERS),
+then they are allowed to access blobs that are contributed by the applications in the other users.
+As of Android S, this permission is only available to following set of applications:
+* Apps signed with the platform certificate
+* Privileged applications
+* Applications holding the
+ [ASSISTANT](https://developer.android.com/reference/android/app/role/RoleManager#ROLE_ASSISTANT)
+ role
+* Development applications
+
+Note that the access modes that applications choose while committing the data blobs still apply
+when these data blobs are accessed across users. So for example, if *appA* contributed a
+data blob in *user0* and specified to share this data blob with only a specific set of
+applications [*appB*, *appC*], then *appD* on *user10* will not be able to access this data blob
+even if the app is granted the `ACCESS_BLOBS_ACROSS_USERS` permission.
+
+When apps that are allowed to access blobs across users
+(i.e. those holding the permission `ACCESS_BLOBS_ACROSS_USERS`) try to access a data blob,
+they can do so as if it is any other data blob. In other words, the applications don’t need to
+know where the data blob is contributed, because the System will automatically check and will
+allow access if this data blob is available either on the user in which the calling application
+is running in or other users.
\ No newline at end of file
diff --git a/apex/jobscheduler/service/java/com/android/server/AnyMotionDetector.java b/apex/jobscheduler/service/java/com/android/server/AnyMotionDetector.java
index 316306d..5ab4207 100644
--- a/apex/jobscheduler/service/java/com/android/server/AnyMotionDetector.java
+++ b/apex/jobscheduler/service/java/com/android/server/AnyMotionDetector.java
@@ -16,6 +16,7 @@
package com.android.server;
+import android.annotation.Nullable;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
@@ -26,6 +27,8 @@
import android.os.SystemClock;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
+
/**
* Determines if the device has been set upon a stationary object.
*/
@@ -54,6 +57,7 @@
private static final int STATE_ACTIVE = 1;
/** Current measurement state. */
+ @GuardedBy("mLock")
private int mState;
/** Threshold energy above which the device is considered moving. */
@@ -82,38 +86,47 @@
private final Handler mHandler;
private final Object mLock = new Object();
- private Sensor mAccelSensor;
- private SensorManager mSensorManager;
- private PowerManager.WakeLock mWakeLock;
+ private final Sensor mAccelSensor;
+ private final SensorManager mSensorManager;
+ private final PowerManager.WakeLock mWakeLock;
/** Threshold angle in degrees beyond which the device is considered moving. */
private final float mThresholdAngle;
/** The minimum number of samples required to detect AnyMotion. */
+ @GuardedBy("mLock")
private int mNumSufficientSamples;
/** True if an orientation measurement is in progress. */
+ @GuardedBy("mLock")
private boolean mMeasurementInProgress;
/** True if sendMessageDelayed() for the mMeasurementTimeout callback has been scheduled */
+ @GuardedBy("mLock")
private boolean mMeasurementTimeoutIsActive;
/** True if sendMessageDelayed() for the mWakelockTimeout callback has been scheduled */
- private boolean mWakelockTimeoutIsActive;
+ private volatile boolean mWakelockTimeoutIsActive;
/** True if sendMessageDelayed() for the mSensorRestart callback has been scheduled */
+ @GuardedBy("mLock")
private boolean mSensorRestartIsActive;
/** The most recent gravity vector. */
+ @Nullable
+ @GuardedBy("mLock")
private Vector3 mCurrentGravityVector = null;
/** The second most recent gravity vector. */
+ @Nullable
+ @GuardedBy("mLock")
private Vector3 mPreviousGravityVector = null;
/** Running sum of squared errors. */
- private RunningSignalStats mRunningStats;
+ @GuardedBy("mLock")
+ private final RunningSignalStats mRunningStats;
- private DeviceIdleCallback mCallback = null;
+ private final DeviceIdleCallback mCallback;
public AnyMotionDetector(PowerManager pm, Handler handler, SensorManager sm,
DeviceIdleCallback callback, float thresholdAngle) {
@@ -149,11 +162,11 @@
* Acquire accel data until we determine AnyMotion status.
*/
public void checkForAnyMotion() {
- if (DEBUG) {
- Slog.d(TAG, "checkForAnyMotion(). mState = " + mState);
- }
- if (mState != STATE_ACTIVE) {
- synchronized (mLock) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "checkForAnyMotion(). mState = " + mState);
+ }
+ if (mState != STATE_ACTIVE) {
mState = STATE_ACTIVE;
if (DEBUG) {
Slog.d(TAG, "Moved from STATE_INACTIVE to STATE_ACTIVE.");
@@ -193,6 +206,7 @@
}
}
+ @GuardedBy("mLock")
private void startOrientationMeasurementLocked() {
if (DEBUG) Slog.d(TAG, "startOrientationMeasurementLocked: mMeasurementInProgress=" +
mMeasurementInProgress + ", (mAccelSensor != null)=" + (mAccelSensor != null));
@@ -208,6 +222,7 @@
}
}
+ @GuardedBy("mLock")
private int stopOrientationMeasurementLocked() {
if (DEBUG) Slog.d(TAG, "stopOrientationMeasurement. mMeasurementInProgress=" +
mMeasurementInProgress);
@@ -231,7 +246,7 @@
Slog.d(TAG, "mCurrentGravityVector = " + currentGravityVectorString);
Slog.d(TAG, "mPreviousGravityVector = " + previousGravityVectorString);
}
- status = getStationaryStatus();
+ status = getStationaryStatusLocked();
mRunningStats.reset();
if (DEBUG) Slog.d(TAG, "getStationaryStatus() returned " + status);
if (status != RESULT_UNKNOWN) {
@@ -261,9 +276,10 @@
}
/*
- * Updates mStatus to the current AnyMotion status.
+ * Returns the current AnyMotion status.
*/
- public int getStationaryStatus() {
+ @GuardedBy("mLock")
+ private int getStationaryStatusLocked() {
if ((mPreviousGravityVector == null) || (mCurrentGravityVector == null)) {
return RESULT_UNKNOWN;
}
@@ -341,13 +357,13 @@
"data within " + ACCELEROMETER_DATA_TIMEOUT_MILLIS + " ms. Stopping " +
"orientation measurement.");
status = stopOrientationMeasurementLocked();
- if (status != RESULT_UNKNOWN) {
- mHandler.removeCallbacks(mWakelockTimeout);
- mWakelockTimeoutIsActive = false;
- mCallback.onAnyMotionResult(status);
- }
}
}
+ if (status != RESULT_UNKNOWN) {
+ mHandler.removeCallbacks(mWakelockTimeout);
+ mWakelockTimeoutIsActive = false;
+ mCallback.onAnyMotionResult(status);
+ }
}
};
@@ -488,9 +504,10 @@
}
}
+ @Nullable
public Vector3 getRunningAverage() {
if (sampleCount > 0) {
- return runningSum.times((float)(1.0f / sampleCount));
+ return runningSum.times(1.0f / sampleCount);
}
return null;
}
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 19aed49..0116364 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -298,27 +298,45 @@
private Intent mLightIdleIntent;
private AnyMotionDetector mAnyMotionDetector;
private final AppStateTrackerImpl mAppStateTracker;
+ @GuardedBy("this")
private boolean mLightEnabled;
+ @GuardedBy("this")
private boolean mDeepEnabled;
+ @GuardedBy("this")
private boolean mQuickDozeActivated;
+ @GuardedBy("this")
private boolean mQuickDozeActivatedWhileIdling;
+ @GuardedBy("this")
private boolean mForceIdle;
+ @GuardedBy("this")
private boolean mNetworkConnected;
+ @GuardedBy("this")
private boolean mScreenOn;
+ @GuardedBy("this")
private boolean mCharging;
+ @GuardedBy("this")
private boolean mNotMoving;
+ @GuardedBy("this")
private boolean mLocating;
+ @GuardedBy("this")
private boolean mLocated;
+ @GuardedBy("this")
private boolean mHasGps;
+ @GuardedBy("this")
private boolean mHasNetworkLocation;
+ @GuardedBy("this")
private Location mLastGenericLocation;
+ @GuardedBy("this")
private Location mLastGpsLocation;
/** Time in the elapsed realtime timebase when this listener last received a motion event. */
+ @GuardedBy("this")
private long mLastMotionEventElapsed;
// Current locked state of the screen
+ @GuardedBy("this")
private boolean mScreenLocked;
+ @GuardedBy("this")
private int mNumBlockingConstraints = 0;
/**
@@ -434,19 +452,30 @@
}
}
+ @GuardedBy("this")
private int mState;
+ @GuardedBy("this")
private int mLightState;
+ @GuardedBy("this")
private long mInactiveTimeout;
+ @GuardedBy("this")
private long mNextAlarmTime;
+ @GuardedBy("this")
private long mNextIdlePendingDelay;
+ @GuardedBy("this")
private long mNextIdleDelay;
+ @GuardedBy("this")
private long mNextLightIdleDelay;
+ @GuardedBy("this")
private long mNextLightIdleDelayFlex;
+ @GuardedBy("this")
private long mNextLightAlarmTime;
+ @GuardedBy("this")
private long mNextSensingTimeoutAlarmTime;
/** How long a light idle maintenance window should last. */
+ @GuardedBy("this")
private long mCurLightIdleBudget;
/**
@@ -454,15 +483,20 @@
* only if {@link #mState} == {@link #STATE_IDLE_MAINTENANCE} or
* {@link #mLightState} == {@link #LIGHT_STATE_IDLE_MAINTENANCE}.
*/
+ @GuardedBy("this")
private long mMaintenanceStartTime;
+ @GuardedBy("this")
private long mIdleStartTime;
+ @GuardedBy("this")
private int mActiveIdleOpCount;
private PowerManager.WakeLock mActiveIdleWakeLock; // held when there are operations in progress
private PowerManager.WakeLock mGoingIdleWakeLock; // held when we are going idle so hardware
// (especially NetworkPolicyManager) can shut
// down.
+ @GuardedBy("this")
private boolean mJobsActive;
+ @GuardedBy("this")
private boolean mAlarmsActive;
/* Factor to apply to INACTIVE_TIMEOUT and IDLE_AFTER_INACTIVE_TIMEOUT in order to enter
@@ -472,8 +506,11 @@
* Also don't apply the factor if the device is in motion because device motion provides a
* stronger signal than a prediction algorithm.
*/
+ @GuardedBy("this")
private float mPreIdleFactor;
+ @GuardedBy("this")
private float mLastPreIdleFactor;
+ @GuardedBy("this")
private int mActiveReason;
public final AtomicFile mConfigFile;
@@ -659,8 +696,8 @@
= new AlarmManager.OnAlarmListener() {
@Override
public void onAlarm() {
- if (mState == STATE_SENSING) {
- synchronized (DeviceIdleController.this) {
+ synchronized (DeviceIdleController.this) {
+ if (mState == STATE_SENSING) {
// Restart the device idle progression in case the device moved but the screen
// didn't turn on.
becomeInactiveIfAppropriateLocked();
@@ -714,6 +751,7 @@
mHandler.sendEmptyMessage(MSG_REPORT_STATIONARY_STATUS);
}
+ @GuardedBy("this")
private boolean isStationaryLocked() {
final long now = mInjector.getElapsedRealtime();
return mMotionListener.active
@@ -1592,27 +1630,21 @@
@Override
public void onAnyMotionResult(int result) {
if (DEBUG) Slog.d(TAG, "onAnyMotionResult(" + result + ")");
- if (result != AnyMotionDetector.RESULT_UNKNOWN) {
- synchronized (this) {
+ synchronized (this) {
+ if (result != AnyMotionDetector.RESULT_UNKNOWN) {
cancelSensingTimeoutAlarmLocked();
}
- }
- if ((result == AnyMotionDetector.RESULT_MOVED) ||
- (result == AnyMotionDetector.RESULT_UNKNOWN)) {
- synchronized (this) {
+ if ((result == AnyMotionDetector.RESULT_MOVED)
+ || (result == AnyMotionDetector.RESULT_UNKNOWN)) {
handleMotionDetectedLocked(mConstants.INACTIVE_TIMEOUT, "non_stationary");
- }
- } else if (result == AnyMotionDetector.RESULT_STATIONARY) {
- if (mState == STATE_SENSING) {
- // If we are currently sensing, it is time to move to locating.
- synchronized (this) {
+ } else if (result == AnyMotionDetector.RESULT_STATIONARY) {
+ if (mState == STATE_SENSING) {
+ // If we are currently sensing, it is time to move to locating.
mNotMoving = true;
stepIdleStateLocked("s:stationary");
- }
- } else if (mState == STATE_LOCATING) {
- // If we are currently locating, note that we are not moving and step
- // if we have located the position.
- synchronized (this) {
+ } else if (mState == STATE_LOCATING) {
+ // If we are currently locating, note that we are not moving and step
+ // if we have located the position.
mNotMoving = true;
if (mLocated) {
stepIdleStateLocked("s:stationary");
@@ -3062,6 +3094,7 @@
}
}
+ @GuardedBy("this")
void updateInteractivityLocked() {
// The interactivity state from the power manager tells us whether the display is
// in a state that we need to keep things running so they will update at a normal
@@ -3089,6 +3122,7 @@
}
}
+ @GuardedBy("this")
void updateChargingLocked(boolean charging) {
if (DEBUG) Slog.i(TAG, "updateChargingLocked: charging=" + charging);
if (!charging && mCharging) {
@@ -3114,6 +3148,7 @@
/** Updates the quick doze flag and enters deep doze if appropriate. */
@VisibleForTesting
+ @GuardedBy("this")
void updateQuickDozeFlagLocked(boolean enabled) {
if (DEBUG) Slog.i(TAG, "updateQuickDozeFlagLocked: enabled=" + enabled);
mQuickDozeActivated = enabled;
@@ -3138,6 +3173,7 @@
}
@VisibleForTesting
+ @GuardedBy("this")
void keyguardShowingLocked(boolean showing) {
if (DEBUG) Slog.i(TAG, "keyguardShowing=" + showing);
if (mScreenLocked != showing) {
@@ -3150,15 +3186,18 @@
}
@VisibleForTesting
+ @GuardedBy("this")
void scheduleReportActiveLocked(String activeReason, int activeUid) {
Message msg = mHandler.obtainMessage(MSG_REPORT_ACTIVE, activeUid, 0, activeReason);
mHandler.sendMessage(msg);
}
+ @GuardedBy("this")
void becomeActiveLocked(String activeReason, int activeUid) {
becomeActiveLocked(activeReason, activeUid, mConstants.INACTIVE_TIMEOUT, true);
}
+ @GuardedBy("this")
private void becomeActiveLocked(String activeReason, int activeUid,
long newInactiveTimeout, boolean changeLightIdle) {
if (DEBUG) {
@@ -3204,6 +3243,7 @@
}
/** Sanity check to make sure DeviceIdleController and AlarmManager are on the same page. */
+ @GuardedBy("this")
private void verifyAlarmStateLocked() {
if (mState == STATE_ACTIVE && mNextAlarmTime != 0) {
Slog.wtf(TAG, "mState=ACTIVE but mNextAlarmTime=" + mNextAlarmTime);
@@ -3221,6 +3261,7 @@
}
}
+ @GuardedBy("this")
void becomeInactiveIfAppropriateLocked() {
verifyAlarmStateLocked();
@@ -3300,6 +3341,7 @@
}
}
+ @GuardedBy("this")
private void resetIdleManagementLocked() {
mNextIdlePendingDelay = 0;
mNextIdleDelay = 0;
@@ -3313,6 +3355,7 @@
updateActiveConstraintsLocked();
}
+ @GuardedBy("this")
private void resetLightIdleManagementLocked() {
mNextLightIdleDelay = 0;
mNextLightIdleDelayFlex = 0;
@@ -3320,6 +3363,7 @@
cancelLightAlarmLocked();
}
+ @GuardedBy("this")
void exitForceIdleLocked() {
if (mForceIdle) {
mForceIdle = false;
@@ -3344,9 +3388,12 @@
@VisibleForTesting
int getLightState() {
- return mLightState;
+ synchronized (this) {
+ return mLightState;
+ }
}
+ @GuardedBy("this")
void stepLightIdleStateLocked(String reason) {
if (mLightState == LIGHT_STATE_OVERRIDE) {
// If we are already in deep device idle mode, then
@@ -3435,7 +3482,9 @@
@VisibleForTesting
int getState() {
- return mState;
+ synchronized (this) {
+ return mState;
+ }
}
/**
@@ -3448,6 +3497,7 @@
}
@VisibleForTesting
+ @GuardedBy("this")
void stepIdleStateLocked(String reason) {
if (DEBUG) Slog.d(TAG, "stepIdleStateLocked: mState=" + mState);
EventLogTags.writeDeviceIdleStep();
@@ -3588,6 +3638,7 @@
}
}
+ @GuardedBy("this")
private void moveToStateLocked(int state, String reason) {
final int oldState = mState;
mState = state;
@@ -3667,22 +3718,26 @@
@VisibleForTesting
float getPreIdleTimeoutFactor() {
- return mPreIdleFactor;
+ synchronized (this) {
+ return mPreIdleFactor;
+ }
}
@VisibleForTesting
int setPreIdleTimeoutFactor(float ratio) {
- if (!mDeepEnabled) {
- if (DEBUG) Slog.d(TAG, "setPreIdleTimeoutFactor: Deep Idle disable");
- return SET_IDLE_FACTOR_RESULT_NOT_SUPPORT;
- } else if (ratio <= MIN_PRE_IDLE_FACTOR_CHANGE) {
- if (DEBUG) Slog.d(TAG, "setPreIdleTimeoutFactor: Invalid input");
- return SET_IDLE_FACTOR_RESULT_INVALID;
- } else if (Math.abs(ratio - mPreIdleFactor) < MIN_PRE_IDLE_FACTOR_CHANGE) {
- if (DEBUG) Slog.d(TAG, "setPreIdleTimeoutFactor: New factor same as previous factor");
- return SET_IDLE_FACTOR_RESULT_IGNORED;
- }
synchronized (this) {
+ if (!mDeepEnabled) {
+ if (DEBUG) Slog.d(TAG, "setPreIdleTimeoutFactor: Deep Idle disable");
+ return SET_IDLE_FACTOR_RESULT_NOT_SUPPORT;
+ } else if (ratio <= MIN_PRE_IDLE_FACTOR_CHANGE) {
+ if (DEBUG) Slog.d(TAG, "setPreIdleTimeoutFactor: Invalid input");
+ return SET_IDLE_FACTOR_RESULT_INVALID;
+ } else if (Math.abs(ratio - mPreIdleFactor) < MIN_PRE_IDLE_FACTOR_CHANGE) {
+ if (DEBUG) {
+ Slog.d(TAG, "setPreIdleTimeoutFactor: New factor same as previous factor");
+ }
+ return SET_IDLE_FACTOR_RESULT_IGNORED;
+ }
mLastPreIdleFactor = mPreIdleFactor;
mPreIdleFactor = ratio;
}
@@ -3744,6 +3799,7 @@
}
}
+ @GuardedBy("this")
private boolean shouldUseIdleTimeoutFactorLocked() {
// exclude ACTIVE_REASON_MOTION, for exclude device in pocket case
if (mActiveReason == ACTIVE_REASON_MOTION) {
@@ -3763,13 +3819,17 @@
@VisibleForTesting
long getNextAlarmTime() {
- return mNextAlarmTime;
+ synchronized (this) {
+ return mNextAlarmTime;
+ }
}
+ @GuardedBy("this")
boolean isOpsInactiveLocked() {
return mActiveIdleOpCount <= 0 && !mJobsActive && !mAlarmsActive;
}
+ @GuardedBy("this")
void exitMaintenanceEarlyIfNeededLocked() {
if (mState == STATE_IDLE_MAINTENANCE || mLightState == LIGHT_STATE_IDLE_MAINTENANCE
|| mLightState == LIGHT_STATE_PRE_IDLE) {
@@ -3794,12 +3854,14 @@
}
}
+ @GuardedBy("this")
void motionLocked() {
if (DEBUG) Slog.d(TAG, "motionLocked()");
mLastMotionEventElapsed = mInjector.getElapsedRealtime();
handleMotionDetectedLocked(mConstants.MOTION_INACTIVE_TIMEOUT, "motion");
}
+ @GuardedBy("this")
void handleMotionDetectedLocked(long timeout, String type) {
if (mStationaryListeners.size() > 0) {
postStationaryStatusUpdated();
@@ -3829,6 +3891,7 @@
}
}
+ @GuardedBy("this")
void receivedGenericLocationLocked(Location location) {
if (mState != STATE_LOCATING) {
cancelLocatingLocked();
@@ -3845,6 +3908,7 @@
}
}
+ @GuardedBy("this")
void receivedGpsLocationLocked(Location location) {
if (mState != STATE_LOCATING) {
cancelLocatingLocked();
@@ -3883,6 +3947,7 @@
}
}
+ @GuardedBy("this")
void cancelAlarmLocked() {
if (mNextAlarmTime != 0) {
mNextAlarmTime = 0;
@@ -3890,6 +3955,7 @@
}
}
+ @GuardedBy("this")
void cancelLightAlarmLocked() {
if (mNextLightAlarmTime != 0) {
mNextLightAlarmTime = 0;
@@ -3897,6 +3963,7 @@
}
}
+ @GuardedBy("this")
void cancelLocatingLocked() {
if (mLocating) {
LocationManager locationManager = mInjector.getLocationManager();
@@ -3914,6 +3981,7 @@
mAlarmManager.cancel(mMotionRegistrationAlarmListener);
}
+ @GuardedBy("this")
void cancelSensingTimeoutAlarmLocked() {
if (mNextSensingTimeoutAlarmTime != 0) {
mNextSensingTimeoutAlarmTime = 0;
@@ -3921,6 +3989,7 @@
}
}
+ @GuardedBy("this")
void scheduleAlarmLocked(long delay, boolean idleUntil) {
if (DEBUG) Slog.d(TAG, "scheduleAlarmLocked(" + delay + ", " + idleUntil + ")");
@@ -3957,6 +4026,7 @@
}
}
+ @GuardedBy("this")
void scheduleLightAlarmLocked(long delay, long flex) {
if (DEBUG) {
Slog.d(TAG, "scheduleLightAlarmLocked(" + delay
@@ -4003,6 +4073,7 @@
}
}
+ @GuardedBy("this")
void scheduleSensingTimeoutAlarmLocked(long delay) {
if (DEBUG) Slog.d(TAG, "scheduleSensingAlarmLocked(" + delay + ")");
mNextSensingTimeoutAlarmTime = SystemClock.elapsedRealtime() + delay;
@@ -4071,6 +4142,7 @@
* @param callingUid the callingUid that setup this temp-allowlist, only valid when param adding
* is true.
*/
+ @GuardedBy("this")
private void updateTempWhitelistAppIdsLocked(int uid, boolean adding, long durationMs,
@TempAllowListType int type, @ReasonCode int reasonCode, @Nullable String reason,
int callingUid) {
@@ -4120,6 +4192,7 @@
mTempWhitelistAppIdArray);
}
+ @GuardedBy("this")
void readConfigFileLocked() {
if (DEBUG) Slog.d(TAG, "Reading config from " + mConfigFile.getBaseFile());
mPowerSaveWhitelistUserApps.clear();
@@ -4142,6 +4215,7 @@
}
}
+ @GuardedBy("this")
private void readConfigFileLocked(XmlPullParser parser) {
final PackageManager pm = getContext().getPackageManager();
@@ -4987,7 +5061,7 @@
pw.println();
}
if (mNextLightIdleDelay != 0) {
- pw.print(" mNextIdleDelay=");
+ pw.print(" mNextLightIdleDelay=");
TimeUtils.formatDuration(mNextLightIdleDelay, pw);
if (mConstants.USE_WINDOW_ALARMS) {
pw.print(" (flex=");
diff --git a/apex/media/framework/java/android/media/MediaCommunicationManager.java b/apex/media/framework/java/android/media/MediaCommunicationManager.java
index b2528cd..0f00dbb 100644
--- a/apex/media/framework/java/android/media/MediaCommunicationManager.java
+++ b/apex/media/framework/java/android/media/MediaCommunicationManager.java
@@ -74,6 +74,9 @@
@GuardedBy("mLock")
private MediaCommunicationServiceCallbackStub mCallbackStub;
+ // TODO: remove this when MCS implements dispatchMediaKeyEvent.
+ private MediaSessionManager mMediaSessionManager;
+
/**
* @hide
*/
@@ -222,6 +225,14 @@
return mService;
}
+ // TODO: remove this when MCS implements dispatchMediaKeyEvent.
+ private MediaSessionManager getMediaSessionManager() {
+ if (mMediaSessionManager == null) {
+ mMediaSessionManager = mContext.getSystemService(MediaSessionManager.class);
+ }
+ return mMediaSessionManager;
+ }
+
private List<Session2Token> getSession2Tokens(int userId) {
try {
MediaParceledListSlice slice = getService().getSession2Tokens(userId);
@@ -243,6 +254,14 @@
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public void dispatchMediaKeyEvent(@NonNull KeyEvent keyEvent, boolean asSystemService) {
Objects.requireNonNull(keyEvent, "keyEvent shouldn't be null");
+
+ // When MCS handles this, caller is changed.
+ // TODO: remove this when MCS implementation is done.
+ if (!asSystemService) {
+ getMediaSessionManager().dispatchMediaKeyEvent(keyEvent, false);
+ return;
+ }
+
try {
getService().dispatchMediaKeyEvent(mContext.getPackageName(),
keyEvent, asSystemService);
diff --git a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
index 03a2372..09f65b5 100644
--- a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
+++ b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
@@ -395,11 +395,7 @@
final long token = Binder.clearCallingIdentity();
try {
//TODO: Dispatch key event to media session 2 if required
- if (asSystemService) {
- mSessionManager.dispatchMediaKeyEventAsSystemService(keyEvent);
- } else {
- mSessionManager.dispatchMediaKeyEvent(keyEvent, false);
- }
+ mSessionManager.dispatchMediaKeyEvent(keyEvent, asSystemService);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index b8ec6fc..21ba183 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -425,6 +425,7 @@
public class DevicePolicyManager {
method public int checkProvisioningPreCondition(@Nullable String, @NonNull String);
+ method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void clearOrganizationId();
method @RequiresPermission(android.Manifest.permission.CLEAR_FREEZE_PERIOD) public void clearSystemUpdatePolicyFreezePeriodRecord();
method @Nullable public android.os.UserHandle createAndProvisionManagedProfile(@NonNull android.app.admin.ManagedProfileProvisioningParams) throws android.app.admin.ProvisioningException;
method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceNetworkLogs();
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 7102314..7a043a8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -13754,6 +13754,23 @@
}
/**
+ * Clears organization ID set by the DPC and resets the precomputed enrollment specific ID.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
+ public void clearOrganizationId() {
+ if (mService == null) {
+ return;
+ }
+ try {
+ mService.clearOrganizationIdForUser(myUserId());
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Creates and provisions a managed profile and sets the
* {@link ManagedProfileProvisioningParams#getProfileAdminComponentName()} as the profile
* owner.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 4e4c2219..fc49f79 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -375,6 +375,7 @@
void setOrganizationColor(in ComponentName admin, in int color);
void setOrganizationColorForUser(in int color, in int userId);
+ void clearOrganizationIdForUser(int userHandle);
int getOrganizationColor(in ComponentName admin);
int getOrganizationColorForUser(int userHandle);
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 865940e..01e237d 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -153,7 +153,8 @@
int index, byte[] value);
private static native void nativeResetStatementAndClearBindings(
long connectionPtr, long statementPtr);
- private static native void nativeExecute(long connectionPtr, long statementPtr);
+ private static native void nativeExecute(long connectionPtr, long statementPtr,
+ boolean isPragmaStmt);
private static native long nativeExecuteForLong(long connectionPtr, long statementPtr);
private static native String nativeExecuteForString(long connectionPtr, long statementPtr);
private static native int nativeExecuteForBlobFileDescriptor(
@@ -699,6 +700,8 @@
final int cookie = mRecentOperations.beginOperation("execute", sql, bindArgs);
try {
+ final boolean isPragmaStmt =
+ DatabaseUtils.getSqlStatementType(sql) == DatabaseUtils.STATEMENT_PRAGMA;
final PreparedStatement statement = acquirePreparedStatement(sql);
try {
throwIfStatementForbidden(statement);
@@ -706,7 +709,7 @@
applyBlockGuardPolicy(statement);
attachCancellationSignal(cancellationSignal);
try {
- nativeExecute(mConnectionPtr, statement.mStatementPtr);
+ nativeExecute(mConnectionPtr, statement.mStatementPtr, isPragmaStmt);
} finally {
detachCancellationSignal(cancellationSignal);
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 7c42dc0..d48d562 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -850,26 +850,6 @@
}
/**
- * Checks if the specified user has enrollments in any of the specified sensors.
- * @hide
- */
- @RequiresPermission(USE_BIOMETRIC_INTERNAL)
- public boolean hasEnrolledTemplatesForAnySensor(int userId,
- @NonNull List<FingerprintSensorPropertiesInternal> sensors) {
- if (mService == null) {
- Slog.w(TAG, "hasEnrolledTemplatesForAnySensor: no fingerprint service");
- return false;
- }
-
- try {
- return mService.hasEnrolledTemplatesForAnySensor(userId, sensors,
- mContext.getOpPackageName());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
diff --git a/core/java/android/hardware/fingerprint/FingerprintStateListener.java b/core/java/android/hardware/fingerprint/FingerprintStateListener.java
index 6e607a2..cf914c5 100644
--- a/core/java/android/hardware/fingerprint/FingerprintStateListener.java
+++ b/core/java/android/hardware/fingerprint/FingerprintStateListener.java
@@ -49,5 +49,10 @@
* Defines behavior in response to state update
* @param newState new state of fingerprint sensor
*/
- public abstract void onStateChanged(@FingerprintStateListener.State int newState);
+ public void onStateChanged(@FingerprintStateListener.State int newState) {};
+
+ /**
+ * Invoked when enrollment state changes for the specified user
+ */
+ public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {};
}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 4774827..de94b2f 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -120,9 +120,6 @@
// Determine if a user has at least one enrolled fingerprint.
boolean hasEnrolledFingerprints(int sensorId, int userId, String opPackageName);
- // Determine if a user has at least one enrolled fingerprint in any of the specified sensors
- boolean hasEnrolledTemplatesForAnySensor(int userId, in List<FingerprintSensorPropertiesInternal> sensors, String opPackageName);
-
// Return the LockoutTracker status for the specified user
int getLockoutModeForUser(int sensorId, int userId);
diff --git a/core/java/android/hardware/fingerprint/IFingerprintStateListener.aidl b/core/java/android/hardware/fingerprint/IFingerprintStateListener.aidl
index 56dba7e..1aa6fa1 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintStateListener.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintStateListener.aidl
@@ -24,4 +24,5 @@
*/
oneway interface IFingerprintStateListener {
void onStateChanged(int newState);
+ void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments);
}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index f132ac5..2bb0ec7 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -304,6 +304,8 @@
@CriticalNative
private static native void nativeMarkSensitive(long nativePtr);
+ @FastNative
+ private static native void nativeMarkForBinder(long nativePtr, IBinder binder);
@CriticalNative
private static native int nativeDataSize(long nativePtr);
@CriticalNative
@@ -528,6 +530,16 @@
/**
* Parcel data should be zero'd before realloc'd or deleted.
+ *
+ * Note: currently this feature requires multiple things to work in concert:
+ * - markSensitive must be called on every relative Parcel
+ * - FLAG_CLEAR_BUF must be passed into the kernel
+ * This requires having code which does the right thing in every method and in every backend
+ * of AIDL. Rather than exposing this API, it should be replaced with a single API on
+ * IBinder objects which can be called once, and the information should be fed into the
+ * Parcel using markForBinder APIs. In terms of code size and number of API calls, this is
+ * much more extensible.
+ *
* @hide
*/
public final void markSensitive() {
@@ -535,9 +547,23 @@
}
/**
+ * Associate this parcel with a binder object. This marks the parcel as being prepared for a
+ * transaction on this specific binder object. Based on this, the format of the wire binder
+ * protocol may change. This should be called before any data is written to the parcel. If this
+ * is called multiple times, this will only be marked for the last binder. For future
+ * compatibility, it is recommended to call this on all parcels which are being sent over
+ * binder.
+ *
+ * @hide
+ */
+ public void markForBinder(@NonNull IBinder binder) {
+ nativeMarkForBinder(mNativePtr, binder);
+ }
+
+ /**
* Returns the total amount of data contained in the parcel.
*/
- public final int dataSize() {
+ public int dataSize() {
return nativeDataSize(mNativePtr);
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9e2b6e0..0dd6e22 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -4031,6 +4031,10 @@
mBlastBufferQueue.setNextTransaction(null);
mBlastBufferQueue.setTransactionCompleteCallback(mRtLastAttemptedDrawFrameNum,
null);
+ // Apply the transactions that were sent to mergeWithNextTransaction since the
+ // frame didn't draw on this vsync. It's possible the frame will draw later, but
+ // it's better to not be sync than to block on a frame that may never come.
+ mBlastBufferQueue.applyPendingTransactions(mRtLastAttemptedDrawFrameNum);
}
mHandler.postAtFrontOfQueue(() -> {
@@ -4086,18 +4090,16 @@
private void addFrameCallbackIfNeeded() {
final boolean nextDrawUseBlastSync = mNextDrawUseBlastSync;
- final boolean reportNextDraw = mReportNextDraw;
final boolean hasBlurUpdates = mBlurRegionAggregator.hasUpdates();
final boolean needsCallbackForBlur = hasBlurUpdates || mBlurRegionAggregator.hasRegions();
- if (!nextDrawUseBlastSync && !reportNextDraw && !needsCallbackForBlur) {
+ if (!nextDrawUseBlastSync && !needsCallbackForBlur) {
return;
}
if (DEBUG_BLAST) {
Log.d(mTag, "Creating frameDrawingCallback"
+ " nextDrawUseBlastSync=" + nextDrawUseBlastSync
- + " reportNextDraw=" + reportNextDraw
+ " hasBlurUpdates=" + hasBlurUpdates);
}
mWaitForBlastSyncComplete = nextDrawUseBlastSync;
@@ -4137,11 +4139,6 @@
}
mHandler.postAtFrontOfQueue(this::clearBlastSync);
});
- } else if (reportNextDraw) {
- // If we need to report next draw, wait for adapter to flush its shadow queue
- // by processing previously queued buffers so that we can submit the
- // transaction a timely manner.
- mBlastBufferQueue.flushShadowQueue();
}
};
registerRtFrameCallback(frameDrawingCallback);
diff --git a/core/java/android/view/translation/Helper.java b/core/java/android/view/translation/Helper.java
new file mode 100644
index 0000000..6e2850f
--- /dev/null
+++ b/core/java/android/view/translation/Helper.java
@@ -0,0 +1,32 @@
+/*
+ * 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 android.view.translation;
+
+/** @hide */
+public class Helper {
+
+ // Debug-level flags are defined when service is bound.
+ public static boolean sDebug = false;
+ public static boolean sVerbose = false;
+
+ // TODO: Use a device config value.
+ public static final int ANIMATION_DURATION_MILLIS = 250;
+
+ private Helper() {
+ throw new UnsupportedOperationException("contains static members only");
+ }
+}
diff --git a/core/java/android/view/translation/Translator.java b/core/java/android/view/translation/Translator.java
index 606f39d..70db6e5 100644
--- a/core/java/android/view/translation/Translator.java
+++ b/core/java/android/view/translation/Translator.java
@@ -53,9 +53,6 @@
private static final String TAG = "Translator";
- // TODO: make this configurable and cross the Translation component
- private static boolean sDEBUG = false;
-
private final Object mLock = new Object();
private int mId;
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index 442d099..1019612 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -16,6 +16,7 @@
package android.view.translation;
+import static android.view.translation.Helper.ANIMATION_DURATION_MILLIS;
import static android.view.translation.UiTranslationManager.STATE_UI_TRANSLATION_FINISHED;
import static android.view.translation.UiTranslationManager.STATE_UI_TRANSLATION_PAUSED;
import static android.view.translation.UiTranslationManager.STATE_UI_TRANSLATION_RESUMED;
@@ -26,6 +27,7 @@
import android.app.Activity;
import android.app.assist.ActivityId;
import android.content.Context;
+import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Process;
@@ -465,9 +467,6 @@
}
}
- // TODO: Use a device config value.
- private static final int ANIMATION_DURATION_MILLIS = 250;
-
/**
* Creates a Translator for the given source and target translation specs and start the ui
* translation when the Translator is created successfully.
@@ -724,7 +723,21 @@
msg.append("text=").append(value.getText() == null
? "null"
: "string[" + value.getText().length() + "], ");
- //TODO: append dictionary results.
+ final Bundle definitions =
+ (Bundle) value.getExtras().get(TranslationResponseValue.EXTRA_DEFINITIONS);
+ if (definitions != null) {
+ msg.append("definitions={");
+ for (String partOfSpeech : definitions.keySet()) {
+ msg.append(partOfSpeech).append(":[");
+ for (CharSequence definition : definitions.getCharSequenceArray(partOfSpeech)) {
+ msg.append(definition == null
+ ? "null, "
+ : "string[" + definition.length() + "], ");
+ }
+ msg.append("], ");
+ }
+ msg.append("}");
+ }
msg.append("transliteration=").append(value.getTransliteration() == null
? "null"
: "string[" + value.getTransliteration().length() + "]}, ");
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 4d0f4c2..019126f 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -140,6 +140,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.function.Supplier;
/**
* The Chooser Activity handles intent resolution specifically for sharing intents -
@@ -234,11 +235,6 @@
private static final float DIRECT_SHARE_EXPANSION_RATE = 0.78f;
- // TODO(b/121287224): Re-evaluate this limit
- private static final int SHARE_TARGET_QUERY_PACKAGE_LIMIT = 20;
-
- private static final int QUERY_TARGET_SERVICE_LIMIT = 5;
-
private static final int DEFAULT_SALT_EXPIRATION_DAYS = 7;
private int mMaxHashSaltDays = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.HASH_SALT_MAX_DAYS,
@@ -258,8 +254,6 @@
private long mQueriedSharingShortcutsTimeMs;
- private int mChooserRowServiceSpacing;
-
private int mCurrAvailableWidth = 0;
private int mLastNumberOfChildren = -1;
@@ -658,9 +652,6 @@
.addTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE, target.getType())
.addTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS, systemCost));
- mChooserRowServiceSpacing = getResources()
- .getDimensionPixelSize(R.dimen.chooser_service_spacing);
-
if (mResolverDrawerLayout != null) {
mResolverDrawerLayout.addOnLayoutChangeListener(this::handleLayoutChange);
@@ -2066,24 +2057,6 @@
return chooserTargetList;
}
- private String convertServiceName(String packageName, String serviceName) {
- if (TextUtils.isEmpty(serviceName)) {
- return null;
- }
-
- final String fullName;
- if (serviceName.startsWith(".")) {
- // Relative to the app package. Prepend the app package name.
- fullName = packageName + serviceName;
- } else if (serviceName.indexOf('.') >= 0) {
- // Fully qualified package name.
- fullName = serviceName;
- } else {
- fullName = null;
- }
- return fullName;
- }
-
private void logDirectShareTargetReceived(int logCategory) {
final int apiLatency = (int) (System.currentTimeMillis() - mQueriedSharingShortcutsTimeMs);
getMetricsLogger().write(new LogMaker(logCategory).setSubtype(apiLatency));
@@ -2265,37 +2238,6 @@
return false;
}
- void filterServiceTargets(Context contextAsUser, String packageName,
- List<ChooserTarget> targets) {
- if (targets == null) {
- return;
- }
-
- final PackageManager pm = contextAsUser.getPackageManager();
- for (int i = targets.size() - 1; i >= 0; i--) {
- final ChooserTarget target = targets.get(i);
- final ComponentName targetName = target.getComponentName();
- if (packageName != null && packageName.equals(targetName.getPackageName())) {
- // Anything from the original target's package is fine.
- continue;
- }
-
- boolean remove;
- try {
- final ActivityInfo ai = pm.getActivityInfo(targetName, 0);
- remove = !ai.exported || ai.permission != null;
- } catch (NameNotFoundException e) {
- Log.e(TAG, "Target " + target + " returned by " + packageName
- + " component not found");
- remove = true;
- }
-
- if (remove) {
- targets.remove(i);
- }
- }
- }
-
/**
* Sort intents alphabetically based on display label.
*/
@@ -2854,7 +2796,7 @@
.setSubtype(previewType));
}
- class ViewHolderBase extends RecyclerView.ViewHolder {
+ abstract static class ViewHolderBase extends RecyclerView.ViewHolder {
private int mViewType;
ViewHolderBase(View itemView, int viewType) {
@@ -2902,7 +2844,7 @@
/**
* Add a footer to the list, to support scrolling behavior below the navbar.
*/
- final class FooterViewHolder extends ViewHolderBase {
+ static final class FooterViewHolder extends ViewHolderBase {
FooterViewHolder(View itemView, int viewType) {
super(itemView, viewType);
}
@@ -3327,7 +3269,8 @@
parentGroup.addView(row2);
mDirectShareViewHolder = new DirectShareViewHolder(parentGroup,
- Lists.newArrayList(row1, row2), getMaxTargetsPerRow(), viewType);
+ Lists.newArrayList(row1, row2), getMaxTargetsPerRow(), viewType,
+ mChooserMultiProfilePagerAdapter::getActiveListAdapter);
loadViewsIntoGroup(mDirectShareViewHolder);
return mDirectShareViewHolder;
@@ -3489,7 +3432,7 @@
* {@link ChooserGridAdapter#VIEW_TYPE_DIRECT_SHARE},
* and {@link ChooserGridAdapter#VIEW_TYPE_CALLER_AND_RANK}.
*/
- abstract class ItemGroupViewHolder extends ViewHolderBase {
+ abstract static class ItemGroupViewHolder extends ViewHolderBase {
protected int mMeasuredRowHeight;
private int[] mItemIndices;
protected final View[] mCells;
@@ -3539,7 +3482,7 @@
}
}
- class SingleRowViewHolder extends ItemGroupViewHolder {
+ static class SingleRowViewHolder extends ItemGroupViewHolder {
private final ViewGroup mRow;
SingleRowViewHolder(ViewGroup row, int cellCount, int viewType) {
@@ -3573,7 +3516,7 @@
}
}
- class DirectShareViewHolder extends ItemGroupViewHolder {
+ static class DirectShareViewHolder extends ItemGroupViewHolder {
private final ViewGroup mParent;
private final List<ViewGroup> mRows;
private int mCellCountPerRow;
@@ -3585,14 +3528,17 @@
private final boolean[] mCellVisibility;
+ private final Supplier<ChooserListAdapter> mListAdapterSupplier;
+
DirectShareViewHolder(ViewGroup parent, List<ViewGroup> rows, int cellCountPerRow,
- int viewType) {
+ int viewType, Supplier<ChooserListAdapter> listAdapterSupplier) {
super(rows.size() * cellCountPerRow, parent, viewType);
this.mParent = parent;
this.mRows = rows;
this.mCellCountPerRow = cellCountPerRow;
this.mCellVisibility = new boolean[rows.size() * cellCountPerRow];
+ this.mListAdapterSupplier = listAdapterSupplier;
}
public ViewGroup addView(int index, View v) {
@@ -3666,8 +3612,7 @@
// only expand if we have more than maxTargetsPerRow, and delay that decision
// until they start to scroll
- ChooserListAdapter adapter =
- mChooserMultiProfilePagerAdapter.getActiveListAdapter();
+ ChooserListAdapter adapter = mListAdapterSupplier.get();
int validTargets = adapter.getSelectableServiceTargetCount();
if (validTargets <= maxTargetsPerRow) {
mHideDirectShareExpansion = true;
diff --git a/core/jni/android_database_SQLiteConnection.cpp b/core/jni/android_database_SQLiteConnection.cpp
index c80f1dc..32697ae 100644
--- a/core/jni/android_database_SQLiteConnection.cpp
+++ b/core/jni/android_database_SQLiteConnection.cpp
@@ -518,23 +518,29 @@
}
}
-static int executeNonQuery(JNIEnv* env, SQLiteConnection* connection, sqlite3_stmt* statement) {
- int err = sqlite3_step(statement);
- if (err == SQLITE_ROW) {
+static int executeNonQuery(JNIEnv* env, SQLiteConnection* connection, sqlite3_stmt* statement,
+ bool isPragmaStmt) {
+ int rc = sqlite3_step(statement);
+ if (isPragmaStmt) {
+ while (rc == SQLITE_ROW) {
+ rc = sqlite3_step(statement);
+ }
+ }
+ if (rc == SQLITE_ROW) {
throw_sqlite3_exception(env,
"Queries can be performed using SQLiteDatabase query or rawQuery methods only.");
- } else if (err != SQLITE_DONE) {
+ } else if (rc != SQLITE_DONE) {
throw_sqlite3_exception(env, connection->db);
}
- return err;
+ return rc;
}
-static void nativeExecute(JNIEnv* env, jclass clazz, jlong connectionPtr,
- jlong statementPtr) {
+static void nativeExecute(JNIEnv* env, jclass clazz, jlong connectionPtr, jlong statementPtr,
+ jboolean isPragmaStmt) {
SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
- executeNonQuery(env, connection, statement);
+ executeNonQuery(env, connection, statement, isPragmaStmt);
}
static jint nativeExecuteForChangedRowCount(JNIEnv* env, jclass clazz,
@@ -542,7 +548,7 @@
SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
- int err = executeNonQuery(env, connection, statement);
+ int err = executeNonQuery(env, connection, statement, false);
return err == SQLITE_DONE ? sqlite3_changes(connection->db) : -1;
}
@@ -551,7 +557,7 @@
SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
- int err = executeNonQuery(env, connection, statement);
+ int err = executeNonQuery(env, connection, statement, false);
return err == SQLITE_DONE && sqlite3_changes(connection->db) > 0
? sqlite3_last_insert_rowid(connection->db) : -1;
}
@@ -912,7 +918,7 @@
(void*)nativeBindBlob },
{ "nativeResetStatementAndClearBindings", "(JJ)V",
(void*)nativeResetStatementAndClearBindings },
- { "nativeExecute", "(JJ)V",
+ { "nativeExecute", "(JJZ)V",
(void*)nativeExecute },
{ "nativeExecuteForLong", "(JJ)J",
(void*)nativeExecuteForLong },
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index 3e31b46..d4fb3e3 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -112,11 +112,6 @@
transaction);
}
-static void nativeFlushShadowQueue(JNIEnv* env, jclass clazz, jlong ptr) {
- sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
- queue->flushShadowQueue();
-}
-
static void nativeMergeWithNextTransaction(JNIEnv*, jclass clazz, jlong ptr, jlong transactionPtr,
jlong framenumber) {
sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
@@ -144,6 +139,11 @@
return queue->getLastAcquiredFrameNum();
}
+static void nativeApplyPendingTransactions(JNIEnv* env, jclass clazz, jlong ptr, jlong frameNum) {
+ sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
+ queue->applyPendingTransactions(frameNum);
+}
+
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
// clang-format off
@@ -152,12 +152,12 @@
{"nativeDestroy", "(J)V", (void*)nativeDestroy},
{"nativeSetNextTransaction", "(JJ)V", (void*)nativeSetNextTransaction},
{"nativeUpdate", "(JJJJIJ)V", (void*)nativeUpdate},
- {"nativeFlushShadowQueue", "(J)V", (void*)nativeFlushShadowQueue},
{"nativeMergeWithNextTransaction", "(JJJ)V", (void*)nativeMergeWithNextTransaction},
{"nativeSetTransactionCompleteCallback",
"(JJLandroid/graphics/BLASTBufferQueue$TransactionCompleteCallback;)V",
(void*)nativeSetTransactionCompleteCallback},
{"nativeGetLastAcquiredFrameNum", "(J)J", (void*)nativeGetLastAcquiredFrameNum},
+ {"nativeApplyPendingTransactions", "(JJ)V", (void*)nativeApplyPendingTransactions},
// clang-format on
};
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index 787d348..aadd320 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -98,6 +98,15 @@
}
}
+static void android_os_Parcel_markForBinder(JNIEnv* env, jclass clazz, jlong nativePtr,
+ jobject binder)
+{
+ Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
+ if (parcel) {
+ parcel->markForBinder(ibinderForJavaObject(env, binder));
+ }
+}
+
static jint android_os_Parcel_dataSize(jlong nativePtr)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
@@ -749,7 +758,9 @@
static const JNINativeMethod gParcelMethods[] = {
// @CriticalNative
- {"nativeMarkSensitive", "(J)V", (void*)android_os_Parcel_markSensitive},
+ {"nativeMarkSensitive", "(J)V", (void*)android_os_Parcel_markSensitive},
+ // @FastNative
+ {"nativeMarkForBinder", "(JLandroid/os/IBinder;)V", (void*)android_os_Parcel_markForBinder},
// @CriticalNative
{"nativeDataSize", "(J)I", (void*)android_os_Parcel_dataSize},
// @CriticalNative
diff --git a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
index 953902a..32c57d1 100644
--- a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
+++ b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
@@ -63,7 +63,7 @@
*vector = counter->getCount(state);
}
-static jobject native_toString(JNIEnv *env, jlong nativePtr, jobject self) {
+static jobject native_toString(JNIEnv *env, jobject self, jlong nativePtr) {
battery::LongArrayMultiStateCounter *counter =
reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
return env->NewStringUTF(counter->toString().c_str());
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 313c4da..a4151c7 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -682,9 +682,6 @@
<dimen name="default_magnifier_horizontal_offset">0dp</dimen>
<item type="dimen" format="float" name="default_magnifier_zoom">1.25</item>
- <!-- Spacing around the background change frome service to non-service -->
- <dimen name="chooser_service_spacing">8dp</dimen>
-
<item type="dimen" name="aerr_padding_list_top">15dp</item>
<item type="dimen" name="aerr_padding_list_bottom">8dp</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7dd94f2..87197ac 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3687,7 +3687,6 @@
<java-symbol type="string" name="popup_window_default_title" />
<java-symbol type="bool" name="config_showAreaUpdateInfoSettings" />
<java-symbol type="layout" name="shutdown_dialog" />
- <java-symbol type="dimen" name="chooser_service_spacing" />
<java-symbol type="bool" name="config_showSysuiShutdown" />
<java-symbol type="drawable" name="chooser_file_generic" />
diff --git a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
index 26296f1..2da9f57 100644
--- a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
@@ -46,5 +46,10 @@
counter.getCounts(longArrayContainer, 1);
longArrayContainer.getValues(result);
assertThat(result).isEqualTo(new long[]{25, 50, 75, 100});
+
+ assertThat(counter.toString()).isEqualTo(
+ "currentState: 0 lastStateChangeTimestamp: 9000 lastUpdateTimestamp: 9000 states:"
+ + " [0: time: 0 counter: { 75, 150, 225, 300}"
+ + ", 1: time: 0 counter: { 25, 50, 75, 100}]");
}
}
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index 0e1360f..e369acc 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -34,12 +34,12 @@
private static native void nativeSetNextTransaction(long ptr, long transactionPtr);
private static native void nativeUpdate(long ptr, long surfaceControl, long width, long height,
int format, long transactionPtr);
- private static native void nativeFlushShadowQueue(long ptr);
private static native void nativeMergeWithNextTransaction(long ptr, long transactionPtr,
long frameNumber);
private static native void nativeSetTransactionCompleteCallback(long ptr, long frameNumber,
TransactionCompleteCallback callback);
private static native long nativeGetLastAcquiredFrameNum(long ptr);
+ private static native void nativeApplyPendingTransactions(long ptr, long frameNumber);
/**
* Callback sent to {@link #setTransactionCompleteCallback(long, TransactionCompleteCallback)}
@@ -125,10 +125,6 @@
}
}
- public void flushShadowQueue() {
- nativeFlushShadowQueue(mNativeObject);
- }
-
/**
* Merge the transaction passed in to the next transaction in BlastBufferQueue. The next
* transaction will be applied or merged when the next frame with specified frame number
@@ -146,6 +142,17 @@
nativeMergeWithNextTransaction(mNativeObject, nativeTransaction, frameNumber);
}
+ /**
+ * Apply any transactions that were passed to {@link #mergeWithNextTransaction} with the
+ * specified frameNumber. This is intended to ensure transactions don't get stuck as pending
+ * if the specified frameNumber is never drawn.
+ *
+ * @param frameNumber The frameNumber used to determine which transactions to apply.
+ */
+ public void applyPendingTransactions(long frameNumber) {
+ nativeApplyPendingTransactions(mNativeObject, frameNumber);
+ }
+
public long getLastAcquiredFrameNum() {
return nativeGetLastAcquiredFrameNum(mNativeObject);
}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 2c299fa..b931d03 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -587,6 +587,7 @@
"HardwareBitmapUploader.cpp",
"HWUIProperties.sysprop",
"JankTracker.cpp",
+ "FrameMetricsReporter.cpp",
"Layer.cpp",
"LayerUpdateQueue.cpp",
"ProfileData.cpp",
@@ -691,6 +692,7 @@
"tests/unit/FatVectorTests.cpp",
"tests/unit/GraphicsStatsServiceTests.cpp",
"tests/unit/JankTrackerTests.cpp",
+ "tests/unit/FrameMetricsReporterTests.cpp",
"tests/unit/LayerUpdateQueueTests.cpp",
"tests/unit/LinearAllocatorTests.cpp",
"tests/unit/MatrixTests.cpp",
diff --git a/libs/hwui/FrameMetricsObserver.h b/libs/hwui/FrameMetricsObserver.h
index ef1f5aa..498ec57 100644
--- a/libs/hwui/FrameMetricsObserver.h
+++ b/libs/hwui/FrameMetricsObserver.h
@@ -26,6 +26,13 @@
virtual void notify(const int64_t* buffer) = 0;
bool waitForPresentTime() const { return mWaitForPresentTime; };
+ void reportMetricsFrom(uint64_t frameNumber, int32_t surfaceControlId) {
+ mAttachedFrameNumber = frameNumber;
+ mSurfaceControlId = surfaceControlId;
+ };
+ uint64_t attachedFrameNumber() const { return mAttachedFrameNumber; };
+ int32_t attachedSurfaceControlId() const { return mSurfaceControlId; };
+
/**
* Create a new metrics observer. An observer that watches present time gets notified at a
* different time than the observer that doesn't.
@@ -42,6 +49,22 @@
private:
const bool mWaitForPresentTime;
+
+ // The id of the surface control (mSurfaceControlGenerationId in CanvasContext)
+ // for which the mAttachedFrameNumber applies to. We rely on this value being
+ // an increasing counter. We will report metrics:
+ // - for all frames if the frame comes from a surface with a surfaceControlId
+ // that is strictly greater than mSurfaceControlId.
+ // - for all frames with a frame number greater than or equal to mAttachedFrameNumber
+ // if the frame comes from a surface with a surfaceControlId that is equal to the
+ // mSurfaceControlId.
+ // We will never report metrics if the frame comes from a surface with a surfaceControlId
+ // that is strictly smaller than mSurfaceControlId.
+ int32_t mSurfaceControlId;
+
+ // The frame number the metrics observer was attached on. Metrics will be sent from this frame
+ // number (inclusive) onwards in the case that the surface id is equal to mSurfaceControlId.
+ uint64_t mAttachedFrameNumber;
};
} // namespace uirenderer
diff --git a/libs/hwui/FrameMetricsReporter.cpp b/libs/hwui/FrameMetricsReporter.cpp
new file mode 100644
index 0000000..ee32ea1
--- /dev/null
+++ b/libs/hwui/FrameMetricsReporter.cpp
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+#include "FrameMetricsReporter.h"
+
+namespace android {
+namespace uirenderer {
+
+void FrameMetricsReporter::reportFrameMetrics(const int64_t* stats, bool hasPresentTime,
+ uint64_t frameNumber, int32_t surfaceControlId) {
+ FatVector<sp<FrameMetricsObserver>, 10> copy;
+ {
+ std::lock_guard lock(mObserversLock);
+ copy.reserve(mObservers.size());
+ for (size_t i = 0; i < mObservers.size(); i++) {
+ auto observer = mObservers[i];
+
+ if (CC_UNLIKELY(surfaceControlId < observer->attachedSurfaceControlId())) {
+ // Don't notify if the metrics are from a frame that was run on an old
+ // surface (one from before the observer was attached).
+ ALOGV("skipped reporting metrics from old surface %d", surfaceControlId);
+ continue;
+ } else if (CC_UNLIKELY(surfaceControlId == observer->attachedSurfaceControlId() &&
+ frameNumber < observer->attachedFrameNumber())) {
+ // Don't notify if the metrics are from a frame that was queued by the
+ // BufferQueueProducer on the render thread before the observer was attached.
+ ALOGV("skipped reporting metrics from old frame %ld", (long)frameNumber);
+ continue;
+ }
+
+ const bool wantsPresentTime = observer->waitForPresentTime();
+ if (hasPresentTime == wantsPresentTime) {
+ copy.push_back(observer);
+ }
+ }
+ }
+ for (size_t i = 0; i < copy.size(); i++) {
+ copy[i]->notify(stats);
+ }
+}
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/FrameMetricsReporter.h b/libs/hwui/FrameMetricsReporter.h
index 0ac025f..7e51df7c 100644
--- a/libs/hwui/FrameMetricsReporter.h
+++ b/libs/hwui/FrameMetricsReporter.h
@@ -63,23 +63,14 @@
* If an observer does not want present time, only notify when 'hasPresentTime' is false.
* Never notify both types of observers from the same callback, because the callback with
* 'hasPresentTime' is sent at a different time than the one without.
+ *
+ * The 'frameNumber' and 'surfaceControlId' associated to the frame whose's stats are being
+ * reported are used to determine whether or not the stats should be reported. We won't report
+ * stats of frames that are from "old" surfaces (i.e. with surfaceControlIds older than the one
+ * the observer was attached on) nor those that are from "old" frame numbers.
*/
- void reportFrameMetrics(const int64_t* stats, bool hasPresentTime) {
- FatVector<sp<FrameMetricsObserver>, 10> copy;
- {
- std::lock_guard lock(mObserversLock);
- copy.reserve(mObservers.size());
- for (size_t i = 0; i < mObservers.size(); i++) {
- const bool wantsPresentTime = mObservers[i]->waitForPresentTime();
- if (hasPresentTime == wantsPresentTime) {
- copy.push_back(mObservers[i]);
- }
- }
- }
- for (size_t i = 0; i < copy.size(); i++) {
- copy[i]->notify(stats);
- }
- }
+ void reportFrameMetrics(const int64_t* stats, bool hasPresentTime, uint64_t frameNumber,
+ int32_t surfaceControlId);
private:
FatVector<sp<FrameMetricsObserver>, 10> mObservers GUARDED_BY(mObserversLock);
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index 34e5577..3e5cbb5 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -164,7 +164,8 @@
- lastFrameOffset + mFrameIntervalLegacy;
}
-void JankTracker::finishFrame(FrameInfo& frame, std::unique_ptr<FrameMetricsReporter>& reporter) {
+void JankTracker::finishFrame(FrameInfo& frame, std::unique_ptr<FrameMetricsReporter>& reporter,
+ int64_t frameNumber, int32_t surfaceControlId) {
std::lock_guard lock(mDataMutex);
calculateLegacyJank(frame);
@@ -253,7 +254,8 @@
}
if (CC_UNLIKELY(reporter.get() != nullptr)) {
- reporter->reportFrameMetrics(frame.data(), false /* hasPresentTime */);
+ reporter->reportFrameMetrics(frame.data(), false /* hasPresentTime */, frameNumber,
+ surfaceControlId);
}
}
diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h
index bdb784d..bcd031e 100644
--- a/libs/hwui/JankTracker.h
+++ b/libs/hwui/JankTracker.h
@@ -57,7 +57,8 @@
}
FrameInfo* startFrame() { return &mFrames.next(); }
- void finishFrame(FrameInfo& frame, std::unique_ptr<FrameMetricsReporter>& reporter);
+ void finishFrame(FrameInfo& frame, std::unique_ptr<FrameMetricsReporter>& reporter,
+ int64_t frameNumber, int32_t surfaceId);
// Calculates the 'legacy' jank information, i.e. with outdated refresh rate information and
// without GPU completion or deadlined information.
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 0cd6ffb..5fa0922 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -203,9 +203,10 @@
mSurfaceControl = surfaceControl;
mSurfaceControlGenerationId++;
mExpectSurfaceStats = surfaceControl != nullptr;
- if (mSurfaceControl != nullptr) {
+ if (mExpectSurfaceStats) {
funcs.acquireFunc(mSurfaceControl);
- funcs.registerListenerFunc(surfaceControl, this, &onSurfaceStatsAvailable);
+ funcs.registerListenerFunc(surfaceControl, mSurfaceControlGenerationId, this,
+ &onSurfaceStatsAvailable);
}
}
@@ -218,7 +219,7 @@
}
- mFrameNumber = -1;
+ mFrameNumber = 0;
if (mNativeSurface != nullptr && hasSurface) {
mHaveNewSurface = true;
@@ -512,7 +513,7 @@
mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes,
&(profiler()));
- int64_t frameCompleteNr = getFrameNumber();
+ uint64_t frameCompleteNr = getFrameNumber();
waitOnFences();
@@ -580,7 +581,7 @@
mCurrentFrameInfo->set(FrameInfoIndex::DequeueBufferDuration) = swap.dequeueDuration;
mCurrentFrameInfo->set(FrameInfoIndex::QueueBufferDuration) = swap.queueDuration;
mHaveNewSurface = false;
- mFrameNumber = -1;
+ mFrameNumber = 0;
} else {
mCurrentFrameInfo->set(FrameInfoIndex::DequeueBufferDuration) = 0;
mCurrentFrameInfo->set(FrameInfoIndex::QueueBufferDuration) = 0;
@@ -613,16 +614,20 @@
if (requireSwap) {
if (mExpectSurfaceStats) {
reportMetricsWithPresentTime();
- std::lock_guard lock(mLast4FrameInfosMutex);
- std::pair<FrameInfo*, int64_t>& next = mLast4FrameInfos.next();
- next.first = mCurrentFrameInfo;
- next.second = frameCompleteNr;
+ { // acquire lock
+ std::lock_guard lock(mLast4FrameMetricsInfosMutex);
+ FrameMetricsInfo& next = mLast4FrameMetricsInfos.next();
+ next.frameInfo = mCurrentFrameInfo;
+ next.frameNumber = frameCompleteNr;
+ next.surfaceId = mSurfaceControlGenerationId;
+ } // release lock
} else {
mCurrentFrameInfo->markFrameCompleted();
mCurrentFrameInfo->set(FrameInfoIndex::GpuCompleted)
= mCurrentFrameInfo->get(FrameInfoIndex::FrameCompleted);
std::scoped_lock lock(mFrameMetricsReporterMutex);
- mJankTracker.finishFrame(*mCurrentFrameInfo, mFrameMetricsReporter);
+ mJankTracker.finishFrame(*mCurrentFrameInfo, mFrameMetricsReporter, frameCompleteNr,
+ mSurfaceControlGenerationId);
}
}
@@ -658,14 +663,18 @@
ATRACE_CALL();
FrameInfo* forthBehind;
int64_t frameNumber;
+ int32_t surfaceControlId;
+
{ // acquire lock
- std::scoped_lock lock(mLast4FrameInfosMutex);
- if (mLast4FrameInfos.size() != mLast4FrameInfos.capacity()) {
+ std::scoped_lock lock(mLast4FrameMetricsInfosMutex);
+ if (mLast4FrameMetricsInfos.size() != mLast4FrameMetricsInfos.capacity()) {
// Not enough frames yet
return;
}
- // Surface object keeps stats for the last 8 frames.
- std::tie(forthBehind, frameNumber) = mLast4FrameInfos.front();
+ auto frameMetricsInfo = mLast4FrameMetricsInfos.front();
+ forthBehind = frameMetricsInfo.frameInfo;
+ frameNumber = frameMetricsInfo.frameNumber;
+ surfaceControlId = frameMetricsInfo.surfaceId;
} // release lock
nsecs_t presentTime = 0;
@@ -680,25 +689,51 @@
{ // acquire lock
std::scoped_lock lock(mFrameMetricsReporterMutex);
if (mFrameMetricsReporter != nullptr) {
- mFrameMetricsReporter->reportFrameMetrics(forthBehind->data(), true /*hasPresentTime*/);
+ mFrameMetricsReporter->reportFrameMetrics(forthBehind->data(), true /*hasPresentTime*/,
+ frameNumber, surfaceControlId);
}
} // release lock
}
-FrameInfo* CanvasContext::getFrameInfoFromLast4(uint64_t frameNumber) {
- std::scoped_lock lock(mLast4FrameInfosMutex);
- for (size_t i = 0; i < mLast4FrameInfos.size(); i++) {
- if (mLast4FrameInfos[i].second == frameNumber) {
- return mLast4FrameInfos[i].first;
+void CanvasContext::addFrameMetricsObserver(FrameMetricsObserver* observer) {
+ std::scoped_lock lock(mFrameMetricsReporterMutex);
+ if (mFrameMetricsReporter.get() == nullptr) {
+ mFrameMetricsReporter.reset(new FrameMetricsReporter());
+ }
+
+ // We want to make sure we aren't reporting frames that have already been queued by the
+ // BufferQueueProducer on the rendner thread but are still pending the callback to report their
+ // their frame metrics.
+ uint64_t nextFrameNumber = getFrameNumber();
+ observer->reportMetricsFrom(nextFrameNumber, mSurfaceControlGenerationId);
+ mFrameMetricsReporter->addObserver(observer);
+}
+
+void CanvasContext::removeFrameMetricsObserver(FrameMetricsObserver* observer) {
+ std::scoped_lock lock(mFrameMetricsReporterMutex);
+ if (mFrameMetricsReporter.get() != nullptr) {
+ mFrameMetricsReporter->removeObserver(observer);
+ if (!mFrameMetricsReporter->hasObservers()) {
+ mFrameMetricsReporter.reset(nullptr);
}
}
+}
+
+FrameInfo* CanvasContext::getFrameInfoFromLast4(uint64_t frameNumber, uint32_t surfaceControlId) {
+ std::scoped_lock lock(mLast4FrameMetricsInfosMutex);
+ for (size_t i = 0; i < mLast4FrameMetricsInfos.size(); i++) {
+ if (mLast4FrameMetricsInfos[i].frameNumber == frameNumber &&
+ mLast4FrameMetricsInfos[i].surfaceId == surfaceControlId) {
+ return mLast4FrameMetricsInfos[i].frameInfo;
+ }
+ }
+
return nullptr;
}
-void CanvasContext::onSurfaceStatsAvailable(void* context, ASurfaceControl* control,
- ASurfaceControlStats* stats) {
-
- CanvasContext* instance = static_cast<CanvasContext*>(context);
+void CanvasContext::onSurfaceStatsAvailable(void* context, int32_t surfaceControlId,
+ ASurfaceControlStats* stats) {
+ auto* instance = static_cast<CanvasContext*>(context);
const ASurfaceControlFunctions& functions =
instance->mRenderThread.getASurfaceControlFunctions();
@@ -706,14 +741,15 @@
nsecs_t gpuCompleteTime = functions.getAcquireTimeFunc(stats);
uint64_t frameNumber = functions.getFrameNumberFunc(stats);
- FrameInfo* frameInfo = instance->getFrameInfoFromLast4(frameNumber);
+ FrameInfo* frameInfo = instance->getFrameInfoFromLast4(frameNumber, surfaceControlId);
if (frameInfo != nullptr) {
frameInfo->set(FrameInfoIndex::FrameCompleted) = std::max(gpuCompleteTime,
frameInfo->get(FrameInfoIndex::SwapBuffersCompleted));
frameInfo->set(FrameInfoIndex::GpuCompleted) = gpuCompleteTime;
std::scoped_lock lock(instance->mFrameMetricsReporterMutex);
- instance->mJankTracker.finishFrame(*frameInfo, instance->mFrameMetricsReporter);
+ instance->mJankTracker.finishFrame(*frameInfo, instance->mFrameMetricsReporter, frameNumber,
+ surfaceControlId);
}
}
@@ -854,9 +890,9 @@
mFrameFences.push_back(CommonPool::async(std::move(func)));
}
-int64_t CanvasContext::getFrameNumber() {
- // mFrameNumber is reset to -1 when the surface changes or we swap buffers
- if (mFrameNumber == -1 && mNativeSurface.get()) {
+uint64_t CanvasContext::getFrameNumber() {
+ // mFrameNumber is reset to 0 when the surface changes or we swap buffers
+ if (mFrameNumber == 0 && mNativeSurface.get()) {
mFrameNumber = ANativeWindow_getNextFrameId(mNativeSurface->getNativeWindow());
}
return mFrameNumber;
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index ed4a620..0caf76a 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -167,29 +167,13 @@
void setContentDrawBounds(const Rect& bounds) { mContentDrawBounds = bounds; }
- void addFrameMetricsObserver(FrameMetricsObserver* observer) {
- std::scoped_lock lock(mFrameMetricsReporterMutex);
- if (mFrameMetricsReporter.get() == nullptr) {
- mFrameMetricsReporter.reset(new FrameMetricsReporter());
- }
-
- mFrameMetricsReporter->addObserver(observer);
- }
-
- void removeFrameMetricsObserver(FrameMetricsObserver* observer) {
- std::scoped_lock lock(mFrameMetricsReporterMutex);
- if (mFrameMetricsReporter.get() != nullptr) {
- mFrameMetricsReporter->removeObserver(observer);
- if (!mFrameMetricsReporter->hasObservers()) {
- mFrameMetricsReporter.reset(nullptr);
- }
- }
- }
+ void addFrameMetricsObserver(FrameMetricsObserver* observer);
+ void removeFrameMetricsObserver(FrameMetricsObserver* observer);
// Used to queue up work that needs to be completed before this frame completes
void enqueueFrameWork(std::function<void()>&& func);
- int64_t getFrameNumber();
+ uint64_t getFrameNumber();
void waitOnFences();
@@ -212,8 +196,8 @@
SkISize getNextFrameSize() const;
// Called when SurfaceStats are available.
- static void onSurfaceStatsAvailable(void* context, ASurfaceControl* control,
- ASurfaceControlStats* stats);
+ static void onSurfaceStatsAvailable(void* context, int32_t surfaceControlId,
+ ASurfaceControlStats* stats);
void setASurfaceTransactionCallback(
const std::function<bool(int64_t, int64_t, int64_t)>& callback) {
@@ -254,7 +238,13 @@
*/
void reportMetricsWithPresentTime();
- FrameInfo* getFrameInfoFromLast4(uint64_t frameNumber);
+ struct FrameMetricsInfo {
+ FrameInfo* frameInfo;
+ int64_t frameNumber;
+ int32_t surfaceId;
+ };
+
+ FrameInfo* getFrameInfoFromLast4(uint64_t frameNumber, uint32_t surfaceControlId);
// The same type as Frame.mWidth and Frame.mHeight
int32_t mLastFrameWidth = 0;
@@ -266,7 +256,10 @@
// NULL to remove the reference
ASurfaceControl* mSurfaceControl = nullptr;
// id to track surface control changes and WebViewFunctor uses it to determine
- // whether reparenting is needed
+ // whether reparenting is needed also used by FrameMetricsReporter to determine
+ // if a frame is from an "old" surface (i.e. one that existed before the
+ // observer was attched) and therefore shouldn't be reported.
+ // NOTE: It is important that this is an increasing counter.
int32_t mSurfaceControlGenerationId = 0;
// stopped indicates the CanvasContext will reject actual redraw operations,
// and defer repaint until it is un-stopped
@@ -288,7 +281,8 @@
// Need at least 4 because we do quad buffer. Add a 5th for good measure.
RingBuffer<SwapHistory, 5> mSwapHistory;
- int64_t mFrameNumber = -1;
+ // Frame numbers start at 1, 0 means uninitialized
+ uint64_t mFrameNumber = 0;
int64_t mDamageId = 0;
// last vsync for a dropped frame due to stuffed queue
@@ -308,10 +302,11 @@
FrameInfo* mCurrentFrameInfo = nullptr;
- // List of frames that are awaiting GPU completion reporting
- RingBuffer<std::pair<FrameInfo*, int64_t>, 4> mLast4FrameInfos
- GUARDED_BY(mLast4FrameInfosMutex);
- std::mutex mLast4FrameInfosMutex;
+ // List of data of frames that are awaiting GPU completion reporting. Used to compute frame
+ // metrics and determine whether or not to report the metrics.
+ RingBuffer<FrameMetricsInfo, 4> mLast4FrameMetricsInfos
+ GUARDED_BY(mLast4FrameMetricsInfosMutex);
+ std::mutex mLast4FrameMetricsInfosMutex;
std::string mName;
JankTracker mJankTracker;
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 05d225b..0b81fc04 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -83,8 +83,9 @@
typedef void (*ASC_acquire)(ASurfaceControl* control);
typedef void (*ASC_release)(ASurfaceControl* control);
-typedef void (*ASC_registerSurfaceStatsListener)(ASurfaceControl* control, void* context,
- ASurfaceControl_SurfaceStatsListener func);
+typedef void (*ASC_registerSurfaceStatsListener)(ASurfaceControl* control, int32_t id,
+ void* context,
+ ASurfaceControl_SurfaceStatsListener func);
typedef void (*ASC_unregisterSurfaceStatsListener)(void* context,
ASurfaceControl_SurfaceStatsListener func);
diff --git a/libs/hwui/tests/unit/FrameMetricsReporterTests.cpp b/libs/hwui/tests/unit/FrameMetricsReporterTests.cpp
new file mode 100644
index 0000000..fb04700
--- /dev/null
+++ b/libs/hwui/tests/unit/FrameMetricsReporterTests.cpp
@@ -0,0 +1,205 @@
+/*
+ * 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.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <FrameMetricsObserver.h>
+#include <FrameMetricsReporter.h>
+#include <utils/TimeUtils.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+using ::testing::NotNull;
+
+class TestFrameMetricsObserver : public FrameMetricsObserver {
+public:
+ explicit TestFrameMetricsObserver(bool waitForPresentTime)
+ : FrameMetricsObserver(waitForPresentTime){};
+
+ MOCK_METHOD(void, notify, (const int64_t* buffer), (override));
+};
+
+TEST(FrameMetricsReporter, reportsAllFramesIfNoFromFrameIsSpecified) {
+ auto reporter = std::make_shared<FrameMetricsReporter>();
+
+ auto observer = sp<TestFrameMetricsObserver>::make(false /*waitForPresentTime*/);
+ EXPECT_CALL(*observer, notify).Times(4);
+
+ reporter->addObserver(observer.get());
+
+ const int64_t* stats;
+ bool hasPresentTime = false;
+ uint64_t frameNumber = 1;
+ int32_t surfaceControlId = 0;
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+
+ frameNumber = 10;
+ surfaceControlId = 0;
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+
+ frameNumber = 0;
+ surfaceControlId = 2;
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+
+ frameNumber = 10;
+ surfaceControlId = 2;
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+}
+
+TEST(FrameMetricsReporter, respectsWaitForPresentTimeUnset) {
+ auto reporter = std::make_shared<FrameMetricsReporter>();
+
+ auto observer = sp<TestFrameMetricsObserver>::make(false /*waitForPresentTime*/);
+ reporter->addObserver(observer.get());
+
+ const int64_t* stats;
+ bool hasPresentTime = false;
+ uint64_t frameNumber = 3;
+ int32_t surfaceControlId = 0;
+
+ EXPECT_CALL(*observer, notify).Times(1);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+
+ EXPECT_CALL(*observer, notify).Times(0);
+ hasPresentTime = true;
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+}
+
+TEST(FrameMetricsReporter, respectsWaitForPresentTimeSet) {
+ auto reporter = std::make_shared<FrameMetricsReporter>();
+
+ auto observer = sp<TestFrameMetricsObserver>::make(true /*waitForPresentTime*/);
+ reporter->addObserver(observer.get());
+
+ const int64_t* stats;
+ bool hasPresentTime = false;
+ uint64_t frameNumber = 3;
+ int32_t surfaceControlId = 0;
+
+ EXPECT_CALL(*observer, notify).Times(0);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+
+ EXPECT_CALL(*observer, notify).Times(1);
+ hasPresentTime = true;
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+}
+
+TEST(FrameMetricsReporter, reportsAllFramesAfterSpecifiedFromFrame) {
+ const int64_t* stats;
+ bool hasPresentTime = false;
+
+ std::vector<uint64_t> frameNumbers{0, 1, 10};
+ std::vector<int32_t> surfaceControlIds{0, 1, 10};
+ for (uint64_t frameNumber : frameNumbers) {
+ for (int32_t surfaceControlId : surfaceControlIds) {
+ auto reporter = std::make_shared<FrameMetricsReporter>();
+
+ auto observer =
+ sp<TestFrameMetricsObserver>::make(hasPresentTime /*waitForPresentTime*/);
+ observer->reportMetricsFrom(frameNumber, surfaceControlId);
+ reporter->addObserver(observer.get());
+
+ EXPECT_CALL(*observer, notify).Times(8);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber + 1, surfaceControlId);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber + 10, surfaceControlId);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId + 1);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber - 1,
+ surfaceControlId + 1);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber + 1,
+ surfaceControlId + 1);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber + 10,
+ surfaceControlId + 1);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber + 10,
+ surfaceControlId + 10);
+ }
+ }
+}
+
+TEST(FrameMetricsReporter, doesNotReportsFramesBeforeSpecifiedFromFrame) {
+ const int64_t* stats;
+ bool hasPresentTime = false;
+
+ std::vector<uint64_t> frameNumbers{1, 10};
+ std::vector<int32_t> surfaceControlIds{0, 1, 10};
+ for (uint64_t frameNumber : frameNumbers) {
+ for (int32_t surfaceControlId : surfaceControlIds) {
+ auto reporter = std::make_shared<FrameMetricsReporter>();
+
+ auto observer =
+ sp<TestFrameMetricsObserver>::make(hasPresentTime /*waitForPresentTime*/);
+ observer->reportMetricsFrom(frameNumber, surfaceControlId);
+ reporter->addObserver(observer.get());
+
+ EXPECT_CALL(*observer, notify).Times(0);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber - 1, surfaceControlId);
+ if (surfaceControlId > 0) {
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber,
+ surfaceControlId - 1);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber - 1,
+ surfaceControlId - 1);
+ }
+ }
+ }
+}
+
+TEST(FrameMetricsReporter, canRemoveObservers) {
+ const int64_t* stats;
+ bool hasPresentTime = false;
+ uint64_t frameNumber = 3;
+ int32_t surfaceControlId = 0;
+
+ auto reporter = std::make_shared<FrameMetricsReporter>();
+
+ auto observer = sp<TestFrameMetricsObserver>::make(hasPresentTime /*waitForPresentTime*/);
+
+ observer->reportMetricsFrom(frameNumber, surfaceControlId);
+ reporter->addObserver(observer.get());
+
+ EXPECT_CALL(*observer, notify).Times(1);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+
+ ASSERT_TRUE(reporter->removeObserver(observer.get()));
+
+ EXPECT_CALL(*observer, notify).Times(0);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+}
+
+TEST(FrameMetricsReporter, canSupportMultipleObservers) {
+ const int64_t* stats;
+ bool hasPresentTime = false;
+ uint64_t frameNumber = 3;
+ int32_t surfaceControlId = 0;
+
+ auto reporter = std::make_shared<FrameMetricsReporter>();
+
+ auto observer1 = sp<TestFrameMetricsObserver>::make(hasPresentTime /*waitForPresentTime*/);
+ auto observer2 = sp<TestFrameMetricsObserver>::make(hasPresentTime /*waitForPresentTime*/);
+ observer1->reportMetricsFrom(frameNumber, surfaceControlId);
+ observer2->reportMetricsFrom(frameNumber + 10, surfaceControlId + 1);
+ reporter->addObserver(observer1.get());
+ reporter->addObserver(observer2.get());
+
+ EXPECT_CALL(*observer1, notify).Times(1);
+ EXPECT_CALL(*observer2, notify).Times(0);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+
+ EXPECT_CALL(*observer1, notify).Times(1);
+ EXPECT_CALL(*observer2, notify).Times(1);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber + 10, surfaceControlId + 1);
+}
diff --git a/libs/hwui/tests/unit/JankTrackerTests.cpp b/libs/hwui/tests/unit/JankTrackerTests.cpp
index f467ebf..5b397de 100644
--- a/libs/hwui/tests/unit/JankTrackerTests.cpp
+++ b/libs/hwui/tests/unit/JankTrackerTests.cpp
@@ -34,6 +34,9 @@
JankTracker jankTracker(&container);
std::unique_ptr<FrameMetricsReporter> reporter = std::make_unique<FrameMetricsReporter>();
+ uint64_t frameNumber = 0;
+ uint32_t surfaceId = 0;
+
FrameInfo* info = jankTracker.startFrame();
info->set(FrameInfoIndex::IntendedVsync) = 100_ms;
info->set(FrameInfoIndex::Vsync) = 101_ms;
@@ -42,7 +45,7 @@
info->set(FrameInfoIndex::FrameCompleted) = 115_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 120_ms;
- jankTracker.finishFrame(*info, reporter);
+ jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
info = jankTracker.startFrame();
info->set(FrameInfoIndex::IntendedVsync) = 116_ms;
@@ -52,7 +55,7 @@
info->set(FrameInfoIndex::FrameCompleted) = 131_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 136_ms;
- jankTracker.finishFrame(*info, reporter);
+ jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
ASSERT_EQ(2, container.get()->totalFrameCount());
ASSERT_EQ(0, container.get()->jankFrameCount());
@@ -65,6 +68,9 @@
JankTracker jankTracker(&container);
std::unique_ptr<FrameMetricsReporter> reporter = std::make_unique<FrameMetricsReporter>();
+ uint64_t frameNumber = 0;
+ uint32_t surfaceId = 0;
+
FrameInfo* info = jankTracker.startFrame();
info->set(FrameInfoIndex::IntendedVsync) = 100_ms;
info->set(FrameInfoIndex::Vsync) = 101_ms;
@@ -73,7 +79,7 @@
info->set(FrameInfoIndex::FrameCompleted) = 121_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 120_ms;
- jankTracker.finishFrame(*info, reporter);
+ jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
ASSERT_EQ(1, container.get()->totalFrameCount());
ASSERT_EQ(1, container.get()->jankFrameCount());
@@ -85,6 +91,9 @@
JankTracker jankTracker(&container);
std::unique_ptr<FrameMetricsReporter> reporter = std::make_unique<FrameMetricsReporter>();
+ uint64_t frameNumber = 0;
+ uint32_t surfaceId = 0;
+
FrameInfo* info = jankTracker.startFrame();
info->set(FrameInfoIndex::IntendedVsync) = 100_ms;
info->set(FrameInfoIndex::Vsync) = 101_ms;
@@ -93,7 +102,7 @@
info->set(FrameInfoIndex::FrameCompleted) = 118_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 120_ms;
- jankTracker.finishFrame(*info, reporter);
+ jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
ASSERT_EQ(1, container.get()->totalFrameCount());
ASSERT_EQ(0, container.get()->jankFrameCount());
@@ -106,6 +115,9 @@
JankTracker jankTracker(&container);
std::unique_ptr<FrameMetricsReporter> reporter = std::make_unique<FrameMetricsReporter>();
+ uint64_t frameNumber = 0;
+ uint32_t surfaceId = 0;
+
// First frame janks
FrameInfo* info = jankTracker.startFrame();
info->set(FrameInfoIndex::IntendedVsync) = 100_ms;
@@ -115,7 +127,7 @@
info->set(FrameInfoIndex::FrameCompleted) = 121_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 120_ms;
- jankTracker.finishFrame(*info, reporter);
+ jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
ASSERT_EQ(1, container.get()->jankFrameCount());
@@ -128,7 +140,7 @@
info->set(FrameInfoIndex::FrameCompleted) = 137_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 136_ms;
- jankTracker.finishFrame(*info, reporter);
+ jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
ASSERT_EQ(2, container.get()->totalFrameCount());
ASSERT_EQ(1, container.get()->jankFrameCount());
@@ -140,6 +152,9 @@
JankTracker jankTracker(&container);
std::unique_ptr<FrameMetricsReporter> reporter = std::make_unique<FrameMetricsReporter>();
+ uint64_t frameNumber = 0;
+ uint32_t surfaceId = 0;
+
// First frame janks
FrameInfo* info = jankTracker.startFrame();
info->set(FrameInfoIndex::IntendedVsync) = 100_ms;
@@ -149,7 +164,7 @@
info->set(FrameInfoIndex::FrameCompleted) = 121_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 120_ms;
- jankTracker.finishFrame(*info, reporter);
+ jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
ASSERT_EQ(1, container.get()->jankFrameCount());
@@ -162,7 +177,7 @@
info->set(FrameInfoIndex::FrameCompleted) = 137_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 136_ms;
- jankTracker.finishFrame(*info, reporter);
+ jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
ASSERT_EQ(1, container.get()->jankFrameCount());
@@ -175,8 +190,8 @@
info->set(FrameInfoIndex::FrameCompleted) = 169_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 168_ms;
- jankTracker.finishFrame(*info, reporter);
+ jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
ASSERT_EQ(3, container.get()->totalFrameCount());
ASSERT_EQ(2, container.get()->jankFrameCount());
-}
\ No newline at end of file
+}
diff --git a/media/Android.bp b/media/Android.bp
index ce62b03..7592c15 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -55,12 +55,18 @@
"aidl/android/media/audio/common/AudioChannelLayout.aidl",
"aidl/android/media/audio/common/AudioConfig.aidl",
"aidl/android/media/audio/common/AudioConfigBase.aidl",
+ "aidl/android/media/audio/common/AudioContentType.aidl",
+ "aidl/android/media/audio/common/AudioEncapsulationMetadataType.aidl",
"aidl/android/media/audio/common/AudioEncapsulationMode.aidl",
+ "aidl/android/media/audio/common/AudioEncapsulationType.aidl",
"aidl/android/media/audio/common/AudioFormatDescription.aidl",
"aidl/android/media/audio/common/AudioFormatType.aidl",
+ "aidl/android/media/audio/common/AudioMode.aidl",
"aidl/android/media/audio/common/AudioOffloadInfo.aidl",
+ "aidl/android/media/audio/common/AudioSource.aidl",
"aidl/android/media/audio/common/AudioStreamType.aidl",
"aidl/android/media/audio/common/AudioUsage.aidl",
+ "aidl/android/media/audio/common/AudioUuid.aidl",
"aidl/android/media/audio/common/PcmType.aidl",
],
stability: "vintf",
diff --git a/media/aidl/android/media/audio/common/AudioContentType.aidl b/media/aidl/android/media/audio/common/AudioContentType.aidl
new file mode 100644
index 0000000..50ac181
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioContentType.aidl
@@ -0,0 +1,53 @@
+/*
+ * 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 android.media.audio.common;
+
+/**
+ * Content type specifies "what" is playing. The content type expresses the
+ * general category of the content: speech, music, movie audio, etc.
+ * This enum corresponds to AudioAttributes.CONTENT_TYPE_* constants in the SDK.
+ *
+ * {@hide}
+ */
+@Backing(type="int")
+@VintfStability
+enum AudioContentType {
+ /**
+ * Content type value to use when the content type is unknown, or other than
+ * the ones defined.
+ */
+ UNKNOWN = 0,
+ /**
+ * Content type value to use when the content type is speech.
+ */
+ SPEECH = 1,
+ /**
+ * Content type value to use when the content type is music.
+ */
+ MUSIC = 2,
+ /**
+ * Content type value to use when the content type is a soundtrack,
+ * typically accompanying a movie or TV program.
+ */
+ MOVIE = 3,
+ /**
+ * Content type value to use when the content type is a sound used to
+ * accompany a user action, such as a beep or sound effect expressing a key
+ * click, or event, such as the type of a sound for a bonus being received
+ * in a game. These sounds are mostly synthesized or short Foley sounds.
+ */
+ SONIFICATION = 4,
+}
diff --git a/media/aidl/android/media/audio/common/AudioEncapsulationMetadataType.aidl b/media/aidl/android/media/audio/common/AudioEncapsulationMetadataType.aidl
new file mode 100644
index 0000000..e0b272c
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioEncapsulationMetadataType.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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 android.media.audio.common;
+
+/**
+ * Enumeration of metadata types permitted for use by encapsulation mode audio
+ * streams (see AudioEncapsulationMode). This type corresponds to
+ * AudioTrack.ENCAPSULATION_METADATA_TYPE_* constants in the SDK.
+ *
+ * {@hide}
+ */
+@Backing(type="int")
+@VintfStability
+enum AudioEncapsulationMetadataType {
+ /** Default value. */
+ NONE = 0,
+ /**
+ * Encapsulation metadata type for framework tuner information.
+ */
+ FRAMEWORK_TUNER = 1,
+ /**
+ * Encapsulation metadata type for DVB AD descriptor.
+ *
+ * This metadata is formatted per ETSI TS 101 154 Table E.1: AD_descriptor.
+ */
+ DVB_AD_DESCRIPTOR = 2,
+}
diff --git a/media/aidl/android/media/audio/common/AudioEncapsulationType.aidl b/media/aidl/android/media/audio/common/AudioEncapsulationType.aidl
new file mode 100644
index 0000000..9e80bd6
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioEncapsulationType.aidl
@@ -0,0 +1,33 @@
+/*
+ * 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 android.media.audio.common;
+
+/**
+ * Audio encapsulation type is used to describe if the audio data should be sent
+ * with a particular encapsulation type or not. This enum corresponds to
+ * AudioProfile.AUDIO_ENCAPSULATION_* constants in the SDK.
+ *
+ * {@hide}
+ */
+@Backing(type="int")
+@VintfStability
+enum AudioEncapsulationType {
+ /** No encapsulation type is specified. */
+ NONE = 0,
+ /** Encapsulation used the format defined in the standard IEC 61937. */
+ IEC61937 = 1,
+}
diff --git a/media/aidl/android/media/audio/common/AudioMode.aidl b/media/aidl/android/media/audio/common/AudioMode.aidl
new file mode 100644
index 0000000..cc03813
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioMode.aidl
@@ -0,0 +1,48 @@
+/*
+ * 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 android.media.audio.common;
+
+/**
+ * Major modes for a mobile device. The current mode setting affects audio
+ * routing.
+ *
+ * {@hide}
+ */
+@Backing(type="int")
+@VintfStability
+enum AudioMode {
+ /**
+ * Used as default value in parcelables to indicate that a value was not
+ * set. Should never be considered a valid setting, except for backward
+ * compatibility scenarios.
+ */
+ SYS_RESERVED_INVALID = -2,
+ /**
+ * Value reserved for system use only. HALs must never return this value to
+ * the system or accept it from the system.
+ */
+ SYS_RESERVED_CURRENT = -1,
+ /** Normal mode (no call in progress). */
+ NORMAL = 0,
+ /** Mobile device is receiving an incoming connection request. */
+ RINGTONE = 1,
+ /** Calls handled by the telephony stack (PSTN). */
+ IN_CALL = 2,
+ /** Calls handled by apps (VoIP). */
+ IN_COMMUNICATION = 3,
+ /** Call screening in progress. */
+ CALL_SCREEN = 4,
+}
diff --git a/media/aidl/android/media/audio/common/AudioSource.aidl b/media/aidl/android/media/audio/common/AudioSource.aidl
new file mode 100644
index 0000000..527ee39
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioSource.aidl
@@ -0,0 +1,90 @@
+/*
+ * 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 android.media.audio.common;
+
+/**
+ * Defines the audio source. An audio source defines both a default physical
+ * source of audio signal, and a recording configuration. This enum corresponds
+ * to MediaRecorder.AudioSource.* constants in the SDK.
+ *
+ * {@hide}
+ */
+@Backing(type="int")
+@VintfStability
+enum AudioSource {
+ /**
+ * Used as default value in parcelables to indicate that a value was not
+ * set. Should never be considered a valid setting, except for backward
+ * compatibility scenarios.
+ */
+ SYS_RESERVED_INVALID = -1,
+ /** Default audio source. */
+ DEFAULT = 0,
+ /** Microphone audio source. */
+ MIC = 1,
+ /** Voice call uplink (Tx) audio source. */
+ VOICE_UPLINK = 2,
+ /** Voice call downlink (Rx) audio source. */
+ VOICE_DOWNLINK = 3,
+ /** Voice call uplink + downlink (duplex) audio source. */
+ VOICE_CALL = 4,
+ /**
+ * Microphone audio source tuned for video recording, with the same
+ * orientation as the camera if available.
+ */
+ CAMCORDER = 5,
+ /** Microphone audio source tuned for voice recognition. */
+ VOICE_RECOGNITION = 6,
+ /**
+ * Microphone audio source tuned for voice communications such as VoIP. It
+ * will for instance take advantage of echo cancellation or automatic gain
+ * control if available.
+ */
+ VOICE_COMMUNICATION = 7,
+ /**
+ * Audio source for a submix of audio streams to be presented remotely. An
+ * application can use this audio source to capture a mix of audio streams
+ * that should be transmitted to a remote receiver such as a Wifi display.
+ * While recording is active, these audio streams are redirected to the
+ * remote submix instead of being played on the device speaker or headset.
+ */
+ REMOTE_SUBMIX = 8,
+ /**
+ * Microphone audio source tuned for unprocessed (raw) sound if available,
+ * behaves like DEFAULT otherwise.
+ */
+ UNPROCESSED = 9,
+ /**
+ * Source for capturing audio meant to be processed in real time and played
+ * back for live performance (e.g karaoke). The capture path will minimize
+ * latency and coupling with playback path.
+ */
+ VOICE_PERFORMANCE = 10,
+ /**
+ * Source for an echo canceller to capture the reference signal to be
+ * canceled. The echo reference signal will be captured as close as
+ * possible to the DAC in order to include all post processing applied to
+ * the playback path.
+ */
+ ECHO_REFERENCE = 1997,
+ /** Audio source for capturing broadcast FM tuner output. */
+ FM_TUNER = 1998,
+ /**
+ * A low-priority, preemptible audio source for for background software
+ * hotword detection. Same tuning as VOICE_RECOGNITION.
+ */
+ HOTWORD = 1999,
+}
diff --git a/media/aidl/android/media/audio/common/AudioStreamType.aidl b/media/aidl/android/media/audio/common/AudioStreamType.aidl
index 1fa3a9c..e7f2961 100644
--- a/media/aidl/android/media/audio/common/AudioStreamType.aidl
+++ b/media/aidl/android/media/audio/common/AudioStreamType.aidl
@@ -37,8 +37,11 @@
* Indicates that the operation is applied to the "default" stream
* in this context, e.g. MUSIC in normal device state, or RING if the
* phone is ringing.
+ *
+ * Value reserved for system use only. HALs must never return this value to
+ * the system or accept it from the system.
*/
- DEFAULT = -1,
+ SYS_RESERVED_DEFAULT = -1,
/** Used to identify the volume of audio streams for phone calls. */
VOICE_CALL = 0,
/** Used to identify the volume of audio streams for system sounds. */
diff --git a/media/aidl/android/media/audio/common/AudioUuid.aidl b/media/aidl/android/media/audio/common/AudioUuid.aidl
new file mode 100644
index 0000000..f2715ff
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioUuid.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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 android.media.audio.common;
+
+/**
+ * Commonly used structure for passing unique identifiers (UUID).
+ * For the definition of UUID, refer to ITU-T X.667 spec.
+ *
+ * {@hide}
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+parcelable AudioUuid {
+ int timeLow;
+ int timeMid;
+ int timeHiAndVersion;
+ int clockSeq;
+ byte[] node; // Length = 6
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioContentType.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioContentType.aidl
new file mode 100644
index 0000000..3798b82
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioContentType.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioContentType {
+ UNKNOWN = 0,
+ SPEECH = 1,
+ MUSIC = 2,
+ MOVIE = 3,
+ SONIFICATION = 4,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioEncapsulationMetadataType.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioEncapsulationMetadataType.aidl
new file mode 100644
index 0000000..0ee0dbb
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioEncapsulationMetadataType.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioEncapsulationMetadataType {
+ NONE = 0,
+ FRAMEWORK_TUNER = 1,
+ DVB_AD_DESCRIPTOR = 2,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioEncapsulationType.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioEncapsulationType.aidl
new file mode 100644
index 0000000..8a31fc4
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioEncapsulationType.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioEncapsulationType {
+ NONE = 0,
+ IEC61937 = 1,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioMode.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioMode.aidl
new file mode 100644
index 0000000..8ae1c10
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioMode.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioMode {
+ SYS_RESERVED_INVALID = -2,
+ SYS_RESERVED_CURRENT = -1,
+ NORMAL = 0,
+ RINGTONE = 1,
+ IN_CALL = 2,
+ IN_COMMUNICATION = 3,
+ CALL_SCREEN = 4,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioSource.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioSource.aidl
new file mode 100644
index 0000000..d1dfe41
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioSource.aidl
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioSource {
+ SYS_RESERVED_INVALID = -1,
+ DEFAULT = 0,
+ MIC = 1,
+ VOICE_UPLINK = 2,
+ VOICE_DOWNLINK = 3,
+ VOICE_CALL = 4,
+ CAMCORDER = 5,
+ VOICE_RECOGNITION = 6,
+ VOICE_COMMUNICATION = 7,
+ REMOTE_SUBMIX = 8,
+ UNPROCESSED = 9,
+ VOICE_PERFORMANCE = 10,
+ ECHO_REFERENCE = 1997,
+ FM_TUNER = 1998,
+ HOTWORD = 1999,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl
index cbca6c5..bcfd374 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl
@@ -36,7 +36,7 @@
@Backing(type="int") @VintfStability
enum AudioStreamType {
INVALID = -2,
- DEFAULT = -1,
+ SYS_RESERVED_DEFAULT = -1,
VOICE_CALL = 0,
SYSTEM = 1,
RING = 2,
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioUuid.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioUuid.aidl
new file mode 100644
index 0000000..af307da
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioUuid.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable AudioUuid {
+ int timeLow;
+ int timeMid;
+ int timeHiAndVersion;
+ int clockSeq;
+ byte[] node;
+}
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 693a027..7f74dd4 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -146,28 +146,24 @@
uint64_t frameNumber;
};
-void ASurfaceControl_registerSurfaceStatsListener(ASurfaceControl* control, void* context,
- ASurfaceControl_SurfaceStatsListener func) {
- SurfaceStatsCallback callback = [func](void* callback_context,
- nsecs_t,
- const sp<Fence>&,
- const SurfaceStats& surfaceStats) {
-
+void ASurfaceControl_registerSurfaceStatsListener(ASurfaceControl* control, int32_t id,
+ void* context,
+ ASurfaceControl_SurfaceStatsListener func) {
+ SurfaceStatsCallback callback = [func, id](void* callback_context, nsecs_t, const sp<Fence>&,
+ const SurfaceStats& surfaceStats) {
ASurfaceControlStats aSurfaceControlStats;
- ASurfaceControl* aSurfaceControl =
- reinterpret_cast<ASurfaceControl*>(surfaceStats.surfaceControl.get());
aSurfaceControlStats.acquireTime = surfaceStats.acquireTime;
aSurfaceControlStats.previousReleaseFence = surfaceStats.previousReleaseFence;
aSurfaceControlStats.frameNumber = surfaceStats.eventStats.frameNumber;
- (*func)(callback_context, aSurfaceControl, &aSurfaceControlStats);
+ (*func)(callback_context, id, &aSurfaceControlStats);
};
+
TransactionCompletedListener::getInstance()->addSurfaceStatsListener(context,
reinterpret_cast<void*>(func), ASurfaceControl_to_SurfaceControl(control), callback);
}
-
void ASurfaceControl_unregisterSurfaceStatsListener(void* context,
ASurfaceControl_SurfaceStatsListener func) {
TransactionCompletedListener::getInstance()->removeSurfaceStatsListener(context,
@@ -662,4 +658,4 @@
Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
transaction->addTransactionCommittedCallback(callback, context);
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/res/layout/qs_user_dialog_content.xml b/packages/SystemUI/res/layout/qs_user_dialog_content.xml
index 377da45..321fe68 100644
--- a/packages/SystemUI/res/layout/qs_user_dialog_content.xml
+++ b/packages/SystemUI/res/layout/qs_user_dialog_content.xml
@@ -31,7 +31,7 @@
android:layout_height="wrap_content"
android:layout_width="0dp"
android:textAlignment="center"
- android:text="Select user"
+ android:text="@string/qs_user_switch_dialog_title"
android:textAppearance="@style/TextAppearance.QSDialog.Title"
android:layout_marginBottom="32dp"
sysui:layout_constraintTop_toTopOf="parent"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 5545437..8f80421 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3030,4 +3030,7 @@
<string name="qs_tile_request_dialog_add">Add tile</string>
<!-- Text for TileService request dialog. Text for button for user to reject adding the tile [CHAR LIMIT=20] -->
<string name="qs_tile_request_dialog_not_add">Do not add tile</string>
+
+ <!-- Title for User Switch dialog. [CHAR LIMIT=20] -->
+ <string name="qs_user_switch_dialog_title">Select user</string>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index bcc0530..0790af9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -42,6 +42,7 @@
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.FingerprintStateListener;
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
import android.hardware.fingerprint.IUdfpsHbmListener;
import android.os.Bundle;
@@ -49,6 +50,7 @@
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
+import android.util.SparseBooleanArray;
import android.view.MotionEvent;
import android.view.WindowManager;
@@ -76,6 +78,9 @@
/**
* Receives messages sent from {@link com.android.server.biometrics.BiometricService} and shows the
* appropriate biometric UI (e.g. BiometricDialogView).
+ *
+ * Also coordinates biometric-related things, such as UDFPS, with
+ * {@link com.android.keyguard.KeyguardUpdateMonitor}
*/
@SysUISingleton
public class AuthController extends SystemUI implements CommandQueue.Callbacks,
@@ -115,6 +120,8 @@
@Nullable private List<FingerprintSensorPropertiesInternal> mUdfpsProps;
@Nullable private List<FingerprintSensorPropertiesInternal> mSidefpsProps;
+ @NonNull private final SparseBooleanArray mUdfpsEnrolledForUser;
+
private class BiometricTaskStackListener extends TaskStackListener {
@Override
public void onTaskStackChanged() {
@@ -122,6 +129,21 @@
}
}
+ private final FingerprintStateListener mFingerprintStateListener =
+ new FingerprintStateListener() {
+ @Override
+ public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
+ Log.d(TAG, "onEnrollmentsChanged, userId: " + userId
+ + ", sensorId: " + sensorId
+ + ", hasEnrollments: " + hasEnrollments);
+ for (FingerprintSensorPropertiesInternal prop : mUdfpsProps) {
+ if (prop.sensorId == sensorId) {
+ mUdfpsEnrolledForUser.put(userId, hasEnrollments);
+ }
+ }
+ }
+ };
+
@NonNull
private final IFingerprintAuthenticatorsRegisteredCallback
mFingerprintAuthenticatorsRegisteredCallback =
@@ -436,6 +458,7 @@
mUdfpsControllerFactory = udfpsControllerFactory;
mSidefpsControllerFactory = sidefpsControllerFactory;
mWindowManager = windowManager;
+ mUdfpsEnrolledForUser = new SparseBooleanArray();
mOrientationListener = new BiometricOrientationEventListener(context,
() -> {
onOrientationChanged();
@@ -474,6 +497,7 @@
if (mFingerprintManager != null) {
mFingerprintManager.addAuthenticatorsRegisteredCallback(
mFingerprintAuthenticatorsRegisteredCallback);
+ mFingerprintManager.registerFingerprintStateListener(mFingerprintStateListener);
}
mTaskStackListener = new BiometricTaskStackListener();
@@ -673,7 +697,7 @@
return false;
}
- return mFingerprintManager.hasEnrolledTemplatesForAnySensor(userId, mUdfpsProps);
+ return mUdfpsEnrolledForUser.get(userId);
}
private void showDialog(SomeArgs args, boolean skipAnimation, Bundle savedState) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index c2716b9..a472710 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -75,7 +75,7 @@
private float mDividerAlpha;
private int mNotificationHeaderMargin;
- private int mNotificatonTopPadding;
+ private int mNotificationTopPadding;
private float mCollapsedBottompadding;
private boolean mChildrenExpanded;
private ExpandableNotificationRow mContainingNotification;
@@ -140,9 +140,9 @@
mDividerAlpha = res.getFloat(R.dimen.notification_divider_alpha);
mNotificationHeaderMargin = res.getDimensionPixelSize(
R.dimen.notification_children_container_margin_top);
- mNotificatonTopPadding = res.getDimensionPixelSize(
+ mNotificationTopPadding = res.getDimensionPixelSize(
R.dimen.notification_children_container_top_padding);
- mHeaderHeight = mNotificationHeaderMargin + mNotificatonTopPadding;
+ mHeaderHeight = mNotificationHeaderMargin + mNotificationTopPadding;
mCollapsedBottompadding = res.getDimensionPixelSize(
com.android.internal.R.dimen.notification_content_margin);
mEnableShadowOnChildNotifications =
@@ -203,7 +203,7 @@
newHeightSpec);
}
int dividerHeightSpec = MeasureSpec.makeMeasureSpec(mDividerHeight, MeasureSpec.EXACTLY);
- int height = mNotificationHeaderMargin + mNotificatonTopPadding;
+ int height = mNotificationHeaderMargin + mNotificationTopPadding;
int childCount =
Math.min(mAttachedChildren.size(), NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
int collapsedChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
@@ -526,11 +526,11 @@
if (mUserLocked) {
intrinsicHeight += NotificationUtils.interpolate(
0,
- mNotificatonTopPadding + mDividerHeight,
+ mNotificationTopPadding + mDividerHeight,
expandFactor);
} else {
intrinsicHeight += childrenExpanded
- ? mNotificatonTopPadding + mDividerHeight
+ ? mNotificationTopPadding + mDividerHeight
: 0;
}
firstChild = false;
@@ -583,10 +583,10 @@
if (expandingToExpandedGroup) {
yPosition += NotificationUtils.interpolate(
0,
- mNotificatonTopPadding + mDividerHeight,
+ mNotificationTopPadding + mDividerHeight,
expandFactor);
} else {
- yPosition += mChildrenExpanded ? mNotificatonTopPadding + mDividerHeight : 0;
+ yPosition += mChildrenExpanded ? mNotificationTopPadding + mDividerHeight : 0;
}
firstChild = false;
}
@@ -1009,7 +1009,7 @@
/* likeHighPriority */);
}
int maxContentHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation
- + mNotificatonTopPadding;
+ + mNotificationTopPadding;
int visibleChildren = 0;
int childCount = mAttachedChildren.size();
for (int i = 0; i < childCount; i++) {
@@ -1072,7 +1072,7 @@
private int getVisibleChildrenExpandHeight() {
int intrinsicHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation
- + mNotificatonTopPadding + mDividerHeight;
+ + mNotificationTopPadding + mDividerHeight;
int visibleChildren = 0;
int childCount = mAttachedChildren.size();
int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* forceCollapsed */);
@@ -1212,7 +1212,7 @@
public int getPositionInLinearLayout(View childInGroup) {
int position = mNotificationHeaderMargin + mCurrentHeaderTranslation
- + mNotificatonTopPadding;
+ + mNotificationTopPadding;
for (int i = 0; i < mAttachedChildren.size(); i++) {
ExpandableNotificationRow child = mAttachedChildren.get(i);
diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
index 0a1c77b..9764a16 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
@@ -26,6 +26,8 @@
import com.android.internal.annotations.VisibleForTesting;
+import java.util.ArrayList;
+import java.util.List;
import java.util.NoSuchElementException;
/**
@@ -70,26 +72,32 @@
}
/** Holder for wrapping multiple handlers into a single Callback. */
- protected static class CompositeCallback implements Callback {
+ public static class CompositeCallback implements Callback {
@NonNull
- private final Callback[] mCallbacks;
+ private final List<Callback> mCallbacks;
public CompositeCallback(@NonNull Callback... callbacks) {
- mCallbacks = callbacks;
+ mCallbacks = new ArrayList<>();
+
+ for (Callback callback : callbacks) {
+ if (callback != null) {
+ mCallbacks.add(callback);
+ }
+ }
}
@Override
public final void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
- for (int i = 0; i < mCallbacks.length; i++) {
- mCallbacks[i].onClientStarted(clientMonitor);
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).onClientStarted(clientMonitor);
}
}
@Override
public final void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
boolean success) {
- for (int i = mCallbacks.length - 1; i >= 0; i--) {
- mCallbacks[i].onClientFinished(clientMonitor, success);
+ for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+ mCallbacks.get(i).onClientFinished(clientMonitor, success);
}
}
}
@@ -256,7 +264,7 @@
return mToken;
}
- public final int getSensorId() {
+ public int getSensorId() {
return mSensorId;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
index a15e14b..9191b8b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
@@ -31,7 +31,7 @@
/**
* A class to keep track of the enrollment state for a given client.
*/
-public abstract class EnrollClient<T> extends AcquisitionClient<T> {
+public abstract class EnrollClient<T> extends AcquisitionClient<T> implements EnrollmentModifier {
private static final String TAG = "Biometrics/EnrollClient";
@@ -40,6 +40,7 @@
protected final BiometricUtils mBiometricUtils;
private long mEnrollmentStartTimeMs;
+ private final boolean mHasEnrollmentsBeforeStarting;
/**
* @return true if the user has already enrolled the maximum number of templates.
@@ -56,6 +57,18 @@
mBiometricUtils = utils;
mHardwareAuthToken = Arrays.copyOf(hardwareAuthToken, hardwareAuthToken.length);
mTimeoutSec = timeoutSec;
+ mHasEnrollmentsBeforeStarting = hasEnrollments();
+ }
+
+ @Override
+ public boolean hasEnrollmentStateChanged() {
+ final boolean hasEnrollmentsNow = hasEnrollments();
+ return hasEnrollmentsNow != mHasEnrollmentsBeforeStarting;
+ }
+
+ @Override
+ public boolean hasEnrollments() {
+ return !mBiometricUtils.getBiometricsForUser(getContext(), getTargetUserId()).isEmpty();
}
public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollmentModifier.java b/services/core/java/com/android/server/biometrics/sensors/EnrollmentModifier.java
new file mode 100644
index 0000000..c2f909b
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollmentModifier.java
@@ -0,0 +1,39 @@
+/*
+ * 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.biometrics.sensors;
+
+/**
+ * Interface for {@link BaseClientMonitor} subclasses that affect the state of enrollment.
+ */
+public interface EnrollmentModifier {
+
+ /**
+ * Callers should typically check this after
+ * {@link BaseClientMonitor.Callback#onClientFinished(BaseClientMonitor, boolean)}
+ *
+ * @return true if the user has gone from:
+ * 1) none-enrolled --> enrolled
+ * 2) enrolled --> none-enrolled
+ * but NOT any-enrolled --> more-enrolled
+ */
+ boolean hasEnrollmentStateChanged();
+
+ /**
+ * @return true if the user has any enrollments
+ */
+ boolean hasEnrollments();
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
index 282261e..579dfd6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
@@ -40,7 +40,8 @@
* {@link #onRemoved(BiometricAuthenticator.Identifier, int)} returns true/
*/
public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Identifier, T>
- extends HalClientMonitor<T> implements EnumerateConsumer, RemovalConsumer {
+ extends HalClientMonitor<T> implements EnumerateConsumer, RemovalConsumer,
+ EnrollmentModifier {
private static final String TAG = "Biometrics/InternalCleanupClient";
@@ -61,6 +62,7 @@
private final BiometricUtils<S> mBiometricUtils;
private final Map<Integer, Long> mAuthenticatorIds;
private final List<S> mEnrolledList;
+ private final boolean mHasEnrollmentsBeforeStarting;
private BaseClientMonitor mCurrentTask;
private final Callback mEnumerateCallback = new Callback() {
@@ -115,6 +117,7 @@
mBiometricUtils = utils;
mAuthenticatorIds = authenticatorIds;
mEnrolledList = enrolledList;
+ mHasEnrollmentsBeforeStarting = !utils.getBiometricsForUser(context, userId).isEmpty();
}
private void startCleanupUnknownHalTemplates() {
@@ -166,6 +169,18 @@
}
@Override
+ public boolean hasEnrollmentStateChanged() {
+ final boolean hasEnrollmentsNow = !mBiometricUtils
+ .getBiometricsForUser(getContext(), getTargetUserId()).isEmpty();
+ return hasEnrollmentsNow != mHasEnrollmentsBeforeStarting;
+ }
+
+ @Override
+ public boolean hasEnrollments() {
+ return !mBiometricUtils.getBiometricsForUser(getContext(), getTargetUserId()).isEmpty();
+ }
+
+ @Override
public void onEnumerationResult(BiometricAuthenticator.Identifier identifier,
int remaining) {
if (!(mCurrentTask instanceof InternalEnumerateClient)) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
index 383efce..2a6677e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
@@ -33,12 +33,13 @@
* A class to keep track of the remove state for a given client.
*/
public abstract class RemovalClient<S extends BiometricAuthenticator.Identifier, T>
- extends HalClientMonitor<T> implements RemovalConsumer {
+ extends HalClientMonitor<T> implements RemovalConsumer, EnrollmentModifier {
private static final String TAG = "Biometrics/RemovalClient";
private final BiometricUtils<S> mBiometricUtils;
private final Map<Integer, Long> mAuthenticatorIds;
+ private final boolean mHasEnrollmentsBeforeStarting;
public RemovalClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
@@ -49,6 +50,7 @@
BiometricsProtoEnums.CLIENT_UNKNOWN);
mBiometricUtils = utils;
mAuthenticatorIds = authenticatorIds;
+ mHasEnrollmentsBeforeStarting = !utils.getBiometricsForUser(context, userId).isEmpty();
}
@Override
@@ -91,6 +93,18 @@
}
@Override
+ public boolean hasEnrollmentStateChanged() {
+ final boolean hasEnrollmentsNow = !mBiometricUtils
+ .getBiometricsForUser(getContext(), getTargetUserId()).isEmpty();
+ return hasEnrollmentsNow != mHasEnrollmentsBeforeStarting;
+ }
+
+ @Override
+ public boolean hasEnrollments() {
+ return !mBiometricUtils.getBiometricsForUser(getContext(), getTargetUserId()).isEmpty();
+ }
+
+ @Override
public int getProtoEnum() {
return BiometricsProto.CM_REMOVE;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index fa02c51..af8e8c2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -35,6 +35,7 @@
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.BiometricsProtoEnums;
@@ -62,11 +63,13 @@
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
import android.util.EventLog;
import android.util.Pair;
@@ -111,6 +114,7 @@
private final FingerprintServiceWrapper mServiceWrapper;
@NonNull private final List<ServiceProvider> mServiceProviders;
@NonNull private final FingerprintStateCallback mFingerprintStateCallback;
+ @NonNull private final Handler mHandler;
@GuardedBy("mLock")
@NonNull private final RemoteCallbackList<IFingerprintAuthenticatorsRegisteredCallback>
@@ -125,6 +129,37 @@
*/
public void registerFingerprintStateListener(@NonNull IFingerprintStateListener listener) {
mFingerprintStateCallback.registerFingerprintStateListener(listener);
+ broadcastCurrentEnrollmentState(listener);
+ }
+
+ /**
+ * @param listener if non-null, notifies only this listener. if null, notifies all listeners
+ * in {@link FingerprintStateCallback}. This is slightly ugly, but reduces
+ * redundant code.
+ */
+ private void broadcastCurrentEnrollmentState(@Nullable IFingerprintStateListener listener) {
+ final UserManager um = UserManager.get(getContext());
+ synchronized (mLock) {
+ // Update the new listener with current state of all sensors
+ for (FingerprintSensorPropertiesInternal prop : mSensorProps) {
+ final ServiceProvider provider = getProviderForSensor(prop.sensorId);
+ for (UserInfo userInfo : um.getAliveUsers()) {
+ final boolean enrolled = !provider
+ .getEnrolledFingerprints(prop.sensorId, userInfo.id).isEmpty();
+
+ // Defer this work and allow the loop to release the lock sooner
+ mHandler.post(() -> {
+ if (listener != null) {
+ mFingerprintStateCallback.notifyFingerprintEnrollmentStateChanged(
+ listener, userInfo.id, prop.sensorId, enrolled);
+ } else {
+ mFingerprintStateCallback.notifyAllFingerprintEnrollmentStateChanged(
+ userInfo.id, prop.sensorId, enrolled);
+ }
+ });
+ }
+ }
+ }
}
/**
@@ -143,8 +178,7 @@
return null;
}
- return provider.createTestSession(sensorId, callback, mFingerprintStateCallback,
- opPackageName);
+ return provider.createTestSession(sensorId, callback, opPackageName);
}
@Override
@@ -227,7 +261,7 @@
}
provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
- receiver, opPackageName, enrollReason, mFingerprintStateCallback);
+ receiver, opPackageName, enrollReason);
}
@Override // Binder call
@@ -306,7 +340,7 @@
}
return provider.second.scheduleAuthenticate(provider.first, token, operationId, userId,
0 /* cookie */, new ClientMonitorCallbackConverter(receiver), opPackageName,
- restricted, statsClient, isKeyguard, mFingerprintStateCallback);
+ restricted, statsClient, isKeyguard);
}
private long authenticateWithPrompt(
@@ -414,7 +448,7 @@
return provider.second.scheduleFingerDetect(provider.first, token, userId,
new ClientMonitorCallbackConverter(receiver), opPackageName,
- BiometricsProtoEnums.CLIENT_KEYGUARD, mFingerprintStateCallback);
+ BiometricsProtoEnums.CLIENT_KEYGUARD);
}
@Override // Binder call
@@ -433,7 +467,7 @@
provider.scheduleAuthenticate(sensorId, token, operationId, userId, cookie,
new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, requestId,
restricted, BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
- allowBackgroundAuthentication, mFingerprintStateCallback);
+ allowBackgroundAuthentication);
}
@Override // Binder call
@@ -687,27 +721,6 @@
.isEmpty();
}
- @Override // Binder call
- public boolean hasEnrolledTemplatesForAnySensor(int userId,
- @NonNull List<FingerprintSensorPropertiesInternal> sensors,
- @NonNull String opPackageName) {
- Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
-
- for (FingerprintSensorPropertiesInternal prop : sensors) {
- final ServiceProvider provider = getProviderForSensor(prop.sensorId);
- if (provider == null) {
- Slog.w(TAG, "Null provider for sensorId: " + prop.sensorId
- + ", caller: " + opPackageName);
- continue;
- }
-
- if (!provider.getEnrolledFingerprints(prop.sensorId, userId).isEmpty()) {
- return true;
- }
- }
- return false;
- }
-
public boolean hasEnrolledFingerprints(int sensorId, int userId, String opPackageName) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
@@ -797,10 +810,12 @@
&& Settings.Secure.getIntForUser(getContext().getContentResolver(),
Fingerprint21UdfpsMock.CONFIG_ENABLE_TEST_UDFPS, 0 /* default */,
UserHandle.USER_CURRENT) != 0) {
- fingerprint21 = Fingerprint21UdfpsMock.newInstance(getContext(), hidlSensor,
+ fingerprint21 = Fingerprint21UdfpsMock.newInstance(getContext(),
+ mFingerprintStateCallback, hidlSensor,
mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
} else {
- fingerprint21 = Fingerprint21.newInstance(getContext(), hidlSensor,
+ fingerprint21 = Fingerprint21.newInstance(getContext(),
+ mFingerprintStateCallback, hidlSensor,
mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
}
mServiceProviders.add(fingerprint21);
@@ -823,8 +838,9 @@
try {
final SensorProps[] props = fp.getSensorProps();
final FingerprintProvider provider =
- new FingerprintProvider(getContext(), props, instance,
- mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
+ new FingerprintProvider(getContext(), mFingerprintStateCallback, props,
+ instance, mLockoutResetDispatcher,
+ mGestureAvailabilityDispatcher);
mServiceProviders.add(provider);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception in getSensorProps: " + fqName);
@@ -878,6 +894,7 @@
}
}
+ broadcastCurrentEnrollmentState(null); // broadcasts to all listeners
broadcastAllAuthenticatorsRegistered();
});
}
@@ -975,6 +992,7 @@
mFingerprintStateCallback = new FingerprintStateCallback();
mAuthenticatorsRegisteredCallbacks = new RemoteCallbackList<>();
mSensorProps = new ArrayList<>();
+ mHandler = new Handler(Looper.getMainLooper());
}
// Notifies the callbacks that all of the authenticators have been registered and removes the
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallback.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallback.java
index 5f998d8..0050a89 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallback.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallback.java
@@ -23,6 +23,7 @@
import static android.hardware.fingerprint.FingerprintStateListener.STATE_KEYGUARD_AUTH;
import android.annotation.NonNull;
+import android.content.Context;
import android.hardware.fingerprint.FingerprintStateListener;
import android.hardware.fingerprint.IFingerprintStateListener;
import android.os.RemoteException;
@@ -31,6 +32,9 @@
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.EnrollClient;
+import com.android.server.biometrics.sensors.EnrollmentModifier;
+import com.android.server.biometrics.sensors.RemovalConsumer;
import com.android.server.biometrics.sensors.fingerprint.hidl.FingerprintEnrollClient;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -39,9 +43,11 @@
* A callback for receiving notifications about changes in fingerprint state.
*/
public class FingerprintStateCallback implements BaseClientMonitor.Callback {
- private @FingerprintStateListener.State int mFingerprintState;
+
@NonNull private final CopyOnWriteArrayList<IFingerprintStateListener>
- mFingerprintStateListeners = new CopyOnWriteArrayList<>();
+ mFingerprintStateListeners = new CopyOnWriteArrayList<>();
+
+ private @FingerprintStateListener.State int mFingerprintState;
public FingerprintStateCallback() {
mFingerprintState = STATE_IDLE;
@@ -54,8 +60,9 @@
@Override
public void onClientStarted(@NonNull BaseClientMonitor client) {
final int previousFingerprintState = mFingerprintState;
+
if (client instanceof AuthenticationClient) {
- AuthenticationClient authClient = (AuthenticationClient) client;
+ final AuthenticationClient<?> authClient = (AuthenticationClient<?>) client;
if (authClient.isKeyguard()) {
mFingerprintState = STATE_KEYGUARD_AUTH;
} else if (authClient.isBiometricPrompt()) {
@@ -70,6 +77,7 @@
"Other authentication client: " + Utils.getClientName(client));
mFingerprintState = STATE_IDLE;
}
+
Slog.d(FingerprintService.TAG, "Fps state updated from " + previousFingerprintState
+ " to " + mFingerprintState + ", client " + client);
notifyFingerprintStateListeners(mFingerprintState);
@@ -81,6 +89,18 @@
Slog.d(FingerprintService.TAG,
"Client finished, fps state updated to " + mFingerprintState + ", client "
+ client);
+
+ if (client instanceof EnrollmentModifier) {
+ EnrollmentModifier enrollmentModifier = (EnrollmentModifier) client;
+ final boolean enrollmentStateChanged = enrollmentModifier.hasEnrollmentStateChanged();
+ Slog.d(FingerprintService.TAG, "Enrollment state changed: " + enrollmentStateChanged);
+ if (enrollmentStateChanged) {
+ notifyAllFingerprintEnrollmentStateChanged(client.getTargetUserId(),
+ client.getSensorId(),
+ enrollmentModifier.hasEnrollments());
+ }
+ }
+
notifyFingerprintStateListeners(mFingerprintState);
}
@@ -95,6 +115,32 @@
}
/**
+ * This should be invoked when:
+ * 1) Enrolled --> None-enrolled
+ * 2) None-enrolled --> enrolled
+ * 3) HAL becomes ready
+ * 4) Listener is registered
+ */
+ void notifyAllFingerprintEnrollmentStateChanged(int userId, int sensorId,
+ boolean hasEnrollments) {
+ for (IFingerprintStateListener listener : mFingerprintStateListeners) {
+ notifyFingerprintEnrollmentStateChanged(listener, userId, sensorId, hasEnrollments);
+ }
+ }
+
+ /**
+ * Notifies the listener of enrollment state changes.
+ */
+ void notifyFingerprintEnrollmentStateChanged(@NonNull IFingerprintStateListener listener,
+ int userId, int sensorId, boolean hasEnrollments) {
+ try {
+ listener.onEnrollmentsChanged(userId, sensorId, hasEnrollments);
+ } catch (RemoteException e) {
+ Slog.e(FingerprintService.TAG, "Remote exception", e);
+ }
+ }
+
+ /**
* Enables clients to register a FingerprintStateListener. Used by FingerprintService to forward
* updates in fingerprint sensor state to the SideFpNsEventHandler
* @param listener
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index b9fcd8e..1772f81 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -90,27 +90,23 @@
*/
void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken,
int userId, @NonNull IFingerprintServiceReceiver receiver,
- @NonNull String opPackageName, @FingerprintManager.EnrollReason int enrollReason,
- @NonNull FingerprintStateCallback fingerprintStateCallback);
+ @NonNull String opPackageName, @FingerprintManager.EnrollReason int enrollReason);
void cancelEnrollment(int sensorId, @NonNull IBinder token);
long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
@NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
- int statsClient,
- @NonNull FingerprintStateCallback fingerprintStateCallback);
+ int statsClient);
void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId,
int cookie, @NonNull ClientMonitorCallbackConverter callback,
@NonNull String opPackageName, long requestId, boolean restricted, int statsClient,
- boolean allowBackgroundAuthentication,
- @NonNull FingerprintStateCallback fingerprintStateCallback);
+ boolean allowBackgroundAuthentication);
long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId,
int cookie, @NonNull ClientMonitorCallbackConverter callback,
@NonNull String opPackageName, boolean restricted, int statsClient,
- boolean allowBackgroundAuthentication,
- @NonNull FingerprintStateCallback fingerprintStateCallback);
+ boolean allowBackgroundAuthentication);
void startPreparedClient(int sensorId, int cookie);
@@ -169,6 +165,5 @@
@NonNull
ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
- @NonNull FingerprintStateCallback fingerprintStateCallback,
@NonNull String opPackageName);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
index 29f2f20..2b50b96 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
@@ -143,8 +143,7 @@
Utils.checkPermission(mContext, TEST_BIOMETRIC);
mProvider.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
- mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL,
- mFingerprintStateCallback);
+ mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 377feca..ca83dda 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -82,6 +82,7 @@
private boolean mTestHalEnabled;
@NonNull private final Context mContext;
+ @NonNull private final FingerprintStateCallback mFingerprintStateCallback;
@NonNull private final String mHalInstanceName;
@NonNull @VisibleForTesting
final SparseArray<Sensor> mSensors; // Map of sensors that this HAL supports
@@ -130,10 +131,13 @@
}
}
- public FingerprintProvider(@NonNull Context context, @NonNull SensorProps[] props,
- @NonNull String halInstanceName, @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ public FingerprintProvider(@NonNull Context context,
+ @NonNull FingerprintStateCallback fingerprintStateCallback,
+ @NonNull SensorProps[] props, @NonNull String halInstanceName,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
mContext = context;
+ mFingerprintStateCallback = fingerprintStateCallback;
mHalInstanceName = halInstanceName;
mSensors = new SparseArray<>();
mHandler = new Handler(Looper.getMainLooper());
@@ -335,8 +339,7 @@
public void scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
- @FingerprintManager.EnrollReason int enrollReason,
- @NonNull FingerprintStateCallback fingerprintStateCallback) {
+ @FingerprintManager.EnrollReason int enrollReason) {
mHandler.post(() -> {
final int maxTemplatesPerUser = mSensors.get(sensorId).getSensorProperties()
.maxEnrollmentsPerUser;
@@ -350,13 +353,13 @@
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
- fingerprintStateCallback.onClientStarted(clientMonitor);
+ mFingerprintStateCallback.onClientStarted(clientMonitor);
}
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
boolean success) {
- fingerprintStateCallback.onClientFinished(clientMonitor, success);
+ mFingerprintStateCallback.onClientFinished(clientMonitor, success);
if (success) {
scheduleLoadAuthenticatorIdsForUser(sensorId, userId);
scheduleInvalidationRequest(sensorId, userId);
@@ -374,17 +377,15 @@
@Override
public long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
@NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
- int statsClient,
- @NonNull FingerprintStateCallback fingerprintStateCallback) {
+ int statsClient) {
final long id = mRequestCounter.incrementAndGet();
-
mHandler.post(() -> {
final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
final FingerprintDetectClient client = new FingerprintDetectClient(mContext,
mSensors.get(sensorId).getLazySession(), token, id, callback, userId,
opPackageName, sensorId, mUdfpsOverlayController, isStrongBiometric,
statsClient);
- scheduleForSensor(sensorId, client, fingerprintStateCallback);
+ scheduleForSensor(sensorId, client, mFingerprintStateCallback);
});
return id;
@@ -394,8 +395,7 @@
public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback,
@NonNull String opPackageName, long requestId, boolean restricted, int statsClient,
- boolean allowBackgroundAuthentication,
- @NonNull FingerprintStateCallback fingerprintStateCallback) {
+ boolean allowBackgroundAuthentication) {
mHandler.post(() -> {
final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
@@ -405,7 +405,7 @@
mTaskStackListener, mSensors.get(sensorId).getLockoutCache(),
mUdfpsOverlayController, allowBackgroundAuthentication,
mSensors.get(sensorId).getSensorProperties());
- scheduleForSensor(sensorId, client, fingerprintStateCallback);
+ scheduleForSensor(sensorId, client, mFingerprintStateCallback);
});
}
@@ -413,13 +413,11 @@
public long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback,
@NonNull String opPackageName, boolean restricted, int statsClient,
- boolean allowBackgroundAuthentication,
- @NonNull FingerprintStateCallback fingerprintStateCallback) {
+ boolean allowBackgroundAuthentication) {
final long id = mRequestCounter.incrementAndGet();
scheduleAuthenticate(sensorId, token, operationId, userId, cookie, callback,
- opPackageName, id, restricted, statsClient, allowBackgroundAuthentication,
- fingerprintStateCallback);
+ opPackageName, id, restricted, statsClient, allowBackgroundAuthentication);
return id;
}
@@ -466,7 +464,7 @@
new ClientMonitorCallbackConverter(receiver), fingerprintIds, userId,
opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
mSensors.get(sensorId).getAuthenticatorIds());
- scheduleForSensor(sensorId, client);
+ scheduleForSensor(sensorId, client, mFingerprintStateCallback);
});
}
@@ -481,7 +479,8 @@
mContext.getOpPackageName(), sensorId, enrolledList,
FingerprintUtils.getInstance(sensorId),
mSensors.get(sensorId).getAuthenticatorIds());
- scheduleForSensor(sensorId, client, callback);
+ scheduleForSensor(sensorId, client, new BaseClientMonitor.CompositeCallback(callback,
+ mFingerprintStateCallback));
});
}
@@ -626,9 +625,8 @@
@NonNull
@Override
public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
- @NonNull FingerprintStateCallback fingerprintStateCallback,
@NonNull String opPackageName) {
- return mSensors.get(sensorId).createTestSession(callback, fingerprintStateCallback);
+ return mSensors.get(sensorId).createTestSession(callback, mFingerprintStateCallback);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
index c00daff..79c6b1b3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
@@ -143,8 +143,7 @@
Utils.checkPermission(mContext, TEST_BIOMETRIC);
mFingerprint21.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
- mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL,
- mFingerprintStateCallback);
+ mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index f17bcc8..d2882aa4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -102,6 +102,7 @@
private boolean mTestHalEnabled;
final Context mContext;
+ @NonNull private final FingerprintStateCallback mFingerprintStateCallback;
private final ActivityTaskManager mActivityTaskManager;
@NonNull private final FingerprintSensorPropertiesInternal mSensorProperties;
private final BiometricScheduler mScheduler;
@@ -317,11 +318,13 @@
}
Fingerprint21(@NonNull Context context,
+ @NonNull FingerprintStateCallback fingerprintStateCallback,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
@NonNull BiometricScheduler scheduler, @NonNull Handler handler,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull HalResultController controller) {
mContext = context;
+ mFingerprintStateCallback = fingerprintStateCallback;
mSensorProperties = sensorProps;
mSensorId = sensorProps.sensorId;
@@ -351,6 +354,7 @@
}
public static Fingerprint21 newInstance(@NonNull Context context,
+ @NonNull FingerprintStateCallback fingerprintStateCallback,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
@@ -362,8 +366,8 @@
final HalResultController controller = new HalResultController(sensorProps.sensorId,
context, handler,
scheduler);
- return new Fingerprint21(context, sensorProps, scheduler, handler, lockoutResetDispatcher,
- controller);
+ return new Fingerprint21(context, fingerprintStateCallback, sensorProps, scheduler, handler,
+ lockoutResetDispatcher, controller);
}
@Override
@@ -557,8 +561,7 @@
public void scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
- @FingerprintManager.EnrollReason int enrollReason,
- @NonNull FingerprintStateCallback fingerprintStateCallback) {
+ @FingerprintManager.EnrollReason int enrollReason) {
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
@@ -570,13 +573,13 @@
mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
- fingerprintStateCallback.onClientStarted(clientMonitor);
+ mFingerprintStateCallback.onClientStarted(clientMonitor);
}
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
boolean success) {
- fingerprintStateCallback.onClientFinished(clientMonitor, success);
+ mFingerprintStateCallback.onClientFinished(clientMonitor, success);
if (success) {
// Update authenticatorIds
scheduleUpdateActiveUserWithoutHandler(clientMonitor.getTargetUserId(),
@@ -597,10 +600,8 @@
@Override
public long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
@NonNull ClientMonitorCallbackConverter listener, @NonNull String opPackageName,
- int statsClient,
- @NonNull FingerprintStateCallback fingerprintStateCallback) {
+ int statsClient) {
final long id = mRequestCounter.incrementAndGet();
-
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
@@ -609,7 +610,7 @@
mLazyDaemon, token, id, listener, userId, opPackageName,
mSensorProperties.sensorId, mUdfpsOverlayController, isStrongBiometric,
statsClient);
- mScheduler.scheduleClientMonitor(client, fingerprintStateCallback);
+ mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback);
});
return id;
@@ -619,8 +620,7 @@
public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
int userId, int cookie, @NonNull ClientMonitorCallbackConverter listener,
@NonNull String opPackageName, long requestId, boolean restricted, int statsClient,
- boolean allowBackgroundAuthentication,
- @NonNull FingerprintStateCallback fingerprintStateCallback) {
+ boolean allowBackgroundAuthentication) {
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
@@ -631,7 +631,7 @@
mSensorProperties.sensorId, isStrongBiometric, statsClient,
mTaskStackListener, mLockoutTracker, mUdfpsOverlayController,
allowBackgroundAuthentication, mSensorProperties);
- mScheduler.scheduleClientMonitor(client, fingerprintStateCallback);
+ mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback);
});
}
@@ -639,13 +639,11 @@
public long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
int userId, int cookie, @NonNull ClientMonitorCallbackConverter listener,
@NonNull String opPackageName, boolean restricted, int statsClient,
- boolean allowBackgroundAuthentication,
- @NonNull FingerprintStateCallback fingerprintStateCallback) {
+ boolean allowBackgroundAuthentication) {
final long id = mRequestCounter.incrementAndGet();
scheduleAuthenticate(sensorId, token, operationId, userId, cookie, listener,
- opPackageName, id, restricted, statsClient, allowBackgroundAuthentication,
- fingerprintStateCallback);
+ opPackageName, id, restricted, statsClient, allowBackgroundAuthentication);
return id;
}
@@ -672,7 +670,7 @@
mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), fingerId,
userId, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
mSensorProperties.sensorId, mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client);
+ mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback);
});
}
@@ -689,7 +687,7 @@
0 /* fingerprintId */, userId, opPackageName,
FingerprintUtils.getLegacyInstance(mSensorId),
mSensorProperties.sensorId, mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client);
+ mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback);
});
}
@@ -711,7 +709,8 @@
@Override
public void scheduleInternalCleanup(int sensorId, int userId,
@Nullable BaseClientMonitor.Callback callback) {
- scheduleInternalCleanup(userId, callback);
+ scheduleInternalCleanup(userId, new BaseClientMonitor.CompositeCallback(callback,
+ mFingerprintStateCallback));
}
@Override
@@ -919,9 +918,8 @@
@NonNull
@Override
public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
- @NonNull FingerprintStateCallback fingerprintStateCallback,
@NonNull String opPackageName) {
return new BiometricTestSessionImpl(mContext, mSensorProperties.sensorId, callback,
- fingerprintStateCallback, this, mHalResultController);
+ mFingerprintStateCallback, this, mHalResultController);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
index 24ce867..79ad8e1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
@@ -26,6 +26,7 @@
import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.FingerprintStateListener;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.Handler;
import android.os.IBinder;
@@ -42,6 +43,7 @@
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintStateCallback;
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
import java.util.ArrayList;
@@ -270,6 +272,7 @@
}
public static Fingerprint21UdfpsMock newInstance(@NonNull Context context,
+ @NonNull FingerprintStateCallback fingerprintStateCallback,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
@@ -280,8 +283,8 @@
new TestableBiometricScheduler(TAG, gestureAvailabilityDispatcher);
final MockHalResultController controller =
new MockHalResultController(sensorProps.sensorId, context, handler, scheduler);
- return new Fingerprint21UdfpsMock(context, sensorProps, scheduler, handler,
- lockoutResetDispatcher, controller);
+ return new Fingerprint21UdfpsMock(context, fingerprintStateCallback, sensorProps, scheduler,
+ handler, lockoutResetDispatcher, controller);
}
private static abstract class FakeFingerRunnable implements Runnable {
@@ -400,17 +403,19 @@
// internal preemption logic is not run.
mFingerprint21.scheduleAuthenticate(mFingerprint21.mSensorProperties.sensorId, token,
operationId, user, cookie, listener, opPackageName, restricted, statsClient,
- isKeyguard, null /* fingerprintStateCallback */);
+ isKeyguard);
}
}
private Fingerprint21UdfpsMock(@NonNull Context context,
+ @NonNull FingerprintStateCallback fingerprintStateCallback,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
@NonNull TestableBiometricScheduler scheduler,
@NonNull Handler handler,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull MockHalResultController controller) {
- super(context, sensorProps, scheduler, handler, lockoutResetDispatcher, controller);
+ super(context, fingerprintStateCallback, sensorProps, scheduler, handler,
+ lockoutResetDispatcher, controller);
mScheduler = scheduler;
mScheduler.init(this);
mHandler = handler;
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 383b392..806a5dd 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -19,7 +19,6 @@
import static android.Manifest.permission.CONTROL_DEVICE_STATE;
import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
-import static android.os.Process.THREAD_PRIORITY_DISPLAY;
import static com.android.server.devicestate.OverrideRequestController.STATUS_ACTIVE;
import static com.android.server.devicestate.OverrideRequestController.STATUS_CANCELED;
@@ -36,7 +35,6 @@
import android.hardware.devicestate.IDeviceStateManagerCallback;
import android.os.Binder;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -48,8 +46,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.DisplayThread;
import com.android.server.LocalServices;
-import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.policy.DeviceStatePolicyImpl;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -93,10 +91,9 @@
private static final boolean DEBUG = false;
private final Object mLock = new Object();
- // Internal system service thread used to dispatch calls to the policy and to registered
+ // Handler on the {@link DisplayThread} used to dispatch calls to the policy and to registered
// callbacks though its handler (mHandler). Provides a guarantee of callback order when
// leveraging mHandler and also enables posting messages with the service lock held.
- private final HandlerThread mHandlerThread;
private final Handler mHandler;
@NonNull
private final DeviceStatePolicy mDeviceStatePolicy;
@@ -149,11 +146,10 @@
@VisibleForTesting
DeviceStateManagerService(@NonNull Context context, @NonNull DeviceStatePolicy policy) {
super(context);
- // Service thread assigned THREAD_PRIORITY_DISPLAY because this service indirectly drives
+ // We use the DisplayThread because this service indirectly drives
// display (on/off) and window (position) events through its callbacks.
- mHandlerThread = new ServiceThread(TAG, THREAD_PRIORITY_DISPLAY, false /* allowIo */);
- mHandlerThread.start();
- mHandler = mHandlerThread.getThreadHandler();
+ DisplayThread displayThread = DisplayThread.get();
+ mHandler = new Handler(displayThread.getLooper());
mOverrideRequestController = new OverrideRequestController(
this::onOverrideRequestStatusChangedLocked);
mDeviceStatePolicy = policy;
@@ -552,7 +548,7 @@
}
ProcessRecord record = new ProcessRecord(callback, pid, this::handleProcessDied,
- mHandlerThread.getThreadHandler());
+ mHandler);
try {
callback.asBinder().linkToDeath(record, 0);
} catch (RemoteException ex) {
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
index a3134a0..a3f1435 100644
--- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
@@ -199,6 +199,30 @@
.addDataType("video/*")
.build();
+ // TODO(b/199068419): Remove once GEM enables the intent for Googlers
+ /** Pick images can be forwarded to work profile. */
+ private static final DefaultCrossProfileIntentFilter ACTION_PICK_IMAGES_TO_PROFILE =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
+ /* flags= */ 0,
+ /* letsPersonalDataIntoProfile= */ true)
+ .addAction(MediaStore.ACTION_PICK_IMAGES)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+ // TODO(b/199068419): Remove once GEM enables the intent for Googlers
+ /** Pick images can be forwarded to work profile. */
+ private static final DefaultCrossProfileIntentFilter
+ ACTION_PICK_IMAGES_WITH_DATA_TYPES_TO_PROFILE =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
+ /* flags= */ 0,
+ /* letsPersonalDataIntoProfile= */ true)
+ .addAction(MediaStore.ACTION_PICK_IMAGES)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addDataType("image/*")
+ .addDataType("video/*")
+ .build();
+
/** Open document intent can be forwarded to parent user. */
private static final DefaultCrossProfileIntentFilter OPEN_DOCUMENT =
new DefaultCrossProfileIntentFilter.Builder(
@@ -312,6 +336,8 @@
ACTION_PICK_DATA,
ACTION_PICK_IMAGES,
ACTION_PICK_IMAGES_WITH_DATA_TYPES,
+ ACTION_PICK_IMAGES_TO_PROFILE,
+ ACTION_PICK_IMAGES_WITH_DATA_TYPES_TO_PROFILE,
OPEN_DOCUMENT,
GET_CONTENT,
USB_DEVICE_ATTACHED,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index a9ddd35..5b205cd 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -16966,6 +16966,19 @@
}
@Override
+ public void clearOrganizationIdForUser(int userHandle) {
+ Preconditions.checkCallAuthorization(
+ hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+
+ synchronized (getLockObject()) {
+ final ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userHandle);
+ owner.mOrganizationId = null;
+ owner.mEnrollmentSpecificId = null;
+ saveSettingsLocked(userHandle);
+ }
+ }
+
+ @Override
public UserHandle createAndProvisionManagedProfile(
@NonNull ManagedProfileProvisioningParams provisioningParams,
@NonNull String callerPackage) {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java
new file mode 100644
index 0000000..09b5c5c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.biometrics.sensors;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+@Presubmit
+@SmallTest
+public class CompositeCallbackTest {
+
+ @Test
+ public void testNullCallback() {
+ BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
+ BaseClientMonitor.Callback callback2 = mock(BaseClientMonitor.Callback.class);
+ BaseClientMonitor.Callback callback3 = null;
+
+ BaseClientMonitor.CompositeCallback callback = new BaseClientMonitor.CompositeCallback(
+ callback1, callback2, callback3);
+
+ BaseClientMonitor clientMonitor = mock(BaseClientMonitor.class);
+
+ callback.onClientStarted(clientMonitor);
+ verify(callback1).onClientStarted(eq(clientMonitor));
+ verify(callback2).onClientStarted(eq(clientMonitor));
+
+ callback.onClientFinished(clientMonitor, true /* success */);
+ verify(callback1).onClientFinished(eq(clientMonitor), eq(true));
+ verify(callback2).onClientFinished(eq(clientMonitor), eq(true));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallbackTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallbackTest.java
new file mode 100644
index 0000000..38e8dfa
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallbackTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.biometrics.sensors.fingerprint;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.fingerprint.FingerprintStateListener;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.biometrics.sensors.AuthenticationClient;
+import com.android.server.biometrics.sensors.EnrollClient;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@Presubmit
+@SmallTest
+public class FingerprintStateCallbackTest {
+
+ private FingerprintStateCallback mCallback;
+
+ @Mock
+ FingerprintStateListener mFingerprintStateListener;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+
+ mCallback = new FingerprintStateCallback();
+ mCallback.registerFingerprintStateListener(mFingerprintStateListener);
+ }
+
+ @Test
+ public void testNoEnrollmentsToEnrollments_callbackNotified() {
+ testEnrollmentCallback(true /* changed */, true /* isNowEnrolled */,
+ true /* expectCallback */, true /* expectedCallbackValue */);
+ }
+
+ @Test
+ public void testEnrollmentsToNoEnrollments_callbackNotified() {
+ testEnrollmentCallback(true /* changed */, false /* isNowEnrolled */,
+ true /* expectCallback */, false /* expectedCallbackValue */);
+ }
+
+ @Test
+ public void testEnrollmentsToEnrollments_callbackNotNotified() {
+ testEnrollmentCallback(false /* changed */, true /* isNowEnrolled */,
+ false /* expectCallback */, false /* expectedCallbackValue */);
+ }
+
+ private void testEnrollmentCallback(boolean changed, boolean isNowEnrolled,
+ boolean expectCallback, boolean expectedCallbackValue) {
+ EnrollClient<?> client = mock(EnrollClient.class);
+
+ final int userId = 10;
+ final int sensorId = 100;
+
+ when(client.hasEnrollmentStateChanged()).thenReturn(changed);
+ when(client.hasEnrollments()).thenReturn(isNowEnrolled);
+ when(client.getTargetUserId()).thenReturn(userId);
+ when(client.getSensorId()).thenReturn(sensorId);
+
+ mCallback.onClientFinished(client, true /* success */);
+ if (expectCallback) {
+ verify(mFingerprintStateListener).onEnrollmentsChanged(eq(userId), eq(sensorId),
+ eq(expectedCallbackValue));
+ } else {
+ verify(mFingerprintStateListener, never()).onEnrollmentsChanged(anyInt(), anyInt(),
+ anyBoolean());
+ }
+ }
+
+ @Test
+ public void testAuthentication_enrollmentCallbackNeverNotified() {
+ AuthenticationClient<?> client = mock(AuthenticationClient.class);
+ mCallback.onClientFinished(client, true /* success */);
+ verify(mFingerprintStateListener, never()).onEnrollmentsChanged(anyInt(), anyInt(),
+ anyBoolean());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
index 35c37ef..b51918e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
@@ -37,6 +37,7 @@
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.HalClientMonitor;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintStateCallback;
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
import org.junit.Before;
@@ -58,6 +59,8 @@
private UserManager mUserManager;
@Mock
private GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
+ @Mock
+ private FingerprintStateCallback mFingerprintStateCallback;
private SensorProps[] mSensorProps;
private LockoutResetDispatcher mLockoutResetDispatcher;
@@ -87,8 +90,8 @@
mLockoutResetDispatcher = new LockoutResetDispatcher(mContext);
- mFingerprintProvider = new TestableFingerprintProvider(mContext, mSensorProps, TAG,
- mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
+ mFingerprintProvider = new TestableFingerprintProvider(mContext, mFingerprintStateCallback,
+ mSensorProps, TAG, mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
}
@SuppressWarnings("rawtypes")
@@ -133,11 +136,12 @@
private static class TestableFingerprintProvider extends FingerprintProvider {
public TestableFingerprintProvider(@NonNull Context context,
+ @NonNull FingerprintStateCallback fingerprintStateCallback,
@NonNull SensorProps[] props,
@NonNull String halInstanceName,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
- super(context, props, halInstanceName, lockoutResetDispatcher,
+ super(context, fingerprintStateCallback, props, halInstanceName, lockoutResetDispatcher,
gestureAvailabilityDispatcher);
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java
index 0a0dcc9..f6b9209 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java
@@ -41,6 +41,7 @@
import com.android.internal.R;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintStateCallback;
import org.junit.Before;
import org.junit.Test;
@@ -67,6 +68,8 @@
Fingerprint21.HalResultController mHalResultController;
@Mock
private BiometricScheduler mScheduler;
+ @Mock
+ private FingerprintStateCallback mFingerprintStateCallback;
private LockoutResetDispatcher mLockoutResetDispatcher;
private Fingerprint21 mFingerprint21;
@@ -96,8 +99,9 @@
componentInfo, FingerprintSensorProperties.TYPE_UNKNOWN,
resetLockoutRequiresHardwareAuthToken);
- mFingerprint21 = new TestableFingerprint21(mContext, sensorProps, mScheduler,
- new Handler(Looper.getMainLooper()), mLockoutResetDispatcher, mHalResultController);
+ mFingerprint21 = new TestableFingerprint21(mContext, mFingerprintStateCallback, sensorProps,
+ mScheduler, new Handler(Looper.getMainLooper()), mLockoutResetDispatcher,
+ mHalResultController);
}
@Test
@@ -118,11 +122,13 @@
private static class TestableFingerprint21 extends Fingerprint21 {
TestableFingerprint21(@NonNull Context context,
+ @NonNull FingerprintStateCallback fingerprintStateCallback,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
@NonNull BiometricScheduler scheduler, @NonNull Handler handler,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull HalResultController controller) {
- super(context, sensorProps, scheduler, handler, lockoutResetDispatcher, controller);
+ super(context, fingerprintStateCallback, sensorProps, scheduler, handler,
+ lockoutResetDispatcher, controller);
}
@Override