diff options
57 files changed, 1305 insertions, 799 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index 295fef142c89..ad9a04d16a7e 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4780,8 +4780,11 @@ package android.net.wifi { method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsConfiguratorInitiator(@NonNull String, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback); method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsEnrolleeInitiator(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback); method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public boolean startScan(android.os.WorkSource); + method @RequiresPermission(anyOf={"android.permission.NETWORK_STACK", android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean startSoftAp(@Nullable android.net.wifi.WifiConfiguration); method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startSubscriptionProvisioning(@NonNull android.net.wifi.hotspot2.OsuProvider, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.hotspot2.ProvisioningCallback); method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void stopEasyConnectSession(); + method @RequiresPermission(anyOf={"android.permission.NETWORK_STACK", android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean stopSoftAp(); + method @RequiresPermission(anyOf={"android.permission.NETWORK_STACK", android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void updateInterfaceIpState(@Nullable String, int); method @RequiresPermission("android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE") public void updateWifiUsabilityScore(int, int, int); field public static final int CHANGE_REASON_ADDED = 0; // 0x0 field public static final int CHANGE_REASON_CONFIG_CHANGE = 2; // 0x2 @@ -4796,10 +4799,16 @@ package android.net.wifi { field public static final String EXTRA_CHANGE_REASON = "changeReason"; field public static final String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges"; field public static final String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state"; + field public static final String EXTRA_WIFI_AP_INTERFACE_NAME = "android.net.wifi.extra.WIFI_AP_INTERFACE_NAME"; + field public static final String EXTRA_WIFI_AP_MODE = "android.net.wifi.extra.WIFI_AP_MODE"; field public static final String EXTRA_WIFI_AP_STATE = "wifi_state"; field public static final String EXTRA_WIFI_CONFIGURATION = "wifiConfiguration"; field public static final String EXTRA_WIFI_CREDENTIAL_EVENT_TYPE = "et"; field public static final String EXTRA_WIFI_CREDENTIAL_SSID = "ssid"; + field public static final int IFACE_IP_MODE_CONFIGURATION_ERROR = 0; // 0x0 + field public static final int IFACE_IP_MODE_LOCAL_ONLY = 2; // 0x2 + field public static final int IFACE_IP_MODE_TETHERED = 1; // 0x1 + field public static final int IFACE_IP_MODE_UNSPECIFIED = -1; // 0xffffffff field public static final int PASSPOINT_HOME_NETWORK = 0; // 0x0 field public static final int PASSPOINT_ROAMING_NETWORK = 1; // 0x1 field public static final String WIFI_AP_STATE_CHANGED_ACTION = "android.net.wifi.WIFI_AP_STATE_CHANGED"; diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index 26c2c0cfdd5d..f4e465ab3adb 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -563,6 +563,21 @@ public class AccountManager { * account, or the AbstractAcccountAuthenticator managing the account did so or because the * client shares a signature with the managing AbstractAccountAuthenticator. * + * <div class="caution"><p><b>Caution: </b>This method returns personal and sensitive user data. + * If your app accesses, collects, uses, or shares personal and sensitive data, you must clearly + * disclose that fact to users. For apps published on Google Play, policies protecting user data + * require that you do the following:</p> + * <ul> + * <li>Disclose to the user how your app accesses, collects, uses, or shares personal and + * sensitive data. Learn more about + * <a href="https://play.google.com/about/privacy-security-deception/user-data/#!#personal-sensitive">acceptable + * disclosure and consent</a>.</li> + * <li>Provide a privacy policy that describes your use of this data on- and off-device.</li> + * </ul> + * <p>To learn more, visit the + * <a href="https://play.google.com/about/privacy-security-deception/user-data">Google Play + * Policy regarding user data</a>.</p></div> + * * <p> * It is safe to call this method from the main thread. * @@ -649,6 +664,22 @@ public class AccountManager { * the account. For example, there are types corresponding to Google and Facebook. The exact * string token to use will be published somewhere associated with the authenticator in * question. + * </p> + * + * <div class="caution"><p><b>Caution: </b>This method returns personal and sensitive user data. + * If your app accesses, collects, uses, or shares personal and sensitive data, you must clearly + * disclose that fact to users. For apps published on Google Play, policies protecting user data + * require that you do the following:</p> + * <ul> + * <li>Disclose to the user how your app accesses, collects, uses, or shares personal and + * sensitive data. Learn more about + * <a href="https://play.google.com/about/privacy-security-deception/user-data/#!#personal-sensitive">acceptable + * disclosure and consent</a>.</li> + * <li>Provide a privacy policy that describes your use of this data on- and off-device.</li> + * </ul> + * <p>To learn more, visit the + * <a href="https://play.google.com/about/privacy-security-deception/user-data">Google Play + * Policy regarding user data</a>.</p></div> * * <p> * It is safe to call this method from the main thread. diff --git a/core/java/android/app/timedetector/ITimeDetectorService.aidl b/core/java/android/app/timedetector/ITimeDetectorService.aidl index f624446a2027..ddc4932d6fec 100644 --- a/core/java/android/app/timedetector/ITimeDetectorService.aidl +++ b/core/java/android/app/timedetector/ITimeDetectorService.aidl @@ -16,10 +16,10 @@ package android.app.timedetector; -import android.app.timedetector.TimeSignal; +import android.app.timedetector.PhoneTimeSuggestion; /** - * System private API to comunicate with time detector service. + * System private API to communicate with time detector service. * * <p>Used by parts of the Android system with signals associated with the device's time to provide * information to the Time Detector Service. @@ -32,5 +32,5 @@ import android.app.timedetector.TimeSignal; * {@hide} */ interface ITimeDetectorService { - void suggestTime(in TimeSignal timeSignal); + void suggestPhoneTime(in PhoneTimeSuggestion timeSuggestion); } diff --git a/core/java/android/app/timedetector/TimeSignal.aidl b/core/java/android/app/timedetector/PhoneTimeSuggestion.aidl index d2ec357555bc..f5e240549a9a 100644 --- a/core/java/android/app/timedetector/TimeSignal.aidl +++ b/core/java/android/app/timedetector/PhoneTimeSuggestion.aidl @@ -16,4 +16,4 @@ package android.app.timedetector; -parcelable TimeSignal;
\ No newline at end of file +parcelable PhoneTimeSuggestion; diff --git a/core/java/android/app/timedetector/PhoneTimeSuggestion.java b/core/java/android/app/timedetector/PhoneTimeSuggestion.java new file mode 100644 index 000000000000..475a4aafd929 --- /dev/null +++ b/core/java/android/app/timedetector/PhoneTimeSuggestion.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.timedetector; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.TimestampedValue; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * A time signal from a telephony source. The value consists of the number of milliseconds elapsed + * since 1/1/1970 00:00:00 UTC and the time according to the elapsed realtime clock when that number + * was established. The elapsed realtime clock is considered accurate but volatile, so time signals + * must not be persisted across device resets. + * + * @hide + */ +public final class PhoneTimeSuggestion implements Parcelable { + + public static final @NonNull Parcelable.Creator<PhoneTimeSuggestion> CREATOR = + new Parcelable.Creator<PhoneTimeSuggestion>() { + public PhoneTimeSuggestion createFromParcel(Parcel in) { + return PhoneTimeSuggestion.createFromParcel(in); + } + + public PhoneTimeSuggestion[] newArray(int size) { + return new PhoneTimeSuggestion[size]; + } + }; + + private final int mPhoneId; + @NonNull + private final TimestampedValue<Long> mUtcTime; + @Nullable + private ArrayList<String> mDebugInfo; + + public PhoneTimeSuggestion(int phoneId, @NonNull TimestampedValue<Long> utcTime) { + mPhoneId = phoneId; + mUtcTime = Objects.requireNonNull(utcTime); + } + + private static PhoneTimeSuggestion createFromParcel(Parcel in) { + int phoneId = in.readInt(); + TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */); + PhoneTimeSuggestion suggestion = new PhoneTimeSuggestion(phoneId, utcTime); + @SuppressWarnings("unchecked") + ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */); + suggestion.mDebugInfo = debugInfo; + return suggestion; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mPhoneId); + dest.writeParcelable(mUtcTime, 0); + dest.writeList(mDebugInfo); + } + + public int getPhoneId() { + return mPhoneId; + } + + @NonNull + public TimestampedValue<Long> getUtcTime() { + return mUtcTime; + } + + @NonNull + public List<String> getDebugInfo() { + return Collections.unmodifiableList(mDebugInfo); + } + + /** + * Associates information with the instance that can be useful for debugging / logging. The + * information is present in {@link #toString()} but is not considered for + * {@link #equals(Object)} and {@link #hashCode()}. + */ + public void addDebugInfo(String... debugInfos) { + if (mDebugInfo == null) { + mDebugInfo = new ArrayList<>(); + } + mDebugInfo.addAll(Arrays.asList(debugInfos)); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PhoneTimeSuggestion that = (PhoneTimeSuggestion) o; + return mPhoneId == that.mPhoneId + && Objects.equals(mUtcTime, that.mUtcTime); + } + + @Override + public int hashCode() { + return Objects.hash(mPhoneId, mUtcTime); + } + + @Override + public String toString() { + return "PhoneTimeSuggestion{" + + "mPhoneId='" + mPhoneId + '\'' + + ", mUtcTime=" + mUtcTime + + ", mDebugInfo=" + mDebugInfo + + '}'; + } +} diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java index 052050df8c9a..334e9582a145 100644 --- a/core/java/android/app/timedetector/TimeDetector.java +++ b/core/java/android/app/timedetector/TimeDetector.java @@ -45,12 +45,12 @@ public final class TimeDetector { * signals are available such as those that come from more reliable sources or were * determined more recently. */ - public void suggestTime(@NonNull TimeSignal timeSignal) { + public void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) { if (DEBUG) { - Log.d(TAG, "suggestTime called: " + timeSignal); + Log.d(TAG, "suggestPhoneTime called: " + timeSuggestion); } try { - mITimeDetectorService.suggestTime(timeSignal); + mITimeDetectorService.suggestPhoneTime(timeSuggestion); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/timedetector/TimeSignal.java b/core/java/android/app/timedetector/TimeSignal.java deleted file mode 100644 index b49426000d88..000000000000 --- a/core/java/android/app/timedetector/TimeSignal.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app.timedetector; - -import android.annotation.NonNull; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.TimestampedValue; - -import java.util.Objects; - -/** - * A time signal from a named source. The value consists of the number of milliseconds elapsed since - * 1/1/1970 00:00:00 UTC and the time according to the elapsed realtime clock when that number was - * established. The elapsed realtime clock is considered accurate but volatile, so time signals - * must not be persisted across device resets. - * - * @hide - */ -public final class TimeSignal implements Parcelable { - - public static final @android.annotation.NonNull Parcelable.Creator<TimeSignal> CREATOR = - new Parcelable.Creator<TimeSignal>() { - public TimeSignal createFromParcel(Parcel in) { - return TimeSignal.createFromParcel(in); - } - - public TimeSignal[] newArray(int size) { - return new TimeSignal[size]; - } - }; - - public static final String SOURCE_ID_NITZ = "nitz"; - - private final String mSourceId; - private final TimestampedValue<Long> mUtcTime; - - public TimeSignal(String sourceId, TimestampedValue<Long> utcTime) { - mSourceId = Objects.requireNonNull(sourceId); - mUtcTime = Objects.requireNonNull(utcTime); - } - - private static TimeSignal createFromParcel(Parcel in) { - String sourceId = in.readString(); - TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */); - return new TimeSignal(sourceId, utcTime); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeString(mSourceId); - dest.writeParcelable(mUtcTime, 0); - } - - @NonNull - public String getSourceId() { - return mSourceId; - } - - @NonNull - public TimestampedValue<Long> getUtcTime() { - return mUtcTime; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - TimeSignal that = (TimeSignal) o; - return Objects.equals(mSourceId, that.mSourceId) - && Objects.equals(mUtcTime, that.mUtcTime); - } - - @Override - public int hashCode() { - return Objects.hash(mSourceId, mUtcTime); - } - - @Override - public String toString() { - return "TimeSignal{" - + "mSourceId='" + mSourceId + '\'' - + ", mUtcTime=" + mUtcTime - + '}'; - } -} diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 2ecfc8e107b3..4e7e7132b7df 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2678,6 +2678,9 @@ public class Intent implements Parcelable, Cloneable { * that application is first launched (that is the first time it is moved * out of the stopped state). The data contains the name of the package. * + * <p>When the application is first launched, the application itself doesn't receive this + * broadcast.</p> + * * <p class="note">This is a protected intent that can only be sent * by the system. */ diff --git a/core/java/android/hardware/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java index 0ec812fe0350..698876b9c59e 100644 --- a/core/java/android/hardware/biometrics/BiometricAuthenticator.java +++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java @@ -36,23 +36,30 @@ public interface BiometricAuthenticator { * @hide */ int TYPE_NONE = 0; + + /** + * Constant representing credential (PIN, pattern, or password). + * @hide + */ + int TYPE_CREDENTIAL = 1 << 0; + /** * Constant representing fingerprint. * @hide */ - int TYPE_FINGERPRINT = 1 << 0; + int TYPE_FINGERPRINT = 1 << 1; /** * Constant representing iris. * @hide */ - int TYPE_IRIS = 1 << 1; + int TYPE_IRIS = 1 << 2; /** * Constant representing face. * @hide */ - int TYPE_FACE = 1 << 2; + int TYPE_FACE = 1 << 3; /** * Container for biometric data diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java index 27c04b407315..c8bf570e1bc8 100644 --- a/core/java/android/hardware/biometrics/BiometricConstants.java +++ b/core/java/android/hardware/biometrics/BiometricConstants.java @@ -134,6 +134,13 @@ public interface BiometricConstants { int BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL = 14; /** + * This constant is only used by SystemUI. It notifies SystemUI that authentication was paused + * because the authentication attempt was unsuccessful. + * @hide + */ + int BIOMETRIC_PAUSED_REJECTED = 100; + + /** * @hide */ @UnsupportedAppUsage diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java index cbe8a052db2f..9d427c8d8bab 100644 --- a/core/java/android/hardware/biometrics/BiometricManager.java +++ b/core/java/android/hardware/biometrics/BiometricManager.java @@ -137,7 +137,7 @@ public class BiometricManager { public boolean hasEnrolledBiometrics(int userId) { if (mService != null) { try { - return mService.hasEnrolledBiometrics(userId); + return mService.hasEnrolledBiometrics(userId, mContext.getOpPackageName()); } catch (RemoteException e) { Slog.w(TAG, "Remote exception in hasEnrolledBiometrics(): " + e); return false; diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index cf86e25112d2..9c51b5246749 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -26,6 +26,8 @@ import android.annotation.RequiresPermission; import android.app.KeyguardManager; import android.content.Context; import android.content.DialogInterface; +import android.hardware.face.FaceManager; +import android.hardware.fingerprint.FingerprintManager; import android.os.Binder; import android.os.Bundle; import android.os.CancellationSignal; @@ -339,9 +341,23 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan } @Override - public void onError(int error, String message) throws RemoteException { + public void onError(int modality, int error, int vendorCode) throws RemoteException { mExecutor.execute(() -> { - mAuthenticationCallback.onAuthenticationError(error, message); + String errorMessage; + switch (modality) { + case TYPE_FACE: + errorMessage = FaceManager.getErrorString(mContext, error, vendorCode); + break; + + case TYPE_FINGERPRINT: + errorMessage = FingerprintManager.getErrorString(mContext, error, + vendorCode); + break; + + default: + errorMessage = ""; + } + mAuthenticationCallback.onAuthenticationError(error, errorMessage); }); } diff --git a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl new file mode 100644 index 000000000000..987d19799644 --- /dev/null +++ b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.biometrics; + +import android.hardware.biometrics.IBiometricServiceReceiverInternal; +import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; +import android.hardware.face.IFaceServiceReceiver; +import android.hardware.face.Face; + +/** + * This interface encapsulates fingerprint, face, iris, etc. authenticators. + * Implementations of this interface are meant to be registered with BiometricService. + * @hide + */ +interface IBiometricAuthenticator { + + // 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 + // by BiometricService. To start authentication after the clients are ready, use + // startPreparedClient(). + void prepareForAuthentication(boolean requireConfirmation, IBinder token, long sessionId, + int userId, IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName, + int cookie, int callingUid, int callingPid, int callingUserId); + + // Starts authentication with the previously prepared client. + void startPreparedClient(int cookie); + + // Same as above, with extra arguments. + void cancelAuthenticationFromService(IBinder token, String opPackageName, + int callingUid, int callingPid, int callingUserId, boolean fromClient); + + // Determine if HAL is loaded and ready + boolean isHardwareDetected(String opPackageName); + + // Determine if a user has at least one enrolled face + boolean hasEnrolledTemplates(int userId, String opPackageName); + + // Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password) + void resetLockout(in byte [] token); + + // Explicitly set the active user (for enrolling work profile) + void setActiveUser(int uid); +} diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl index 6a3bf38a97e1..06336a55ac5e 100644 --- a/core/java/android/hardware/biometrics/IBiometricService.aidl +++ b/core/java/android/hardware/biometrics/IBiometricService.aidl @@ -19,6 +19,7 @@ package android.hardware.biometrics; import android.os.Bundle; import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; import android.hardware.biometrics.IBiometricServiceReceiver; +import android.hardware.biometrics.IBiometricAuthenticator; /** * Communication channel from BiometricPrompt and BiometricManager to BiometricService. The @@ -40,7 +41,12 @@ interface IBiometricService { int canAuthenticate(String opPackageName, int userId); // Checks if any biometrics are enrolled. - boolean hasEnrolledBiometrics(int userId); + boolean hasEnrolledBiometrics(int userId, String opPackageName); + + // Registers an authenticator (e.g. face, fingerprint, iris). + // Id must be unique, whereas strength and modality don't need to be. + // TODO(b/123321528): Turn strength and modality into enums. + void registerAuthenticator(int id, int strength, int modality, IBiometricAuthenticator authenticator); // Register callback for when keyguard biometric eligibility changes. void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback); diff --git a/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl index 22ef33e86e17..c960049438f1 100644 --- a/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl +++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl @@ -25,7 +25,7 @@ oneway interface IBiometricServiceReceiver { // Noties that authentication failed. void onAuthenticationFailed(); // Notify BiometricPrompt that an error has occurred. - void onError(int error, String message); + void onError(int modality, int error, int vendorCode); // Notifies that a biometric has been acquired. void onAcquired(int acquiredInfo, String message); // Notifies that the SystemUI dialog has been dismissed. diff --git a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl index 66b6e896fc13..61310f302fe4 100644 --- a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl +++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl @@ -31,7 +31,7 @@ oneway interface IBiometricServiceReceiverInternal { void onAuthenticationFailed(); // Notify BiometricService than an error has occured. Forward to the correct receiver depending // on the cookie. - void onError(int cookie, int error, String message); + void onError(int cookie, int modality, int error, int vendorCode); // Notifies that a biometric has been acquired. void onAcquired(int acquiredInfo, String message); // Notifies that the SystemUI dialog has been dismissed. diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index 12b285a0f0ab..55ebe285af1e 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -504,8 +504,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan public boolean isHardwareDetected() { if (mService != null) { try { - long deviceId = 0; /* TODO: plumb hardware id to FPMS */ - return mService.isHardwareDetected(deviceId, mContext.getOpPackageName()); + return mService.isHardwareDetected(mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl index b6a0afbf716c..68a4aef7af08 100644 --- a/core/java/android/hardware/face/IFaceService.aidl +++ b/core/java/android/hardware/face/IFaceService.aidl @@ -67,7 +67,7 @@ interface IFaceService { List<Face> getEnrolledFaces(int userId, String opPackageName); // Determine if HAL is loaded and ready - boolean isHardwareDetected(long deviceId, String opPackageName); + boolean isHardwareDetected(String opPackageName); // Get a pre-enrollment authentication token long generateChallenge(IBinder token); diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index d0622c88b4ca..22f8590a4e97 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -691,8 +691,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing public boolean isHardwareDetected() { if (mService != null) { try { - long deviceId = 0; /* TODO: plumb hardware id to FPMS */ - return mService.isHardwareDetected(deviceId, mContext.getOpPackageName()); + return mService.isHardwareDetected(mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index dd6b29d87d67..1a7e12856753 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -71,7 +71,7 @@ interface IFingerprintService { List<Fingerprint> getEnrolledFingerprints(int groupId, String opPackageName); // Determine if HAL is loaded and ready - boolean isHardwareDetected(long deviceId, String opPackageName); + boolean isHardwareDetected(String opPackageName); // Get a pre-enrollment authentication token long preEnroll(IBinder token); diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index d9e2ba3e0975..317469e58245 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -155,12 +155,12 @@ oneway interface IStatusBar // Used to show the authentication dialog (Biometrics, Device Credential) void showAuthenticationDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int biometricModality, boolean requireConfirmation, int userId, String opPackageName); - // Used to notify the authentication dialog that a biometric has been authenticated or rejected - void onBiometricAuthenticated(boolean authenticated, String failureReason); + // Used to notify the authentication dialog that a biometric has been authenticated + void onBiometricAuthenticated(); // Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc void onBiometricHelp(String message); - // Used to set a message - the dialog will dismiss after a certain amount of time - void onBiometricError(int errorCode, String error); + // Used to show an error - the dialog will dismiss after a certain amount of time + void onBiometricError(int modality, int error, int vendorCode); // Used to hide the authentication dialog, e.g. when the application cancels authentication void hideAuthenticationDialog(); diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index 659134adec78..499a4d2fb949 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -104,12 +104,12 @@ interface IStatusBarService // Used to show the authentication dialog (Biometrics, Device Credential) void showAuthenticationDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int biometricModality, boolean requireConfirmation, int userId, String opPackageName); - // Used to notify the authentication dialog that a biometric has been authenticated or rejected - void onBiometricAuthenticated(boolean authenticated, String failureReason); + // Used to notify the authentication dialog that a biometric has been authenticated + void onBiometricAuthenticated(); // Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc void onBiometricHelp(String message); - // Used to set a message - the dialog will dismiss after a certain amount of time - void onBiometricError(int errorCode, String error); + // Used to show an error - the dialog will dismiss after a certain amount of time + void onBiometricError(int modality, int error, int vendorCode); // Used to hide the authentication dialog, e.g. when the application cancels authentication void hideAuthenticationDialog(); } diff --git a/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java b/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java new file mode 100644 index 000000000000..1b5ad8868a01 --- /dev/null +++ b/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java @@ -0,0 +1,75 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.timedetector; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import android.os.Parcel; +import android.os.Parcelable; +import android.util.TimestampedValue; + +import org.junit.Test; + +public class PhoneTimeSuggestionTest { + private static final int PHONE_ID = 99999; + + @Test + public void testEquals() { + PhoneTimeSuggestion one = + new PhoneTimeSuggestion(PHONE_ID, new TimestampedValue<>(1111L, 2222L)); + assertEquals(one, one); + + PhoneTimeSuggestion two = + new PhoneTimeSuggestion(PHONE_ID, new TimestampedValue<>(1111L, 2222L)); + assertEquals(one, two); + assertEquals(two, one); + + PhoneTimeSuggestion three = + new PhoneTimeSuggestion(PHONE_ID + 1, new TimestampedValue<>(1111L, 2222L)); + assertNotEquals(one, three); + assertNotEquals(three, one); + + // DebugInfo must not be considered in equals(). + one.addDebugInfo("Debug info 1"); + two.addDebugInfo("Debug info 2"); + assertEquals(one, two); + } + + @Test + public void testParcelable() { + PhoneTimeSuggestion one = + new PhoneTimeSuggestion(PHONE_ID, new TimestampedValue<>(1111L, 2222L)); + assertEquals(one, roundTripParcelable(one)); + + // DebugInfo should also be stored (but is not checked by equals() + one.addDebugInfo("This is debug info"); + PhoneTimeSuggestion two = roundTripParcelable(one); + assertEquals(one.getDebugInfo(), two.getDebugInfo()); + } + + @SuppressWarnings("unchecked") + private static <T extends Parcelable> T roundTripParcelable(T one) { + Parcel parcel = Parcel.obtain(); + parcel.writeTypedObject(one, 0); + parcel.setDataPosition(0); + + T toReturn = (T) parcel.readTypedObject(PhoneTimeSuggestion.CREATOR); + parcel.recycle(); + return toReturn; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index cdc2623d34fd..b75873100025 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -16,6 +16,9 @@ package com.android.systemui.biometrics; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; + import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.IActivityTaskManager; @@ -27,6 +30,8 @@ import android.hardware.biometrics.Authenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricPrompt; import android.hardware.biometrics.IBiometricServiceReceiverInternal; +import android.hardware.face.FaceManager; +import android.hardware.fingerprint.FingerprintManager; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -34,6 +39,7 @@ import android.os.RemoteException; import android.util.Log; import android.view.WindowManager; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.SomeArgs; import com.android.systemui.SystemUI; @@ -229,15 +235,8 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, } @Override - public void onBiometricAuthenticated(boolean authenticated, String failureReason) { - if (DEBUG) Log.d(TAG, "onBiometricAuthenticated: " + authenticated - + " reason: " + failureReason); - - if (authenticated) { - mCurrentDialog.onAuthenticationSucceeded(); - } else { - mCurrentDialog.onAuthenticationFailed(failureReason); - } + public void onBiometricAuthenticated() { + mCurrentDialog.onAuthenticationSucceeded(); } @Override @@ -247,16 +246,45 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, mCurrentDialog.onHelp(message); } + private String getErrorString(int modality, int error, int vendorCode) { + switch (modality) { + case TYPE_FACE: + return FaceManager.getErrorString(mContext, error, vendorCode); + + case TYPE_FINGERPRINT: + return FingerprintManager.getErrorString(mContext, error, vendorCode); + + default: + return ""; + } + } + @Override - public void onBiometricError(int errorCode, String error) { - if (DEBUG) Log.d(TAG, "onBiometricError: " + errorCode + ", " + error); + public void onBiometricError(int modality, int error, int vendorCode) { + if (DEBUG) { + Log.d(TAG, String.format("onBiometricError(%d, %d, %d)", modality, error, vendorCode)); + } + + final boolean isLockout = (error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT) + || (error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT); + + // TODO(b/141025588): Create separate methods for handling hard and soft errors. + final boolean isSoftError = (error == BiometricConstants.BIOMETRIC_PAUSED_REJECTED + || error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT); - final boolean isLockout = errorCode == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT - || errorCode == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT; if (mCurrentDialog.isAllowDeviceCredentials() && isLockout) { + if (DEBUG) Log.d(TAG, "onBiometricError, lockout"); mCurrentDialog.animateToCredentialUI(); + } else if (isSoftError) { + final String errorMessage = (error == BiometricConstants.BIOMETRIC_PAUSED_REJECTED) + ? mContext.getString(R.string.biometric_not_recognized) + : getErrorString(modality, error, vendorCode); + if (DEBUG) Log.d(TAG, "onBiometricError, soft error: " + errorMessage); + mCurrentDialog.onAuthenticationFailed(errorMessage); } else { - mCurrentDialog.onError(error); + final String errorMessage = getErrorString(modality, error, vendorCode); + if (DEBUG) Log.d(TAG, "onBiometricError, hard error: " + errorMessage); + mCurrentDialog.onError(errorMessage); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index c9dc08e78afb..34f543702d96 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -261,9 +261,9 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< default void showAuthenticationDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver, int biometricModality, boolean requireConfirmation, int userId, String opPackageName) { } - default void onBiometricAuthenticated(boolean authenticated, String failureReason) { } + default void onBiometricAuthenticated() { } default void onBiometricHelp(String message) { } - default void onBiometricError(int errorCode, String error) { } + default void onBiometricError(int modality, int error, int vendorCode) { } default void hideAuthenticationDialog() { } /** @@ -792,12 +792,9 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< } @Override - public void onBiometricAuthenticated(boolean authenticated, String failureReason) { + public void onBiometricAuthenticated() { synchronized (mLock) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = authenticated; - args.arg2 = failureReason; - mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED, args).sendToTarget(); + mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED).sendToTarget(); } } @@ -809,9 +806,13 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< } @Override - public void onBiometricError(int errorCode, String error) { + public void onBiometricError(int modality, int error, int vendorCode) { synchronized (mLock) { - mHandler.obtainMessage(MSG_BIOMETRIC_ERROR, errorCode, 0, error).sendToTarget(); + SomeArgs args = SomeArgs.obtain(); + args.argi1 = modality; + args.argi2 = error; + args.argi3 = vendorCode; + mHandler.obtainMessage(MSG_BIOMETRIC_ERROR, args).sendToTarget(); } } @@ -1098,13 +1099,9 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< break; } case MSG_BIOMETRIC_AUTHENTICATED: { - SomeArgs someArgs = (SomeArgs) msg.obj; for (int i = 0; i < mCallbacks.size(); i++) { - mCallbacks.get(i).onBiometricAuthenticated( - (boolean) someArgs.arg1 /* authenticated */, - (String) someArgs.arg2 /* failureReason */); + mCallbacks.get(i).onBiometricAuthenticated(); } - someArgs.recycle(); break; } case MSG_BIOMETRIC_HELP: @@ -1113,9 +1110,15 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< } break; case MSG_BIOMETRIC_ERROR: + SomeArgs someArgs = (SomeArgs) msg.obj; for (int i = 0; i < mCallbacks.size(); i++) { - mCallbacks.get(i).onBiometricError(msg.arg1, (String) msg.obj); + mCallbacks.get(i).onBiometricError( + someArgs.argi1 /* modality */, + someArgs.argi2 /* error */, + someArgs.argi3 /* vendorCode */ + ); } + someArgs.recycle(); break; case MSG_BIOMETRIC_HIDE: for (int i = 0; i < mCallbacks.size(); i++) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index e1eb3b0c81b2..b089b740fc47 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -38,15 +38,18 @@ import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.hardware.biometrics.Authenticator; +import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricPrompt; import android.hardware.biometrics.IBiometricServiceReceiverInternal; +import android.hardware.face.FaceManager; import android.os.Bundle; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableContext; import android.testing.TestableLooper.RunWithLooper; +import com.android.internal.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.StatusBar; @@ -89,9 +92,9 @@ public class AuthControllerTest extends SysuiTestCase { when(context.getPackageManager()).thenReturn(mPackageManager); when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) - .thenReturn(true); + .thenReturn(true); when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) - .thenReturn(true); + .thenReturn(true); when(mDialog1.getOpPackageName()).thenReturn("Dialog1"); when(mDialog2.getOpPackageName()).thenReturn("Dialog2"); @@ -170,20 +173,34 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testOnAuthenticationSucceededInvoked_whenSystemRequested() { showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE); - mAuthController.onBiometricAuthenticated(true, null /* failureReason */); + mAuthController.onBiometricAuthenticated(); verify(mDialog1).onAuthenticationSucceeded(); } @Test - public void testOnAuthenticationFailedInvoked_whenSystemRequested() { + public void testOnAuthenticationFailedInvoked_whenBiometricRejected() { showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE); - final String failureReason = "failure reason"; - mAuthController.onBiometricAuthenticated(false, failureReason); + mAuthController.onBiometricError(BiometricAuthenticator.TYPE_NONE, + BiometricConstants.BIOMETRIC_PAUSED_REJECTED, + 0 /* vendorCode */); ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class); verify(mDialog1).onAuthenticationFailed(captor.capture()); - assertEquals(captor.getValue(), failureReason); + assertEquals(captor.getValue(), mContext.getString(R.string.biometric_not_recognized)); + } + + @Test + public void testOnAuthenticationFailedInvoked_whenBiometricTimedOut() { + showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE); + final int error = BiometricConstants.BIOMETRIC_ERROR_TIMEOUT; + final int vendorCode = 0; + mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode); + + ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class); + verify(mDialog1).onAuthenticationFailed(captor.capture()); + + assertEquals(captor.getValue(), FaceManager.getErrorString(mContext, error, vendorCode)); } @Test @@ -199,27 +216,27 @@ public class AuthControllerTest extends SysuiTestCase { } @Test - public void testOnErrorInvoked_whenSystemRequested() { + public void testOnErrorInvoked_whenSystemRequested() throws Exception { showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE); final int error = 1; - final String errMessage = "error message"; - mAuthController.onBiometricError(error, errMessage); + final int vendorCode = 0; + mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode); ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class); verify(mDialog1).onError(captor.capture()); - assertEquals(captor.getValue(), errMessage); + assertEquals(captor.getValue(), FaceManager.getErrorString(mContext, error, vendorCode)); } @Test public void testErrorLockout_whenCredentialAllowed_AnimatesToCredentialUI() { showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE); final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT; - final String errorString = "lockout"; + final int vendorCode = 0; when(mDialog1.isAllowDeviceCredentials()).thenReturn(true); - mAuthController.onBiometricError(error, errorString); + mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode); verify(mDialog1, never()).onError(anyString()); verify(mDialog1).animateToCredentialUI(); } @@ -228,11 +245,11 @@ public class AuthControllerTest extends SysuiTestCase { public void testErrorLockoutPermanent_whenCredentialAllowed_AnimatesToCredentialUI() { showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE); final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT; - final String errorString = "lockout_permanent"; + final int vendorCode = 0; when(mDialog1.isAllowDeviceCredentials()).thenReturn(true); - mAuthController.onBiometricError(error, errorString); + mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode); verify(mDialog1, never()).onError(anyString()); verify(mDialog1).animateToCredentialUI(); } @@ -241,12 +258,12 @@ public class AuthControllerTest extends SysuiTestCase { public void testErrorLockout_whenCredentialNotAllowed_sendsOnError() { showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE); final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT; - final String errorString = "lockout"; + final int vendorCode = 0; when(mDialog1.isAllowDeviceCredentials()).thenReturn(false); - mAuthController.onBiometricError(error, errorString); - verify(mDialog1).onError(eq(errorString)); + mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode); + verify(mDialog1).onError(eq(FaceManager.getErrorString(mContext, error, vendorCode))); verify(mDialog1, never()).animateToCredentialUI(); } @@ -254,12 +271,12 @@ public class AuthControllerTest extends SysuiTestCase { public void testErrorLockoutPermanent_whenCredentialNotAllowed_sendsOnError() { showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE); final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT; - final String errorString = "lockout_permanent"; + final int vendorCode = 0; when(mDialog1.isAllowDeviceCredentials()).thenReturn(false); - mAuthController.onBiometricError(error, errorString); - verify(mDialog1).onError(eq(errorString)); + mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode); + verify(mDialog1).onError(eq(FaceManager.getErrorString(mContext, error, vendorCode))); verify(mDialog1, never()).animateToCredentialUI(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java index eb198c6ad4f7..8c9ae71dc0f3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java @@ -418,10 +418,9 @@ public class CommandQueueTest extends SysuiTestCase { @Test public void testOnBiometricAuthenticated() { - String failureReason = "test_failure_reason"; - mCommandQueue.onBiometricAuthenticated(true /* authenticated */, failureReason); + mCommandQueue.onBiometricAuthenticated(); waitForIdleSync(); - verify(mCallbacks).onBiometricAuthenticated(eq(true), eq(failureReason)); + verify(mCallbacks).onBiometricAuthenticated(); } @Test @@ -434,11 +433,12 @@ public class CommandQueueTest extends SysuiTestCase { @Test public void testOnBiometricError() { - final int errorCode = 1; - String errorMessage = "test_error_message"; - mCommandQueue.onBiometricError(errorCode, errorMessage); + final int modality = 1; + final int error = 2; + final int vendorCode = 3; + mCommandQueue.onBiometricError(modality, error, vendorCode); waitForIdleSync(); - verify(mCallbacks).onBiometricError(eq(errorCode), eq(errorMessage)); + verify(mCallbacks).onBiometricError(eq(modality), eq(error), eq(vendorCode)); } @Test diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index d18b4f64f039..a33fcd557369 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -720,6 +720,8 @@ public final class BatteryService extends SystemService { event.putInt(BatteryManager.EXTRA_SCALE, BATTERY_SCALE); event.putInt(BatteryManager.EXTRA_PLUGGED, mPlugType); event.putInt(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltage); + event.putInt(BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.batteryTemperature); + event.putInt(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounter); event.putLong(BatteryManager.EXTRA_EVENT_TIMESTAMP, now); boolean queueWasEmpty = mBatteryLevelsEventQueue.isEmpty(); diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index 4f1db3c96faf..619c21e7646b 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -37,13 +37,12 @@ import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricPrompt; import android.hardware.biometrics.BiometricSourceType; import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.IBiometricAuthenticator; import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; import android.hardware.biometrics.IBiometricService; import android.hardware.biometrics.IBiometricServiceReceiver; import android.hardware.biometrics.IBiometricServiceReceiverInternal; -import android.hardware.face.FaceManager; import android.hardware.face.IFaceService; -import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.IFingerprintService; import android.net.Uri; import android.os.Binder; @@ -68,6 +67,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.SomeArgs; import com.android.internal.statusbar.IStatusBarService; import com.android.server.SystemService; +import com.android.server.biometrics.face.FaceAuthenticator; +import com.android.server.biometrics.fingerprint.FingerprintAuthenticator; import java.util.ArrayList; import java.util.HashMap; @@ -95,11 +96,6 @@ public class BiometricService extends SystemService { private static final int MSG_CANCEL_AUTHENTICATION = 10; private static final int MSG_ON_AUTHENTICATION_TIMED_OUT = 11; private static final int MSG_ON_DEVICE_CREDENTIAL_PRESSED = 12; - private static final int[] FEATURE_ID = { - TYPE_FINGERPRINT, - TYPE_IRIS, - TYPE_FACE - }; /** * Authentication either just called and we have not transitioned to the CALLED state, or @@ -172,7 +168,7 @@ public class BiometricService extends SystemService { byte[] mTokenEscrow; // Waiting for SystemUI to complete animation int mErrorEscrow; - String mErrorStringEscrow; + int mVendorCodeEscrow; // Timestamp when authentication started private long mStartTimeMs; @@ -219,19 +215,15 @@ public class BiometricService extends SystemService { private final Injector mInjector; @VisibleForTesting final IBiometricService.Stub mImpl; + private final boolean mHasFeatureFace; private final boolean mHasFeatureFingerprint; private final boolean mHasFeatureIris; - private final boolean mHasFeatureFace; @VisibleForTesting final SettingObserver mSettingObserver; private final List<EnabledOnKeyguardCallback> mEnabledOnKeyguardCallbacks; private final Random mRandom = new Random(); @VisibleForTesting - IFingerprintService mFingerprintService; - @VisibleForTesting - IFaceService mFaceService; - @VisibleForTesting IStatusBarService mStatusBarService; @VisibleForTesting KeyStore mKeyStore; @@ -262,7 +254,7 @@ public class BiometricService extends SystemService { } case MSG_ON_AUTHENTICATION_REJECTED: { - handleAuthenticationRejected((String) msg.obj /* failureReason */); + handleAuthenticationRejected(); break; } @@ -270,8 +262,9 @@ public class BiometricService extends SystemService { SomeArgs args = (SomeArgs) msg.obj; handleOnError( args.argi1 /* cookie */, - args.argi2 /* error */, - (String) args.arg1 /* message */); + args.argi2 /* modality */, + args.argi3 /* error */, + args.argi4 /* vendorCode */); args.recycle(); break; } @@ -331,7 +324,12 @@ public class BiometricService extends SystemService { } case MSG_ON_AUTHENTICATION_TIMED_OUT: { - handleAuthenticationTimedOut((String) msg.obj /* errorMessage */); + SomeArgs args = (SomeArgs) msg.obj; + handleAuthenticationTimedOut( + args.argi1 /* modality */, + args.argi2 /* error */, + args.argi3 /* vendorCode */); + args.recycle(); break; } @@ -347,21 +345,23 @@ public class BiometricService extends SystemService { } }; - private final class AuthenticatorWrapper { - final int mType; - final BiometricAuthenticator mAuthenticator; - - AuthenticatorWrapper(int type, BiometricAuthenticator authenticator) { - mType = type; - mAuthenticator = authenticator; - } - - int getType() { - return mType; - } - - BiometricAuthenticator getAuthenticator() { - return mAuthenticator; + /** + * Wraps IBiometricAuthenticator implementation and stores information about the authenticator. + * TODO(b/141025588): Consider refactoring the tests to not rely on this implementation detail. + */ + @VisibleForTesting + public static final class AuthenticatorWrapper { + public final int id; + public final int strength; + public final int modality; + public final IBiometricAuthenticator impl; + + AuthenticatorWrapper(int id, int strength, int modality, + IBiometricAuthenticator impl) { + this.id = id; + this.strength = strength; + this.modality = modality; + this.impl = impl; } } @@ -521,23 +521,28 @@ public class BiometricService extends SystemService { @Override public void onAuthenticationFailed() throws RemoteException { - String failureReason = getContext().getString(R.string.biometric_not_recognized); - Slog.v(TAG, "onAuthenticationFailed: " + failureReason); - mHandler.obtainMessage(MSG_ON_AUTHENTICATION_REJECTED, failureReason).sendToTarget(); + Slog.v(TAG, "onAuthenticationFailed"); + mHandler.obtainMessage(MSG_ON_AUTHENTICATION_REJECTED).sendToTarget(); } @Override - public void onError(int cookie, int error, String message) throws RemoteException { + public void onError(int cookie, int modality, int error, int vendorCode) + throws RemoteException { // Determine if error is hard or soft error. Certain errors (such as TIMEOUT) are // soft errors and we should allow the user to try authenticating again instead of // dismissing BiometricPrompt. if (error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT) { - mHandler.obtainMessage(MSG_ON_AUTHENTICATION_TIMED_OUT, message).sendToTarget(); + SomeArgs args = SomeArgs.obtain(); + args.argi1 = modality; + args.argi2 = error; + args.argi3 = vendorCode; + mHandler.obtainMessage(MSG_ON_AUTHENTICATION_TIMED_OUT, args).sendToTarget(); } else { SomeArgs args = SomeArgs.obtain(); args.argi1 = cookie; - args.argi2 = error; - args.arg1 = message; + args.argi2 = modality; + args.argi3 = error; + args.argi4 = vendorCode; mHandler.obtainMessage(MSG_ON_ERROR, args).sendToTarget(); } } @@ -666,7 +671,8 @@ public class BiometricService extends SystemService { final long ident = Binder.clearCallingIdentity(); int error; try { - final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId); + final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId, + opPackageName); error = result.second; } finally { Binder.restoreCallingIdentity(ident); @@ -675,22 +681,30 @@ public class BiometricService extends SystemService { } @Override - public boolean hasEnrolledBiometrics(int userId) { + public boolean hasEnrolledBiometrics(int userId, String opPackageName) { checkInternalPermission(); final long ident = Binder.clearCallingIdentity(); try { - for (int i = 0; i < mAuthenticators.size(); i++) { - if (mAuthenticators.get(i).mAuthenticator.hasEnrolledTemplates(userId)) { + for (AuthenticatorWrapper authenticator : mAuthenticators) { + if (authenticator.impl.hasEnrolledTemplates(userId, opPackageName)) { return true; } } + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); } finally { Binder.restoreCallingIdentity(ident); } return false; } + @Override + public void registerAuthenticator(int id, int strength, int modality, + IBiometricAuthenticator authenticator) { + mAuthenticators.add(new AuthenticatorWrapper(id, strength, modality, authenticator)); + } + @Override // Binder call public void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback) throws RemoteException { @@ -710,9 +724,11 @@ public class BiometricService extends SystemService { checkInternalPermission(); final long ident = Binder.clearCallingIdentity(); try { - for (int i = 0; i < mAuthenticators.size(); i++) { - mAuthenticators.get(i).getAuthenticator().setActiveUser(userId); + for (AuthenticatorWrapper authenticator : mAuthenticators) { + authenticator.impl.setActiveUser(userId); } + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); } finally { Binder.restoreCallingIdentity(ident); } @@ -723,11 +739,8 @@ public class BiometricService extends SystemService { checkInternalPermission(); final long ident = Binder.clearCallingIdentity(); try { - if (mFingerprintService != null) { - mFingerprintService.resetTimeout(token); - } - if (mFaceService != null) { - mFaceService.resetLockout(token); + for (AuthenticatorWrapper authenticator : mAuthenticators) { + authenticator.impl.resetLockout(token); } } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); @@ -750,40 +763,69 @@ public class BiometricService extends SystemService { } } + /** + * Class for injecting dependencies into BiometricService. + * TODO(b/141025588): Replace with a dependency injection framework (e.g. Guice, Dagger). + */ @VisibleForTesting - static class Injector { - IActivityManager getActivityManagerService() { + public static class Injector { + + @VisibleForTesting + public IActivityManager getActivityManagerService() { return ActivityManager.getService(); } - IStatusBarService getStatusBarService() { + @VisibleForTesting + public IStatusBarService getStatusBarService() { return IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); } - IFingerprintService getFingerprintService() { - return IFingerprintService.Stub.asInterface( - ServiceManager.getService(Context.FINGERPRINT_SERVICE)); + /** + * Allows to mock FaceAuthenticator for testing. + */ + @VisibleForTesting + public IBiometricAuthenticator getFingerprintAuthenticator() { + return new FingerprintAuthenticator(IFingerprintService.Stub.asInterface( + ServiceManager.getService(Context.FINGERPRINT_SERVICE))); } - IFaceService getFaceService() { - return IFaceService.Stub.asInterface(ServiceManager.getService(Context.FACE_SERVICE)); + /** + * Allows to mock FaceAuthenticator for testing. + */ + @VisibleForTesting + public IBiometricAuthenticator getFaceAuthenticator() { + return new FaceAuthenticator( + IFaceService.Stub.asInterface(ServiceManager.getService(Context.FACE_SERVICE))); } - SettingObserver getSettingObserver(Context context, Handler handler, + /** + * Allows to mock SettingObserver for testing. + */ + @VisibleForTesting + public SettingObserver getSettingObserver(Context context, Handler handler, List<EnabledOnKeyguardCallback> callbacks) { return new SettingObserver(context, handler, callbacks); } - KeyStore getKeyStore() { + @VisibleForTesting + public KeyStore getKeyStore() { return KeyStore.getInstance(); } - boolean isDebugEnabled(Context context, int userId) { + /** + * Allows to enable/disable debug logs. + */ + @VisibleForTesting + public boolean isDebugEnabled(Context context, int userId) { return Utils.isDebugEnabled(context, userId); } - void publishBinderService(BiometricService service, IBiometricService.Stub impl) { + /** + * Allows to stub publishBinderService(...) for testing. + */ + @VisibleForTesting + public void publishBinderService(BiometricService service, IBiometricService.Stub impl) { service.publishBinderService(Context.BIOMETRIC_SERVICE, impl); } } @@ -812,9 +854,9 @@ public class BiometricService extends SystemService { mEnabledOnKeyguardCallbacks); final PackageManager pm = context.getPackageManager(); + mHasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE); mHasFeatureFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT); mHasFeatureIris = pm.hasSystemFeature(PackageManager.FEATURE_IRIS); - mHasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE); try { injector.getActivityManagerService().registerUserSwitchObserver( @@ -833,26 +875,30 @@ public class BiometricService extends SystemService { @Override public void onStart() { - // TODO: maybe get these on-demand - if (mHasFeatureFingerprint) { - mFingerprintService = mInjector.getFingerprintService(); - } - if (mHasFeatureFace) { - mFaceService = mInjector.getFaceService(); + // TODO(b/141025588): remove this code block once AuthService is integrated. + { + if (mHasFeatureFace) { + try { + mImpl.registerAuthenticator(0, 0, TYPE_FACE, mInjector.getFaceAuthenticator()); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + } + if (mHasFeatureFingerprint) { + try { + mImpl.registerAuthenticator(0, 0, TYPE_FINGERPRINT, + mInjector.getFingerprintAuthenticator()); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + } + if (mHasFeatureIris) { + Slog.e(TAG, "Iris is not supported"); + } } mKeyStore = mInjector.getKeyStore(); mStatusBarService = mInjector.getStatusBarService(); - - // Cache the authenticators - for (int featureId : FEATURE_ID) { - if (hasFeature(featureId)) { - AuthenticatorWrapper authenticator = - new AuthenticatorWrapper(featureId, getAuthenticator(featureId)); - mAuthenticators.add(authenticator); - } - } - mInjector.publishBinderService(this, mImpl); } @@ -868,7 +914,7 @@ public class BiometricService extends SystemService { * {@link BiometricAuthenticator#TYPE_FACE} * and the error containing one of the {@link BiometricConstants} errors. */ - private Pair<Integer, Integer> checkAndGetBiometricModality(int userId) { + private Pair<Integer, Integer> checkAndGetBiometricModality(int userId, String opPackageName) { // No biometric features, send error if (mAuthenticators.isEmpty()) { return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT); @@ -886,23 +932,26 @@ public class BiometricService extends SystemService { int modality = TYPE_NONE; int firstHwAvailable = TYPE_NONE; - for (AuthenticatorWrapper authenticatorWrapper : mAuthenticators) { - modality = authenticatorWrapper.getType(); - BiometricAuthenticator authenticator = authenticatorWrapper.getAuthenticator(); - if (authenticator.isHardwareDetected()) { - isHardwareDetected = true; - if (firstHwAvailable == TYPE_NONE) { - // Store the first one since we want to return the error in correct priority - // order. - firstHwAvailable = modality; - } - if (authenticator.hasEnrolledTemplates(userId)) { - hasTemplatesEnrolled = true; - if (isEnabledForApp(modality, userId)) { - enabledForApps = true; - break; + for (AuthenticatorWrapper authenticator : mAuthenticators) { + modality = authenticator.modality; + try { + if (authenticator.impl.isHardwareDetected(opPackageName)) { + isHardwareDetected = true; + if (firstHwAvailable == TYPE_NONE) { + // Store the first one since we want to return the error in correct priority + // order. + firstHwAvailable = modality; + } + if (authenticator.impl.hasEnrolledTemplates(userId, opPackageName)) { + hasTemplatesEnrolled = true; + if (isEnabledForApp(modality, userId)) { + enabledForApps = true; + break; + } } } + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); } } @@ -926,7 +975,7 @@ public class BiometricService extends SystemService { } private boolean isEnabledForApp(int modality, int userId) { - switch(modality) { + switch (modality) { case TYPE_FINGERPRINT: return true; case TYPE_IRIS: @@ -939,47 +988,16 @@ public class BiometricService extends SystemService { } } - private String getErrorString(int type, int error, int vendorCode) { - switch (type) { - case TYPE_FINGERPRINT: - return FingerprintManager.getErrorString(getContext(), error, vendorCode); - case TYPE_IRIS: - Slog.w(TAG, "Modality not supported"); - return null; // not supported - case TYPE_FACE: - return FaceManager.getErrorString(getContext(), error, vendorCode); - default: - Slog.w(TAG, "Unable to get error string for modality: " + type); - return null; - } - } - - private BiometricAuthenticator getAuthenticator(int type) { - switch (type) { - case TYPE_FINGERPRINT: - return (FingerprintManager) - getContext().getSystemService(Context.FINGERPRINT_SERVICE); - case TYPE_IRIS: - return null; - case TYPE_FACE: - return (FaceManager) - getContext().getSystemService(Context.FACE_SERVICE); - default: - return null; - } - } - - private boolean hasFeature(int type) { - switch (type) { - case TYPE_FINGERPRINT: - return mHasFeatureFingerprint; - case TYPE_IRIS: - return mHasFeatureIris; - case TYPE_FACE: - return mHasFeatureFace; - default: - return false; + private String getErrorString(int modality, int error, int vendorCode) { + for (AuthenticatorWrapper authenticator : mAuthenticators) { + if (authenticator.modality == modality) { + // TODO(b/141025588): Refactor IBiometricServiceReceiver.aidl#onError(...) to not + // ask for a String error message, but derive it from the error code instead. + return ""; + } } + Slog.w(TAG, "Unable to get error string for modality: " + modality); + return null; } private void logDialogDismissed(int reason) { @@ -1081,14 +1099,14 @@ public class BiometricService extends SystemService { // Notify SysUI that the biometric has been authenticated. SysUI already knows // the implicit/explicit state and will react accordingly. - mStatusBarService.onBiometricAuthenticated(true, null /* failureReason */); + mStatusBarService.onBiometricAuthenticated(); } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); } } - private void handleAuthenticationRejected(String failureReason) { - Slog.v(TAG, "handleAuthenticationRejected: " + failureReason); + private void handleAuthenticationRejected() { + Slog.v(TAG, "handleAuthenticationRejected()"); try { // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded // after user dismissed/canceled dialog). @@ -1097,7 +1115,8 @@ public class BiometricService extends SystemService { return; } - mStatusBarService.onBiometricAuthenticated(false, failureReason); + mStatusBarService.onBiometricError(TYPE_NONE, + BiometricConstants.BIOMETRIC_PAUSED_REJECTED, 0 /* vendorCode */); // TODO: This logic will need to be updated if BP is multi-modal if ((mCurrentAuthSession.mModality & TYPE_FACE) != 0) { @@ -1112,8 +1131,9 @@ public class BiometricService extends SystemService { } } - private void handleAuthenticationTimedOut(String message) { - Slog.v(TAG, "handleAuthenticationTimedOut: " + message); + private void handleAuthenticationTimedOut(int modality, int error, int vendorCode) { + Slog.v(TAG, String.format("handleAuthenticationTimedOut(%d, %d, %d)", modality, error, + vendorCode)); try { // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded // after user dismissed/canceled dialog). @@ -1122,14 +1142,14 @@ public class BiometricService extends SystemService { return; } - mStatusBarService.onBiometricAuthenticated(false, message); + mStatusBarService.onBiometricError(modality, error, vendorCode); mCurrentAuthSession.mState = STATE_AUTH_PAUSED; } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); } } - private void handleOnError(int cookie, int error, String message) { + private void handleOnError(int cookie, int modality, int error, int vendorCode) { Slog.d(TAG, "handleOnError: " + error + " cookie: " + cookie); // Errors can either be from the current auth session or the pending auth session. // The pending auth session may receive errors such as ERROR_LOCKOUT before @@ -1140,7 +1160,7 @@ public class BiometricService extends SystemService { try { if (mCurrentAuthSession != null && mCurrentAuthSession.containsCookie(cookie)) { mCurrentAuthSession.mErrorEscrow = error; - mCurrentAuthSession.mErrorStringEscrow = message; + mCurrentAuthSession.mVendorCodeEscrow = vendorCode; if (mCurrentAuthSession.mState == STATE_AUTH_STARTED) { final boolean errorLockout = error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT @@ -1148,20 +1168,20 @@ public class BiometricService extends SystemService { if (mCurrentAuthSession.isAllowDeviceCredential() && errorLockout) { // SystemUI handles transition from biometric to device credential. mCurrentAuthSession.mState = STATE_SHOWING_DEVICE_CREDENTIAL; - mStatusBarService.onBiometricError(error, message); + mStatusBarService.onBiometricError(modality, error, vendorCode); } else { mCurrentAuthSession.mState = STATE_ERROR_PENDING_SYSUI; if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) { mStatusBarService.hideAuthenticationDialog(); } else { - mStatusBarService.onBiometricError(error, message); + mStatusBarService.onBiometricError(modality, error, vendorCode); } } } else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED) { // In the "try again" state, we should forward canceled errors to // the client and and clean up. The only error we should get here is // ERROR_CANCELED due to another client kicking us out. - mCurrentAuthSession.mClientReceiver.onError(error, message); + mCurrentAuthSession.mClientReceiver.onError(modality, error, vendorCode); mStatusBarService.hideAuthenticationDialog(); mCurrentAuthSession = null; } else if (mCurrentAuthSession.mState == STATE_SHOWING_DEVICE_CREDENTIAL) { @@ -1197,7 +1217,7 @@ public class BiometricService extends SystemService { mCurrentAuthSession.mUserId, mCurrentAuthSession.mOpPackageName); } else { - mPendingAuthSession.mClientReceiver.onError(error, message); + mPendingAuthSession.mClientReceiver.onError(modality, error, vendorCode); mPendingAuthSession = null; } } else { @@ -1261,8 +1281,10 @@ public class BiometricService extends SystemService { case BiometricPrompt.DISMISSED_REASON_USER_CANCEL: mCurrentAuthSession.mClientReceiver.onError( + mCurrentAuthSession.mModality, BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED, - getContext().getString(R.string.biometric_error_user_canceled)); + 0 /* vendorCode */ + ); // Cancel authentication. Skip the token/package check since we are cancelling // from system server. The interface is permission protected so this is fine. cancelInternal(null /* token */, null /* package */, false /* fromClient */); @@ -1270,8 +1292,11 @@ public class BiometricService extends SystemService { case BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED: case BiometricPrompt.DISMISSED_REASON_ERROR: - mCurrentAuthSession.mClientReceiver.onError(mCurrentAuthSession.mErrorEscrow, - mCurrentAuthSession.mErrorStringEscrow); + mCurrentAuthSession.mClientReceiver.onError( + mCurrentAuthSession.mModality, + mCurrentAuthSession.mErrorEscrow, + mCurrentAuthSession.mVendorCodeEscrow + ); break; default: @@ -1354,30 +1379,35 @@ public class BiometricService extends SystemService { mPendingAuthSession = null; mCurrentAuthSession.mState = STATE_AUTH_STARTED; - try { - int modality = TYPE_NONE; - it = mCurrentAuthSession.mModalitiesMatched.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry<Integer, Integer> pair = (Map.Entry) it.next(); - if (pair.getKey() == TYPE_FINGERPRINT) { - mFingerprintService.startPreparedClient(pair.getValue()); - } else if (pair.getKey() == TYPE_IRIS) { - Slog.e(TAG, "Iris unsupported"); - } else if (pair.getKey() == TYPE_FACE) { - mFaceService.startPreparedClient(pair.getValue()); - } else { - Slog.e(TAG, "Unknown modality: " + pair.getKey()); + int modality = TYPE_NONE; + it = mCurrentAuthSession.mModalitiesMatched.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry<Integer, Integer> pair = (Map.Entry) it.next(); + boolean foundAuthenticator = false; + for (AuthenticatorWrapper authenticator : mAuthenticators) { + if (authenticator.modality == pair.getKey()) { + foundAuthenticator = true; + try { + authenticator.impl.startPreparedClient(pair.getValue()); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } } - modality |= pair.getKey(); } + if (!foundAuthenticator) { + Slog.e(TAG, "Unknown modality: " + pair.getKey()); + } + modality |= pair.getKey(); + } - if (!continuing) { + if (!continuing) { + try { mStatusBarService.showAuthenticationDialog(mCurrentAuthSession.mBundle, mInternalReceiver, modality, requireConfirmation, userId, mCurrentAuthSession.mOpPackageName); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); } - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception", e); } } } @@ -1387,7 +1417,8 @@ public class BiometricService extends SystemService { int callingUid, int callingPid, int callingUserId) { mHandler.post(() -> { - final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId); + final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId, + opPackageName); final int modality = result.first; final int error = result.second; @@ -1400,23 +1431,7 @@ public class BiometricService extends SystemService { } else if (error != BiometricConstants.BIOMETRIC_SUCCESS) { // Check for errors, notify callback, and return try { - final String hardwareUnavailable = - getContext().getString(R.string.biometric_error_hw_unavailable); - switch (error) { - case BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT: - receiver.onError(error, hardwareUnavailable); - break; - case BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE: - receiver.onError(error, hardwareUnavailable); - break; - case BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS: - receiver.onError(error, - getErrorString(modality, error, 0 /* vendorCode */)); - break; - default: - Slog.e(TAG, "Unhandled error"); - break; - } + receiver.onError(modality, error, 0 /* vendorCode */); } catch (RemoteException e) { Slog.e(TAG, "Unable to send error", e); } @@ -1435,41 +1450,41 @@ public class BiometricService extends SystemService { * modality/modalities to start authenticating with. authenticateInternal() should only be * used for: * 1) Preparing <Biometric>Services for authentication when BiometricPrompt#authenticate is, - * invoked, shortly after which BiometricPrompt is shown and authentication starts + * invoked, shortly after which BiometricPrompt is shown and authentication starts * 2) Preparing <Biometric>Services for authentication when BiometricPrompt is already shown - * and the user has pressed "try again" + * and the user has pressed "try again" */ private void authenticateInternal(IBinder token, long sessionId, int userId, IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle, int callingUid, int callingPid, int callingUserId, int modality) { - try { - boolean requireConfirmation = bundle.getBoolean( - BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true /* default */); - if ((modality & TYPE_FACE) != 0) { - // Check if the user has forced confirmation to be required in Settings. - requireConfirmation = requireConfirmation - || mSettingObserver.getFaceAlwaysRequireConfirmation(userId); - } - // Generate random cookies to pass to the services that should prepare to start - // authenticating. Store the cookie here and wait for all services to "ack" - // with the cookie. Once all cookies are received, we can show the prompt - // and let the services start authenticating. The cookie should be non-zero. - final int cookie = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1; - final int authenticators = bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED); - Slog.d(TAG, "Creating auth session. Modality: " + modality - + ", cookie: " + cookie - + ", authenticators: " + authenticators); - final HashMap<Integer, Integer> modalities = new HashMap<>(); - - // If it's only device credential, we don't need to wait - LockSettingsService is - // always ready to check credential (SystemUI invokes that path). - if ((authenticators & ~Authenticator.TYPE_CREDENTIAL) != 0) { - modalities.put(modality, cookie); - } - mPendingAuthSession = new AuthSession(modalities, token, sessionId, userId, - receiver, opPackageName, bundle, callingUid, callingPid, callingUserId, - modality, requireConfirmation); + boolean requireConfirmation = bundle.getBoolean( + BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true /* default */); + if ((modality & TYPE_FACE) != 0) { + // Check if the user has forced confirmation to be required in Settings. + requireConfirmation = requireConfirmation + || mSettingObserver.getFaceAlwaysRequireConfirmation(userId); + } + // Generate random cookies to pass to the services that should prepare to start + // authenticating. Store the cookie here and wait for all services to "ack" + // with the cookie. Once all cookies are received, we can show the prompt + // and let the services start authenticating. The cookie should be non-zero. + final int cookie = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1; + final int authenticators = bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED); + Slog.d(TAG, "Creating auth session. Modality: " + modality + + ", cookie: " + cookie + + ", authenticators: " + authenticators); + final HashMap<Integer, Integer> modalities = new HashMap<>(); + + // If it's only device credential, we don't need to wait - LockSettingsService is + // always ready to check credential (SystemUI invokes that path). + if ((authenticators & ~Authenticator.TYPE_CREDENTIAL) != 0) { + modalities.put(modality, cookie); + } + mPendingAuthSession = new AuthSession(modalities, token, sessionId, userId, + receiver, opPackageName, bundle, callingUid, callingPid, callingUserId, + modality, requireConfirmation); + try { if (authenticators == Authenticator.TYPE_CREDENTIAL) { mPendingAuthSession.mState = STATE_SHOWING_DEVICE_CREDENTIAL; mCurrentAuthSession = mPendingAuthSession; @@ -1484,19 +1499,10 @@ public class BiometricService extends SystemService { mCurrentAuthSession.mOpPackageName); } else { mPendingAuthSession.mState = STATE_AUTH_CALLED; - // No polymorphism :( - if ((modality & TYPE_FINGERPRINT) != 0) { - mFingerprintService.prepareForAuthentication(token, sessionId, userId, - mInternalReceiver, opPackageName, cookie, - callingUid, callingPid, callingUserId); - } - if ((modality & TYPE_IRIS) != 0) { - Slog.w(TAG, "Iris unsupported"); - } - if ((modality & TYPE_FACE) != 0) { - mFaceService.prepareForAuthentication(requireConfirmation, - token, sessionId, userId, mInternalReceiver, opPackageName, - cookie, callingUid, callingPid, callingUserId); + for (AuthenticatorWrapper authenticator : mAuthenticators) { + authenticator.impl.prepareForAuthentication(requireConfirmation, token, + sessionId, userId, mInternalReceiver, opPackageName, cookie, callingUid, + callingPid, callingUserId); } } } catch (RemoteException e) { @@ -1517,11 +1523,10 @@ public class BiometricService extends SystemService { try { // Send error to client mCurrentAuthSession.mClientReceiver.onError( + mCurrentAuthSession.mModality, BiometricConstants.BIOMETRIC_ERROR_CANCELED, - getContext().getString( - com.android.internal.R.string.biometric_error_user_canceled) + 0 /* vendorCode */ ); - mCurrentAuthSession = null; mStatusBarService.hideAuthenticationDialog(); } catch (RemoteException e) { @@ -1537,30 +1542,25 @@ public class BiometricService extends SystemService { final int callingPid = Binder.getCallingPid(); final int callingUserId = UserHandle.getCallingUserId(); - try { - if (mCurrentAuthSession == null) { - Slog.w(TAG, "Skipping cancelInternal"); - return; - } else if (mCurrentAuthSession.mState != STATE_AUTH_STARTED) { - Slog.w(TAG, "Skipping cancelInternal, state: " + mCurrentAuthSession.mState); - return; - } + if (mCurrentAuthSession == null) { + Slog.w(TAG, "Skipping cancelInternal"); + return; + } else if (mCurrentAuthSession.mState != STATE_AUTH_STARTED) { + Slog.w(TAG, "Skipping cancelInternal, state: " + mCurrentAuthSession.mState); + return; + } - // TODO: For multiple modalities, send a single ERROR_CANCELED only when all - // drivers have canceled authentication. - if ((mCurrentAuthSession.mModality & TYPE_FINGERPRINT) != 0) { - mFingerprintService.cancelAuthenticationFromService(token, opPackageName, - callingUid, callingPid, callingUserId, fromClient); - } - if ((mCurrentAuthSession.mModality & TYPE_IRIS) != 0) { - Slog.w(TAG, "Iris unsupported"); - } - if ((mCurrentAuthSession.mModality & TYPE_FACE) != 0) { - mFaceService.cancelAuthenticationFromService(token, opPackageName, - callingUid, callingPid, callingUserId, fromClient); + // TODO: For multiple modalities, send a single ERROR_CANCELED only when all + // drivers have canceled authentication. + for (AuthenticatorWrapper authenticator : mAuthenticators) { + if ((authenticator.modality & mCurrentAuthSession.mModality) != 0) { + try { + authenticator.impl.cancelAuthenticationFromService(token, opPackageName, + callingUid, callingPid, callingUserId, fromClient); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to cancel authentication"); + } } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to cancel authentication"); } } } diff --git a/services/core/java/com/android/server/biometrics/face/FaceAuthenticator.java b/services/core/java/com/android/server/biometrics/face/FaceAuthenticator.java new file mode 100644 index 000000000000..5df5b29d58a8 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/face/FaceAuthenticator.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2019 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.face; + +import android.hardware.biometrics.IBiometricAuthenticator; +import android.hardware.biometrics.IBiometricServiceReceiverInternal; +import android.hardware.face.IFaceService; +import android.os.IBinder; +import android.os.RemoteException; + +/** + * TODO(b/141025588): Add JavaDoc. + */ +public final class FaceAuthenticator extends IBiometricAuthenticator.Stub { + private final IFaceService mFaceService; + + public FaceAuthenticator(IFaceService faceService) { + mFaceService = faceService; + } + + @Override + public void prepareForAuthentication(boolean requireConfirmation, IBinder token, + long sessionId, int userId, IBiometricServiceReceiverInternal wrapperReceiver, + String opPackageName, int cookie, int callingUid, int callingPid, int callingUserId) + throws RemoteException { + mFaceService.prepareForAuthentication(requireConfirmation, token, sessionId, userId, + wrapperReceiver, opPackageName, cookie, callingUid, callingPid, callingUserId); + } + + @Override + public void startPreparedClient(int cookie) throws RemoteException { + mFaceService.startPreparedClient(cookie); + } + + @Override + public void cancelAuthenticationFromService(IBinder token, String opPackageName, int callingUid, + int callingPid, int callingUserId, boolean fromClient) throws RemoteException { + mFaceService.cancelAuthenticationFromService(token, opPackageName, callingUid, callingPid, + callingUserId, fromClient); + } + + @Override + public boolean isHardwareDetected(String opPackageName) throws RemoteException { + return mFaceService.isHardwareDetected(opPackageName); + } + + @Override + public boolean hasEnrolledTemplates(int userId, String opPackageName) throws RemoteException { + return mFaceService.hasEnrolledFaces(userId, opPackageName); + } + + @Override + public void resetLockout(byte[] token) throws RemoteException { + mFaceService.resetLockout(token); + } + + @Override + public void setActiveUser(int uid) throws RemoteException { + mFaceService.setActiveUser(uid); + } +} diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java index 1b132120f709..a0c8e2325f77 100644 --- a/services/core/java/com/android/server/biometrics/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/face/FaceService.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.Manifest.permission.MANAGE_BIOMETRIC; import static android.Manifest.permission.RESET_FACE_LOCKOUT; import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; import android.app.ActivityManager; import android.app.AppOpsManager; @@ -538,7 +539,7 @@ public class FaceService extends BiometricServiceBase { // TODO: refactor out common code here @Override // Binder call - public boolean isHardwareDetected(long deviceId, String opPackageName) { + public boolean isHardwareDetected(String opPackageName) { checkPermission(USE_BIOMETRIC_INTERNAL); if (!canUseBiometric(opPackageName, false /* foregroundOnly */, Binder.getCallingUid(), Binder.getCallingPid(), @@ -752,8 +753,7 @@ public class FaceService extends BiometricServiceBase { public void onError(long deviceId, int error, int vendorCode, int cookie) throws RemoteException { if (getWrapperReceiver() != null) { - getWrapperReceiver().onError(cookie, error, - FaceManager.getErrorString(getContext(), error, vendorCode)); + getWrapperReceiver().onError(cookie, TYPE_FACE, error, vendorCode); } } } diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintAuthenticator.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintAuthenticator.java new file mode 100644 index 000000000000..6150de151ccc --- /dev/null +++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintAuthenticator.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2019 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.fingerprint; + +import android.hardware.biometrics.IBiometricAuthenticator; +import android.hardware.biometrics.IBiometricServiceReceiverInternal; +import android.hardware.fingerprint.IFingerprintService; +import android.os.IBinder; +import android.os.RemoteException; + +/** + * TODO(b/141025588): Add JavaDoc. + */ +public final class FingerprintAuthenticator extends IBiometricAuthenticator.Stub { + private final IFingerprintService mFingerprintService; + + public FingerprintAuthenticator(IFingerprintService fingerprintService) { + mFingerprintService = fingerprintService; + } + + @Override + public void prepareForAuthentication(boolean requireConfirmation, IBinder token, + long sessionId, int userId, IBiometricServiceReceiverInternal wrapperReceiver, + String opPackageName, int cookie, int callingUid, int callingPid, int callingUserId) + throws RemoteException { + mFingerprintService.prepareForAuthentication(token, sessionId, userId, wrapperReceiver, + opPackageName, cookie, callingUid, callingPid, callingUserId); + } + + @Override + public void startPreparedClient(int cookie) throws RemoteException { + mFingerprintService.startPreparedClient(cookie); + } + + @Override + public void cancelAuthenticationFromService(IBinder token, String opPackageName, int callingUid, + int callingPid, int callingUserId, boolean fromClient) throws RemoteException { + mFingerprintService.cancelAuthenticationFromService(token, opPackageName, callingUid, + callingPid, callingUserId, fromClient); + } + + @Override + public boolean isHardwareDetected(String opPackageName) throws RemoteException { + return mFingerprintService.isHardwareDetected(opPackageName); + } + + @Override + public boolean hasEnrolledTemplates(int userId, String opPackageName) throws RemoteException { + return mFingerprintService.hasEnrolledFingerprints(userId, opPackageName); + } + + @Override + public void resetLockout(byte[] token) throws RemoteException { + mFingerprintService.resetTimeout(token); + } + + @Override + public void setActiveUser(int uid) throws RemoteException { + mFingerprintService.setActiveUser(uid); + } +} diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java index d85af2eb0b32..44797ad97b37 100644 --- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java @@ -22,6 +22,7 @@ import static android.Manifest.permission.MANAGE_FINGERPRINT; import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT; import static android.Manifest.permission.USE_BIOMETRIC; import static android.Manifest.permission.USE_FINGERPRINT; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; import android.app.ActivityManager; import android.app.AlarmManager; @@ -350,7 +351,7 @@ public class FingerprintService extends BiometricServiceBase { // TODO: refactor out common code here @Override // Binder call - public boolean isHardwareDetected(long deviceId, String opPackageName) { + public boolean isHardwareDetected(String opPackageName) { if (!canUseBiometric(opPackageName, false /* foregroundOnly */, Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.getCallingUserId())) { @@ -480,8 +481,7 @@ public class FingerprintService extends BiometricServiceBase { public void onError(long deviceId, int error, int vendorCode, int cookie) throws RemoteException { if (getWrapperReceiver() != null) { - getWrapperReceiver().onError(cookie, error, - FingerprintManager.getErrorString(getContext(), error, vendorCode)); + getWrapperReceiver().onError(cookie, TYPE_FINGERPRINT, error, vendorCode); } } } diff --git a/services/core/java/com/android/server/biometrics/iris/IrisAuthenticator.java b/services/core/java/com/android/server/biometrics/iris/IrisAuthenticator.java new file mode 100644 index 000000000000..c44b8e7227e3 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/iris/IrisAuthenticator.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2019 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.iris; + +import android.hardware.biometrics.IBiometricAuthenticator; +import android.hardware.biometrics.IBiometricServiceReceiverInternal; +import android.hardware.iris.IIrisService; +import android.os.IBinder; +import android.os.RemoteException; + +/** + * TODO(b/141025588): Add JavaDoc. + */ +public final class IrisAuthenticator extends IBiometricAuthenticator.Stub { + private final IIrisService mIrisService; + + public IrisAuthenticator(IIrisService irisService) { + mIrisService = irisService; + } + + @Override + public void prepareForAuthentication(boolean requireConfirmation, IBinder token, + long sessionId, int userId, IBiometricServiceReceiverInternal wrapperReceiver, + String opPackageName, int cookie, int callingUid, int callingPid, int callingUserId) + throws RemoteException { + } + + @Override + public void startPreparedClient(int cookie) throws RemoteException { + } + + @Override + public void cancelAuthenticationFromService(IBinder token, String opPackageName, int callingUid, + int callingPid, int callingUserId, boolean fromClient) throws RemoteException { + } + + @Override + public boolean isHardwareDetected(String opPackageName) throws RemoteException { + return false; + } + + @Override + public boolean hasEnrolledTemplates(int userId, String opPackageName) throws RemoteException { + return false; + } + + @Override + public void resetLockout(byte[] token) throws RemoteException { + } + + @Override + public void setActiveUser(int uid) throws RemoteException { + } +} 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 9e88d0c74df3..bb3388cc5b9a 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -53,7 +53,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; -import android.app.AppOpsManager; import android.app.ApplicationPackageManager; import android.app.IActivityManager; import android.content.Context; @@ -1542,7 +1541,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { } }; - final AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class); for (int i = 0; i < permissionCount; i++) { final String permName = pkg.requestedPermissions.get(i); final BasePermission bp; @@ -1608,16 +1606,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { // If this permission was granted by default, make sure it is. if ((oldFlags & FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0) { + // PermissionPolicyService will handle the app op for runtime permissions later. grantRuntimePermissionInternal(permName, packageName, false, Process.SYSTEM_UID, userId, delayingPermCallback); - // Allow app op later as we are holding mPackages - // PermissionPolicyService will handle the app op for foreground/background - // permissions. - String appOp = AppOpsManager.permissionToOp(permName); - if (appOp != null) { - mHandler.post(() -> appOpsManager.setUidMode(appOp, uid, - AppOpsManager.MODE_ALLOWED)); - } // If permission review is enabled the permissions for a legacy apps // are represented as constantly granted runtime ones, so don't revoke. } else if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0) { @@ -4233,8 +4224,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Override - public @NonNull ArrayList<PermissionInfo> getAllPermissionWithProtectionLevel( - @PermissionInfo.Protection int protectionLevel) { + public @NonNull ArrayList<PermissionInfo> getAllPermissionWithProtection( + @PermissionInfo.Protection int protection) { ArrayList<PermissionInfo> matchingPermissions = new ArrayList<>(); synchronized (PermissionManagerService.this.mLock) { @@ -4244,7 +4235,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { BasePermission bp = mSettings.mPermissions.valueAt(i); if (bp.perm != null && bp.perm.info != null - && bp.protectionLevel == protectionLevel) { + && bp.perm.info.getProtection() == protection) { matchingPermissions.add(bp.perm.info); } } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index 04ec5ba04bb6..a807a7e6313c 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -291,8 +291,8 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager public abstract @Nullable BasePermission getPermissionTEMP(@NonNull String permName); /** Get all permission that have a certain protection level */ - public abstract @NonNull ArrayList<PermissionInfo> getAllPermissionWithProtectionLevel( - @PermissionInfo.Protection int protectionLevel); + public abstract @NonNull ArrayList<PermissionInfo> getAllPermissionWithProtection( + @PermissionInfo.Protection int protection); /** * Returns the delegate used to influence permission checking. diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java index b892360ed61e..9b9f93f7b5c4 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyService.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java @@ -48,12 +48,12 @@ import android.os.UserManagerInternal; import android.permission.PermissionControllerManager; import android.provider.Telephony; import android.telecom.TelecomManager; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.LongSparseLongArray; import android.util.Pair; import android.util.Slog; import android.util.SparseBooleanArray; -import android.util.SparseIntArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IAppOpsCallback; @@ -67,6 +67,7 @@ import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.policy.PermissionPolicyInternal.OnInitializedCallback; import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CountDownLatch; /** @@ -144,7 +145,7 @@ public final class PermissionPolicyService extends SystemService { }; final ArrayList<PermissionInfo> dangerousPerms = - permManagerInternal.getAllPermissionWithProtectionLevel( + permManagerInternal.getAllPermissionWithProtection( PermissionInfo.PROTECTION_DANGEROUS); try { @@ -152,7 +153,7 @@ public final class PermissionPolicyService extends SystemService { for (int i = 0; i < numDangerousPerms; i++) { PermissionInfo perm = dangerousPerms.get(i); - if (perm.isRestricted() || perm.backgroundPermission != null) { + if (perm.isRuntime()) { appOpsService.startWatchingMode(getSwitchOp(perm.name), null, appOpsListener); } if (perm.isSoftRestricted()) { @@ -389,8 +390,7 @@ public final class PermissionPolicyService extends SystemService { private final @NonNull PackageManager mPackageManager; private final @NonNull AppOpsManager mAppOpsManager; - /** All uid that need to be synchronized */ - private final @NonNull SparseIntArray mAllUids = new SparseIntArray(); + private final @NonNull ArrayMap<String, PermissionInfo> mRuntimePermissionInfos; /** * All ops that need to be flipped to allow. @@ -428,6 +428,18 @@ public final class PermissionPolicyService extends SystemService { mContext = context; mPackageManager = context.getPackageManager(); mAppOpsManager = context.getSystemService(AppOpsManager.class); + + mRuntimePermissionInfos = new ArrayMap<>(); + PermissionManagerServiceInternal permissionManagerInternal = LocalServices.getService( + PermissionManagerServiceInternal.class); + List<PermissionInfo> permissionInfos = + permissionManagerInternal.getAllPermissionWithProtection( + PermissionInfo.PROTECTION_DANGEROUS); + int permissionInfosSize = permissionInfos.size(); + for (int i = 0; i < permissionInfosSize; i++) { + PermissionInfo permissionInfo = permissionInfos.get(i); + mRuntimePermissionInfos.put(permissionInfo.name, permissionInfo); + } } /** @@ -489,7 +501,7 @@ public final class PermissionPolicyService extends SystemService { * Note: Called with the package lock held. Do <u>not</u> call into app-op manager. */ private void addAppOps(@NonNull PackageInfo packageInfo, @NonNull String permissionName) { - PermissionInfo permissionInfo = getPermissionInfo(permissionName); + PermissionInfo permissionInfo = mRuntimePermissionInfos.get(permissionName); if (permissionInfo == null) { return; } @@ -499,8 +511,7 @@ public final class PermissionPolicyService extends SystemService { private void addPermissionAppOp(@NonNull PackageInfo packageInfo, @NonNull PermissionInfo permissionInfo) { - // TODO: Sync all permissions in the future. - if (!permissionInfo.isRestricted() && permissionInfo.backgroundPermission == null) { + if (!permissionInfo.isRuntime()) { return; } @@ -525,7 +536,7 @@ public final class PermissionPolicyService extends SystemService { boolean shouldGrantAppOp = shouldGrantAppOp(packageInfo, permissionInfo); if (shouldGrantAppOp) { if (permissionInfo.backgroundPermission != null) { - PermissionInfo backgroundPermissionInfo = getPermissionInfo( + PermissionInfo backgroundPermissionInfo = mRuntimePermissionInfos.get( permissionInfo.backgroundPermission); boolean shouldGrantBackgroundAppOp = backgroundPermissionInfo != null && shouldGrantAppOp(packageInfo, backgroundPermissionInfo); @@ -552,15 +563,6 @@ public final class PermissionPolicyService extends SystemService { } } - @Nullable - private PermissionInfo getPermissionInfo(@NonNull String permissionName) { - try { - return mPackageManager.getPermissionInfo(permissionName, 0); - } catch (PackageManager.NameNotFoundException e) { - return null; - } - } - private boolean shouldGrantAppOp(@NonNull PackageInfo packageInfo, @NonNull PermissionInfo permissionInfo) { String permissionName = permissionInfo.name; @@ -638,8 +640,6 @@ public final class PermissionPolicyService extends SystemService { return; } - mAllUids.put(pkg.applicationInfo.uid, pkg.applicationInfo.uid); - if (pkg.requestedPermissions == null) { return; } diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 3c1a6afccda1..489c34359645 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -655,11 +655,11 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } @Override - public void onBiometricAuthenticated(boolean authenticated, String failureReason) { + public void onBiometricAuthenticated() { enforceBiometricDialog(); if (mBar != null) { try { - mBar.onBiometricAuthenticated(authenticated, failureReason); + mBar.onBiometricAuthenticated(); } catch (RemoteException ex) { } } @@ -677,11 +677,11 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } @Override - public void onBiometricError(int errorCode, String error) { + public void onBiometricError(int modality, int error, int vendorCode) { enforceBiometricDialog(); if (mBar != null) { try { - mBar.onBiometricError(errorCode, error); + mBar.onBiometricError(modality, error, vendorCode); } catch (RemoteException ex) { } } diff --git a/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java index 7bdc8a32815a..9dbbf16e7734 100644 --- a/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java +++ b/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java @@ -19,7 +19,7 @@ package com.android.server.timedetector; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AlarmManager; -import android.app.timedetector.TimeSignal; +import android.app.timedetector.PhoneTimeSuggestion; import android.content.Intent; import android.util.Slog; import android.util.TimestampedValue; @@ -48,9 +48,8 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { // @NonNull after initialize() private Callback mCallback; - // NITZ state. - @Nullable private TimestampedValue<Long> mLastNitzTime; - + // Last phone suggestion. + @Nullable private PhoneTimeSuggestion mLastPhoneSuggestion; // Information about the last time signal received: Used when toggling auto-time. @Nullable private TimestampedValue<Long> mLastSystemClockTime; @@ -65,46 +64,40 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { } @Override - public void suggestTime(@NonNull TimeSignal timeSignal) { - if (!TimeSignal.SOURCE_ID_NITZ.equals(timeSignal.getSourceId())) { - Slog.w(TAG, "Ignoring signal from unsupported source: " + timeSignal); - return; - } - + public void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) { // NITZ logic - TimestampedValue<Long> newNitzUtcTime = timeSignal.getUtcTime(); - boolean nitzTimeIsValid = validateNewNitzTime(newNitzUtcTime, mLastNitzTime); - if (!nitzTimeIsValid) { + boolean timeSuggestionIsValid = + validateNewPhoneSuggestion(timeSuggestion, mLastPhoneSuggestion); + if (!timeSuggestionIsValid) { return; } // Always store the last NITZ value received, regardless of whether we go on to use it to // update the system clock. This is so that we can validate future NITZ signals. - mLastNitzTime = newNitzUtcTime; + mLastPhoneSuggestion = timeSuggestion; // System clock update logic. // Historically, Android has sent a telephony broadcast only when setting the time using // NITZ. - final boolean sendNetworkBroadcast = - TimeSignal.SOURCE_ID_NITZ.equals(timeSignal.getSourceId()); + final boolean sendNetworkBroadcast = true; - final TimestampedValue<Long> newUtcTime = newNitzUtcTime; + final TimestampedValue<Long> newUtcTime = timeSuggestion.getUtcTime(); setSystemClockIfRequired(newUtcTime, sendNetworkBroadcast); } - private static boolean validateNewNitzTime(TimestampedValue<Long> newNitzUtcTime, - TimestampedValue<Long> lastNitzTime) { + private static boolean validateNewPhoneSuggestion(@NonNull PhoneTimeSuggestion newSuggestion, + @Nullable PhoneTimeSuggestion lastSuggestion) { - if (lastNitzTime != null) { - long referenceTimeDifference = - TimestampedValue.referenceTimeDifference(newNitzUtcTime, lastNitzTime); + if (lastSuggestion != null) { + long referenceTimeDifference = TimestampedValue.referenceTimeDifference( + newSuggestion.getUtcTime(), lastSuggestion.getUtcTime()); if (referenceTimeDifference < 0 || referenceTimeDifference > Integer.MAX_VALUE) { // Out of order or bogus. Slog.w(TAG, "validateNewNitzTime: Bad NITZ signal received." + " referenceTimeDifference=" + referenceTimeDifference - + " lastNitzTime=" + lastNitzTime - + " newNitzUtcTime=" + newNitzUtcTime); + + " lastSuggestion=" + lastSuggestion + + " newSuggestion=" + newSuggestion); return false; } } @@ -182,7 +175,7 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { @Override public void dump(@NonNull PrintWriter pw, @Nullable String[] args) { - pw.println("mLastNitzTime=" + mLastNitzTime); + pw.println("mLastPhoneSuggestion=" + mLastPhoneSuggestion); pw.println("mLastSystemClockTimeSet=" + mLastSystemClockTimeSet); pw.println("mLastSystemClockTime=" + mLastSystemClockTime); pw.println("mLastSystemClockTimeSendNetworkBroadcast=" diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java index 9c830003cab4..ee42279f7d50 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java @@ -19,7 +19,7 @@ package com.android.server.timedetector; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.timedetector.ITimeDetectorService; -import android.app.timedetector.TimeSignal; +import android.app.timedetector.PhoneTimeSuggestion; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; @@ -96,14 +96,14 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { } @Override - public void suggestTime(@NonNull TimeSignal timeSignal) { + public void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSignal) { enforceSetTimePermission(); Objects.requireNonNull(timeSignal); long idToken = Binder.clearCallingIdentity(); try { synchronized (mStrategyLock) { - mTimeDetectorStrategy.suggestTime(timeSignal); + mTimeDetectorStrategy.suggestPhoneTime(timeSignal); } } finally { Binder.restoreCallingIdentity(idToken); diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java index e050865d96c7..7c2a945854f5 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java @@ -18,7 +18,7 @@ package com.android.server.timedetector; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.timedetector.TimeSignal; +import android.app.timedetector.PhoneTimeSuggestion; import android.content.Intent; import android.util.TimestampedValue; @@ -72,7 +72,7 @@ public interface TimeDetectorStrategy { void initialize(@NonNull Callback callback); /** Process the suggested time. */ - void suggestTime(@NonNull TimeSignal timeSignal); + void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion); /** Handle the auto-time setting being toggled on or off. */ void handleAutoTimeDetectionToggle(boolean enabled); diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index 1bc36bbcd57c..887ece544d3e 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -2086,7 +2086,6 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { final TaskRecord task = r.getTaskRecord(); final ActivityStack stack = task.getStack(); - r.mLaunchTaskBehind = false; mRecentTasks.add(task); mService.getTaskChangeNotificationController().notifyTaskStackChanged(); stack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index 12579e6b4058..b7d25c374a15 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -247,7 +247,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, "startRecentsActivity"); mWindowManager.initializeRecentsAnimation(mTargetActivityType, recentsAnimationRunner, this, mDefaultDisplay.mDisplayId, - mStackSupervisor.mRecentTasks.getRecentTaskIds()); + mStackSupervisor.mRecentTasks.getRecentTaskIds(), targetActivity); // If we updated the launch-behind state, update the visibility of the activities after // we fetch the visible tasks to be controlled by the animation diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 9a58a68995fe..7a3e43b127f4 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -358,7 +358,8 @@ public class RecentsAnimationController implements DeathRecipient { * because it may call cancelAnimation() which needs to properly clean up the controller * in the window manager. */ - public void initialize(int targetActivityType, SparseBooleanArray recentTaskIds) { + public void initialize(int targetActivityType, SparseBooleanArray recentTaskIds, + ActivityRecord targetActivity) { mTargetActivityType = targetActivityType; mDisplayContent.mAppTransition.registerListenerLocked(mAppTransitionListener); @@ -400,16 +401,12 @@ public class RecentsAnimationController implements DeathRecipient { } // Adjust the wallpaper visibility for the showing target activity - final ActivityRecord recentsComponentActivity = - targetStack.getTopChild().getTopFullscreenActivity(); - if (recentsComponentActivity != null) { - ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, - "setHomeApp(%s)", recentsComponentActivity.getName()); - mTargetActivityRecord = recentsComponentActivity; - if (recentsComponentActivity.windowsCanBeWallpaperTarget()) { - mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; - mDisplayContent.setLayoutNeeded(); - } + ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, + "setHomeApp(%s)", targetActivity.getName()); + mTargetActivityRecord = targetActivity; + if (targetActivity.windowsCanBeWallpaperTarget()) { + mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; + mDisplayContent.setLayoutNeeded(); } // Save the minimized home height diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index e90f3da96409..63ce1b1c7ad7 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2652,11 +2652,11 @@ public class WindowManagerService extends IWindowManager.Stub void initializeRecentsAnimation(int targetActivityType, IRecentsAnimationRunner recentsAnimationRunner, RecentsAnimationController.RecentsAnimationCallbacks callbacks, int displayId, - SparseBooleanArray recentTaskIds) { + SparseBooleanArray recentTaskIds, ActivityRecord targetActivity) { mRecentsAnimationController = new RecentsAnimationController(this, recentsAnimationRunner, callbacks, displayId); mRoot.getDisplayContent(displayId).mAppTransition.updateBooster(); - mRecentsAnimationController.initialize(targetActivityType, recentTaskIds); + mRecentsAnimationController.initialize(targetActivityType, recentTaskIds, targetActivity); } @VisibleForTesting 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 4aeeb0af1bf8..ec47a959de30 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java @@ -33,7 +33,6 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.AppOpsManager; import android.app.IActivityManager; import android.content.ContentResolver; import android.content.Context; @@ -43,17 +42,15 @@ import android.hardware.biometrics.Authenticator; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricPrompt; +import android.hardware.biometrics.IBiometricAuthenticator; import android.hardware.biometrics.IBiometricService; import android.hardware.biometrics.IBiometricServiceReceiver; import android.hardware.biometrics.IBiometricServiceReceiverInternal; -import android.hardware.face.FaceManager; -import android.hardware.face.IFaceService; import android.hardware.fingerprint.FingerprintManager; -import android.hardware.fingerprint.IFingerprintService; import android.os.Binder; import android.os.Bundle; -import android.os.Handler; import android.os.IBinder; +import android.os.RemoteException; import android.security.KeyStore; import androidx.test.InstrumentationRegistry; @@ -68,8 +65,6 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.util.List; - @SmallTest public class BiometricServiceTest { @@ -98,71 +93,33 @@ public class BiometricServiceTest { @Mock private PackageManager mPackageManager; @Mock - private AppOpsManager mAppOpsManager; - @Mock IBiometricServiceReceiver mReceiver1; @Mock IBiometricServiceReceiver mReceiver2; @Mock - FingerprintManager mFingerprintManager; + BiometricService.Injector mInjector; @Mock - FaceManager mFaceManager; - - private static class MockInjector extends BiometricService.Injector { - @Override - IActivityManager getActivityManagerService() { - return mock(IActivityManager.class); - } - - @Override - IStatusBarService getStatusBarService() { - return mock(IStatusBarService.class); - } - - @Override - IFingerprintService getFingerprintService() { - return mock(IFingerprintService.class); - } - - @Override - IFaceService getFaceService() { - return mock(IFaceService.class); - } - - @Override - BiometricService.SettingObserver getSettingObserver(Context context, Handler handler, - List<BiometricService.EnabledOnKeyguardCallback> callbacks) { - return mock(BiometricService.SettingObserver.class); - } - - @Override - KeyStore getKeyStore() { - return mock(KeyStore.class); - } - - @Override - boolean isDebugEnabled(Context context, int userId) { - return false; - } - - @Override - void publishBinderService(BiometricService service, IBiometricService.Stub impl) { - // no-op for test - } - } + IBiometricAuthenticator mFingerprintAuthenticator; + @Mock + IBiometricAuthenticator mFaceAuthenticator; @Before public void setUp() { MockitoAnnotations.initMocks(this); when(mContext.getPackageManager()).thenReturn(mPackageManager); - when(mContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager); - when(mContext.getSystemService(Context.FINGERPRINT_SERVICE)) - .thenReturn(mFingerprintManager); - when(mContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mFaceManager); when(mContext.getContentResolver()).thenReturn(mContentResolver); when(mContext.getResources()).thenReturn(mResources); + when(mInjector.getActivityManagerService()).thenReturn(mock(IActivityManager.class)); + when(mInjector.getStatusBarService()).thenReturn(mock(IStatusBarService.class)); + when(mInjector.getFingerprintAuthenticator()).thenReturn(mFingerprintAuthenticator); + when(mInjector.getFaceAuthenticator()).thenReturn(mFaceAuthenticator); + when(mInjector.getSettingObserver(any(), any(), any())).thenReturn( + mock(BiometricService.SettingObserver.class)); + when(mInjector.getKeyStore()).thenReturn(mock(KeyStore.class)); + when(mInjector.isDebugEnabled(any(), anyInt())).thenReturn(false); + when(mResources.getString(R.string.biometric_error_hw_unavailable)) .thenReturn(ERROR_HW_UNAVAILABLE); when(mResources.getString(R.string.biometric_not_recognized)) @@ -172,61 +129,69 @@ public class BiometricServiceTest { } @Test - public void testAuthenticate_withoutHardware_returnsErrorHardwareNotPresent() throws Exception { + public void testAuthenticate_withoutHardware_returnsErrorHardwareNotPresent() throws + Exception { when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) .thenReturn(false); when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_IRIS)).thenReturn(false); when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(false); - mBiometricService = new BiometricService(mContext, new MockInjector()); + mBiometricService = new BiometricService(mContext, mInjector); mBiometricService.onStart(); invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, false /* allowDeviceCredential */); waitForIdle(); verify(mReceiver1).onError( - eq(BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT), eq(ERROR_HW_UNAVAILABLE)); + eq(BiometricAuthenticator.TYPE_NONE), + eq(BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT), + eq(0 /* vendorCode */)); } @Test public void testAuthenticate_withoutEnrolled_returnsErrorNoBiometrics() throws Exception { when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true); - when(mFingerprintManager.isHardwareDetected()).thenReturn(true); + when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(true); - mBiometricService = new BiometricService(mContext, new MockInjector()); + mBiometricService = new BiometricService(mContext, mInjector); mBiometricService.onStart(); invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, false /* allowDeviceCredential */); waitForIdle(); verify(mReceiver1).onError( - eq(BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS), any()); + eq(BiometricAuthenticator.TYPE_FINGERPRINT), + eq(BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS), + eq(0 /* vendorCode */)); } @Test - public void testAuthenticate_whenHalIsDead_returnsErrorHardwareUnavailable() throws Exception { + public void testAuthenticate_whenHalIsDead_returnsErrorHardwareUnavailable() throws + Exception { when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true); - when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true); - when(mFingerprintManager.isHardwareDetected()).thenReturn(false); + when(mFingerprintAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true); + when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(false); - mBiometricService = new BiometricService(mContext, new MockInjector()); + mBiometricService = new BiometricService(mContext, mInjector); mBiometricService.onStart(); invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, false /* allowDeviceCredential */); waitForIdle(); verify(mReceiver1).onError( - eq(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE), eq(ERROR_HW_UNAVAILABLE)); + eq(BiometricAuthenticator.TYPE_NONE), + eq(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE), + eq(0 /* vendorCode */)); } @Test public void testAuthenticateFace_respectsUserSetting() throws Exception { when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true); - when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true); - when(mFaceManager.isHardwareDetected()).thenReturn(true); + when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true); + when(mFaceAuthenticator.isHardwareDetected(any())).thenReturn(true); - mBiometricService = new BiometricService(mContext, new MockInjector()); + mBiometricService = new BiometricService(mContext, mInjector); mBiometricService.onStart(); // Disabled in user settings receives onError @@ -235,7 +200,9 @@ public class BiometricServiceTest { false /* allowDeviceCredential */); waitForIdle(); verify(mReceiver1).onError( - eq(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE), eq(ERROR_HW_UNAVAILABLE)); + eq(BiometricAuthenticator.TYPE_NONE), + eq(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE), + eq(0 /* vendorCode */)); // Enrolled, not disabled in settings, user requires confirmation in settings resetReceiver(); @@ -245,8 +212,8 @@ public class BiometricServiceTest { invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, false /* allowDeviceCredential */); waitForIdle(); - verify(mReceiver1, never()).onError(anyInt(), any(String.class)); - verify(mBiometricService.mFaceService).prepareForAuthentication( + verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt()); + verify(mBiometricService.mAuthenticators.get(0).impl).prepareForAuthentication( eq(true) /* requireConfirmation */, any(IBinder.class), anyLong() /* sessionId */, @@ -265,7 +232,7 @@ public class BiometricServiceTest { invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, false /* allowDeviceCredential */); waitForIdle(); - verify(mBiometricService.mFaceService).prepareForAuthentication( + verify(mBiometricService.mAuthenticators.get(0).impl).prepareForAuthentication( eq(false) /* requireConfirmation */, any(IBinder.class), anyLong() /* sessionId */, @@ -281,7 +248,7 @@ public class BiometricServiceTest { @Test public void testAuthenticate_happyPathWithoutConfirmation() throws Exception { setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT); - mBiometricService = new BiometricService(mContext, new MockInjector()); + mBiometricService = new BiometricService(mContext, mInjector); mBiometricService.onStart(); // Start testing the happy path @@ -295,8 +262,9 @@ public class BiometricServiceTest { // Invokes <Modality>Service#prepareForAuthentication ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class); - verify(mReceiver1, never()).onError(anyInt(), any(String.class)); - verify(mBiometricService.mFingerprintService).prepareForAuthentication( + verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt()); + verify(mBiometricService.mAuthenticators.get(0).impl).prepareForAuthentication( + anyBoolean() /* requireConfirmation */, any(IBinder.class), anyLong() /* sessionId */, anyInt() /* userId */, @@ -316,7 +284,7 @@ public class BiometricServiceTest { BiometricService.STATE_AUTH_STARTED); // startPreparedClient invoked - verify(mBiometricService.mFingerprintService) + verify(mBiometricService.mAuthenticators.get(0).impl) .startPreparedClient(cookieCaptor.getValue()); // StatusBar showBiometricDialog invoked @@ -337,8 +305,7 @@ public class BiometricServiceTest { assertEquals(mBiometricService.mCurrentAuthSession.mState, BiometricService.STATE_AUTHENTICATED_PENDING_SYSUI); // Notify SystemUI hardware authenticated - verify(mBiometricService.mStatusBarService).onBiometricAuthenticated( - eq(true) /* authenticated */, eq(null) /* failureReason */); + verify(mBiometricService.mStatusBarService).onBiometricAuthenticated(); // SystemUI sends callback with dismissed reason mBiometricService.mInternalReceiver.onDialogDismissed( @@ -355,7 +322,7 @@ public class BiometricServiceTest { @Test public void testAuthenticate_noBiometrics_credentialAllowed() throws Exception { setupAuthForOnly(BiometricAuthenticator.TYPE_FACE); - when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false); + when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(false); invokeAuthenticate(mBiometricService.mImpl, mReceiver1, true /* requireConfirmation */, true /* allowDeviceCredential */); waitForIdle(); @@ -409,8 +376,10 @@ public class BiometricServiceTest { mBiometricService.mInternalReceiver.onAuthenticationFailed(); waitForIdle(); - verify(mBiometricService.mStatusBarService) - .onBiometricAuthenticated(eq(false), eq(ERROR_NOT_RECOGNIZED)); + verify(mBiometricService.mStatusBarService).onBiometricError( + eq(BiometricAuthenticator.TYPE_NONE), + eq(BiometricConstants.BIOMETRIC_PAUSED_REJECTED), + eq(0 /* vendorCode */)); verify(mReceiver1).onAuthenticationFailed(); assertEquals(mBiometricService.mCurrentAuthSession.mState, BiometricService.STATE_AUTH_PAUSED); @@ -426,15 +395,18 @@ public class BiometricServiceTest { mBiometricService.mInternalReceiver.onAuthenticationFailed(); waitForIdle(); - verify(mBiometricService.mStatusBarService) - .onBiometricAuthenticated(eq(false), eq(ERROR_NOT_RECOGNIZED)); + verify(mBiometricService.mStatusBarService).onBiometricError( + eq(BiometricAuthenticator.TYPE_NONE), + eq(BiometricConstants.BIOMETRIC_PAUSED_REJECTED), + eq(0 /* vendorCode */)); verify(mReceiver1).onAuthenticationFailed(); assertEquals(mBiometricService.mCurrentAuthSession.mState, BiometricService.STATE_AUTH_STARTED); } @Test - public void testErrorCanceled_whenAuthenticating_notifiesSystemUIAndClient() throws Exception { + public void testErrorCanceled_whenAuthenticating_notifiesSystemUIAndClient() throws + Exception { setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT); invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, false /* allowDeviceCredential */); @@ -451,15 +423,15 @@ public class BiometricServiceTest { BiometricService.STATE_AUTH_STARTED); mBiometricService.mInternalReceiver.onError( getCookieForCurrentSession(mBiometricService.mCurrentAuthSession), - BiometricConstants.BIOMETRIC_ERROR_CANCELED, ERROR_CANCELED); + BiometricAuthenticator.TYPE_FINGERPRINT, + BiometricConstants.BIOMETRIC_ERROR_CANCELED, 0 /* vendorCode */); waitForIdle(); // Auth session doesn't become null until SystemUI responds that the animation is completed assertNotNull(mBiometricService.mCurrentAuthSession); // ERROR_CANCELED is not sent until SystemUI responded that animation is completed - verify(mReceiver1, never()).onError( - anyInt(), anyString()); - verify(mReceiver2, never()).onError(anyInt(), any(String.class)); + verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt()); + verify(mReceiver2, never()).onError(anyInt(), anyInt(), anyInt()); // SystemUI dialog closed verify(mBiometricService.mStatusBarService).hideAuthenticationDialog(); @@ -469,8 +441,9 @@ public class BiometricServiceTest { .onDialogDismissed(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED); waitForIdle(); verify(mReceiver1).onError( + eq(BiometricAuthenticator.TYPE_FINGERPRINT), eq(BiometricConstants.BIOMETRIC_ERROR_CANCELED), - eq(ERROR_CANCELED)); + eq(0 /* vendorCode */)); assertNull(mBiometricService.mCurrentAuthSession); } @@ -482,14 +455,17 @@ public class BiometricServiceTest { mBiometricService.mInternalReceiver.onError( getCookieForCurrentSession(mBiometricService.mCurrentAuthSession), + BiometricAuthenticator.TYPE_FACE, BiometricConstants.BIOMETRIC_ERROR_TIMEOUT, - ERROR_TIMEOUT); + 0 /* vendorCode */); waitForIdle(); assertEquals(mBiometricService.mCurrentAuthSession.mState, BiometricService.STATE_AUTH_PAUSED); - verify(mBiometricService.mStatusBarService) - .onBiometricAuthenticated(eq(false), eq(ERROR_TIMEOUT)); + verify(mBiometricService.mStatusBarService).onBiometricError( + eq(BiometricAuthenticator.TYPE_FACE), + eq(BiometricConstants.BIOMETRIC_ERROR_TIMEOUT), + eq(0 /* vendorCode */)); // Timeout does not count as fail as per BiometricPrompt documentation. verify(mReceiver1, never()).onAuthenticationFailed(); @@ -524,22 +500,25 @@ public class BiometricServiceTest { public void testErrorFromHal_whenPaused_notifiesSystemUIAndClient() throws Exception { setupAuthForOnly(BiometricAuthenticator.TYPE_FACE); invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1, - false /* requireCOnfirmation */, false /* allowDeviceCredential */); + false /* requireConfirmation */, false /* allowDeviceCredential */); mBiometricService.mInternalReceiver.onError( getCookieForCurrentSession(mBiometricService.mCurrentAuthSession), + BiometricAuthenticator.TYPE_FACE, BiometricConstants.BIOMETRIC_ERROR_TIMEOUT, - ERROR_TIMEOUT); + 0 /* vendorCode */); mBiometricService.mInternalReceiver.onError( getCookieForCurrentSession(mBiometricService.mCurrentAuthSession), + BiometricAuthenticator.TYPE_FACE, BiometricConstants.BIOMETRIC_ERROR_CANCELED, - ERROR_CANCELED); + 0 /* vendorCode */); waitForIdle(); // Client receives error immediately verify(mReceiver1).onError( + eq(BiometricAuthenticator.TYPE_FACE), eq(BiometricConstants.BIOMETRIC_ERROR_CANCELED), - eq(ERROR_CANCELED)); + eq(0 /* vendorCode */)); // Dialog is hidden immediately verify(mBiometricService.mStatusBarService).hideAuthenticationDialog(); // Auth session is over @@ -558,26 +537,29 @@ public class BiometricServiceTest { mBiometricService.mInternalReceiver.onError( getCookieForCurrentSession(mBiometricService.mCurrentAuthSession), + BiometricAuthenticator.TYPE_FINGERPRINT, BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_PROCESS, - ERROR_UNABLE_TO_PROCESS); + 0 /* vendorCode */); waitForIdle(); // Sends error to SystemUI and does not notify client yet assertEquals(mBiometricService.mCurrentAuthSession.mState, BiometricService.STATE_ERROR_PENDING_SYSUI); verify(mBiometricService.mStatusBarService).onBiometricError( + eq(BiometricAuthenticator.TYPE_FINGERPRINT), eq(BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_PROCESS), - eq(ERROR_UNABLE_TO_PROCESS)); + eq(0 /* vendorCode */)); verify(mBiometricService.mStatusBarService, never()).hideAuthenticationDialog(); - verify(mReceiver1, never()).onError(anyInt(), anyString()); + verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt()); // SystemUI animation completed, client is notified, auth session is over mBiometricService.mInternalReceiver .onDialogDismissed(BiometricPrompt.DISMISSED_REASON_ERROR); waitForIdle(); verify(mReceiver1).onError( + eq(BiometricAuthenticator.TYPE_FINGERPRINT), eq(BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_PROCESS), - eq(ERROR_UNABLE_TO_PROCESS)); + eq(0 /* vendorCode */)); assertNull(mBiometricService.mCurrentAuthSession); } @@ -590,8 +572,9 @@ public class BiometricServiceTest { mBiometricService.mInternalReceiver.onError( getCookieForPendingSession(mBiometricService.mPendingAuthSession), + BiometricAuthenticator.TYPE_FACE, BiometricConstants.BIOMETRIC_ERROR_LOCKOUT, - ERROR_LOCKOUT); + 0 /* vendorCode */); waitForIdle(); // Pending auth session becomes current auth session, since device credential should @@ -622,8 +605,9 @@ public class BiometricServiceTest { mBiometricService.mInternalReceiver.onError( getCookieForPendingSession(mBiometricService.mPendingAuthSession), + BiometricAuthenticator.TYPE_FINGERPRINT, BiometricConstants.BIOMETRIC_ERROR_LOCKOUT, - ERROR_LOCKOUT); + 0 /* vendorCode */); waitForIdle(); // Error is sent to client @@ -690,17 +674,18 @@ public class BiometricServiceTest { assertEquals(BiometricService.STATE_SHOWING_DEVICE_CREDENTIAL, mBiometricService.mCurrentAuthSession.mState); - verify(mReceiver1, never()).onError(anyInt(), anyString()); + verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt()); mBiometricService.mInternalReceiver.onError( getCookieForCurrentSession(mBiometricService.mCurrentAuthSession), + BiometricAuthenticator.TYPE_FINGERPRINT, BiometricConstants.BIOMETRIC_ERROR_CANCELED, - ERROR_CANCELED); + 0 /* vendorCode */); waitForIdle(); assertEquals(BiometricService.STATE_SHOWING_DEVICE_CREDENTIAL, mBiometricService.mCurrentAuthSession.mState); - verify(mReceiver1, never()).onError(anyInt(), anyString()); + verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt()); } @Test @@ -714,15 +699,17 @@ public class BiometricServiceTest { mBiometricService.mInternalReceiver.onError( getCookieForCurrentSession(mBiometricService.mCurrentAuthSession), + BiometricAuthenticator.TYPE_FINGERPRINT, BiometricConstants.BIOMETRIC_ERROR_LOCKOUT, - ERROR_LOCKOUT); + 0 /* vendorCode */); waitForIdle(); assertEquals(BiometricService.STATE_SHOWING_DEVICE_CREDENTIAL, mBiometricService.mCurrentAuthSession.mState); verify(mBiometricService.mStatusBarService).onBiometricError( + eq(BiometricAuthenticator.TYPE_FINGERPRINT), eq(BiometricConstants.BIOMETRIC_ERROR_LOCKOUT), - eq(ERROR_LOCKOUT)); + eq(0 /* vendorCode */)); } @Test @@ -736,15 +723,17 @@ public class BiometricServiceTest { mBiometricService.mInternalReceiver.onError( getCookieForCurrentSession(mBiometricService.mCurrentAuthSession), + BiometricAuthenticator.TYPE_FINGERPRINT, BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_PROCESS, - ERROR_UNABLE_TO_PROCESS); + 0 /* vendorCode */); waitForIdle(); assertEquals(BiometricService.STATE_ERROR_PENDING_SYSUI, mBiometricService.mCurrentAuthSession.mState); verify(mBiometricService.mStatusBarService).onBiometricError( + eq(BiometricAuthenticator.TYPE_FINGERPRINT), eq(BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_PROCESS), - eq(ERROR_UNABLE_TO_PROCESS)); + eq(0 /* vendorCode */)); } @Test @@ -758,9 +747,10 @@ public class BiometricServiceTest { .onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL); waitForIdle(); verify(mReceiver1).onError( + eq(BiometricAuthenticator.TYPE_FINGERPRINT), eq(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED), - eq(ERROR_USER_CANCELED)); - verify(mBiometricService.mFingerprintService).cancelAuthenticationFromService( + eq(0 /* vendorCode */)); + verify(mBiometricService.mAuthenticators.get(0).impl).cancelAuthenticationFromService( any(), any(), anyInt(), @@ -778,13 +768,15 @@ public class BiometricServiceTest { mBiometricService.mInternalReceiver.onError( getCookieForCurrentSession(mBiometricService.mCurrentAuthSession), + BiometricAuthenticator.TYPE_FACE, BiometricConstants.BIOMETRIC_ERROR_TIMEOUT, - ERROR_TIMEOUT); + 0 /* vendorCode */); mBiometricService.mInternalReceiver.onDialogDismissed( BiometricPrompt.DISMISSED_REASON_NEGATIVE); waitForIdle(); - verify(mBiometricService.mFaceService, never()).cancelAuthenticationFromService( + verify(mBiometricService.mAuthenticators.get(0).impl, + never()).cancelAuthenticationFromService( any(), any(), anyInt(), @@ -794,20 +786,23 @@ public class BiometricServiceTest { } @Test - public void testDismissedReasonUserCancel_whilePaused_doesntInvokeHalCancel() throws Exception { + public void testDismissedReasonUserCancel_whilePaused_doesntInvokeHalCancel() throws + Exception { setupAuthForOnly(BiometricAuthenticator.TYPE_FACE); invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, false /* allowDeviceCredential */); mBiometricService.mInternalReceiver.onError( getCookieForCurrentSession(mBiometricService.mCurrentAuthSession), + BiometricAuthenticator.TYPE_FACE, BiometricConstants.BIOMETRIC_ERROR_TIMEOUT, - ERROR_TIMEOUT); + 0 /* vendorCode */); mBiometricService.mInternalReceiver.onDialogDismissed( BiometricPrompt.DISMISSED_REASON_USER_CANCEL); waitForIdle(); - verify(mBiometricService.mFaceService, never()).cancelAuthenticationFromService( + verify(mBiometricService.mAuthenticators.get(0).impl, + never()).cancelAuthenticationFromService( any(), any(), anyInt(), @@ -830,7 +825,8 @@ public class BiometricServiceTest { waitForIdle(); // doesn't send cancel to HAL - verify(mBiometricService.mFaceService, never()).cancelAuthenticationFromService( + verify(mBiometricService.mAuthenticators.get(0).impl, + never()).cancelAuthenticationFromService( any(), any(), anyInt(), @@ -838,8 +834,9 @@ public class BiometricServiceTest { anyInt(), anyBoolean()); verify(mReceiver1).onError( + eq(BiometricAuthenticator.TYPE_FACE), eq(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED), - eq(ERROR_USER_CANCELED)); + eq(0 /* vendorCode */)); assertNull(mBiometricService.mCurrentAuthSession); } @@ -863,7 +860,7 @@ public class BiometricServiceTest { // Helper methods - private void setupAuthForOnly(int modality) { + private void setupAuthForOnly(int modality) throws RemoteException { when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) .thenReturn(false); when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(false); @@ -871,17 +868,17 @@ public class BiometricServiceTest { if (modality == BiometricAuthenticator.TYPE_FINGERPRINT) { when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) .thenReturn(true); - when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true); - when(mFingerprintManager.isHardwareDetected()).thenReturn(true); + when(mFingerprintAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true); + when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(true); } else if (modality == BiometricAuthenticator.TYPE_FACE) { when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true); - when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true); - when(mFaceManager.isHardwareDetected()).thenReturn(true); + when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true); + when(mFaceAuthenticator.isHardwareDetected(any())).thenReturn(true); } else { fail("Unknown modality: " + modality); } - mBiometricService = new BiometricService(mContext, new MockInjector()); + mBiometricService = new BiometricService(mContext, mInjector); mBiometricService.onStart(); when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(true); diff --git a/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java b/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java index 9e000770fe42..d79795593456 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java @@ -23,7 +23,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import android.app.timedetector.TimeSignal; +import android.app.timedetector.PhoneTimeSuggestion; import android.content.Intent; import android.icu.util.Calendar; import android.icu.util.GregorianCalendar; @@ -45,6 +45,8 @@ public class SimpleTimeZoneDetectorStrategyTest { .setActualTimeUtc(2018, 1, 1, 12, 0, 0) .build(); + private static final int ARBITRARY_PHONE_ID = 123456; + private Script mScript; @Before @@ -53,30 +55,32 @@ public class SimpleTimeZoneDetectorStrategyTest { } @Test - public void testSuggestTime_nitz_timeDetectionEnabled() { + public void testSuggestPhoneTime_nitz_timeDetectionEnabled() { Scenario scenario = SCENARIO_1; mScript.pokeFakeClocks(scenario) .pokeTimeDetectionEnabled(true); - TimeSignal timeSignal = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ); + PhoneTimeSuggestion timeSuggestion = + scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID); final int clockIncrement = 1000; long expectSystemClockMillis = scenario.getActualTimeMillis() + clockIncrement; mScript.simulateTimePassing(clockIncrement) - .simulateTimeSignalReceived(timeSignal) + .simulatePhoneTimeSuggestion(timeSuggestion) .verifySystemClockWasSetAndResetCallTracking(expectSystemClockMillis); } @Test - public void testSuggestTime_systemClockThreshold() { + public void testSuggestPhoneTime_systemClockThreshold() { Scenario scenario = SCENARIO_1; final int systemClockUpdateThresholdMillis = 1000; mScript.pokeFakeClocks(scenario) .pokeThresholds(systemClockUpdateThresholdMillis) .pokeTimeDetectionEnabled(true); - TimeSignal timeSignal1 = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ); - TimestampedValue<Long> utcTime1 = timeSignal1.getUtcTime(); + PhoneTimeSuggestion timeSuggestion1 = + scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID); + TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime(); final int clockIncrement = 100; // Increment the the device clocks to simulate the passage of time. @@ -86,7 +90,7 @@ public class SimpleTimeZoneDetectorStrategyTest { TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis()); // Send the first time signal. It should be used. - mScript.simulateTimeSignalReceived(timeSignal1) + mScript.simulatePhoneTimeSuggestion(timeSuggestion1) .verifySystemClockWasSetAndResetCallTracking(expectSystemClockMillis1); // Now send another time signal, but one that is too similar to the last one and should be @@ -95,9 +99,9 @@ public class SimpleTimeZoneDetectorStrategyTest { TimestampedValue<Long> utcTime2 = new TimestampedValue<>( mScript.peekElapsedRealtimeMillis(), mScript.peekSystemClockMillis() + underThresholdMillis); - TimeSignal timeSignal2 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime2); + PhoneTimeSuggestion timeSuggestion2 = new PhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime2); mScript.simulateTimePassing(clockIncrement) - .simulateTimeSignalReceived(timeSignal2) + .simulatePhoneTimeSuggestion(timeSuggestion2) .verifySystemClockWasNotSetAndResetCallTracking(); // Now send another time signal, but one that is on the threshold and so should be used. @@ -105,42 +109,44 @@ public class SimpleTimeZoneDetectorStrategyTest { mScript.peekElapsedRealtimeMillis(), mScript.peekSystemClockMillis() + systemClockUpdateThresholdMillis); - TimeSignal timeSignal3 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime3); + PhoneTimeSuggestion timeSuggestion3 = new PhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime3); mScript.simulateTimePassing(clockIncrement); long expectSystemClockMillis3 = TimeDetectorStrategy.getTimeAt(utcTime3, mScript.peekElapsedRealtimeMillis()); - mScript.simulateTimeSignalReceived(timeSignal3) + mScript.simulatePhoneTimeSuggestion(timeSuggestion3) .verifySystemClockWasSetAndResetCallTracking(expectSystemClockMillis3); } @Test - public void testSuggestTime_nitz_timeDetectionDisabled() { + public void testSuggestPhoneTime_nitz_timeDetectionDisabled() { Scenario scenario = SCENARIO_1; mScript.pokeFakeClocks(scenario) .pokeTimeDetectionEnabled(false); - TimeSignal timeSignal = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ); - mScript.simulateTimeSignalReceived(timeSignal) + PhoneTimeSuggestion timeSuggestion = + scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID); + mScript.simulatePhoneTimeSuggestion(timeSuggestion) .verifySystemClockWasNotSetAndResetCallTracking(); } @Test - public void testSuggestTime_nitz_invalidNitzReferenceTimesIgnored() { + public void testSuggestPhoneTime_nitz_invalidNitzReferenceTimesIgnored() { Scenario scenario = SCENARIO_1; final int systemClockUpdateThreshold = 2000; mScript.pokeFakeClocks(scenario) .pokeThresholds(systemClockUpdateThreshold) .pokeTimeDetectionEnabled(true); - TimeSignal timeSignal1 = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ); - TimestampedValue<Long> utcTime1 = timeSignal1.getUtcTime(); + PhoneTimeSuggestion timeSuggestion1 = + scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID); + TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime(); // Initialize the strategy / device with a time set from NITZ. mScript.simulateTimePassing(100); long expectedSystemClockMillis1 = TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis()); - mScript.simulateTimeSignalReceived(timeSignal1) + mScript.simulatePhoneTimeSuggestion(timeSuggestion1) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1); // The UTC time increment should be larger than the system clock update threshold so we @@ -152,8 +158,8 @@ public class SimpleTimeZoneDetectorStrategyTest { long referenceTimeBeforeLastSignalMillis = utcTime1.getReferenceTimeMillis() - 1; TimestampedValue<Long> utcTime2 = new TimestampedValue<>( referenceTimeBeforeLastSignalMillis, validUtcTimeMillis); - TimeSignal timeSignal2 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime2); - mScript.simulateTimeSignalReceived(timeSignal2) + PhoneTimeSuggestion timeSuggestion2 = new PhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime2); + mScript.simulatePhoneTimeSuggestion(timeSuggestion2) .verifySystemClockWasNotSetAndResetCallTracking(); // Now supply a new signal that has an obviously bogus reference time : substantially in the @@ -162,8 +168,8 @@ public class SimpleTimeZoneDetectorStrategyTest { utcTime1.getReferenceTimeMillis() + Integer.MAX_VALUE + 1; TimestampedValue<Long> utcTime3 = new TimestampedValue<>( referenceTimeInFutureMillis, validUtcTimeMillis); - TimeSignal timeSignal3 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime3); - mScript.simulateTimeSignalReceived(timeSignal3) + PhoneTimeSuggestion timeSuggestion3 = new PhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime3); + mScript.simulatePhoneTimeSuggestion(timeSuggestion3) .verifySystemClockWasNotSetAndResetCallTracking(); // Just to prove validUtcTimeMillis is valid. @@ -172,13 +178,13 @@ public class SimpleTimeZoneDetectorStrategyTest { validReferenceTimeMillis, validUtcTimeMillis); long expectedSystemClockMillis4 = TimeDetectorStrategy.getTimeAt(utcTime4, mScript.peekElapsedRealtimeMillis()); - TimeSignal timeSignal4 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime4); - mScript.simulateTimeSignalReceived(timeSignal4) + PhoneTimeSuggestion timeSuggestion4 = new PhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime4); + mScript.simulatePhoneTimeSuggestion(timeSuggestion4) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis4); } @Test - public void testSuggestTime_timeDetectionToggled() { + public void testSuggestPhoneTime_timeDetectionToggled() { Scenario scenario = SCENARIO_1; final int clockIncrementMillis = 100; final int systemClockUpdateThreshold = 2000; @@ -186,15 +192,16 @@ public class SimpleTimeZoneDetectorStrategyTest { .pokeThresholds(systemClockUpdateThreshold) .pokeTimeDetectionEnabled(false); - TimeSignal timeSignal1 = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ); - TimestampedValue<Long> utcTime1 = timeSignal1.getUtcTime(); + PhoneTimeSuggestion timeSuggestion1 = + scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID); + TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime(); // Simulate time passing. mScript.simulateTimePassing(clockIncrementMillis); // Simulate the time signal being received. It should not be used because auto time // detection is off but it should be recorded. - mScript.simulateTimeSignalReceived(timeSignal1) + mScript.simulatePhoneTimeSuggestion(timeSuggestion1) .verifySystemClockWasNotSetAndResetCallTracking(); // Simulate more time passing. @@ -216,7 +223,7 @@ public class SimpleTimeZoneDetectorStrategyTest { TimestampedValue<Long> utcTime2 = new TimestampedValue<>( mScript.peekElapsedRealtimeMillis(), mScript.peekSystemClockMillis() + systemClockUpdateThreshold); - TimeSignal timeSignal2 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime2); + PhoneTimeSuggestion timeSuggestion2 = new PhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime2); // Simulate more time passing. mScript.simulateTimePassing(clockIncrementMillis); @@ -226,7 +233,7 @@ public class SimpleTimeZoneDetectorStrategyTest { // The new time, though valid, should not be set in the system clock because auto time is // disabled. - mScript.simulateTimeSignalReceived(timeSignal2) + mScript.simulatePhoneTimeSuggestion(timeSuggestion2) .verifySystemClockWasNotSetAndResetCallTracking(); // Turn on auto time detection. @@ -234,17 +241,6 @@ public class SimpleTimeZoneDetectorStrategyTest { .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis2); } - @Test - public void testSuggestTime_unknownSource() { - Scenario scenario = SCENARIO_1; - mScript.pokeFakeClocks(scenario) - .pokeTimeDetectionEnabled(true); - - TimeSignal timeSignal = scenario.createTimeSignalForActual("unknown"); - mScript.simulateTimeSignalReceived(timeSignal) - .verifySystemClockWasNotSetAndResetCallTracking(); - } - /** * A fake implementation of TimeDetectorStrategy.Callback. Besides tracking changes and behaving * like the real thing should, it also asserts preconditions. @@ -407,8 +403,8 @@ public class SimpleTimeZoneDetectorStrategyTest { return mFakeCallback.peekSystemClockMillis(); } - Script simulateTimeSignalReceived(TimeSignal timeSignal) { - mSimpleTimeDetectorStrategy.suggestTime(timeSignal); + Script simulatePhoneTimeSuggestion(PhoneTimeSuggestion timeSuggestion) { + mSimpleTimeDetectorStrategy.suggestPhoneTime(timeSuggestion); return this; } @@ -466,10 +462,10 @@ public class SimpleTimeZoneDetectorStrategyTest { return mActualTimeMillis; } - TimeSignal createTimeSignalForActual(String sourceId) { + PhoneTimeSuggestion createPhoneTimeSuggestionForActual(int phoneId) { TimestampedValue<Long> time = new TimestampedValue<>( mInitialDeviceRealtimeMillis, mActualTimeMillis); - return new TimeSignal(sourceId, time); + return new PhoneTimeSuggestion(phoneId, time); } static class Builder { diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java index 45fef764482f..37da01824e88 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java @@ -28,7 +28,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.timedetector.TimeSignal; +import android.app.timedetector.PhoneTimeSuggestion; import android.content.Context; import android.content.pm.PackageManager; import android.util.TimestampedValue; @@ -67,10 +67,10 @@ public class TimeDetectorServiceTest { public void testStubbedCall_withoutPermission() { doThrow(new SecurityException("Mock")) .when(mMockContext).enforceCallingPermission(anyString(), any()); - TimeSignal timeSignal = createNitzTimeSignal(); + PhoneTimeSuggestion phoneTimeSuggestion = createPhoneTimeSuggestion(); try { - mTimeDetectorService.suggestTime(timeSignal); + mTimeDetectorService.suggestPhoneTime(phoneTimeSuggestion); } finally { verify(mMockContext).enforceCallingPermission( eq(android.Manifest.permission.SET_TIME), anyString()); @@ -78,15 +78,15 @@ public class TimeDetectorServiceTest { } @Test - public void testSuggestTime() { + public void testSuggestPhoneTime() { doNothing().when(mMockContext).enforceCallingPermission(anyString(), any()); - TimeSignal timeSignal = createNitzTimeSignal(); - mTimeDetectorService.suggestTime(timeSignal); + PhoneTimeSuggestion phoneTimeSuggestion = createPhoneTimeSuggestion(); + mTimeDetectorService.suggestPhoneTime(phoneTimeSuggestion); verify(mMockContext) .enforceCallingPermission(eq(android.Manifest.permission.SET_TIME), anyString()); - mStubbedTimeDetectorStrategy.verifySuggestTimeCalled(timeSignal); + mStubbedTimeDetectorStrategy.verifySuggestPhoneTimeCalled(phoneTimeSuggestion); } @Test @@ -115,15 +115,16 @@ public class TimeDetectorServiceTest { mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled(false); } - private static TimeSignal createNitzTimeSignal() { + private static PhoneTimeSuggestion createPhoneTimeSuggestion() { + int phoneId = 1234; TimestampedValue<Long> timeValue = new TimestampedValue<>(100L, 1_000_000L); - return new TimeSignal(TimeSignal.SOURCE_ID_NITZ, timeValue); + return new PhoneTimeSuggestion(phoneId, timeValue); } private static class StubbedTimeDetectorStrategy implements TimeDetectorStrategy { // Call tracking. - private TimeSignal mLastSuggestedTime; + private PhoneTimeSuggestion mLastPhoneSuggestion; private Boolean mLastAutoTimeDetectionToggle; private boolean mDumpCalled; @@ -132,9 +133,9 @@ public class TimeDetectorServiceTest { } @Override - public void suggestTime(TimeSignal timeSignal) { + public void suggestPhoneTime(PhoneTimeSuggestion timeSuggestion) { resetCallTracking(); - mLastSuggestedTime = timeSignal; + mLastPhoneSuggestion = timeSuggestion; } @Override @@ -150,13 +151,13 @@ public class TimeDetectorServiceTest { } void resetCallTracking() { - mLastSuggestedTime = null; + mLastPhoneSuggestion = null; mLastAutoTimeDetectionToggle = null; mDumpCalled = false; } - void verifySuggestTimeCalled(TimeSignal expectedSignal) { - assertEquals(expectedSignal, mLastSuggestedTime); + void verifySuggestPhoneTimeCalled(PhoneTimeSuggestion expectedSignal) { + assertEquals(expectedSignal, mLastPhoneSuggestion); } void verifyHandleAutoTimeDetectionToggleCalled(boolean expectedEnable) { diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index fff77f56d7c8..702600402d8e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -152,7 +152,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { hiddenActivity.setHidden(true); mDisplayContent.getConfiguration().windowConfiguration.setRotation( mDisplayContent.getRotation()); - mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray()); + mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray(), homeActivity); // Ensure that we are animating the target activity as well assertTrue(mController.isAnimatingTask(homeActivity.getTask())); @@ -181,7 +181,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { mDisplayContent.getConfiguration().windowConfiguration.setRotation( mDisplayContent.getRotation()); - mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray()); + mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray(), homeAppWindow); mController.startAnimation(); // Ensure that we are animating the app and wallpaper target @@ -210,7 +210,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { mDisplayContent.getConfiguration().windowConfiguration.setRotation( mDisplayContent.getRotation()); - mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray()); + mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray(), homeActivity); mController.startAnimation(); // Cancel the animation and ensure the controller is still running @@ -242,7 +242,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible(); // Start and finish the animation - mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray()); + mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray(), homeActivity); mController.startAnimation(); // Reset at this point since we may remove adapters that couldn't be created reset(mController); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java index ebedde7d4460..839ddb2038ff 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java @@ -78,7 +78,7 @@ public class RecentsAnimationTest extends ActivityTestsBase { mRecentsAnimationController = mock(RecentsAnimationController.class); mService.mWindowManager.setRecentsAnimationController(mRecentsAnimationController); doNothing().when(mService.mWindowManager).initializeRecentsAnimation( - anyInt(), any(), any(), anyInt(), any()); + anyInt(), any(), any(), anyInt(), any(), any()); doReturn(true).when(mService.mWindowManager).canStartRecentsAnimation(); final RecentTasks recentTasks = mService.getRecentTasks(); @@ -385,7 +385,8 @@ public class RecentsAnimationTest extends ActivityTestsBase { return null; }).when(mService.mWindowManager).initializeRecentsAnimation( anyInt() /* targetActivityType */, any() /* recentsAnimationRunner */, - any() /* callbacks */, anyInt() /* displayId */, any() /* recentTaskIds */); + any() /* callbacks */, anyInt() /* displayId */, any() /* recentTaskIds */, + any() /* targetActivity */); } Intent recentsIntent = new Intent(); diff --git a/tests/BootImageProfileTest/AndroidTest.xml b/tests/BootImageProfileTest/AndroidTest.xml index c13200778c4b..b4f2663585cc 100644 --- a/tests/BootImageProfileTest/AndroidTest.xml +++ b/tests/BootImageProfileTest/AndroidTest.xml @@ -22,8 +22,8 @@ <!-- we need this magic flag, otherwise it always reboots and breaks the selinux --> <option name="force-skip-system-props" value="true" /> - <option name="run-command" value="setprop dalvik.vm.profilesystemserver true" /> - <option name="run-command" value="setprop dalvik.vm.profilebootclasspath true" /> + <option name="run-command" value="device_config put runtime_native_boot profilesystemserver true" /> + <option name="run-command" value="device_config put runtime_native_boot profilebootclasspath true" /> <!-- Profiling does not pick up the above changes we restart the shell --> <option name="run-command" value="stop" /> diff --git a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java index fe1d9d26c7e7..ccdd452b3f1e 100644 --- a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java +++ b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java @@ -46,9 +46,11 @@ public class BootImageProfileTest implements IDeviceTest { */ @Test public void testProperties() throws Exception { - String res = mTestDevice.getProperty("dalvik.vm.profilebootclasspath"); + String res = mTestDevice.getProperty( + "persist.device_config.runtime_native_boot.profilebootclasspath"); assertTrue("profile boot class path not enabled", res != null && res.equals("true")); - res = mTestDevice.getProperty("dalvik.vm.profilesystemserver"); + res = mTestDevice.getProperty( + "persist.device_config.runtime_native_boot.profilesystemserver"); assertTrue("profile system server not enabled", res != null && res.equals("true")); } diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java index db5e7f8d337f..cdc21ef8f971 100644 --- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java @@ -158,18 +158,6 @@ public class StagedRollbackTest { assertThat(rollback.getCommittedSessionId()).isNotEqualTo(-1); } - @Test - public void resetNetworkStack() throws Exception { - RollbackManager rm = RollbackUtils.getRollbackManager(); - String networkStack = getNetworkStackPackageName(); - - rm.expireRollbackForPackage(networkStack); - Uninstall.packages(networkStack); - - assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), - networkStack)).isNull(); - } - /** * Stage install ModuleMetadata package to simulate a Mainline module update. */ @@ -210,43 +198,35 @@ public class StagedRollbackTest { @Test public void testNetworkFailedRollback_Phase1() throws Exception { - resetNetworkStack(); - } - - @Test - public void testNetworkFailedRollback_Phase2() throws Exception { - assertNetworkStackRollbackAvailable(); - } + RollbackManager rm = RollbackUtils.getRollbackManager(); + String networkStack = getNetworkStackPackageName(); - @Test - public void testNetworkFailedRollback_Phase3() throws Exception { - assertNoNetworkStackRollbackCommitted(); - } + rm.expireRollbackForPackage(networkStack); + Uninstall.packages(networkStack); - @Test - public void testNetworkFailedRollback_Phase4() throws Exception { - assertNetworkStackRollbackCommitted(); + assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), + networkStack)).isNull(); } @Test - public void assertNetworkStackRollbackAvailable() throws Exception { + public void testNetworkFailedRollback_Phase2() throws Exception { RollbackManager rm = RollbackUtils.getRollbackManager(); assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), getNetworkStackPackageName())).isNotNull(); } @Test - public void assertNetworkStackRollbackCommitted() throws Exception { + public void testNetworkFailedRollback_Phase3() throws Exception { RollbackManager rm = RollbackUtils.getRollbackManager(); assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(), - getNetworkStackPackageName())).isNotNull(); + getNetworkStackPackageName())).isNull(); } @Test - public void assertNoNetworkStackRollbackCommitted() throws Exception { + public void testNetworkFailedRollback_Phase4() throws Exception { RollbackManager rm = RollbackUtils.getRollbackManager(); assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(), - getNetworkStackPackageName())).isNull(); + getNetworkStackPackageName())).isNotNull(); } private String getNetworkStackPackageName() { @@ -294,17 +274,28 @@ public class StagedRollbackTest { @Test public void testNetworkPassedDoesNotRollback_Phase1() throws Exception { - resetNetworkStack(); + RollbackManager rm = RollbackUtils.getRollbackManager(); + String networkStack = getNetworkStackPackageName(); + + rm.expireRollbackForPackage(networkStack); + Uninstall.packages(networkStack); + + assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), + networkStack)).isNull(); } @Test public void testNetworkPassedDoesNotRollback_Phase2() throws Exception { - assertNetworkStackRollbackAvailable(); + RollbackManager rm = RollbackUtils.getRollbackManager(); + assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), + getNetworkStackPackageName())).isNotNull(); } @Test public void testNetworkPassedDoesNotRollback_Phase3() throws Exception { - assertNoNetworkStackRollbackCommitted(); + RollbackManager rm = RollbackUtils.getRollbackManager(); + assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(), + getNetworkStackPackageName())).isNull(); } @Nullable diff --git a/tools/aapt2/format/Container.cpp b/tools/aapt2/format/Container.cpp index f1890488276c..9cef7b3d2ce3 100644 --- a/tools/aapt2/format/Container.cpp +++ b/tools/aapt2/format/Container.cpp @@ -30,6 +30,7 @@ namespace aapt { constexpr const static uint32_t kContainerFormatMagic = 0x54504141u; constexpr const static uint32_t kContainerFormatVersion = 1u; +constexpr const static size_t kPaddingAlignment = 4u; ContainerWriter::ContainerWriter(ZeroCopyOutputStream* out, size_t entry_count) : out_(out), total_entry_count_(entry_count), current_entry_count_(0u) { @@ -49,11 +50,17 @@ ContainerWriter::ContainerWriter(ZeroCopyOutputStream* out, size_t entry_count) } } -inline static void WritePadding(int padding, CodedOutputStream* out) { - if (padding < 4) { - const uint32_t zero = 0u; - out->WriteRaw(&zero, padding); - } +inline static size_t CalculatePaddingForAlignment(size_t size) { + size_t overage = size % kPaddingAlignment; + return overage == 0 ? 0 : kPaddingAlignment - overage; +} + +inline static void WritePadding(size_t padding, CodedOutputStream* out) { + CHECK(padding < kPaddingAlignment); + const uint32_t zero = 0u; + static_assert(sizeof(zero) >= kPaddingAlignment, "Not enough source bytes for padding"); + + out->WriteRaw(&zero, padding); } bool ContainerWriter::AddResTableEntry(const pb::ResourceTable& table) { @@ -70,7 +77,7 @@ bool ContainerWriter::AddResTableEntry(const pb::ResourceTable& table) { // Write the aligned size. const ::google::protobuf::uint64 size = table.ByteSize(); - const int padding = 4 - (size % 4); + const int padding = CalculatePaddingForAlignment(size); coded_out.WriteLittleEndian64(size); // Write the table. @@ -103,9 +110,9 @@ bool ContainerWriter::AddResFileEntry(const pb::internal::CompiledFile& file, // Write the aligned size. const ::google::protobuf::uint32 header_size = file.ByteSize(); - const int header_padding = 4 - (header_size % 4); + const int header_padding = CalculatePaddingForAlignment(header_size); const ::google::protobuf::uint64 data_size = in->TotalSize(); - const int data_padding = 4 - (data_size % 4); + const int data_padding = CalculatePaddingForAlignment(data_size); coded_out.WriteLittleEndian64(kResFileEntryHeaderSize + header_size + header_padding + data_size + data_padding); diff --git a/tools/aapt2/formats.md b/tools/aapt2/formats.md index bb31a005ef42..25a0e798dea2 100644 --- a/tools/aapt2/formats.md +++ b/tools/aapt2/formats.md @@ -23,7 +23,7 @@ boundary, so if a previous entry ends unaligned, padding must be inserted. | Size (in bytes) | Field | Description | |:----------------|:---------------|:----------------------------------------------------------------------------------------------------------| | `4` | `entry_type` | The type of the entry. This can be one of two types: `RES_TABLE (0x00000000)` or `RES_FILE (0x00000001)`. | -| `8` | `entry_length` | The length of the data that follows. | +| `8` | `entry_length` | The length of the data that follows. Do not use if `entry_type` is `RES_FILE`; this value may be wrong. | | `entry_length` | `data` | The payload. The contents of this varies based on the `entry_type`. | If the `entry_type` is equal to `RES_TABLE (0x00000000)`, the `data` field contains a serialized @@ -32,13 +32,14 @@ If the `entry_type` is equal to `RES_TABLE (0x00000000)`, the `data` field conta If the `entry_type` is equal to `RES_FILE (0x00000001)`, the `data` field contains the following: -| Size (in bytes) | Field | Description | -|:----------------|:---------------|:----------------------------------------------------------------------------------------------------------| -| `4` | `header_size` | The size of the `header` field. | -| `8` | `data_size` | The size of the `data` field. | -| `header_size` | `header` | The serialized Protobuf message [aapt.pb.internal.CompiledFile](ResourcesInternal.proto). | -| `x` | `padding` | Up to 4 bytes of zeros, if padding is necessary to align the `data` field on a 32-bit boundary. | -| `data_size` | `data` | The payload, which is determined by the `type` field in the `aapt.pb.internal.CompiledFile`. This can be a PNG file, binary XML, or [aapt.pb.XmlNode](Resources.proto). | +| Size (in bytes) | Field | Description | +|:----------------|:-----------------|:----------------------------------------------------------------------------------------------------------| +| `4` | `header_size` | The size of the `header` field. | +| `8` | `data_size` | The size of the `data` field. | +| `header_size` | `header` | The serialized Protobuf message [aapt.pb.internal.CompiledFile](ResourcesInternal.proto). | +| `x` | `header_padding` | Up to 3 bytes of zeros, if padding is necessary to align the `data` field on a 32-bit boundary. | +| `data_size` | `data` | The payload, which is determined by the `type` field in the `aapt.pb.internal.CompiledFile`. This can be a PNG file, binary XML, or [aapt.pb.XmlNode](Resources.proto). | +| `y` | `data_padding` | Up to 3 bytes of zeros, if `data_size` is not a multiple of 4. | ## AAPT2 Static Library Format (extension `.sapk`) diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index dd9ee3b4d793..04b073be28e2 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -38,6 +38,7 @@ import android.net.DhcpInfo; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; +import android.net.NetworkStack; import android.net.wifi.hotspot2.IProvisioningCallback; import android.net.wifi.hotspot2.OsuProvider; import android.net.wifi.hotspot2.PasspointConfiguration; @@ -533,7 +534,9 @@ public class WifiManager { * * @hide */ - public static final String EXTRA_WIFI_AP_INTERFACE_NAME = "wifi_ap_interface_name"; + @SystemApi + public static final String EXTRA_WIFI_AP_INTERFACE_NAME = + "android.net.wifi.extra.WIFI_AP_INTERFACE_NAME"; /** * The intended ip mode for this softap. * @see #IFACE_IP_MODE_TETHERED @@ -541,7 +544,8 @@ public class WifiManager { * * @hide */ - public static final String EXTRA_WIFI_AP_MODE = "wifi_ap_mode"; + @SystemApi + public static final String EXTRA_WIFI_AP_MODE = "android.net.wifi.extra.WIFI_AP_MODE"; /** @hide */ @IntDef(flag = false, prefix = { "WIFI_AP_STATE_" }, value = { @@ -647,6 +651,7 @@ public class WifiManager { * * @hide */ + @SystemApi public static final int IFACE_IP_MODE_UNSPECIFIED = -1; /** @@ -656,6 +661,7 @@ public class WifiManager { * * @hide */ + @SystemApi public static final int IFACE_IP_MODE_CONFIGURATION_ERROR = 0; /** @@ -665,6 +671,7 @@ public class WifiManager { * * @hide */ + @SystemApi public static final int IFACE_IP_MODE_TETHERED = 1; /** @@ -674,6 +681,7 @@ public class WifiManager { * * @hide */ + @SystemApi public static final int IFACE_IP_MODE_LOCAL_ONLY = 2; /** @@ -2661,16 +2669,21 @@ public class WifiManager { /** * Call allowing ConnectivityService to update WifiService with interface mode changes. * - * The possible modes include: {@link #IFACE_IP_MODE_TETHERED}, - * {@link #IFACE_IP_MODE_LOCAL_ONLY}, - * {@link #IFACE_IP_MODE_CONFIGURATION_ERROR} - * - * @param ifaceName String name of the updated interface - * @param mode int representing the new mode + * @param ifaceName String name of the updated interface, or null to represent all interfaces + * @param mode int representing the new mode, one of: + * {@link #IFACE_IP_MODE_TETHERED}, + * {@link #IFACE_IP_MODE_LOCAL_ONLY}, + * {@link #IFACE_IP_MODE_CONFIGURATION_ERROR}, + * {@link #IFACE_IP_MODE_UNSPECIFIED} * * @hide */ - public void updateInterfaceIpState(String ifaceName, int mode) { + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_STACK, + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK + }) + public void updateInterfaceIpState(@Nullable String ifaceName, @IfaceIpMode int mode) { try { IWifiManager iWifiManager = getIWifiManager(); if (iWifiManager == null) { @@ -2693,6 +2706,11 @@ public class WifiManager { * * @hide */ + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_STACK, + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK + }) public boolean startSoftAp(@Nullable WifiConfiguration wifiConfig) { try { IWifiManager iWifiManager = getIWifiManager(); @@ -2710,6 +2728,11 @@ public class WifiManager { * * @hide */ + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_STACK, + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK + }) public boolean stopSoftAp() { try { IWifiManager iWifiManager = getIWifiManager(); diff --git a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java index 6c2d7ff882d3..ba9dd37398a1 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java +++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java @@ -157,7 +157,8 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc */ public @NonNull Builder setBssidPattern( @NonNull MacAddress baseAddress, @NonNull MacAddress mask) { - checkNotNull(baseAddress, mask); + checkNotNull(baseAddress); + checkNotNull(mask); mBssidPatternMatcher = Pair.create(baseAddress, mask); return this; } |