Merge "Remove current.txt files"
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index f7bbcbb..dbb3ecd 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -10581,6 +10581,32 @@
// A random number used as the dimension field to pull multiple atoms.
optional int32 dimension = 25;
+
+ // Signal strength at the end of the call. This value is applicable to both cellular and WiFi.
+ optional android.telephony.SignalStrengthEnum signal_strength_at_end = 26;
+
+ // Band at the end of the call. Value 0 is used if the band is unknown.
+ // See GeranBands, UtranBands and EutranBands in IRadio interface, depending on the RAT at
+ // the end of the call.
+ optional int32 band_at_end = 27;
+
+ // Time spent setting up the call in milliseconds.
+ // The time is measured from dial to ringing for outgoing calls, and from answer to connected
+ // for incoming calls.
+ optional int32 setup_duration_millis = 28;
+
+ // Main codec quality. The codec quality was equal to or greater than this value for at least
+ // 70% of the call.
+ optional android.telephony.CodecQuality main_codec_quality = 29;
+
+ // Whether video was enabled at any point during the call.
+ optional bool video_enabled = 30;
+
+ // Radio access technology (RAT) used when call is connected.
+ optional android.telephony.NetworkTypeEnum rat_at_connected = 31;
+
+ // Whether the call was a conference call (applicable only for calls over IMS).
+ optional bool is_multiparty = 32;
}
/**
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 1c72d02..f19b704 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -698,6 +698,7 @@
public class BiometricManager {
method @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public android.hardware.biometrics.BiometricTestSession createTestSession(int);
method @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public java.util.List<android.hardware.biometrics.SensorProperties> getSensorProperties();
+ method @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public String getUiPackage();
}
public class BiometricTestSession implements java.lang.AutoCloseable {
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 25c749b..987d790 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -208,7 +208,17 @@
@NonNull
@RequiresPermission(TEST_BIOMETRIC)
public List<SensorProperties> getSensorProperties() {
- return new ArrayList<>(); // TODO(169459906)
+ try {
+ final List<SensorPropertiesInternal> internalProperties =
+ mService.getSensorProperties(mContext.getOpPackageName());
+ final List<SensorProperties> properties = new ArrayList<>();
+ for (SensorPropertiesInternal internalProp : internalProperties) {
+ properties.add(SensorProperties.from(internalProp));
+ }
+ return properties;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -219,7 +229,27 @@
@NonNull
@RequiresPermission(TEST_BIOMETRIC)
public BiometricTestSession createTestSession(int sensorId) {
- return null; // TODO(169459906)
+ try {
+ return new BiometricTestSession(mContext,
+ mService.createTestSession(sensorId, mContext.getOpPackageName()));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Retrieves the package where BiometricPrompt's UI is implemented.
+ * @hide
+ */
+ @TestApi
+ @NonNull
+ @RequiresPermission(TEST_BIOMETRIC)
+ public String getUiPackage() {
+ try {
+ return mService.getUiPackage();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
diff --git a/core/java/android/hardware/biometrics/IAuthService.aidl b/core/java/android/hardware/biometrics/IAuthService.aidl
index dd6aa73..8e7f5ce 100644
--- a/core/java/android/hardware/biometrics/IAuthService.aidl
+++ b/core/java/android/hardware/biometrics/IAuthService.aidl
@@ -18,7 +18,9 @@
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricServiceReceiver;
+import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.PromptInfo;
+import android.hardware.biometrics.SensorPropertiesInternal;
/**
* Communication channel from BiometricPrompt and BiometricManager to AuthService. The
@@ -28,6 +30,15 @@
* @hide
*/
interface IAuthService {
+ // Creates a test session with the specified sensorId
+ ITestSession createTestSession(int sensorId, String opPackageName);
+
+ // Retrieve static sensor properties for all biometric sensors
+ List<SensorPropertiesInternal> getSensorProperties(String opPackageName);
+
+ // Retrieve the package where BIometricOrompt's UI is implemented
+ String getUiPackage();
+
// Requests authentication. The service choose the appropriate biometric to use, and show
// the corresponding BiometricDialog.
void authenticate(IBinder token, long sessionId, int userId,
diff --git a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
index 5e6fe68..cb43943 100644
--- a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
@@ -18,6 +18,8 @@
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.face.IFaceServiceReceiver;
import android.hardware.face.Face;
@@ -28,6 +30,15 @@
*/
interface IBiometricAuthenticator {
+ // Creates a test session
+ ITestSession createTestSession(String opPackageName);
+
+ // Retrieve static sensor properties
+ SensorPropertiesInternal getSensorProperties(String opPackageName);
+
+ // Requests a proto dump of the service. See biometrics.proto
+ byte[] dumpSensorServiceStateProto();
+
// This method prepares the service to start authenticating, but doesn't start authentication.
// This is protected by the MANAGE_BIOMETRIC signature permission. This method should only be
// called from BiometricService. The additional uid, pid, userId arguments should be determined
@@ -35,14 +46,13 @@
// startPreparedClient().
void prepareForAuthentication(boolean requireConfirmation, IBinder token, long operationId,
int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName,
- int cookie, int callingUid, int callingPid, int callingUserId);
+ int cookie);
// Starts authentication with the previously prepared client.
void startPreparedClient(int cookie);
// Cancels authentication.
- void cancelAuthenticationFromService(IBinder token, String opPackageName,
- int callingUid, int callingPid, int callingUserId);
+ void cancelAuthenticationFromService(IBinder token, String opPackageName);
// Determine if HAL is loaded and ready
boolean isHardwareDetected(String opPackageName);
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index 005ed32..6f7bcb6 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -19,22 +19,28 @@
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IBiometricAuthenticator;
+import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.PromptInfo;
+import android.hardware.biometrics.SensorPropertiesInternal;
/**
* Communication channel from AuthService to BiometricService.
* @hide
*/
interface IBiometricService {
+ // Creates a test session with the specified sensorId
+ ITestSession createTestSession(int sensorId, String opPackageName);
+
+ // Retrieve static sensor properties for all biometric sensors
+ List<SensorPropertiesInternal> getSensorProperties(String opPackageName);
+
// Requests authentication. The service choose the appropriate biometric to use, and show
// the corresponding BiometricDialog.
void authenticate(IBinder token, long operationId, int userId,
- IBiometricServiceReceiver receiver, String opPackageName, in PromptInfo promptInfo,
- int callingUid, int callingPid, int callingUserId);
+ IBiometricServiceReceiver receiver, String opPackageName, in PromptInfo promptInfo);
// Cancel authentication for the given session.
- void cancelAuthentication(IBinder token, String opPackageName, int callingUid, int callingPid,
- int callingUserId);
+ void cancelAuthentication(IBinder token, String opPackageName);
// Checks if biometrics can be used.
int canAuthenticate(String opPackageName, int userId, int callingUserId, int authenticators);
diff --git a/core/java/android/hardware/biometrics/SensorProperties.java b/core/java/android/hardware/biometrics/SensorProperties.java
index 5b1b5e6..360f138 100644
--- a/core/java/android/hardware/biometrics/SensorProperties.java
+++ b/core/java/android/hardware/biometrics/SensorProperties.java
@@ -81,4 +81,12 @@
public int getSensorStrength() {
return mSensorStrength;
}
+
+ /**
+ * Constructs a {@link SensorProperties} from the internal parcelable representation.
+ * @hide
+ */
+ public static SensorProperties from(SensorPropertiesInternal internalProp) {
+ return new SensorProperties(internalProp.sensorId, internalProp.sensorStrength);
+ }
}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index 27cdda7..c5c51e4 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -17,6 +17,7 @@
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.ITestSession;
import android.hardware.face.IFaceServiceReceiver;
import android.hardware.face.Face;
import android.hardware.face.FaceSensorPropertiesInternal;
@@ -28,9 +29,19 @@
* @hide
*/
interface IFaceService {
+
+ // Creates a test session with the specified sensorId
+ ITestSession createTestSession(int sensorId, String opPackageName);
+
+ // Requests a proto dump of the service to the specified fd
+ byte[] dumpSensorServiceStateProto();
+
// Retrieve static sensor properties for all face sensors
List<FaceSensorPropertiesInternal> getSensorPropertiesInternal(String opPackageName);
+ // Retrieve static sensor properties for the specified sensor
+ FaceSensorPropertiesInternal getSensorProperties(int sensorId, String opPackageName);
+
// Authenticate the given sessionId with a face
void authenticate(IBinder token, long operationId, int userId, IFaceServiceReceiver receiver,
String opPackageName);
@@ -46,7 +57,7 @@
// startPreparedClient().
void prepareForAuthentication(int sensorId, boolean requireConfirmation, IBinder token, long operationId,
int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName,
- int cookie, int callingUid, int callingPid, int callingUserId);
+ int cookie);
// Starts authentication with the previously prepared client.
void startPreparedClient(int sensorId, int cookie);
@@ -58,8 +69,7 @@
void cancelFaceDetect(IBinder token, String opPackageName);
// Same as above, with extra arguments.
- void cancelAuthenticationFromService(int sensorId, IBinder token, String opPackageName,
- int callingUid, int callingPid, int callingUserId);
+ void cancelAuthenticationFromService(int sensorId, IBinder token, String opPackageName);
// Start face enrollment
void enroll(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 2128d67..9248b08 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -34,9 +34,15 @@
// Creates a test session with the specified sensorId
ITestSession createTestSession(int sensorId, String opPackageName);
+ // Requests a proto dump of the service to the specified fd
+ byte[] dumpSensorServiceStateProto();
+
// Retrieve static sensor properties for all fingerprint sensors
List<FingerprintSensorPropertiesInternal> getSensorPropertiesInternal(String opPackageName);
+ // Retrieve static sensor properties for the specified sensor
+ FingerprintSensorPropertiesInternal getSensorProperties(int sensorId, String opPackageName);
+
// Authenticate the given sessionId with a fingerprint. This is protected by
// USE_FINGERPRINT/USE_BIOMETRIC permission. This is effectively deprecated, since it only comes
// through FingerprintManager now.
@@ -54,8 +60,7 @@
// by BiometricService. To start authentication after the clients are ready, use
// startPreparedClient().
void prepareForAuthentication(int sensorId, IBinder token, long operationId, int userId,
- IBiometricSensorReceiver sensorReceiver, String opPackageName, int cookie,
- int callingUid, int callingPid, int callingUserId);
+ IBiometricSensorReceiver sensorReceiver, String opPackageName, int cookie);
// Starts authentication with the previously prepared client.
void startPreparedClient(int sensorId, int cookie);
@@ -68,8 +73,7 @@
// Same as above, except this is protected by the MANAGE_BIOMETRIC signature permission. Takes
// an additional uid, pid, userid.
- void cancelAuthenticationFromService(int sensorId, IBinder token, String opPackageName,
- int callingUid, int callingPid, int callingUserId);
+ void cancelAuthenticationFromService(int sensorId, IBinder token, String opPackageName);
// Start fingerprint enrollment
void enroll(IBinder token, in byte [] hardwareAuthToken, int userId, IFingerprintServiceReceiver receiver,
diff --git a/core/proto/android/server/biometrics.proto b/core/proto/android/server/biometrics.proto
new file mode 100644
index 0000000..632c1e5
--- /dev/null
+++ b/core/proto/android/server/biometrics.proto
@@ -0,0 +1,130 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+package com.android.server.biometrics;
+
+import "frameworks/base/core/proto/android/privacy.proto";
+
+option java_multiple_files = true;
+option java_outer_classname = "BiometricsProto";
+
+// Overall state of BiometricService (and not <Biometric>Service), including any
+// <Biomteric>Service(s), for example FingerprintService or FaceService.
+message BiometricServiceStateProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ enum AuthSessionState {
+ /**
+ * Authentication either just called and we have not transitioned to the CALLED state, or
+ * authentication terminated (success or error).
+ */
+ STATE_AUTH_IDLE = 0;
+
+ /**
+ * Authentication was called and we are waiting for the <Biometric>Services to return their
+ * cookies before starting the hardware and showing the BiometricPrompt.
+ */
+ STATE_AUTH_CALLED = 1;
+
+ /**
+ * Authentication started, BiometricPrompt is showing and the hardware is authenticating.
+ */
+ STATE_AUTH_STARTED = 2;
+
+ /**
+ * Same as {@link #STATE_AUTH_STARTED}, except the BiometricPrompt UI is done animating in.
+ */
+ STATE_AUTH_STARTED_UI_SHOWING = 3;
+
+ /**
+ * Authentication is paused, waiting for the user to press "try again" button. Only
+ * passive modalities such as Face or Iris should have this state. Note that for passive
+ * modalities, the HAL enters the idle state after onAuthenticated(false) which differs from
+ * fingerprint.
+ */
+ STATE_AUTH_PAUSED = 4;
+
+ /**
+ * Paused, but "try again" was pressed. Sensors have new cookies and we're now waiting for
+ * all cookies to be returned.
+ */
+ STATE_AUTH_PAUSED_RESUMING = 5;
+
+ /**
+ * Authentication is successful, but we're waiting for the user to press "confirm" button.
+ */
+ STATE_AUTH_PENDING_CONFIRM = 6;
+
+ /**
+ * Biometric authenticated, waiting for SysUI to finish animation
+ */
+ STATE_AUTHENTICATED_PENDING_SYSUI = 7;
+
+ /**
+ * Biometric error, waiting for SysUI to finish animation
+ */
+ STATE_ERROR_PENDING_SYSUI = 8;
+
+ /**
+ * Device credential in AuthController is showing
+ */
+ STATE_SHOWING_DEVICE_CREDENTIAL = 9;
+
+ /**
+ * The client binder died, and sensors were authenticating at the time. Cancel has been
+ * requested and we're waiting for the HAL(s) to send ERROR_CANCELED.
+ */
+ STATE_CLIENT_DIED_CANCELLING = 10;
+ }
+
+ repeated SensorServiceStateProto sensor_service_states = 1;
+
+ optional AuthSessionState auth_session_state = 2;
+}
+
+// Overall state for an instance of a <Biometric>Service, for example FingerprintService or
+// FaceService. Note that a single service may provide multiple sensors.
+message SensorServiceStateProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ repeated SensorStateProto sensor_states = 1;
+}
+
+// State of a single sensor.
+message SensorStateProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ // Unique sensorId
+ optional int32 sensor_id = 1;
+
+ // State of the sensor's scheduler. True if currently handling an operation, false if idle.
+ optional bool is_busy = 2;
+
+ // User states for this sensor.
+ repeated UserStateProto user_states = 3;
+}
+
+// State of a specific user for a specific sensor.
+message UserStateProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ // Android user ID
+ optional int32 user_id = 1;
+
+ // Number of fingerprints enrolled
+ optional int32 num_enrolled = 2;
+}
\ No newline at end of file
diff --git a/core/proto/android/server/fingerprint.proto b/core/proto/android/server/fingerprint.proto
index a49a1ad..e6b2df5 100644
--- a/core/proto/android/server/fingerprint.proto
+++ b/core/proto/android/server/fingerprint.proto
@@ -65,37 +65,4 @@
// Total number of permanent lockouts.
optional int32 permanent_lockout = 5;
-}
-
-// Internal FingerprintService states. The above messages (FingerprintServiceDumpProto, etc)
-// are used for legacy metrics and should not be modified.
-message FingerprintServiceStateProto {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
- repeated SensorStateProto sensor_states = 1;
-}
-
-// State of a single sensor.
-message SensorStateProto {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
- // Unique sensorId
- optional int32 sensor_id = 1;
-
- // State of the sensor's scheduler. True if currently handling an operation, false if idle.
- optional bool is_busy = 2;
-
- // User states for this sensor.
- repeated UserStateProto user_states = 3;
-}
-
-// State of a specific user for a specific sensor.
-message UserStateProto {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
- // Android user ID
- optional int32 user_id = 1;
-
- // Number of fingerprints enrolled
- optional int32 num_enrolled = 2;
}
\ No newline at end of file
diff --git a/core/proto/android/telephony/enums.proto b/core/proto/android/telephony/enums.proto
index 2546b51..b435fe7 100644
--- a/core/proto/android/telephony/enums.proto
+++ b/core/proto/android/telephony/enums.proto
@@ -99,7 +99,7 @@
ROAMING_TYPE_ROAMING_INTERNATIONAL = 3;
}
-// Signal strength levels, primarily used by android/telephony/SignalStrength.java.
+// Signal strength levels, as defined in android/telephony/SignalStrength.java.
enum SignalStrengthEnum {
SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0;
SIGNAL_STRENGTH_POOR = 1;
@@ -267,4 +267,22 @@
RECOVERY_ACTION_CLEANUP = 1;
RECOVERY_ACTION_REREGISTER = 2;
RECOVERY_ACTION_RADIO_RESTART = 3;
-}
\ No newline at end of file
+}
+
+// Codec quality
+enum CodecQuality {
+ /** Codec quality: unknown */
+ CODEC_QUALITY_UNKNOWN = 0;
+
+ /** Codec quality: narrowband */
+ CODEC_QUALITY_NARROWBAND = 1;
+
+ /** Codec quality: wideband */
+ CODEC_QUALITY_WIDEBAND = 2;
+
+ /** Codec quality: super-wideband */
+ CODEC_QUALITY_SUPER_WIDEBAND = 3;
+
+ /** Codec quality: fullband */
+ CODEC_QUALITY_FULLBAND = 4;
+}
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index c83594a..eda1c7a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4267,6 +4267,7 @@
<!-- Which binder services to include in incident reports containing restricted images. -->
<string-array name="config_restrictedImagesServices" translatable="false"/>
+ <string name="config_biometric_prompt_ui_package">com.android.systemui</string>
<!-- List of biometric sensors on the device, in decreasing strength. Consumed by AuthService
when registering authenticators with BiometricService. Format must be ID:Modality:Strength,
where: IDs are unique per device, Modality as defined in BiometricAuthenticator.java,
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index acfafa4..966cb37 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2517,6 +2517,7 @@
<java-symbol type="string" name="face_authenticated_confirmation_required" />
<java-symbol type="string" name="face_error_security_update_required" />
+ <java-symbol type="string" name="config_biometric_prompt_ui_package" />
<java-symbol type="array" name="config_biometric_sensors" />
<java-symbol type="bool" name="allow_test_udfps" />
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index eeec1bb..cf8bfbc 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -20,6 +20,7 @@
// TODO(b/141025588): Create separate internal and external permissions for AuthService.
// TODO(b/141025588): Get rid of the USE_FINGERPRINT permission.
+import static android.Manifest.permission.TEST_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;
@@ -28,6 +29,7 @@
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_IRIS;
import static android.hardware.biometrics.BiometricManager.Authenticators;
+import android.annotation.NonNull;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -36,7 +38,9 @@
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceReceiver;
+import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.PromptInfo;
+import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.face.IFaceService;
import android.hardware.fingerprint.IFingerprintService;
import android.hardware.iris.IIrisService;
@@ -54,6 +58,8 @@
import com.android.server.biometrics.sensors.fingerprint.FingerprintAuthenticator;
import com.android.server.biometrics.sensors.iris.IrisAuthenticator;
+import java.util.List;
+
/**
* System service that provides an interface for authenticating with biometrics and
* PIN/pattern/password to BiometricPrompt and lock screen.
@@ -137,6 +143,42 @@
private final class AuthServiceImpl extends IAuthService.Stub {
@Override
+ public ITestSession createTestSession(int sensorId, @NonNull String opPackageName)
+ throws RemoteException {
+ Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return mInjector.getBiometricService().createTestSession(sensorId, opPackageName);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public List<SensorPropertiesInternal> getSensorProperties(String opPackageName)
+ throws RemoteException {
+ Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ // Get the result from BiometricService, since it is the source of truth for all
+ // biometric sensors.
+ return mInjector.getBiometricService().getSensorProperties(opPackageName);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public String getUiPackage() {
+ Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+
+ return getContext().getResources()
+ .getString(R.string.config_biometric_prompt_ui_package);
+ }
+
+ @Override
public void authenticate(IBinder token, long sessionId, int userId,
IBiometricServiceReceiver receiver, String opPackageName, PromptInfo promptInfo)
throws RemoteException {
@@ -176,8 +218,7 @@
final long identity = Binder.clearCallingIdentity();
try {
mBiometricService.authenticate(
- token, sessionId, userId, receiver, opPackageName, promptInfo, callingUid,
- callingPid, callingUserId);
+ token, sessionId, userId, receiver, opPackageName, promptInfo);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -193,13 +234,9 @@
return;
}
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final int callingUserId = UserHandle.getCallingUserId();
final long identity = Binder.clearCallingIdentity();
try {
- mBiometricService.cancelAuthentication(token, opPackageName, callingUid,
- callingPid, callingUserId);
+ mBiometricService.cancelAuthentication(token, opPackageName);
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java
index 637a896..efc025d 100644
--- a/services/core/java/com/android/server/biometrics/AuthSession.java
+++ b/services/core/java/com/android/server/biometrics/AuthSession.java
@@ -20,6 +20,8 @@
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
+import static com.android.server.biometrics.BiometricServiceStateProto.*;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -48,7 +50,6 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
import java.util.List;
import java.util.Random;
@@ -61,60 +62,11 @@
private static final String TAG = "BiometricService/AuthSession";
private static final boolean DEBUG = false;
- /**
- * Authentication either just called and we have not transitioned to the CALLED state, or
- * authentication terminated (success or error).
- */
- static final int STATE_AUTH_IDLE = 0;
- /**
- * Authentication was called and we are waiting for the <Biometric>Services to return their
- * cookies before starting the hardware and showing the BiometricPrompt.
- */
- static final int STATE_AUTH_CALLED = 1;
- /**
- * Authentication started, BiometricPrompt is showing and the hardware is authenticating. At
- * this point, the BiometricPrompt UI has been requested, but is not necessarily done animating
- * in yet.
- */
- static final int STATE_AUTH_STARTED = 2;
- /**
- * Same as {@link #STATE_AUTH_STARTED}, except the BiometricPrompt UI is done animating in.
- */
- static final int STATE_AUTH_STARTED_UI_SHOWING = 3;
- /**
- * Authentication is paused, waiting for the user to press "try again" button. Only
- * passive modalities such as Face or Iris should have this state. Note that for passive
- * modalities, the HAL enters the idle state after onAuthenticated(false) which differs from
- * fingerprint.
- */
- static final int STATE_AUTH_PAUSED = 4;
- /**
- * Paused, but "try again" was pressed. Sensors have new cookies and we're now waiting for all
- * cookies to be returned.
- */
- static final int STATE_AUTH_PAUSED_RESUMING = 5;
- /**
- * Authentication is successful, but we're waiting for the user to press "confirm" button.
- */
- static final int STATE_AUTH_PENDING_CONFIRM = 6;
- /**
- * Biometric authenticated, waiting for SysUI to finish animation
- */
- static final int STATE_AUTHENTICATED_PENDING_SYSUI = 7;
- /**
- * Biometric error, waiting for SysUI to finish animation
- */
- static final int STATE_ERROR_PENDING_SYSUI = 8;
- /**
- * Device credential in AuthController is showing
- */
- static final int STATE_SHOWING_DEVICE_CREDENTIAL = 9;
- /**
- * The client binder died, and sensors were authenticating at the time. Cancel has been
- * requested and we're waiting for the HAL(s) to send ERROR_CANCELED.
- */
- static final int STATE_CLIENT_DIED_CANCELLING = 10;
+
+ /*
+ * Defined in biometrics.proto
+ */
@IntDef({
STATE_AUTH_IDLE,
STATE_AUTH_CALLED,
@@ -157,9 +109,6 @@
// Original receiver from BiometricPrompt.
private final IBiometricServiceReceiver mClientReceiver;
private final String mOpPackageName;
- private final int mCallingUid;
- private final int mCallingPid;
- private final int mCallingUserId;
private final boolean mDebugEnabled;
private final List<FingerprintSensorPropertiesInternal> mFingerprintSensorProperties;
@@ -191,9 +140,6 @@
@NonNull IBiometricServiceReceiver clientReceiver,
@NonNull String opPackageName,
@NonNull PromptInfo promptInfo,
- int callingUid,
- int callingPid,
- int callingUserId,
boolean debugEnabled,
@NonNull List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties) {
mContext = context;
@@ -210,9 +156,6 @@
mClientReceiver = clientReceiver;
mOpPackageName = opPackageName;
mPromptInfo = promptInfo;
- mCallingUid = callingUid;
- mCallingPid = callingPid;
- mCallingUserId = callingUserId;
mDebugEnabled = debugEnabled;
mFingerprintSensorProperties = fingerprintSensorProperties;
@@ -254,8 +197,7 @@
final int cookie = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
final boolean requireConfirmation = isConfirmationRequired(sensor);
sensor.goToStateWaitingForCookie(requireConfirmation, mToken, mOperationId,
- mUserId, mSensorReceiver, mOpPackageName, cookie, mCallingUid, mCallingPid,
- mCallingUserId);
+ mUserId, mSensorReceiver, mOpPackageName, cookie);
}
}
@@ -264,7 +206,7 @@
// Only device credential should be shown. In this case, we don't need to wait,
// since LockSettingsService/Gatekeeper is always ready to check for credential.
// SystemUI invokes that path.
- mState = AuthSession.STATE_SHOWING_DEVICE_CREDENTIAL;
+ mState = STATE_SHOWING_DEVICE_CREDENTIAL;
mStatusBarService.showAuthenticationDialog(
mPromptInfo,
@@ -278,7 +220,7 @@
} else if (!mPreAuthInfo.eligibleSensors.isEmpty()) {
// Some combination of biometric or biometric|credential is requested
setSensorsToStateWaitingForCookie();
- mState = AuthSession.STATE_AUTH_CALLED;
+ mState = STATE_AUTH_CALLED;
} else {
// No authenticators requested. This should never happen - an exception should have
// been thrown earlier in the pipeline.
@@ -386,8 +328,7 @@
// sending the final error callback to the application.
for (BiometricSensor sensor : mPreAuthInfo.eligibleSensors) {
try {
- sensor.goToStateCancelling(mToken, mOpPackageName, mCallingUid, mCallingPid,
- mCallingUserId);
+ sensor.goToStateCancelling(mToken, mOpPackageName);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to cancel authentication");
}
@@ -429,7 +370,7 @@
authenticators = Utils.removeBiometricBits(authenticators);
mPromptInfo.setAuthenticators(authenticators);
- mState = AuthSession.STATE_SHOWING_DEVICE_CREDENTIAL;
+ mState = STATE_SHOWING_DEVICE_CREDENTIAL;
mStatusBarService.showAuthenticationDialog(
mPromptInfo,
@@ -453,7 +394,7 @@
|| error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
if (isAllowDeviceCredential() && errorLockout) {
// SystemUI handles transition from biometric to device credential.
- mState = AuthSession.STATE_SHOWING_DEVICE_CREDENTIAL;
+ mState = STATE_SHOWING_DEVICE_CREDENTIAL;
mStatusBarService.onBiometricError(modality, error, vendorCode);
} else if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
mStatusBarService.hideAuthenticationDialog();
@@ -463,7 +404,7 @@
mClientReceiver.onError(modality, error, vendorCode);
return true;
} else {
- mState = AuthSession.STATE_ERROR_PENDING_SYSUI;
+ mState = STATE_ERROR_PENDING_SYSUI;
mStatusBarService.onBiometricError(modality, error, vendorCode);
}
break;
@@ -582,7 +523,7 @@
if (hasPausableBiometric()) {
// Pause authentication. onBiometricAuthenticated(false) causes the
// dialog to show a "try again" button for passive modalities.
- mState = AuthSession.STATE_AUTH_PAUSED;
+ mState = STATE_AUTH_PAUSED;
}
mClientReceiver.onAuthenticationFailed();
diff --git a/services/core/java/com/android/server/biometrics/BiometricSensor.java b/services/core/java/com/android/server/biometrics/BiometricSensor.java
index 09d0583..17ec112 100644
--- a/services/core/java/com/android/server/biometrics/BiometricSensor.java
+++ b/services/core/java/com/android/server/biometrics/BiometricSensor.java
@@ -16,6 +16,8 @@
package com.android.server.biometrics;
+import static android.hardware.biometrics.BiometricManager.Authenticators;
+
import android.annotation.IntDef;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
@@ -61,11 +63,11 @@
@interface SensorState {}
public final int id;
- public final int oemStrength; // strength as configured by the OEM
+ public final @Authenticators.Types int oemStrength; // strength as configured by the OEM
public final int modality;
public final IBiometricAuthenticator impl;
- private int mUpdatedStrength; // strength updated by BiometricStrengthController
+ private @Authenticators.Types int mUpdatedStrength; // updated by BiometricStrengthController
private @SensorState int mSensorState;
private @BiometricConstants.Errors int mError;
@@ -82,7 +84,7 @@
*/
abstract boolean confirmationSupported();
- BiometricSensor(int id, int modality, int strength,
+ BiometricSensor(int id, int modality, @Authenticators.Types int strength,
IBiometricAuthenticator impl) {
this.id = id;
this.modality = modality;
@@ -101,12 +103,11 @@
void goToStateWaitingForCookie(boolean requireConfirmation, IBinder token, long sessionId,
int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName,
- int cookie, int callingUid, int callingPid, int callingUserId)
+ int cookie)
throws RemoteException {
mCookie = cookie;
impl.prepareForAuthentication(requireConfirmation, token,
- sessionId, userId, sensorReceiver, opPackageName, mCookie,
- callingUid, callingPid, callingUserId);
+ sessionId, userId, sensorReceiver, opPackageName, mCookie);
mSensorState = STATE_WAITING_FOR_COOKIE;
}
@@ -122,10 +123,8 @@
mSensorState = STATE_AUTHENTICATING;
}
- void goToStateCancelling(IBinder token, String opPackageName, int callingUid,
- int callingPid, int callingUserId) throws RemoteException {
- impl.cancelAuthenticationFromService(token, opPackageName, callingUid, callingPid,
- callingUserId);
+ void goToStateCancelling(IBinder token, String opPackageName) throws RemoteException {
+ impl.cancelAuthenticationFromService(token, opPackageName);
mSensorState = STATE_CANCELING;
}
@@ -143,7 +142,7 @@
* strength.
* @return a bitfield, see {@link BiometricManager.Authenticators}
*/
- int getCurrentStrength() {
+ @Authenticators.Types int getCurrentStrength() {
return oemStrength | mUpdatedStrength;
}
@@ -160,7 +159,7 @@
* is checked.
* @param newStrength
*/
- void updateStrength(int newStrength) {
+ void updateStrength(@Authenticators.Types int newStrength) {
String log = "updateStrength: Before(" + toString() + ")";
mUpdatedStrength = newStrength;
log += " After(" + toString() + ")";
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 196582a..a471664 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -19,6 +19,9 @@
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.hardware.biometrics.BiometricManager.Authenticators;
+import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_IDLE;
+
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.IActivityManager;
@@ -38,7 +41,9 @@
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IBiometricSysuiReceiver;
+import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.PromptInfo;
+import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.net.Uri;
@@ -56,6 +61,7 @@
import android.text.TextUtils;
import android.util.Pair;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -185,10 +191,7 @@
args.argi1 /* userid */,
(IBiometricServiceReceiver) args.arg3 /* receiver */,
(String) args.arg4 /* opPackageName */,
- (PromptInfo) args.arg5 /* promptInfo */,
- args.argi2 /* callingUid */,
- args.argi3 /* callingPid */,
- args.argi4 /* callingUserId */);
+ (PromptInfo) args.arg5 /* promptInfo */);
args.recycle();
break;
}
@@ -476,6 +479,34 @@
*/
private final class BiometricServiceWrapper extends IBiometricService.Stub {
@Override // Binder call
+ public ITestSession createTestSession(int sensorId, @NonNull String opPackageName)
+ throws RemoteException {
+ checkInternalPermission();
+
+ for (BiometricSensor sensor : mSensors) {
+ if (sensor.id == sensorId) {
+ return sensor.impl.createTestSession(opPackageName);
+ }
+ }
+
+ Slog.e(TAG, "Unknown sensor for createTestSession: " + sensorId);
+ return null;
+ }
+
+ @Override // Binder call
+ public List<SensorPropertiesInternal> getSensorProperties(String opPackageName)
+ throws RemoteException {
+ checkInternalPermission();
+
+ final List<SensorPropertiesInternal> sensors = new ArrayList<>();
+ for (BiometricSensor sensor : mSensors) {
+ sensors.add(sensor.impl.getSensorProperties(opPackageName));
+ }
+
+ return sensors;
+ }
+
+ @Override // Binder call
public void onReadyForAuthentication(int cookie) {
checkInternalPermission();
@@ -486,8 +517,7 @@
@Override // Binder call
public void authenticate(IBinder token, long operationId, int userId,
- IBiometricServiceReceiver receiver, String opPackageName, PromptInfo promptInfo,
- int callingUid, int callingPid, int callingUserId) {
+ IBiometricServiceReceiver receiver, String opPackageName, PromptInfo promptInfo) {
checkInternalPermission();
if (token == null || receiver == null || opPackageName == null || promptInfo == null) {
@@ -516,16 +546,12 @@
args.arg3 = receiver;
args.arg4 = opPackageName;
args.arg5 = promptInfo;
- args.argi2 = callingUid;
- args.argi3 = callingPid;
- args.argi4 = callingUserId;
mHandler.obtainMessage(MSG_AUTHENTICATE, args).sendToTarget();
}
@Override // Binder call
- public void cancelAuthentication(IBinder token, String opPackageName,
- int callingUid, int callingPid, int callingUserId) {
+ public void cancelAuthentication(IBinder token, String opPackageName) {
checkInternalPermission();
mHandler.obtainMessage(MSG_CANCEL_AUTHENTICATION).sendToTarget();
@@ -577,7 +603,7 @@
}
@Override
- public void registerAuthenticator(int id, int modality, int strength,
+ public void registerAuthenticator(int id, int modality, @Authenticators.Types int strength,
IBiometricAuthenticator authenticator) {
checkInternalPermission();
@@ -677,14 +703,28 @@
}
@Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
return;
}
final long ident = Binder.clearCallingIdentity();
try {
- dumpInternal(pw);
+ if (args.length > 0 && "--proto".equals(args[0])) {
+ final ProtoOutputStream proto = new ProtoOutputStream(fd);
+ proto.write(BiometricServiceStateProto.AUTH_SESSION_STATE,
+ mCurrentAuthSession != null ? mCurrentAuthSession.getState()
+ : STATE_AUTH_IDLE);
+ for (BiometricSensor sensor : mSensors) {
+ byte[] serviceState = sensor.impl.dumpSensorServiceStateProto();
+ proto.write(BiometricServiceStateProto.SENSOR_SERVICE_STATES, serviceState);
+ }
+ proto.flush();
+ } else {
+ dumpInternal(pw);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -992,8 +1032,7 @@
}
private void handleAuthenticate(IBinder token, long operationId, int userId,
- IBiometricServiceReceiver receiver, String opPackageName, PromptInfo promptInfo,
- int callingUid, int callingPid, int callingUserId) {
+ IBiometricServiceReceiver receiver, String opPackageName, PromptInfo promptInfo) {
mHandler.post(() -> {
try {
@@ -1017,7 +1056,7 @@
}
authenticateInternal(token, operationId, userId, receiver, opPackageName,
- promptInfo, callingUid, callingPid, callingUserId, preAuthInfo);
+ promptInfo, preAuthInfo);
} else {
receiver.onError(preAuthStatus.first /* modality */,
preAuthStatus.second /* errorCode */,
@@ -1040,7 +1079,7 @@
*/
private void authenticateInternal(IBinder token, long operationId, int userId,
IBiometricServiceReceiver receiver, String opPackageName, PromptInfo promptInfo,
- int callingUid, int callingPid, int callingUserId, PreAuthInfo preAuthInfo) {
+ PreAuthInfo preAuthInfo) {
Slog.d(TAG, "Creating authSession with authRequest: " + preAuthInfo);
// No need to dismiss dialog / send error yet if we're continuing authentication, e.g.
@@ -1057,8 +1096,7 @@
final boolean debugEnabled = mInjector.isDebugEnabled(getContext(), userId);
mCurrentAuthSession = new AuthSession(getContext(), mStatusBarService, mSysuiReceiver,
mKeyStore, mRandom, mClientDeathReceiver, preAuthInfo, token, operationId, userId,
- mBiometricSensorReceiver, receiver, opPackageName, promptInfo, callingUid,
- callingPid, callingUserId, debugEnabled,
+ mBiometricSensorReceiver, receiver, opPackageName, promptInfo, debugEnabled,
mInjector.getFingerprintSensorProperties(getContext()));
try {
mCurrentAuthSession.goToInitialState();
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
index b3e6cad..62c9295 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
@@ -16,8 +16,11 @@
package com.android.server.biometrics.sensors.face;
+import android.annotation.NonNull;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricSensorReceiver;
+import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.face.IFaceService;
import android.os.IBinder;
import android.os.RemoteException;
@@ -32,20 +35,34 @@
private final IFaceService mFaceService;
private final int mSensorId;
- public FaceAuthenticator(IFaceService faceService, int sensorId)
- throws RemoteException {
+ public FaceAuthenticator(IFaceService faceService, int sensorId) throws RemoteException {
mFaceService = faceService;
mSensorId = sensorId;
}
@Override
+ public ITestSession createTestSession(@NonNull String opPackageName) throws RemoteException {
+ return mFaceService.createTestSession(mSensorId, opPackageName);
+ }
+
+ @Override
+ public SensorPropertiesInternal getSensorProperties(@NonNull String opPackageName)
+ throws RemoteException {
+ return mFaceService.getSensorProperties(mSensorId, opPackageName);
+ }
+
+ @Override
+ public byte[] dumpSensorServiceStateProto() throws RemoteException {
+ return mFaceService.dumpSensorServiceStateProto();
+ }
+
+ @Override
public void prepareForAuthentication(boolean requireConfirmation, IBinder token,
long operationId, int userId, IBiometricSensorReceiver sensorReceiver,
- String opPackageName, int cookie, int callingUid, int callingPid, int callingUserId)
+ String opPackageName, int cookie)
throws RemoteException {
mFaceService.prepareForAuthentication(mSensorId, requireConfirmation, token, operationId,
- userId, sensorReceiver, opPackageName, cookie, callingUid, callingPid,
- callingUserId);
+ userId, sensorReceiver, opPackageName, cookie);
}
@Override
@@ -54,10 +71,9 @@
}
@Override
- public void cancelAuthenticationFromService(IBinder token, String opPackageName, int callingUid,
- int callingPid, int callingUserId) throws RemoteException {
- mFaceService.cancelAuthenticationFromService(mSensorId, token, opPackageName, callingUid,
- callingPid, callingUserId);
+ public void cancelAuthenticationFromService(IBinder token, String opPackageName)
+ throws RemoteException {
+ mFaceService.cancelAuthenticationFromService(mSensorId, token, opPackageName);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 1e0764a..4906aac 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -29,6 +29,7 @@
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.SensorProps;
import android.hardware.face.Face;
@@ -38,8 +39,8 @@
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Process;
import android.os.NativeHandle;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -140,6 +141,34 @@
* Receives the incoming binder calls from FaceManager.
*/
private final class FaceServiceWrapper extends IFaceService.Stub {
+ @Override
+ public ITestSession createTestSession(int sensorId, @NonNull String opPackageName) {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+
+ final ServiceProvider provider = getProviderForSensor(sensorId);
+
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for createTestSession, sensorId: " + sensorId);
+ return null;
+ }
+
+ return provider.createTestSession(sensorId, opPackageName);
+ }
+
+ @Override
+ public byte[] dumpSensorServiceStateProto() {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+
+ final ProtoOutputStream proto = new ProtoOutputStream();
+ for (ServiceProvider provider : mServiceProviders) {
+ for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) {
+ provider.dumpProtoState(props.sensorId, proto);
+ }
+ }
+ proto.flush();
+ return proto.getBytes();
+ }
+
@Override // Binder call
public List<FaceSensorPropertiesInternal> getSensorPropertiesInternal(
String opPackageName) {
@@ -154,6 +183,21 @@
}
@Override // Binder call
+ public FaceSensorPropertiesInternal getSensorProperties(int sensorId,
+ @NonNull String opPackageName) {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+
+ final ServiceProvider provider = getProviderForSensor(sensorId);
+ if (provider == null) {
+ Slog.w(TAG, "No matching sensor for getSensorProperties, sensorId: " + sensorId
+ + ", caller: " + opPackageName);
+ return null;
+ }
+
+ return provider.getSensorProperties(sensorId);
+ }
+
+ @Override // Binder call
public void generateChallenge(IBinder token, int sensorId, int userId,
IFaceServiceReceiver receiver, String opPackageName) {
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
@@ -270,8 +314,7 @@
@Override // Binder call
public void prepareForAuthentication(int sensorId, boolean requireConfirmation,
IBinder token, long operationId, int userId,
- IBiometricSensorReceiver sensorReceiver, String opPackageName, int cookie,
- int callingUid, int callingPid, int callingUserId) {
+ IBiometricSensorReceiver sensorReceiver, String opPackageName, int cookie) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
final ServiceProvider provider = getProviderForSensor(sensorId);
@@ -326,7 +369,7 @@
@Override // Binder call
public void cancelAuthenticationFromService(int sensorId, final IBinder token,
- final String opPackageName, int callingUid, int callingPid, int callingUserId) {
+ final String opPackageName) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
final ServiceProvider provider = getProviderForSensor(sensorId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
index e3fb750..d1578fa 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
@@ -18,7 +18,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.hardware.biometrics.ITestSession;
import android.hardware.face.Face;
+import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.IFaceServiceReceiver;
import android.os.IBinder;
@@ -61,6 +63,9 @@
List<FaceSensorPropertiesInternal> getSensorProperties();
@NonNull
+ FaceSensorPropertiesInternal getSensorProperties(int sensorId);
+
+ @NonNull
List<Face> getEnrolledFaces(int sensorId, int userId);
@LockoutTracker.LockoutMode
@@ -110,4 +115,7 @@
void dumpProtoMetrics(int sensorId, @NonNull FileDescriptor fd);
void dumpInternal(int sensorId, @NonNull PrintWriter pw);
+
+ @NonNull
+ ITestSession createTestSession(int sensorId, @NonNull String opPackageName);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 590579a..01c16fd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -24,6 +24,7 @@
import android.app.TaskStackListener;
import android.content.Context;
import android.content.pm.UserInfo;
+import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.SensorProps;
import android.hardware.face.Face;
@@ -254,6 +255,12 @@
@NonNull
@Override
+ public FaceSensorPropertiesInternal getSensorProperties(int sensorId) {
+ return mSensors.get(sensorId).getSensorProperties();
+ }
+
+ @NonNull
+ @Override
public List<Face> getEnrolledFaces(int sensorId, int userId) {
return FaceUtils.getInstance(sensorId).getBiometricsForUser(mContext, userId);
}
@@ -566,6 +573,12 @@
mUsageStats.print(pw);
}
+ @NonNull
+ @Override
+ public ITestSession createTestSession(int sensorId, @NonNull String opPackageName) {
+ return null; // TODO
+ }
+
@Override
public void binderDied() {
Slog.e(getTag(), "HAL died");
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
new file mode 100644
index 0000000..4c983fb
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face.hidl;
+
+import static android.Manifest.permission.TEST_BIOMETRIC;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.ITestSession;
+import android.hardware.face.Face;
+import android.hardware.face.IFaceServiceReceiver;
+import android.os.Binder;
+import android.util.Slog;
+
+import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.sensors.face.FaceUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+public class BiometricTestSessionImpl extends ITestSession.Stub {
+ private static final String TAG = "BiometricTestSessionImpl";
+
+ @NonNull private final Context mContext;
+ private final int mSensorId;
+ @NonNull private final Face10 mFace10;
+ @NonNull private final Face10.HalResultController mHalResultController;
+ @NonNull private final Set<Integer> mEnrollmentIds;
+ @NonNull private final Random mRandom;
+
+ private final IFaceServiceReceiver mReceiver = new IFaceServiceReceiver.Stub() {
+ @Override
+ public void onEnrollResult(Face face, int remaining) {
+
+ }
+
+ @Override
+ public void onAcquired(int acquiredInfo, int vendorCode) {
+
+ }
+
+ @Override
+ public void onAuthenticationSucceeded(Face face, int userId, boolean isStrongBiometric) {
+
+ }
+
+ @Override
+ public void onFaceDetected(int sensorId, int userId, boolean isStrongBiometric) {
+
+ }
+
+ @Override
+ public void onAuthenticationFailed() {
+
+ }
+
+ @Override
+ public void onError(int error, int vendorCode) {
+
+ }
+
+ @Override
+ public void onRemoved(Face face, int remaining) {
+
+ }
+
+ @Override
+ public void onFeatureSet(boolean success, int feature) {
+
+ }
+
+ @Override
+ public void onFeatureGet(boolean success, int feature, boolean value) {
+
+ }
+
+ @Override
+ public void onChallengeGenerated(int sensorId, long challenge) {
+
+ }
+
+ @Override
+ public void onChallengeInterrupted(int sensorId) {
+
+ }
+
+ @Override
+ public void onChallengeInterruptFinished(int sensorId) {
+
+ }
+ };
+
+ BiometricTestSessionImpl(@NonNull Context context, int sensorId, @NonNull Face10 face10,
+ @NonNull Face10.HalResultController halResultController) {
+ mContext = context;
+ mSensorId = sensorId;
+ mFace10 = face10;
+ mHalResultController = halResultController;
+ mEnrollmentIds = new HashSet<>();
+ mRandom = new Random();
+ }
+
+ @Override
+ public void setTestHalEnabled(boolean enabled) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ mFace10.setTestHalEnabled(enabled);
+ }
+
+ @Override
+ public void startEnroll(int userId) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ mFace10.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
+ mContext.getOpPackageName(), new int[0] /* disabledFeatures */,
+ null /* surfaceHandle */);
+ }
+
+ @Override
+ public void finishEnroll(int userId) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ int nextRandomId = mRandom.nextInt();
+ while (mEnrollmentIds.contains(nextRandomId)) {
+ nextRandomId = mRandom.nextInt();
+ }
+
+ mEnrollmentIds.add(nextRandomId);
+ mHalResultController.onEnrollResult(0 /* deviceId */,
+ nextRandomId /* faceId */, userId, 0);
+ }
+
+ @Override
+ public void acceptAuthentication(int userId) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ // Fake authentication with any of the existing fingers
+ List<Face> faces = FaceUtils.getInstance().getBiometricsForUser(mContext, userId);
+ if (faces.isEmpty()) {
+ Slog.w(TAG, "No faces, returning");
+ return;
+ }
+ final int fid = faces.get(0).getBiometricId();
+ final ArrayList<Byte> hat = new ArrayList<>(Collections.nCopies(69, (byte) 0));
+ mHalResultController.onAuthenticated(0 /* deviceId */, fid, userId, hat);
+ }
+
+ @Override
+ public void rejectAuthentication(int userId) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ mHalResultController.onAuthenticated(0 /* deviceId */, 0 /* faceId */, userId, null);
+ }
+
+ @Override
+ public void notifyAcquired(int userId, int acquireInfo) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ mHalResultController.onAcquired(0 /* deviceId */, userId, acquireInfo, 0 /* vendorCode */);
+ }
+
+ @Override
+ public void notifyError(int userId, int errorCode) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ mHalResultController.onError(0 /* deviceId */, userId, errorCode, 0 /* vendorCode */);
+ }
+
+ @Override
+ public void cleanupInternalState(int userId) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ mFace10.scheduleInternalCleanup(mSensorId, userId);
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index 7b70bd8a..27ca33d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -28,6 +28,7 @@
import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
import android.hardware.face.Face;
@@ -51,6 +52,9 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.biometrics.SensorServiceStateProto;
+import com.android.server.biometrics.SensorStateProto;
+import com.android.server.biometrics.UserStateProto;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
@@ -67,6 +71,7 @@
import com.android.server.biometrics.sensors.face.LockoutHalImpl;
import com.android.server.biometrics.sensors.face.ServiceProvider;
import com.android.server.biometrics.sensors.face.UsageStats;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
import org.json.JSONArray;
import org.json.JSONException;
@@ -93,6 +98,8 @@
static final String NOTIFICATION_TAG = "FaceService";
static final int NOTIFICATION_ID = 1;
+ private boolean mTestHalEnabled;
+
@NonNull private final FaceSensorPropertiesInternal mSensorProperties;
@NonNull private final Context mContext;
@NonNull private final BiometricScheduler mScheduler;
@@ -104,6 +111,7 @@
@NonNull private final NotificationManager mNotificationManager;
@NonNull private final Map<Integer, Long> mAuthenticatorIds;
@Nullable private IBiometricsFace mDaemon;
+ @NonNull private final HalResultController mHalResultController;
// If a challenge is generated, keep track of its owner. Since IBiometricsFace@1.0 only
// supports a single in-flight challenge, we must notify the interrupted owner that its
// challenge is no longer valid. The interrupted owner will be notified when the interrupter
@@ -122,174 +130,207 @@
}
};
- private final IBiometricsFaceClientCallback mDaemonCallback =
- new IBiometricsFaceClientCallback.Stub() {
- @Override
- public void onEnrollResult(long deviceId, int faceId, int userId, int remaining) {
- mHandler.post(() -> {
- final CharSequence name = FaceUtils.getInstance()
- .getUniqueName(mContext, userId);
- final Face face = new Face(name, faceId, deviceId);
+ public static class HalResultController extends IBiometricsFaceClientCallback.Stub {
+ /**
+ * Interface to sends results to the HalResultController's owner.
+ */
+ public interface Callback {
+ /**
+ * Invoked when the HAL sends ERROR_HW_UNAVAILABLE.
+ */
+ void onHardwareUnavailable();
+ }
- final ClientMonitor<?> client = mScheduler.getCurrentClient();
- if (!(client instanceof FaceEnrollClient)) {
- Slog.e(TAG, "onEnrollResult for non-enroll client: "
- + Utils.getClientName(client));
- return;
- }
+ private final int mSensorId;
+ @NonNull private final Context mContext;
+ @NonNull private final Handler mHandler;
+ @NonNull private final BiometricScheduler mScheduler;
+ @Nullable private Callback mCallback;
+ @NonNull private final LockoutHalImpl mLockoutTracker;
+ @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher;
- final FaceEnrollClient enrollClient = (FaceEnrollClient) client;
- enrollClient.onEnrollResult(face, remaining);
- });
+ HalResultController(int sensorId, @NonNull Context context, @NonNull Handler handler,
+ @NonNull BiometricScheduler scheduler, @NonNull LockoutHalImpl lockoutTracker,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
+ mSensorId = sensorId;
+ mContext = context;
+ mHandler = handler;
+ mScheduler = scheduler;
+ mLockoutTracker = lockoutTracker;
+ mLockoutResetDispatcher = lockoutResetDispatcher;
+ }
+
+ public void setCallback(@Nullable Callback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void onEnrollResult(long deviceId, int faceId, int userId, int remaining) {
+ mHandler.post(() -> {
+ final CharSequence name = FaceUtils.getInstance()
+ .getUniqueName(mContext, userId);
+ final Face face = new Face(name, faceId, deviceId);
+
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof FaceEnrollClient)) {
+ Slog.e(TAG, "onEnrollResult for non-enroll client: "
+ + Utils.getClientName(client));
+ return;
}
- @Override
- public void onAuthenticated(long deviceId, int faceId, int userId,
- ArrayList<Byte> token) {
- mHandler.post(() -> {
- final ClientMonitor<?> client = mScheduler.getCurrentClient();
- if (!(client instanceof AuthenticationConsumer)) {
- Slog.e(TAG, "onAuthenticated for non-authentication consumer: "
- + Utils.getClientName(client));
- return;
- }
+ final FaceEnrollClient enrollClient = (FaceEnrollClient) client;
+ enrollClient.onEnrollResult(face, remaining);
+ });
+ }
- final AuthenticationConsumer authenticationConsumer =
- (AuthenticationConsumer) client;
- final boolean authenticated = faceId != 0;
- final Face face = new Face("", faceId, deviceId);
- authenticationConsumer.onAuthenticated(face, authenticated, token);
- });
+ @Override
+ public void onAuthenticated(long deviceId, int faceId, int userId,
+ ArrayList<Byte> token) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof AuthenticationConsumer)) {
+ Slog.e(TAG, "onAuthenticated for non-authentication consumer: "
+ + Utils.getClientName(client));
+ return;
}
- @Override
- public void onAcquired(long deviceId, int userId, int acquiredInfo,
- int vendorCode) {
- mHandler.post(() -> {
- final ClientMonitor<?> client = mScheduler.getCurrentClient();
- if (!(client instanceof AcquisitionClient)) {
- Slog.e(TAG, "onAcquired for non-acquire client: "
- + Utils.getClientName(client));
- return;
- }
+ final AuthenticationConsumer authenticationConsumer =
+ (AuthenticationConsumer) client;
+ final boolean authenticated = faceId != 0;
+ final Face face = new Face("", faceId, deviceId);
+ authenticationConsumer.onAuthenticated(face, authenticated, token);
+ });
+ }
- final AcquisitionClient<?> acquisitionClient =
- (AcquisitionClient<?>) client;
- acquisitionClient.onAcquired(acquiredInfo, vendorCode);
- });
+ @Override
+ public void onAcquired(long deviceId, int userId, int acquiredInfo,
+ int vendorCode) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof AcquisitionClient)) {
+ Slog.e(TAG, "onAcquired for non-acquire client: "
+ + Utils.getClientName(client));
+ return;
}
- @Override
- public void onError(long deviceId, int userId, int error, int vendorCode) {
- mHandler.post(() -> {
- final ClientMonitor<?> client = mScheduler.getCurrentClient();
- Slog.d(TAG, "handleError"
- + ", client: " + (client != null ? client.getOwnerString() : null)
- + ", error: " + error
- + ", vendorCode: " + vendorCode);
- if (!(client instanceof Interruptable)) {
- Slog.e(TAG, "onError for non-error consumer: " + Utils.getClientName(
- client));
- return;
- }
+ final AcquisitionClient<?> acquisitionClient =
+ (AcquisitionClient<?>) client;
+ acquisitionClient.onAcquired(acquiredInfo, vendorCode);
+ });
+ }
- final Interruptable interruptable = (Interruptable) client;
- interruptable.onError(error, vendorCode);
-
- if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
- Slog.e(TAG, "Got ERROR_HW_UNAVAILABLE");
- mDaemon = null;
- mCurrentUserId = UserHandle.USER_NULL;
- }
- });
+ @Override
+ public void onError(long deviceId, int userId, int error, int vendorCode) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ Slog.d(TAG, "handleError"
+ + ", client: " + (client != null ? client.getOwnerString() : null)
+ + ", error: " + error
+ + ", vendorCode: " + vendorCode);
+ if (!(client instanceof Interruptable)) {
+ Slog.e(TAG, "onError for non-error consumer: " + Utils.getClientName(
+ client));
+ return;
}
- @Override
- public void onRemoved(long deviceId, ArrayList<Integer> removed, int userId) {
- mHandler.post(() -> {
- final ClientMonitor<?> client = mScheduler.getCurrentClient();
- if (!(client instanceof RemovalConsumer)) {
- Slog.e(TAG, "onRemoved for non-removal consumer: "
- + Utils.getClientName(client));
- return;
- }
+ final Interruptable interruptable = (Interruptable) client;
+ interruptable.onError(error, vendorCode);
- final RemovalConsumer removalConsumer = (RemovalConsumer) client;
+ if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
+ Slog.e(TAG, "Got ERROR_HW_UNAVAILABLE");
+ if (mCallback != null) {
+ mCallback.onHardwareUnavailable();
+ }
+ }
+ });
+ }
- if (!removed.isEmpty()) {
- // Convert to old fingerprint-like behavior, where remove() receives
- // one removal
- // at a time. This way, remove can share some more common code.
- for (int i = 0; i < removed.size(); i++) {
- final int id = removed.get(i);
- final Face face = new Face("", id, deviceId);
- final int remaining = removed.size() - i - 1;
- Slog.d(TAG, "Removed, faceId: " + id + ", remaining: " + remaining);
- removalConsumer.onRemoved(face, remaining);
- }
- } else {
- removalConsumer.onRemoved(null, 0 /* remaining */);
- }
-
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.FACE_UNLOCK_RE_ENROLL, 0, UserHandle.USER_CURRENT);
- });
+ @Override
+ public void onRemoved(long deviceId, ArrayList<Integer> removed, int userId) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof RemovalConsumer)) {
+ Slog.e(TAG, "onRemoved for non-removal consumer: "
+ + Utils.getClientName(client));
+ return;
}
- @Override
- public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId) {
- mHandler.post(() -> {
- final ClientMonitor<?> client = mScheduler.getCurrentClient();
- if (!(client instanceof EnumerateConsumer)) {
- Slog.e(TAG, "onEnumerate for non-enumerate consumer: "
- + Utils.getClientName(client));
- return;
- }
+ final RemovalConsumer removalConsumer = (RemovalConsumer) client;
- final EnumerateConsumer enumerateConsumer = (EnumerateConsumer) client;
-
- if (!faceIds.isEmpty()) {
- // Convert to old fingerprint-like behavior, where enumerate()
- // receives one
- // template at a time. This way, enumerate can share some more common
- // code.
- for (int i = 0; i < faceIds.size(); i++) {
- final Face face = new Face("", faceIds.get(i), deviceId);
- enumerateConsumer.onEnumerationResult(face, faceIds.size() - i - 1);
- }
- } else {
- // For face, the HIDL contract is to receive an empty list when there
- // are no
- // templates enrolled. Send a null identifier since we don't consume
- // them
- // anywhere, and send remaining == 0 so this code can be shared with
- // Fingerprint@2.1
- enumerateConsumer.onEnumerationResult(null /* identifier */, 0);
- }
- });
+ if (!removed.isEmpty()) {
+ // Convert to old fingerprint-like behavior, where remove() receives
+ // one removal
+ // at a time. This way, remove can share some more common code.
+ for (int i = 0; i < removed.size(); i++) {
+ final int id = removed.get(i);
+ final Face face = new Face("", id, deviceId);
+ final int remaining = removed.size() - i - 1;
+ Slog.d(TAG, "Removed, faceId: " + id + ", remaining: " + remaining);
+ removalConsumer.onRemoved(face, remaining);
+ }
+ } else {
+ removalConsumer.onRemoved(null, 0 /* remaining */);
}
- @Override
- public void onLockoutChanged(long duration) {
- mHandler.post(() -> {
- Slog.d(TAG, "onLockoutChanged: " + duration);
- final @LockoutTracker.LockoutMode int lockoutMode;
- if (duration == 0) {
- lockoutMode = LockoutTracker.LOCKOUT_NONE;
- } else if (duration == -1 || duration == Long.MAX_VALUE) {
- lockoutMode = LockoutTracker.LOCKOUT_PERMANENT;
- } else {
- lockoutMode = LockoutTracker.LOCKOUT_TIMED;
- }
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.FACE_UNLOCK_RE_ENROLL, 0, UserHandle.USER_CURRENT);
+ });
+ }
- mLockoutTracker.setCurrentUserLockoutMode(lockoutMode);
-
- if (duration == 0) {
- mLockoutResetDispatcher.notifyLockoutResetCallbacks(mSensorId);
- }
- });
+ @Override
+ public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof EnumerateConsumer)) {
+ Slog.e(TAG, "onEnumerate for non-enumerate consumer: "
+ + Utils.getClientName(client));
+ return;
}
- };
+
+ final EnumerateConsumer enumerateConsumer = (EnumerateConsumer) client;
+
+ if (!faceIds.isEmpty()) {
+ // Convert to old fingerprint-like behavior, where enumerate()
+ // receives one
+ // template at a time. This way, enumerate can share some more common
+ // code.
+ for (int i = 0; i < faceIds.size(); i++) {
+ final Face face = new Face("", faceIds.get(i), deviceId);
+ enumerateConsumer.onEnumerationResult(face, faceIds.size() - i - 1);
+ }
+ } else {
+ // For face, the HIDL contract is to receive an empty list when there
+ // are no
+ // templates enrolled. Send a null identifier since we don't consume
+ // them
+ // anywhere, and send remaining == 0 so this code can be shared with
+ // Fingerprint@2.1
+ enumerateConsumer.onEnumerationResult(null /* identifier */, 0);
+ }
+ });
+ }
+
+ @Override
+ public void onLockoutChanged(long duration) {
+ mHandler.post(() -> {
+ Slog.d(TAG, "onLockoutChanged: " + duration);
+ final @LockoutTracker.LockoutMode int lockoutMode;
+ if (duration == 0) {
+ lockoutMode = LockoutTracker.LOCKOUT_NONE;
+ } else if (duration == -1 || duration == Long.MAX_VALUE) {
+ lockoutMode = LockoutTracker.LOCKOUT_PERMANENT;
+ } else {
+ lockoutMode = LockoutTracker.LOCKOUT_TIMED;
+ }
+
+ mLockoutTracker.setCurrentUserLockoutMode(lockoutMode);
+
+ if (duration == 0) {
+ mLockoutResetDispatcher.notifyLockoutResetCallbacks(mSensorId);
+ }
+ });
+ }
+ }
@VisibleForTesting
public Face10(@NonNull Context context, int sensorId,
@@ -309,6 +350,12 @@
mNotificationManager = mContext.getSystemService(NotificationManager.class);
mLockoutTracker = new LockoutHalImpl();
mLockoutResetDispatcher = lockoutResetDispatcher;
+ mHalResultController = new HalResultController(sensorId, context, mHandler, mScheduler,
+ mLockoutTracker, lockoutResetDispatcher);
+ mHalResultController.setCallback(() -> {
+ mDaemon = null;
+ mCurrentUserId = UserHandle.USER_NULL;
+ });
try {
ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG);
@@ -351,6 +398,12 @@
}
private synchronized IBiometricsFace getDaemon() {
+ if (mTestHalEnabled) {
+ final TestHal testHal = new TestHal();
+ testHal.setCallback(mHalResultController);
+ return testHal;
+ }
+
if (mDaemon != null) {
return mDaemon;
}
@@ -378,7 +431,7 @@
// successfully set.
long halId = 0;
try {
- halId = mDaemon.setCallback(mDaemonCallback).value;
+ halId = mDaemon.setCallback(mHalResultController).value;
} catch (RemoteException e) {
Slog.e(TAG, "Failed to set callback for face HAL", e);
mDaemon = null;
@@ -413,6 +466,12 @@
return properties;
}
+ @NonNull
+ @Override
+ public FaceSensorPropertiesInternal getSensorProperties(int sensorId) {
+ return mSensorProperties;
+ }
+
@Override
@NonNull
public List<Face> getEnrolledFaces(int sensorId, int userId) {
@@ -712,6 +771,22 @@
@Override
public void dumpProtoState(int sensorId, ProtoOutputStream proto) {
+ final long sensorToken = proto.start(SensorServiceStateProto.SENSOR_STATES);
+
+ proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId);
+ proto.write(SensorStateProto.IS_BUSY, mScheduler.getCurrentClient() != null);
+
+ for (UserInfo user : UserManager.get(mContext).getUsers()) {
+ final int userId = user.getUserHandle().getIdentifier();
+
+ final long userToken = proto.start(SensorStateProto.USER_STATES);
+ proto.write(UserStateProto.USER_ID, userId);
+ proto.write(UserStateProto.NUM_ENROLLED, FaceUtils.getInstance()
+ .getBiometricsForUser(mContext, userId).size());
+ proto.end(userToken);
+ }
+
+ proto.end(sensorToken);
}
@Override
@@ -844,4 +919,14 @@
}
}
}
+
+ void setTestHalEnabled(boolean enabled) {
+ mTestHalEnabled = enabled;
+ }
+
+ @NonNull
+ @Override
+ public ITestSession createTestSession(int sensorId, @NonNull String opPackageName) {
+ return new BiometricTestSessionImpl(mContext, mSensorId, this, mHalResultController);
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/TestHal.java
new file mode 100644
index 0000000..bab1114d
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/TestHal.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face.hidl;
+
+import android.annotation.Nullable;
+import android.hardware.biometrics.face.V1_0.FaceError;
+import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
+import android.hardware.biometrics.face.V1_0.OptionalBool;
+import android.hardware.biometrics.face.V1_0.OptionalUint64;
+import android.hardware.biometrics.face.V1_0.Status;
+import android.hardware.biometrics.face.V1_1.IBiometricsFace;
+import android.os.NativeHandle;
+import android.os.RemoteException;
+
+import java.util.ArrayList;
+
+public class TestHal extends IBiometricsFace.Stub {
+ @Nullable
+ private IBiometricsFaceClientCallback mCallback;
+
+ @Override
+ public OptionalUint64 setCallback(IBiometricsFaceClientCallback clientCallback) {
+ mCallback = clientCallback;
+ final OptionalUint64 result = new OptionalUint64();
+ result.status = Status.OK;
+ return new OptionalUint64();
+ }
+
+ @Override
+ public int setActiveUser(int userId, String storePath) {
+ return 0;
+ }
+
+ @Override
+ public OptionalUint64 generateChallenge(int challengeTimeoutSec) {
+ final OptionalUint64 result = new OptionalUint64();
+ result.status = Status.OK;
+ result.value = 0;
+ return result;
+ }
+
+ @Override
+ public int enroll(ArrayList<Byte> hat, int timeoutSec, ArrayList<Integer> disabledFeatures) {
+ return 0;
+ }
+
+ @Override
+ public int revokeChallenge() {
+ return 0;
+ }
+
+ @Override
+ public int setFeature(int feature, boolean enabled, ArrayList<Byte> hat, int faceId) {
+ return 0;
+ }
+
+ @Override
+ public OptionalBool getFeature(int feature, int faceId) {
+ final OptionalBool result = new OptionalBool();
+ result.status = Status.OK;
+ result.value = true;
+ return result;
+ }
+
+ @Override
+ public OptionalUint64 getAuthenticatorId() {
+ final OptionalUint64 result = new OptionalUint64();
+ result.status = Status.OK;
+ result.value = 0;
+ return result;
+ }
+
+ @Override
+ public int cancel() throws RemoteException {
+ if (mCallback != null) {
+ mCallback.onError(0 /* deviceId */, 0 /* userId */, FaceError.CANCELED,
+ 0 /* vendorCode */);
+ }
+ return 0;
+ }
+
+ @Override
+ public int enumerate() {
+ return 0;
+ }
+
+ @Override
+ public int remove(int faceId) {
+ return 0;
+ }
+
+ @Override
+ public int authenticate(long operationId) {
+ return 0;
+ }
+
+ @Override
+ public int userActivity() {
+ return 0;
+ }
+
+ @Override
+ public int resetLockout(ArrayList<Byte> hat) {
+ return 0;
+ }
+
+ @Override
+ public int enrollRemotely(ArrayList<Byte> hat, int timeoutSec,
+ ArrayList<Integer> disabledFeatures) {
+ return 0;
+ }
+
+ @Override
+ public int enroll_1_1(ArrayList<Byte> hat, int timeoutSec, ArrayList<Integer> disabledFeatures,
+ NativeHandle nativeHandle) {
+ return 0;
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
index 9f9d9cc..1616457 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
@@ -16,8 +16,11 @@
package com.android.server.biometrics.sensors.fingerprint;
+import android.annotation.NonNull;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricSensorReceiver;
+import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintService;
import android.os.IBinder;
import android.os.RemoteException;
@@ -39,12 +42,28 @@
}
@Override
+ public ITestSession createTestSession(@NonNull String opPackageName) throws RemoteException {
+ return mFingerprintService.createTestSession(mSensorId, opPackageName);
+ }
+
+ @Override
+ public SensorPropertiesInternal getSensorProperties(@NonNull String opPackageName)
+ throws RemoteException {
+ return mFingerprintService.getSensorProperties(mSensorId, opPackageName);
+ }
+
+ @Override
+ public byte[] dumpSensorServiceStateProto() throws RemoteException {
+ return mFingerprintService.dumpSensorServiceStateProto();
+ }
+
+ @Override
public void prepareForAuthentication(boolean requireConfirmation, IBinder token,
long operationId, int userId, IBiometricSensorReceiver sensorReceiver,
- String opPackageName, int cookie, int callingUid, int callingPid, int callingUserId)
+ String opPackageName, int cookie)
throws RemoteException {
mFingerprintService.prepareForAuthentication(mSensorId, token, operationId, userId,
- sensorReceiver, opPackageName, cookie, callingUid, callingPid, callingUserId);
+ sensorReceiver, opPackageName, cookie);
}
@Override
@@ -53,10 +72,9 @@
}
@Override
- public void cancelAuthenticationFromService(IBinder token, String opPackageName, int callingUid,
- int callingPid, int callingUserId) throws RemoteException {
- mFingerprintService.cancelAuthenticationFromService(mSensorId, token, opPackageName,
- callingUid, callingPid, callingUserId);
+ public void cancelAuthenticationFromService(IBinder token, String opPackageName)
+ throws RemoteException {
+ mFingerprintService.cancelAuthenticationFromService(mSensorId, token, opPackageName);
}
@Override
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 b84d095..af0935f 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
@@ -100,21 +100,37 @@
*/
private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
@Override
- public ITestSession createTestSession(int sensorId, String opPackageName) {
+ public ITestSession createTestSession(int sensorId, @NonNull String opPackageName) {
Utils.checkPermission(getContext(), TEST_BIOMETRIC);
- for (ServiceProvider provider : mServiceProviders) {
- if (provider.containsSensor(sensorId)) {
- return provider.createTestSession(sensorId, opPackageName);
- }
+ final ServiceProvider provider = getProviderForSensor(sensorId);
+
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for createTestSession, sensorId: " + sensorId);
+ return null;
}
- return null;
+ return provider.createTestSession(sensorId, opPackageName);
+ }
+
+ @Override
+ public byte[] dumpSensorServiceStateProto() {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+
+ final ProtoOutputStream proto = new ProtoOutputStream();
+ for (ServiceProvider provider : mServiceProviders) {
+ for (FingerprintSensorPropertiesInternal props
+ : provider.getSensorProperties()) {
+ provider.dumpProtoState(props.sensorId, proto);
+ }
+ }
+ proto.flush();
+ return proto.getBytes();
}
@Override // Binder call
public List<FingerprintSensorPropertiesInternal> getSensorPropertiesInternal(
- String opPackageName) {
+ @NonNull String opPackageName) {
if (getContext().checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL)
!= PackageManager.PERMISSION_GRANTED) {
Utils.checkPermission(getContext(), TEST_BIOMETRIC);
@@ -128,6 +144,20 @@
return properties;
}
+ @Override
+ public FingerprintSensorPropertiesInternal getSensorProperties(int sensorId,
+ @NonNull String opPackageName) {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+
+ final ServiceProvider provider = getProviderForSensor(sensorId);
+ if (provider == null) {
+ Slog.w(TAG, "No matching sensor for getSensorProperties, sensorId: " + sensorId
+ + ", caller: " + opPackageName);
+ return null;
+ }
+ return provider.getSensorProperties(sensorId);
+ }
+
@Override // Binder call
public void generateChallenge(IBinder token, int sensorId, int userId,
IFingerprintServiceReceiver receiver, String opPackageName) {
@@ -262,7 +292,7 @@
@Override // Binder call
public void prepareForAuthentication(int sensorId, IBinder token, long operationId,
int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName,
- int cookie, int callingUid, int callingPid, int callingUserId) {
+ int cookie) {
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
final ServiceProvider provider = getProviderForSensor(sensorId);
@@ -334,7 +364,7 @@
@Override // Binder call
public void cancelAuthenticationFromService(final int sensorId, final IBinder token,
- final String opPackageName, int callingUid, int callingPid, int callingUserId) {
+ final String opPackageName) {
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
final ServiceProvider provider = getProviderForSensor(sensorId);
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 c13a656..6e36cd2 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
@@ -64,6 +64,9 @@
@NonNull
List<FingerprintSensorPropertiesInternal> getSensorProperties();
+ @NonNull
+ FingerprintSensorPropertiesInternal getSensorProperties(int sensorId);
+
void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken);
void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token,
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 0ebfe93..5f3be488c1 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
@@ -226,6 +226,12 @@
return props;
}
+ @NonNull
+ @Override
+ public FingerprintSensorPropertiesInternal getSensorProperties(int sensorId) {
+ return mSensors.get(sensorId).getSensorProperties();
+ }
+
private void scheduleLoadAuthenticatorIds(int sensorId) {
for (UserInfo user : UserManager.get(mContext).getAliveUsers()) {
scheduleLoadAuthenticatorIdsForUser(sensorId, user.id);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index 2e78912..380608f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -39,10 +39,10 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.SensorServiceStateProto;
+import com.android.server.biometrics.SensorStateProto;
+import com.android.server.biometrics.UserStateProto;
import com.android.server.biometrics.Utils;
-import com.android.server.biometrics.fingerprint.FingerprintServiceStateProto;
-import com.android.server.biometrics.fingerprint.SensorStateProto;
-import com.android.server.biometrics.fingerprint.UserStateProto;
import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BiometricScheduler;
@@ -491,7 +491,7 @@
}
void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto) {
- final long sensorToken = proto.start(FingerprintServiceStateProto.SENSOR_STATES);
+ final long sensorToken = proto.start(SensorServiceStateProto.SENSOR_STATES);
proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId);
proto.write(SensorStateProto.IS_BUSY, mScheduler.getCurrentClient() != null);
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 8c766d6..9924d47 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
@@ -49,13 +49,13 @@
import com.android.internal.R;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.biometrics.SensorServiceStateProto;
+import com.android.server.biometrics.SensorStateProto;
+import com.android.server.biometrics.UserStateProto;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.fingerprint.FingerprintServiceDumpProto;
-import com.android.server.biometrics.fingerprint.FingerprintServiceStateProto;
import com.android.server.biometrics.fingerprint.FingerprintUserStatsProto;
import com.android.server.biometrics.fingerprint.PerformanceStatsProto;
-import com.android.server.biometrics.fingerprint.SensorStateProto;
-import com.android.server.biometrics.fingerprint.UserStateProto;
import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
@@ -397,7 +397,9 @@
private synchronized IBiometricsFingerprint getDaemon() {
if (mTestHalEnabled) {
- return new TestHal();
+ final TestHal testHal = new TestHal();
+ testHal.setNotify(mHalResultController);
+ return testHal;
}
if (mDaemon != null) {
@@ -504,6 +506,12 @@
return properties;
}
+ @NonNull
+ @Override
+ public FingerprintSensorPropertiesInternal getSensorProperties(int sensorId) {
+ return mSensorProperties;
+ }
+
@Override
public void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken) {
// Fingerprint2.1 keeps track of lockout in the framework. Let's just do it on the handler
@@ -701,7 +709,7 @@
@Override
public void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto) {
- final long sensorToken = proto.start(FingerprintServiceStateProto.SENSOR_STATES);
+ final long sensorToken = proto.start(SensorServiceStateProto.SENSOR_STATES);
proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId);
proto.write(SensorStateProto.IS_BUSY, mScheduler.getCurrentClient() != null);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/TestHal.java
index 86c0875..b7aec0e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/TestHal.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/TestHal.java
@@ -16,6 +16,8 @@
package com.android.server.biometrics.sensors.fingerprint.hidl;
+import android.annotation.Nullable;
+import android.hardware.biometrics.fingerprint.V2_1.FingerprintError;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprintClientCallback;
import android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint;
import android.os.RemoteException;
@@ -24,6 +26,9 @@
* Test HAL that provides only provides no-ops.
*/
public class TestHal extends IBiometricsFingerprint.Stub {
+ @Nullable
+ private IBiometricsFingerprintClientCallback mCallback;
+
@Override
public boolean isUdfps(int sensorId) {
return false;
@@ -41,6 +46,7 @@
@Override
public long setNotify(IBiometricsFingerprintClientCallback clientCallback) {
+ mCallback = clientCallback;
return 0;
}
@@ -65,7 +71,10 @@
}
@Override
- public int cancel() {
+ public int cancel() throws RemoteException {
+ if (mCallback != null) {
+ mCallback.onError(0, FingerprintError.ERROR_CANCELED, 0 /* vendorCode */);
+ }
return 0;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
index b756d8e..6f437a7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
@@ -16,8 +16,11 @@
package com.android.server.biometrics.sensors.iris;
+import android.annotation.NonNull;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricSensorReceiver;
+import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.iris.IIrisService;
import android.os.IBinder;
import android.os.RemoteException;
@@ -36,10 +39,25 @@
}
@Override
+ public ITestSession createTestSession(@NonNull String opPackageName) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public SensorPropertiesInternal getSensorProperties(@NonNull String opPackageName)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public byte[] dumpSensorServiceStateProto() throws RemoteException {
+ return null;
+ }
+
+ @Override
public void prepareForAuthentication(boolean requireConfirmation, IBinder token,
long sessionId, int userId, IBiometricSensorReceiver sensorReceiver,
- String opPackageName, int cookie, int callingUid, int callingPid, int callingUserId)
- throws RemoteException {
+ String opPackageName, int cookie) throws RemoteException {
}
@Override
@@ -47,8 +65,8 @@
}
@Override
- public void cancelAuthenticationFromService(IBinder token, String opPackageName, int callingUid,
- int callingPid, int callingUserId) throws RemoteException {
+ public void cancelAuthenticationFromService(IBinder token, String opPackageName)
+ throws RemoteException {
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 34019c7..4a799b5 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -320,6 +320,10 @@
final ArraySet<File> unclaimedStages = newArraySet(
stagingDir.listFiles(sStageFilter));
+ // We also need to clean up orphaned staging directory for staged sessions
+ final File stagedSessionStagingDir = Environment.getDataStagingDirectory(volumeUuid);
+ unclaimedStages.addAll(newArraySet(stagedSessionStagingDir.listFiles()));
+
// Ignore stages claimed by active sessions
for (int i = 0; i < mSessions.size(); i++) {
final PackageInstallerSession session = mSessions.valueAt(i);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index ab5b9d1..ef947d8 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1678,6 +1678,10 @@
destroyInternal();
// Dispatch message to remove session from PackageInstallerService.
dispatchSessionFinished(error, detailMessage, null);
+ // TODO(b/173194203): clean up staged session in destroyInternal() call instead
+ if (isStaged() && stageDir != null) {
+ cleanStageDir();
+ }
}
private void onSessionVerificationFailure(int error, String msg) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 955e51f..f819063 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -3372,16 +3372,28 @@
SystemConfig.getInstance().getSplitPermissions());
}
+ @NonNull
private OneTimePermissionUserManager getOneTimePermissionUserManager(@UserIdInt int userId) {
OneTimePermissionUserManager oneTimePermissionUserManager;
synchronized (mLock) {
- oneTimePermissionUserManager =
- mOneTimePermissionUserManagers.get(userId);
+ oneTimePermissionUserManager = mOneTimePermissionUserManagers.get(userId);
if (oneTimePermissionUserManager != null) {
return oneTimePermissionUserManager;
}
- oneTimePermissionUserManager = new OneTimePermissionUserManager(
- mContext.createContextAsUser(UserHandle.of(userId), /*flags*/ 0));
+ }
+ // We cannot create a new instance of OneTimePermissionUserManager while holding our own
+ // lock, which may lead to a deadlock with the package manager lock. So we do it in a
+ // retry-like way, and just discard the newly created instance if someone else managed to be
+ // a little bit faster than us when we dropped our own lock.
+ final OneTimePermissionUserManager newOneTimePermissionUserManager =
+ new OneTimePermissionUserManager(mContext.createContextAsUser(UserHandle.of(userId),
+ /*flags*/ 0));
+ synchronized (mLock) {
+ oneTimePermissionUserManager = mOneTimePermissionUserManagers.get(userId);
+ if (oneTimePermissionUserManager != null) {
+ return oneTimePermissionUserManager;
+ }
+ oneTimePermissionUserManager = newOneTimePermissionUserManager;
mOneTimePermissionUserManagers.put(userId, oneTimePermissionUserManager);
}
oneTimePermissionUserManager.registerUninstallListener();
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index a82133d..2de4a71 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -244,12 +244,12 @@
setServerVisible(mWin.wouldBeVisibleIfPolicyIgnored() && mWin.isVisibleByPolicy());
updateSourceFrame();
if (mControl != null) {
- final Rect frame = mWin.getWindowFrames().mFrame;
- if (mControl.setSurfacePosition(frame.left, frame.top) && mControlTarget != null) {
+ final Point position = getWindowFrameSurfacePosition();
+ if (mControl.setSurfacePosition(position.x, position.y) && mControlTarget != null) {
if (!mWin.getWindowFrames().didFrameSizeChange()) {
- updateLeashPosition(frame, -1 /* frameNumber */);
+ updateLeashPosition(-1 /* frameNumber */);
} else if (mWin.mInRelayout) {
- updateLeashPosition(frame, mWin.getFrameNumber());
+ updateLeashPosition(mWin.getFrameNumber());
} else {
mWin.mPendingPositionChanged = this;
}
@@ -258,20 +258,26 @@
}
}
- void updateLeashPosition(Rect frame, long frameNumber) {
+ void updateLeashPosition(long frameNumber) {
if (mControl == null) {
return;
}
final SurfaceControl leash = mControl.getLeash();
if (leash != null) {
final Transaction t = mDisplayContent.getPendingTransaction();
- Point position = new Point();
- mWin.transformFrameToSurfacePosition(frame.left, frame.top, position);
+ final Point position = mControl.getSurfacePosition();
t.setPosition(leash, position.x, position.y);
deferTransactionUntil(t, leash, frameNumber);
}
}
+ private Point getWindowFrameSurfacePosition() {
+ final Rect frame = mWin.getFrame();
+ final Point position = new Point();
+ mWin.transformFrameToSurfacePosition(frame.left, frame.top, position);
+ return position;
+ }
+
private void deferTransactionUntil(Transaction t, SurfaceControl leash, long frameNumber) {
if (frameNumber >= 0) {
final SurfaceControl barrier = mWin.getClientViewRootSurface();
@@ -319,7 +325,8 @@
setClientVisible(InsetsState.getDefaultVisibility(mSource.getType()));
return;
}
- mAdapter = new ControlAdapter();
+ final Point surfacePosition = getWindowFrameSurfacePosition();
+ mAdapter = new ControlAdapter(surfacePosition);
if (getSource().getType() == ITYPE_IME) {
setClientVisible(InsetsState.getDefaultVisibility(mSource.getType()));
}
@@ -343,8 +350,7 @@
}
mControlTarget = target;
updateVisibility();
- mControl = new InsetsSourceControl(mSource.getType(), leash,
- new Point(mWin.getWindowFrames().mFrame.left, mWin.getWindowFrames().mFrame.top));
+ mControl = new InsetsSourceControl(mSource.getType(), leash, surfacePosition);
ProtoLog.d(WM_DEBUG_IME,
"InsetsSource Control %s for target %s", mControl, mControlTarget);
}
@@ -527,8 +533,13 @@
private class ControlAdapter implements AnimationAdapter {
+ private final Point mSurfacePosition;
private SurfaceControl mCapturedLeash;
+ ControlAdapter(Point surfacePosition) {
+ mSurfacePosition = surfacePosition;
+ }
+
@Override
public boolean getShowWallpaper() {
return false;
@@ -549,11 +560,7 @@
mControlTarget);
mCapturedLeash = animationLeash;
- final Rect frame = mWin.getWindowFrames().mFrame;
- Point position = new Point();
- mWin.transformFrameToSurfacePosition(frame.left, frame.top, position);
-
- t.setPosition(mCapturedLeash, position.x, position.y);
+ t.setPosition(mCapturedLeash, mSurfacePosition.x, mSurfacePosition.y);
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8a09bd4..9b91e3a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2195,7 +2195,7 @@
}
if (win.mPendingPositionChanged != null) {
- win.mPendingPositionChanged.updateLeashPosition(win.getFrame(), frameNumber);
+ win.mPendingPositionChanged.updateLeashPosition(frameNumber);
win.mPendingPositionChanged = null;
}
diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
index c1f83cb..eae4a08 100644
--- a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
@@ -452,22 +452,15 @@
new long[]{10, 10, 10}, new int[]{100, 200, 50}, -1);
vibrate(service, effect);
- verify(mNativeWrapperMock).vibratorOff();
-
- // Wait for VibrateThread to turn vibrator ON with total timing and no callback.
- Thread.sleep(5);
- verify(mNativeWrapperMock).vibratorOn(eq(30L), anyLong());
-
- // First amplitude set right away.
- verify(mNativeWrapperMock).vibratorSetAmplitude(eq(100));
-
- // Second amplitude set after first timing is finished.
- Thread.sleep(10);
- verify(mNativeWrapperMock).vibratorSetAmplitude(eq(200));
-
- // Third amplitude set after second timing is finished.
- Thread.sleep(10);
- verify(mNativeWrapperMock).vibratorSetAmplitude(eq(50));
+ // Wait for VibrateThread to finish: 10ms 100, 10ms 200, 10ms 50.
+ Thread.sleep(40);
+ InOrder inOrderVerifier = inOrder(mNativeWrapperMock);
+ inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
+ inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(30L), anyLong());
+ inOrderVerifier.verify(mNativeWrapperMock).vibratorSetAmplitude(eq(100));
+ inOrderVerifier.verify(mNativeWrapperMock).vibratorSetAmplitude(eq(200));
+ inOrderVerifier.verify(mNativeWrapperMock).vibratorSetAmplitude(eq(50));
+ inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
}
@Test
@@ -696,7 +689,7 @@
HAPTIC_FEEDBACK_ATTRS);
// Waveform effect runs on a separate thread.
- Thread.sleep(5);
+ Thread.sleep(15);
// Alarm vibration is never scaled.
verify(mNativeWrapperMock).vibratorSetAmplitude(eq(100));
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index e011c797..e4be448 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -47,7 +47,6 @@
import org.junit.Before;
import org.junit.Test;
-import org.mockito.AdditionalMatchers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -170,10 +169,7 @@
eq(userId),
eq(mReceiver),
eq(TEST_OP_PACKAGE_NAME),
- eq(promptInfo),
- eq(Binder.getCallingUid()),
- eq(Binder.getCallingPid()),
- eq(UserHandle.getCallingUserId()));
+ eq(promptInfo));
}
@Test
@@ -202,10 +198,7 @@
eq(userId),
eq(mReceiver),
eq(TEST_OP_PACKAGE_NAME),
- eq(promptInfo),
- eq(Binder.getCallingUid()),
- eq(Binder.getCallingPid()),
- eq(UserHandle.getCallingUserId()));
+ eq(promptInfo));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
index 6b000f3..8d890b9c 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
@@ -19,6 +19,8 @@
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+import static com.android.server.biometrics.BiometricServiceStateProto.*;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -29,7 +31,6 @@
import static org.mockito.ArgumentMatchers.anyLong;
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;
@@ -108,10 +109,7 @@
false /* checkDevicePolicyManager */,
Authenticators.BIOMETRIC_STRONG,
0 /* operationId */,
- 0 /* userId */,
- 0 /* callingUid */,
- 0 /* callingPid */,
- 0 /* callingUserId */);
+ 0 /* userId */);
for (BiometricSensor sensor : session.mPreAuthInfo.eligibleSensors) {
assertEquals(BiometricSensor.STATE_UNKNOWN, sensor.getSensorState());
@@ -126,18 +124,12 @@
final boolean requireConfirmation = true;
final long operationId = 123;
final int userId = 10;
- final int callingUid = 100;
- final int callingPid = 1000;
- final int callingUserId = 10000;
final AuthSession session = createAuthSession(mSensors,
false /* checkDevicePolicyManager */,
Authenticators.BIOMETRIC_STRONG,
operationId,
- userId,
- callingUid,
- callingPid,
- callingUserId);
+ userId);
assertEquals(mSensors.size(), session.mPreAuthInfo.eligibleSensors.size());
for (BiometricSensor sensor : session.mPreAuthInfo.eligibleSensors) {
@@ -156,10 +148,7 @@
eq(userId),
eq(mSensorReceiver),
eq(TEST_PACKAGE),
- eq(sensor.getCookie()),
- eq(callingUid),
- eq(callingPid),
- eq(callingUserId));
+ eq(sensor.getCookie()));
}
final int cookie1 = session.mPreAuthInfo.eligibleSensors.get(0).getCookie();
@@ -200,10 +189,7 @@
false /* checkDevicePolicyManager */,
Authenticators.BIOMETRIC_STRONG,
operationId,
- userId,
- callingUid,
- callingPid,
- callingUserId);
+ userId);
assertEquals(mSensors.size(), session.mPreAuthInfo.eligibleSensors.size());
for (BiometricSensor sensor : session.mPreAuthInfo.eligibleSensors) {
@@ -225,13 +211,13 @@
assertTrue(session.allCookiesReceived());
// UDFPS does not start even if all cookies are received
- assertEquals(AuthSession.STATE_AUTH_STARTED, session.getState());
+ assertEquals(STATE_AUTH_STARTED, session.getState());
verify(mStatusBarService).showAuthenticationDialog(any(), any(), any(),
anyBoolean(), anyBoolean(), anyInt(), any(), anyLong());
// Notify AuthSession that the UI is shown. Then, UDFPS sensor should be started.
session.onDialogAnimatedIn();
- assertEquals(AuthSession.STATE_AUTH_STARTED_UI_SHOWING, session.getState());
+ assertEquals(STATE_AUTH_STARTED_UI_SHOWING, session.getState());
assertEquals(BiometricSensor.STATE_AUTHENTICATING,
session.mPreAuthInfo.eligibleSensors.get(0).getSensorState());
@@ -251,9 +237,7 @@
private AuthSession createAuthSession(List<BiometricSensor> sensors,
boolean checkDevicePolicyManager, @Authenticators.Types int authenticators,
- long operationId, int userId,
- int callingUid, int callingPid, int callingUserId)
- throws RemoteException {
+ long operationId, int userId) throws RemoteException {
final PromptInfo promptInfo = createPromptInfo(authenticators);
@@ -261,8 +245,8 @@
checkDevicePolicyManager);
return new AuthSession(mContext, mStatusBarService, mSysuiReceiver, mKeyStore,
mRandom, mClientDeathReceiver, preAuthInfo, mToken, operationId, userId,
- mSensorReceiver, mClientReceiver, TEST_PACKAGE, promptInfo, callingUid,
- callingPid, callingUserId, false /* debugEnabled */, mFingerprintSensorProps);
+ mSensorReceiver, mClientReceiver, TEST_PACKAGE, promptInfo,
+ false /* debugEnabled */, mFingerprintSensorProps);
}
private PromptInfo createPromptInfo(@Authenticators.Types int authenticators) {
@@ -299,7 +283,7 @@
}
private void setupFace(int id, boolean confirmationAlwaysRequired) throws RemoteException {
- IBiometricAuthenticator faceAuthenticator = mock(IBiometricAuthenticator.class);
+ IBiometricAuthenticator faceAuthenticator = mock(IBiometricAuthenticator.class);
when(faceAuthenticator.isHardwareDetected(any())).thenReturn(true);
when(faceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true);
mSensors.add(new BiometricSensor(id,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 435c700..0c95e05 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -18,6 +18,8 @@
import static android.hardware.biometrics.BiometricManager.Authenticators;
+import static com.android.server.biometrics.BiometricServiceStateProto.*;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -174,8 +176,7 @@
0 /* vendorCode */);
waitForIdle();
- assertEquals(AuthSession.STATE_AUTH_PAUSED,
- mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_PAUSED, mBiometricService.mCurrentAuthSession.getState());
mBiometricService.mCurrentAuthSession.binderDied();
waitForIdle();
@@ -195,22 +196,18 @@
verify(mReceiver1.asBinder()).linkToDeath(eq(mBiometricService.mCurrentAuthSession),
anyInt());
- assertEquals(AuthSession.STATE_AUTH_STARTED,
- mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_STARTED, mBiometricService.mCurrentAuthSession.getState());
mBiometricService.mCurrentAuthSession.binderDied();
waitForIdle();
assertNotNull(mBiometricService.mCurrentAuthSession);
verify(mBiometricService.mStatusBarService, never()).hideAuthenticationDialog();
- assertEquals(AuthSession.STATE_CLIENT_DIED_CANCELLING,
+ assertEquals(STATE_CLIENT_DIED_CANCELLING,
mBiometricService.mCurrentAuthSession.getState());
verify(mBiometricService.mCurrentAuthSession.mPreAuthInfo.eligibleSensors.get(0).impl)
.cancelAuthenticationFromService(any(),
- any(),
- anyInt(),
- anyInt(),
- anyInt());
+ any());
// Simulate ERROR_CANCELED received from HAL
mBiometricService.mBiometricSensorReceiver.onError(
@@ -256,7 +253,7 @@
waitForIdle();
assertNotNull(mBiometricService.mCurrentAuthSession);
- assertEquals(AuthSession.STATE_SHOWING_DEVICE_CREDENTIAL,
+ assertEquals(STATE_SHOWING_DEVICE_CREDENTIAL,
mBiometricService.mCurrentAuthSession.getState());
// StatusBar showBiometricDialog invoked
verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
@@ -406,7 +403,7 @@
HAT);
waitForIdle();
// Confirmation is required
- assertEquals(AuthSession.STATE_AUTH_PENDING_CONFIRM,
+ assertEquals(STATE_AUTH_PENDING_CONFIRM,
mBiometricService.mCurrentAuthSession.getState());
// Enrolled, not disabled in settings, user doesn't require confirmation in settings
@@ -422,7 +419,7 @@
HAT);
waitForIdle();
// Confirmation not required, waiting for dialog to dismiss
- assertEquals(AuthSession.STATE_AUTHENTICATED_PENDING_SYSUI,
+ assertEquals(STATE_AUTHENTICATED_PENDING_SYSUI,
mBiometricService.mCurrentAuthSession.getState());
}
@@ -447,8 +444,7 @@
waitForIdle();
// Creates a pending auth session with the correct initial states
- assertEquals(AuthSession.STATE_AUTH_CALLED,
- mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_CALLED, mBiometricService.mCurrentAuthSession.getState());
// Invokes <Modality>Service#prepareForAuthentication
ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -460,16 +456,12 @@
anyInt() /* userId */,
any(IBiometricSensorReceiver.class),
anyString() /* opPackageName */,
- cookieCaptor.capture() /* cookie */,
- anyInt() /* callingUid */,
- anyInt() /* callingPid */,
- anyInt() /* callingUserId */);
+ cookieCaptor.capture() /* cookie */);
// onReadyForAuthentication, mCurrentAuthSession state OK
mBiometricService.mImpl.onReadyForAuthentication(cookieCaptor.getValue());
waitForIdle();
- assertEquals(AuthSession.STATE_AUTH_STARTED,
- mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_STARTED, mBiometricService.mCurrentAuthSession.getState());
// startPreparedClient invoked
verify(mBiometricService.mSensors.get(0).impl)
@@ -493,7 +485,7 @@
HAT);
waitForIdle();
// Waiting for SystemUI to send dismissed callback
- assertEquals(AuthSession.STATE_AUTHENTICATED_PENDING_SYSUI,
+ assertEquals(STATE_AUTHENTICATED_PENDING_SYSUI,
mBiometricService.mCurrentAuthSession.getState());
// Notify SystemUI hardware authenticated
verify(mBiometricService.mStatusBarService).onBiometricAuthenticated();
@@ -526,7 +518,7 @@
Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_WEAK);
waitForIdle();
- assertEquals(AuthSession.STATE_SHOWING_DEVICE_CREDENTIAL,
+ assertEquals(STATE_SHOWING_DEVICE_CREDENTIAL,
mBiometricService.mCurrentAuthSession.getState());
assertEquals(Authenticators.DEVICE_CREDENTIAL,
mBiometricService.mCurrentAuthSession.mPromptInfo.getAuthenticators());
@@ -566,8 +558,7 @@
HAT);
waitForIdle();
// Waiting for SystemUI to send confirmation callback
- assertEquals(AuthSession.STATE_AUTH_PENDING_CONFIRM,
- mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_PENDING_CONFIRM, mBiometricService.mCurrentAuthSession.getState());
verify(mBiometricService.mKeyStore, never()).addAuthToken(any(byte[].class));
// SystemUI sends confirm, HAT is sent to keystore and client is notified.
@@ -615,8 +606,7 @@
eq(BiometricConstants.BIOMETRIC_PAUSED_REJECTED),
eq(0 /* vendorCode */));
verify(mReceiver1).onAuthenticationFailed();
- assertEquals(AuthSession.STATE_AUTH_PAUSED,
- mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_PAUSED, mBiometricService.mCurrentAuthSession.getState());
}
@Test
@@ -634,8 +624,7 @@
eq(BiometricConstants.BIOMETRIC_PAUSED_REJECTED),
eq(0 /* vendorCode */));
verify(mReceiver1).onAuthenticationFailed();
- assertEquals(AuthSession.STATE_AUTH_STARTED,
- mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_STARTED, mBiometricService.mCurrentAuthSession.getState());
}
@Test
@@ -670,8 +659,7 @@
0 /* vendorCode */);
waitForIdle();
- assertEquals(AuthSession.STATE_AUTH_PAUSED,
- mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_PAUSED, mBiometricService.mCurrentAuthSession.getState());
verify(mBiometricService.mStatusBarService).onBiometricError(
eq(BiometricAuthenticator.TYPE_FACE),
eq(BiometricConstants.BIOMETRIC_ERROR_TIMEOUT),
@@ -680,8 +668,7 @@
verify(mReceiver1, never()).onAuthenticationFailed();
// No auth session. Pressing try again will create one.
- assertEquals(AuthSession.STATE_AUTH_PAUSED,
- mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_PAUSED, mBiometricService.mCurrentAuthSession.getState());
// Pressing "Try again" on SystemUI
mBiometricService.mSysuiReceiver.onTryAgainPressed();
@@ -689,8 +676,7 @@
verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt());
// AuthSession is now resuming
- assertEquals(AuthSession.STATE_AUTH_PAUSED_RESUMING,
- mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_PAUSED_RESUMING, mBiometricService.mCurrentAuthSession.getState());
// Test resuming when hardware becomes ready. SystemUI should not be requested to
// show another dialog since it's already showing.
@@ -755,8 +741,7 @@
waitForIdle();
// Sends error to SystemUI and does not notify client yet
- assertEquals(AuthSession.STATE_ERROR_PENDING_SYSUI,
- mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_ERROR_PENDING_SYSUI, mBiometricService.mCurrentAuthSession.getState());
verify(mBiometricService.mStatusBarService).onBiometricError(
eq(BiometricAuthenticator.TYPE_FINGERPRINT),
eq(BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_PROCESS),
@@ -783,8 +768,7 @@
Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_WEAK);
waitForIdle();
- assertEquals(AuthSession.STATE_AUTH_CALLED,
- mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_CALLED, mBiometricService.mCurrentAuthSession.getState());
mBiometricService.mBiometricSensorReceiver.onError(
SENSOR_ID_FINGERPRINT,
getCookieForPendingSession(mBiometricService.mCurrentAuthSession),
@@ -794,7 +778,7 @@
// We should be showing device credential now
assertNotNull(mBiometricService.mCurrentAuthSession);
- assertEquals(AuthSession.STATE_SHOWING_DEVICE_CREDENTIAL,
+ assertEquals(STATE_SHOWING_DEVICE_CREDENTIAL,
mBiometricService.mCurrentAuthSession.getState());
assertEquals(Authenticators.DEVICE_CREDENTIAL,
mBiometricService.mCurrentAuthSession.mPromptInfo.getAuthenticators());
@@ -873,7 +857,7 @@
verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt());
assertNotNull(mBiometricService.mCurrentAuthSession);
- assertEquals(AuthSession.STATE_SHOWING_DEVICE_CREDENTIAL,
+ assertEquals(STATE_SHOWING_DEVICE_CREDENTIAL,
mBiometricService.mCurrentAuthSession.getState());
assertEquals(Authenticators.DEVICE_CREDENTIAL,
mBiometricService.mCurrentAuthSession.mPromptInfo.getAuthenticators());
@@ -950,7 +934,7 @@
mBiometricService.mSysuiReceiver.onDeviceCredentialPressed();
waitForIdle();
- assertEquals(AuthSession.STATE_SHOWING_DEVICE_CREDENTIAL,
+ assertEquals(STATE_SHOWING_DEVICE_CREDENTIAL,
mBiometricService.mCurrentAuthSession.getState());
verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt());
@@ -961,7 +945,7 @@
0 /* vendorCode */);
waitForIdle();
- assertEquals(AuthSession.STATE_SHOWING_DEVICE_CREDENTIAL,
+ assertEquals(STATE_SHOWING_DEVICE_CREDENTIAL,
mBiometricService.mCurrentAuthSession.getState());
verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt());
}
@@ -973,8 +957,7 @@
false /* requireConfirmation */,
Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_WEAK);
- assertEquals(AuthSession.STATE_AUTH_STARTED,
- mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_STARTED, mBiometricService.mCurrentAuthSession.getState());
mBiometricService.mBiometricSensorReceiver.onError(
SENSOR_ID_FINGERPRINT,
@@ -983,7 +966,7 @@
0 /* vendorCode */);
waitForIdle();
- assertEquals(AuthSession.STATE_SHOWING_DEVICE_CREDENTIAL,
+ assertEquals(STATE_SHOWING_DEVICE_CREDENTIAL,
mBiometricService.mCurrentAuthSession.getState());
verify(mBiometricService.mStatusBarService).onBiometricError(
eq(BiometricAuthenticator.TYPE_FINGERPRINT),
@@ -997,8 +980,7 @@
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
false /* requireConfirmation */, null /* authenticators */);
- assertEquals(AuthSession.STATE_AUTH_STARTED,
- mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_STARTED, mBiometricService.mCurrentAuthSession.getState());
mBiometricService.mBiometricSensorReceiver.onError(
SENSOR_ID_FINGERPRINT,
@@ -1007,7 +989,7 @@
0 /* vendorCode */);
waitForIdle();
- assertEquals(AuthSession.STATE_ERROR_PENDING_SYSUI,
+ assertEquals(STATE_ERROR_PENDING_SYSUI,
mBiometricService.mCurrentAuthSession.getState());
verify(mBiometricService.mStatusBarService).onBiometricError(
eq(BiometricAuthenticator.TYPE_FINGERPRINT),
@@ -1031,10 +1013,7 @@
eq(0 /* vendorCode */));
verify(mBiometricService.mSensors.get(0).impl).cancelAuthenticationFromService(
any(),
- any(),
- anyInt(),
- anyInt(),
- anyInt());
+ any());
assertNull(mBiometricService.mCurrentAuthSession);
}
@@ -1056,10 +1035,7 @@
verify(mBiometricService.mSensors.get(0).impl,
never()).cancelAuthenticationFromService(
any(),
- any(),
- anyInt(),
- anyInt(),
- anyInt());
+ any());
}
@Test
@@ -1081,10 +1057,7 @@
verify(mBiometricService.mSensors.get(0).impl,
never()).cancelAuthenticationFromService(
any(),
- any(),
- anyInt(),
- anyInt(),
- anyInt());
+ any());
}
@Test
@@ -1104,10 +1077,7 @@
verify(mBiometricService.mSensors.get(0).impl,
never()).cancelAuthenticationFromService(
any(),
- any(),
- anyInt(),
- anyInt(),
- anyInt());
+ any());
verify(mReceiver1).onError(
eq(BiometricAuthenticator.TYPE_FACE),
eq(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED),
@@ -1134,8 +1104,7 @@
// string is retrieved for now, but it's also very unlikely to break anyway.
verify(mBiometricService.mStatusBarService)
.onBiometricHelp(anyString());
- assertEquals(AuthSession.STATE_AUTH_STARTED,
- mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_STARTED, mBiometricService.mCurrentAuthSession.getState());
}
@Test
@@ -1145,7 +1114,7 @@
false /* requireConfirmation */, null /* authenticators */);
mBiometricService.mImpl.cancelAuthentication(mBiometricService.mCurrentAuthSession.mToken,
- TEST_PACKAGE_NAME, 0 /* callingUId */, 0 /* callingPid */, 0 /* callingUserId */);
+ TEST_PACKAGE_NAME);
waitForIdle();
// Pretend that the HAL has responded to cancel with ERROR_CANCELED
@@ -1491,12 +1460,10 @@
invokeAuthenticateForWorkApp(mBiometricService.mImpl, mReceiver1,
Authenticators.BIOMETRIC_STRONG);
waitForIdle();
- assertEquals(AuthSession.STATE_AUTH_CALLED,
- mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_CALLED, mBiometricService.mCurrentAuthSession.getState());
startPendingAuthSession(mBiometricService);
waitForIdle();
- assertEquals(AuthSession.STATE_AUTH_STARTED,
- mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_STARTED, mBiometricService.mCurrentAuthSession.getState());
}
@Test
@@ -1508,8 +1475,7 @@
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
false /* requireConfirmation */, Authenticators.BIOMETRIC_STRONG);
waitForIdle();
- assertEquals(AuthSession.STATE_AUTH_STARTED,
- mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_STARTED, mBiometricService.mCurrentAuthSession.getState());
}
@Test
@@ -1522,12 +1488,10 @@
invokeAuthenticateForWorkApp(mBiometricService.mImpl, mReceiver1,
Authenticators.BIOMETRIC_STRONG);
waitForIdle();
- assertEquals(AuthSession.STATE_AUTH_CALLED,
- mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_CALLED, mBiometricService.mCurrentAuthSession.getState());
startPendingAuthSession(mBiometricService);
waitForIdle();
- assertEquals(AuthSession.STATE_AUTH_STARTED,
- mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_STARTED, mBiometricService.mCurrentAuthSession.getState());
}
@Test
@@ -1549,7 +1513,7 @@
Authenticators.BIOMETRIC_STRONG | Authenticators.DEVICE_CREDENTIAL);
waitForIdle();
assertNotNull(mBiometricService.mCurrentAuthSession);
- assertEquals(AuthSession.STATE_SHOWING_DEVICE_CREDENTIAL,
+ assertEquals(STATE_SHOWING_DEVICE_CREDENTIAL,
mBiometricService.mCurrentAuthSession.getState());
verify(mReceiver2, never()).onError(anyInt(), anyInt(), anyInt());
}
@@ -1647,8 +1611,7 @@
waitForIdle();
assertNotNull(mBiometricService.mCurrentAuthSession);
- assertEquals(AuthSession.STATE_AUTH_STARTED,
- mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_STARTED, mBiometricService.mCurrentAuthSession.getState());
}
private static void startPendingAuthSession(BiometricService service) throws Exception {
@@ -1674,10 +1637,7 @@
receiver,
TEST_PACKAGE_NAME /* packageName */,
createTestPromptInfo(requireConfirmation, authenticators,
- false /* checkDevicePolicy */),
- 0 /* callingUid */,
- 0 /* callingPid */,
- 0 /* callingUserId */);
+ false /* checkDevicePolicy */));
}
private static void invokeAuthenticateForWorkApp(IBiometricService.Stub service,
@@ -1689,10 +1649,7 @@
receiver,
TEST_PACKAGE_NAME /* packageName */,
createTestPromptInfo(false /* requireConfirmation */, authenticators,
- true /* checkDevicePolicy */),
- 0 /* callingUid */,
- 0 /* callingPid */,
- 0 /* callingUserId */);
+ true /* checkDevicePolicy */));
}
private static PromptInfo createTestPromptInfo(
diff --git a/telephony/java/android/telephony/AccessNetworkUtils.java b/telephony/java/android/telephony/AccessNetworkUtils.java
index 981ed450..7661a32 100644
--- a/telephony/java/android/telephony/AccessNetworkUtils.java
+++ b/telephony/java/android/telephony/AccessNetworkUtils.java
@@ -5,8 +5,11 @@
import static android.telephony.ServiceState.DUPLEX_MODE_UNKNOWN;
import android.telephony.AccessNetworkConstants.EutranBand;
+import android.telephony.AccessNetworkConstants.GeranBand;
+import android.telephony.AccessNetworkConstants.UtranBand;
import android.telephony.ServiceState.DuplexMode;
+import java.util.Arrays;
/**
* Utilities to map between radio constants.
@@ -20,6 +23,9 @@
public static final int INVALID_BAND = -1;
+ /** ISO country code of Japan. */
+ private static final String JAPAN_ISO_COUNTRY_CODE = "jp";
+
/**
* Gets the duplex mode for the given EUTRAN operating band.
*
@@ -50,7 +56,7 @@
/**
* Gets the EUTRAN Operating band for a given downlink EARFCN.
*
- * <p>See 3GPP 36.101 sec 5.7.3-1 for calculation.
+ * <p>See 3GPP TS 36.101 clause 5.7.3-1 for calculation.
*
* @param earfcn The downlink EARFCN
* @return Operating band number, or {@link #INVALID_BAND} if no corresponding band exists
@@ -198,4 +204,125 @@
return INVALID_BAND;
}
+
+ /**
+ * Gets the GERAN Operating band for a given ARFCN.
+ *
+ * <p>See 3GPP TS 45.005 clause 2 for calculation.
+ *
+ * @param arfcn The ARFCN
+ * @return Operating band number, or {@link #INVALID_BAND} if no corresponding band exists
+ */
+ public static int getOperatingBandForArfcn(int arfcn) {
+ if (arfcn >= 0 && arfcn <= 124) {
+ return GeranBand.BAND_E900;
+ } else if (arfcn >= 128 && arfcn <= 251) {
+ return GeranBand.BAND_850;
+ } else if (arfcn >= 259 && arfcn <= 293) {
+ return GeranBand.BAND_450;
+ } else if (arfcn >= 306 && arfcn <= 340) {
+ return GeranBand.BAND_480;
+ } else if (arfcn >= 438 && arfcn <= 511) {
+ return GeranBand.BAND_750;
+ } else if (arfcn >= 512 && arfcn <= 885) {
+ // ARFCN between 512 and 810 are also part of BAND_PCS1900.
+ // Returning BAND_DCS1800 in both cases.
+ return GeranBand.BAND_DCS1800;
+ } else if (arfcn >= 940 && arfcn <= 974) {
+ return GeranBand.BAND_ER900;
+ } else if (arfcn >= 975 && arfcn <= 1023) {
+ return GeranBand.BAND_E900;
+ }
+ return INVALID_BAND;
+ }
+
+ /**
+ * Gets the UTRAN Operating band for a given downlink UARFCN.
+ *
+ * <p>See 3GPP TS 25.101 clause 5.4.4 for calculation.
+ *
+ * @param uarfcn The downlink UARFCN
+ * @return Operating band number, or {@link #INVALID_BAND} if no corresponding band exists
+ */
+ public static int getOperatingBandForUarfcn(int uarfcn) {
+ // List of additional bands defined in TS 25.101.
+ int[] addlBand2 = {412, 437, 462, 487, 512, 537, 562, 587, 612, 637, 662, 687};
+ int[] addlBand4 = {1887, 1912, 1937, 1962, 1987, 2012, 2037, 2062, 2087};
+ int[] addlBand5 = {1007, 1012, 1032, 1037, 1062, 1087};
+ int[] addlBand6 = {1037, 1062};
+ int[] addlBand7 =
+ {2587, 2612, 2637, 2662, 2687, 2712, 2737, 2762, 2787, 2812, 2837, 2862,
+ 2887, 2912};
+ int[] addlBand10 =
+ {3412, 3437, 3462, 3487, 3512, 3537, 3562, 3587, 3612, 3637, 3662, 3687};
+ int[] addlBand12 = {3932, 3957, 3962, 3987, 3992};
+ int[] addlBand13 = {4067, 4092};
+ int[] addlBand14 = {4167, 4192};
+ int[] addlBand19 = {787, 812, 837};
+ int[] addlBand25 =
+ {6292, 6317, 6342, 6367, 6392, 6417, 6442, 6467, 6492, 6517, 6542, 6567, 6592};
+ int[] addlBand26 = {5937, 5962, 5987, 5992, 6012, 6017, 6037, 6042, 6062, 6067, 6087};
+
+ if (uarfcn >= 10562 && uarfcn <= 10838) {
+ return UtranBand.BAND_1;
+ } else if ((uarfcn >= 9662 && uarfcn <= 9938)
+ || Arrays.binarySearch(addlBand2, uarfcn) >= 0) {
+ return UtranBand.BAND_2;
+ } else if (uarfcn >= 1162 && uarfcn <= 1513) {
+ return UtranBand.BAND_3;
+ } else if ((uarfcn >= 1537 && uarfcn <= 1738)
+ || Arrays.binarySearch(addlBand4, uarfcn) >= 0) {
+ return UtranBand.BAND_4;
+ } else if (uarfcn >= 4387 && uarfcn <= 4413) {
+ // Band 6 is a subset of band 5. Only Japan uses band 6 and Japan does not have band 5.
+ String country = TelephonyManager.getDefault().getNetworkCountryIso();
+ if (JAPAN_ISO_COUNTRY_CODE.compareToIgnoreCase(country) == 0) {
+ return UtranBand.BAND_6;
+ } else {
+ return UtranBand.BAND_5;
+ }
+ } else if ((uarfcn >= 4357 && uarfcn <= 4458)
+ || Arrays.binarySearch(addlBand5, uarfcn) >= 0) {
+ return UtranBand.BAND_5;
+ } else if (Arrays.binarySearch(addlBand6, uarfcn) >= 0) {
+ return UtranBand.BAND_6;
+ } else if ((uarfcn >= 2237 && uarfcn <= 2563)
+ || Arrays.binarySearch(addlBand7, uarfcn) >= 0) {
+ return UtranBand.BAND_7;
+ } else if (uarfcn >= 2937 && uarfcn <= 3088) {
+ return UtranBand.BAND_8;
+ } else if (uarfcn >= 9237 && uarfcn <= 9387) {
+ return UtranBand.BAND_9;
+ } else if ((uarfcn >= 3112 && uarfcn <= 3388)
+ || Arrays.binarySearch(addlBand10, uarfcn) >= 0) {
+ return UtranBand.BAND_10;
+ } else if (uarfcn >= 3712 && uarfcn <= 3787) {
+ return UtranBand.BAND_11;
+ } else if ((uarfcn >= 3842 && uarfcn <= 3903)
+ || Arrays.binarySearch(addlBand12, uarfcn) >= 0) {
+ return UtranBand.BAND_12;
+ } else if ((uarfcn >= 4017 && uarfcn <= 4043)
+ || Arrays.binarySearch(addlBand13, uarfcn) >= 0) {
+ return UtranBand.BAND_13;
+ } else if ((uarfcn >= 4117 && uarfcn <= 4143)
+ || Arrays.binarySearch(addlBand14, uarfcn) >= 0) {
+ return UtranBand.BAND_14;
+ } else if ((uarfcn >= 712 && uarfcn <= 763)
+ || Arrays.binarySearch(addlBand19, uarfcn) >= 0) {
+ return UtranBand.BAND_19;
+ } else if (uarfcn >= 4512 && uarfcn <= 4638) {
+ return UtranBand.BAND_20;
+ } else if (uarfcn >= 862 && uarfcn <= 912) {
+ return UtranBand.BAND_21;
+ } else if (uarfcn >= 4662 && uarfcn <= 5038) {
+ return UtranBand.BAND_22;
+ } else if ((uarfcn >= 5112 && uarfcn <= 5413)
+ || Arrays.binarySearch(addlBand25, uarfcn) >= 0) {
+ return UtranBand.BAND_25;
+ } else if ((uarfcn >= 5762 && uarfcn <= 5913)
+ || Arrays.binarySearch(addlBand26, uarfcn) >= 0) {
+ return UtranBand.BAND_26;
+ }
+ return INVALID_BAND;
+ }
}
diff --git a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
index e5411de..2a601e5 100644
--- a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
@@ -127,6 +127,19 @@
InstallUtils.getPackageInstaller().abandonSession(id4);
}
+ @Test
+ public void testStagedInstallationShouldCleanUpOnValidationFailure() throws Exception {
+ InstallUtils.commitExpectingFailure(AssertionError.class, "INSTALL_FAILED_INVALID_APK",
+ Install.single(TestApp.AIncompleteSplit).setStaged());
+ }
+
+ @Test
+ public void testStagedInstallationShouldCleanUpOnValidationFailureMultiPackage()
+ throws Exception {
+ InstallUtils.commitExpectingFailure(AssertionError.class, "INSTALL_FAILED_INVALID_APK",
+ Install.multi(TestApp.AIncompleteSplit, TestApp.B1, TestApp.Apex1).setStaged());
+ }
+
private static void assertSessionReady(int sessionId) {
assertSessionState(sessionId,
(session) -> assertThat(session.isStagedSessionReady()).isTrue());
diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
index 702f871..27ccbc78 100644
--- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
@@ -30,6 +30,7 @@
import com.android.ddmlib.Log;
import com.android.tests.rollback.host.AbandonSessionsRule;
import com.android.tests.util.ModuleTestUtils;
+import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.util.CommandResult;
@@ -250,9 +251,40 @@
assertThat(after).isEqualTo(before);
}
- private List<String> getStagingDirectories() {
+ @Test
+ public void testStagedInstallationShouldCleanUpOnValidationFailure() throws Exception {
+ List<String> before = getStagingDirectories();
+ runPhase("testStagedInstallationShouldCleanUpOnValidationFailure");
+ List<String> after = getStagingDirectories();
+ assertThat(after).isEqualTo(before);
+ }
+
+ @Test
+ public void testStagedInstallationShouldCleanUpOnValidationFailureMultiPackage()
+ throws Exception {
+ List<String> before = getStagingDirectories();
+ runPhase("testStagedInstallationShouldCleanUpOnValidationFailureMultiPackage");
+ List<String> after = getStagingDirectories();
+ assertThat(after).isEqualTo(before);
+ }
+
+ @Test
+ public void testOrphanedStagingDirectoryGetsCleanedUpOnReboot() throws Exception {
+ //create random directories in /data/app-staging folder
+ getDevice().enableAdbRoot();
+ getDevice().executeShellCommand("mkdir /data/app-staging/session_123");
+ getDevice().executeShellCommand("mkdir /data/app-staging/random_name");
+ getDevice().disableAdbRoot();
+
+ assertThat(getStagingDirectories()).isNotEmpty();
+ getDevice().reboot();
+ assertThat(getStagingDirectories()).isEmpty();
+ }
+
+ private List<String> getStagingDirectories() throws DeviceNotAvailableException {
String baseDir = "/data/app-staging";
try {
+ getDevice().enableAdbRoot();
return getDevice().getFileEntry(baseDir).getChildren(false)
.stream().filter(entry -> entry.getName().matches("session_\\d+"))
.map(entry -> entry.getName())
@@ -260,6 +292,8 @@
} catch (Exception e) {
// Return an empty list if any error
return Collections.EMPTY_LIST;
+ } finally {
+ getDevice().disableAdbRoot();
}
}