diff options
60 files changed, 1156 insertions, 726 deletions
diff --git a/config/preloaded-classes b/config/preloaded-classes index 1812c2bb61d6..fd4e3dfcaf95 100644 --- a/config/preloaded-classes +++ b/config/preloaded-classes @@ -6634,12 +6634,6 @@ android.security.Scrypt android.security.attestationverification.AttestationVerificationManager android.security.keymaster.ExportResult$1 android.security.keymaster.ExportResult -android.security.keymaster.IKeyAttestationApplicationIdProvider$Stub -android.security.keymaster.IKeyAttestationApplicationIdProvider -android.security.keymaster.KeyAttestationApplicationId$1 -android.security.keymaster.KeyAttestationApplicationId -android.security.keymaster.KeyAttestationPackageInfo$1 -android.security.keymaster.KeyAttestationPackageInfo android.security.keymaster.KeyCharacteristics$1 android.security.keymaster.KeyCharacteristics android.security.keymaster.KeymasterArgument$1 @@ -6664,7 +6658,13 @@ android.security.keystore.AttestationUtils android.security.keystore.BackendBusyException android.security.keystore.DelegatingX509Certificate android.security.keystore.DeviceIdAttestationException +android.security.keystore.IKeyAttestationApplicationIdProvider$Stub +android.security.keystore.IKeyAttestationApplicationIdProvider +android.security.keystore.KeyAttestationApplicationId$Stub +android.security.keystore.KeyAttestationApplicationId android.security.keystore.KeyAttestationException +android.security.keystore.KeyAttestationPackageInfo$Stub +android.security.keystore.KeyAttestationPackageInfo android.security.keystore.KeyExpiredException android.security.keystore.KeyGenParameterSpec$Builder android.security.keystore.KeyGenParameterSpec @@ -6687,6 +6687,8 @@ android.security.keystore.KeystoreResponse$1 android.security.keystore.KeystoreResponse android.security.keystore.ParcelableKeyGenParameterSpec$1 android.security.keystore.ParcelableKeyGenParameterSpec +android.security.keystore.Signature$Stub +android.security.keystore.Signature android.security.keystore.SecureKeyImportUnavailableException android.security.keystore.StrongBoxUnavailableException android.security.keystore.UserAuthArgs diff --git a/core/api/current.txt b/core/api/current.txt index 7ee12d1c3ae0..dd98aa569ddb 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -29084,6 +29084,8 @@ package android.nfc { method @Nullable public android.nfc.NfcAntennaInfo getNfcAntennaInfo(); method public boolean ignore(android.nfc.Tag, int, android.nfc.NfcAdapter.OnTagRemovedListener, android.os.Handler); method public boolean isEnabled(); + method @FlaggedApi("android.nfc.enable_nfc_reader_option") public boolean isReaderOptionEnabled(); + method @FlaggedApi("android.nfc.enable_nfc_reader_option") public boolean isReaderOptionSupported(); method public boolean isSecureNfcEnabled(); method public boolean isSecureNfcSupported(); field public static final String ACTION_ADAPTER_STATE_CHANGED = "android.nfc.action.ADAPTER_STATE_CHANGED"; @@ -32493,6 +32495,7 @@ package android.os { field public static final int BATTERY_PROPERTY_CURRENT_AVERAGE = 3; // 0x3 field public static final int BATTERY_PROPERTY_CURRENT_NOW = 2; // 0x2 field public static final int BATTERY_PROPERTY_ENERGY_COUNTER = 5; // 0x5 + field @FlaggedApi("android.os.state_of_health_public") public static final int BATTERY_PROPERTY_STATE_OF_HEALTH = 10; // 0xa field public static final int BATTERY_PROPERTY_STATUS = 6; // 0x6 field public static final int BATTERY_STATUS_CHARGING = 2; // 0x2 field public static final int BATTERY_STATUS_DISCHARGING = 3; // 0x3 diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 0e4a52401f38..147a37022b58 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -10216,6 +10216,7 @@ package android.nfc { method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disable(); method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disable(boolean); method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enable(); + method @FlaggedApi("android.nfc.enable_nfc_reader_option") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableReaderOption(boolean); method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean); method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public java.util.Map<java.lang.String,java.lang.Boolean> getTagIntentAppPreferenceForUser(int); method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOn(); @@ -10315,7 +10316,6 @@ package android.os { field @RequiresPermission(android.Manifest.permission.BATTERY_STATS) public static final int BATTERY_PROPERTY_CHARGING_POLICY = 9; // 0x9 field @RequiresPermission(android.Manifest.permission.BATTERY_STATS) public static final int BATTERY_PROPERTY_FIRST_USAGE_DATE = 8; // 0x8 field @RequiresPermission(android.Manifest.permission.BATTERY_STATS) public static final int BATTERY_PROPERTY_MANUFACTURING_DATE = 7; // 0x7 - field @RequiresPermission(android.Manifest.permission.BATTERY_STATS) public static final int BATTERY_PROPERTY_STATE_OF_HEALTH = 10; // 0xa field public static final int CHARGING_POLICY_ADAPTIVE_AC = 3; // 0x3 field public static final int CHARGING_POLICY_ADAPTIVE_AON = 2; // 0x2 field public static final int CHARGING_POLICY_ADAPTIVE_LONGLIFE = 4; // 0x4 diff --git a/core/java/Android.bp b/core/java/Android.bp index 5091b52f7fd0..70864d532986 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -23,11 +23,6 @@ filegroup { visibility: ["//frameworks/base"], } -filegroup { - name: "IKeyAttestationApplicationIdProvider.aidl", - srcs: ["android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl"], -} - aidl_library { name: "IDropBoxManagerService_aidl", srcs: [ diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java index 7ed3a1d63e4f..6a51171b8f90 100644 --- a/core/java/android/app/KeyguardManager.java +++ b/core/java/android/app/KeyguardManager.java @@ -60,6 +60,7 @@ import com.android.internal.widget.IWeakEscrowTokenRemovedListener; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternView; import com.android.internal.widget.LockscreenCredential; +import com.android.internal.widget.PasswordValidationError; import com.android.internal.widget.VerifyCredentialResponse; import java.nio.charset.Charset; @@ -916,17 +917,14 @@ public class KeyguardManager { if (!checkInitialLockMethodUsage()) { return false; } + Objects.requireNonNull(password, "Password cannot be null."); complexity = PasswordMetrics.sanitizeComplexityLevel(complexity); - // TODO: b/131755827 add devicePolicyManager support for Auto - DevicePolicyManager devicePolicyManager = - (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); PasswordMetrics adminMetrics = - devicePolicyManager.getPasswordMinimumMetrics(mContext.getUserId()); - // Check if the password fits the mold of a pin or pattern. - boolean isPinOrPattern = lockType != PASSWORD; - - return PasswordMetrics.validatePassword( - adminMetrics, complexity, isPinOrPattern, password).size() == 0; + mLockPatternUtils.getRequestedPasswordMetrics(mContext.getUserId()); + try (LockscreenCredential credential = createLockscreenCredential(lockType, password)) { + return PasswordMetrics.validateCredential(adminMetrics, complexity, + credential).size() == 0; + } } /** @@ -945,11 +943,8 @@ public class KeyguardManager { return -1; } complexity = PasswordMetrics.sanitizeComplexityLevel(complexity); - // TODO: b/131755827 add devicePolicyManager support for Auto - DevicePolicyManager devicePolicyManager = - (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); PasswordMetrics adminMetrics = - devicePolicyManager.getPasswordMinimumMetrics(mContext.getUserId()); + mLockPatternUtils.getRequestedPasswordMetrics(mContext.getUserId()); PasswordMetrics minMetrics = PasswordMetrics.applyComplexity(adminMetrics, isPin, complexity); return minMetrics.length; @@ -1171,6 +1166,14 @@ public class KeyguardManager { currentLockType, currentPassword); LockscreenCredential newCredential = createLockscreenCredential( newLockType, newPassword); + PasswordMetrics adminMetrics = + mLockPatternUtils.getRequestedPasswordMetrics(mContext.getUserId()); + List<PasswordValidationError> errors = PasswordMetrics.validateCredential(adminMetrics, + DevicePolicyManager.PASSWORD_COMPLEXITY_NONE, newCredential); + if (!errors.isEmpty()) { + Log.e(TAG, "New credential is not valid: " + errors.get(0)); + return false; + } return mLockPatternUtils.setLockCredential(newCredential, currentCredential, userId); } diff --git a/core/java/android/app/admin/PasswordMetrics.java b/core/java/android/app/admin/PasswordMetrics.java index ab48791d43ef..dd311755bf3f 100644 --- a/core/java/android/app/admin/PasswordMetrics.java +++ b/core/java/android/app/admin/PasswordMetrics.java @@ -30,6 +30,7 @@ import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSW import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN; import static com.android.internal.widget.LockPatternUtils.MIN_LOCK_PASSWORD_SIZE; +import static com.android.internal.widget.LockPatternUtils.MIN_LOCK_PATTERN_SIZE; import static com.android.internal.widget.PasswordValidationError.CONTAINS_INVALID_CHARACTERS; import static com.android.internal.widget.PasswordValidationError.CONTAINS_SEQUENCE; import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_DIGITS; @@ -134,17 +135,6 @@ public final class PasswordMetrics implements Parcelable { } } - private static boolean hasInvalidCharacters(byte[] password) { - // Allow non-control Latin-1 characters only. - for (byte b : password) { - char c = (char) b; - if (c < 32 || c > 127) { - return true; - } - } - return false; - } - @Override public int describeContents() { return 0; @@ -189,19 +179,15 @@ public final class PasswordMetrics implements Parcelable { }; /** - * Returns the {@code PasswordMetrics} for a given credential. - * - * If the credential is a pin or a password, equivalent to - * {@link #computeForPasswordOrPin(byte[], boolean)}. {@code credential} cannot be null - * when {@code type} is - * {@link com.android.internal.widget.LockPatternUtils#CREDENTIAL_TYPE_PASSWORD}. + * Returns the {@code PasswordMetrics} for the given credential. */ public static PasswordMetrics computeForCredential(LockscreenCredential credential) { if (credential.isPassword() || credential.isPin()) { - return PasswordMetrics.computeForPasswordOrPin(credential.getCredential(), - credential.isPin()); + return computeForPasswordOrPin(credential.getCredential(), credential.isPin()); } else if (credential.isPattern()) { - return new PasswordMetrics(CREDENTIAL_TYPE_PATTERN); + PasswordMetrics metrics = new PasswordMetrics(CREDENTIAL_TYPE_PATTERN); + metrics.length = credential.size(); + return metrics; } else if (credential.isNone()) { return new PasswordMetrics(CREDENTIAL_TYPE_NONE); } else { @@ -210,10 +196,10 @@ public final class PasswordMetrics implements Parcelable { } /** - * Returns the {@code PasswordMetrics} for a given password or pin + * Returns the {@code PasswordMetrics} for the given password or pin. */ - public static PasswordMetrics computeForPasswordOrPin(byte[] password, boolean isPin) { - // Analyse the characters used + private static PasswordMetrics computeForPasswordOrPin(byte[] credential, boolean isPin) { + // Analyze the characters used. int letters = 0; int upperCase = 0; int lowerCase = 0; @@ -221,8 +207,8 @@ public final class PasswordMetrics implements Parcelable { int symbols = 0; int nonLetter = 0; int nonNumeric = 0; - final int length = password.length; - for (byte b : password) { + final int length = credential.length; + for (byte b : credential) { switch (categoryChar((char) b)) { case CHAR_LOWER_CASE: letters++; @@ -247,7 +233,7 @@ public final class PasswordMetrics implements Parcelable { } final int credType = isPin ? CREDENTIAL_TYPE_PIN : CREDENTIAL_TYPE_PASSWORD; - final int seqLength = maxLengthSequence(password); + final int seqLength = maxLengthSequence(credential); return new PasswordMetrics(credType, length, letters, upperCase, lowerCase, numeric, symbols, nonLetter, nonNumeric, seqLength); } @@ -513,26 +499,24 @@ public final class PasswordMetrics implements Parcelable { } /** - * Validates password against minimum metrics and complexity. + * Validates a proposed lockscreen credential against minimum metrics and complexity. * - * @param adminMetrics - minimum metrics to satisfy admin requirements. - * @param minComplexity - minimum complexity imposed by the requester. - * @param isPin - whether it is PIN that should be only digits - * @param password - password to validate. - * @return a list of password validation errors. An empty list means the password is OK. + * @param adminMetrics minimum metrics to satisfy admin requirements + * @param minComplexity minimum complexity imposed by the requester + * @param credential the proposed lockscreen credential + * + * @return a list of validation errors. An empty list means the credential is OK. * * TODO: move to PasswordPolicy */ - public static List<PasswordValidationError> validatePassword( - PasswordMetrics adminMetrics, int minComplexity, boolean isPin, byte[] password) { - - if (hasInvalidCharacters(password)) { + public static List<PasswordValidationError> validateCredential( + PasswordMetrics adminMetrics, int minComplexity, LockscreenCredential credential) { + if (credential.hasInvalidChars()) { return Collections.singletonList( new PasswordValidationError(CONTAINS_INVALID_CHARACTERS, 0)); } - - final PasswordMetrics enteredMetrics = computeForPasswordOrPin(password, isPin); - return validatePasswordMetrics(adminMetrics, minComplexity, enteredMetrics); + PasswordMetrics actualMetrics = computeForCredential(credential); + return validatePasswordMetrics(adminMetrics, minComplexity, actualMetrics); } /** @@ -555,9 +539,18 @@ public final class PasswordMetrics implements Parcelable { || !bucket.allowsCredType(actualMetrics.credType)) { return Collections.singletonList(new PasswordValidationError(WEAK_CREDENTIAL_TYPE, 0)); } - if (actualMetrics.credType != CREDENTIAL_TYPE_PASSWORD - && actualMetrics.credType != CREDENTIAL_TYPE_PIN) { - return Collections.emptyList(); // Nothing to check for pattern or none. + if (actualMetrics.credType == CREDENTIAL_TYPE_PATTERN) { + // For pattern, only need to check the length against the hardcoded minimum. If the + // pattern length is unavailable (e.g., PasswordMetrics that was stored on-disk before + // the pattern length started being included in it), assume it is okay. + if (actualMetrics.length != 0 && actualMetrics.length < MIN_LOCK_PATTERN_SIZE) { + return Collections.singletonList(new PasswordValidationError(TOO_SHORT, + MIN_LOCK_PATTERN_SIZE)); + } + return Collections.emptyList(); + } + if (actualMetrics.credType == CREDENTIAL_TYPE_NONE) { + return Collections.emptyList(); // Nothing to check for none. } if (actualMetrics.credType == CREDENTIAL_TYPE_PIN && actualMetrics.nonNumeric > 0) { diff --git a/core/java/android/content/pm/Signature.aidl b/core/java/android/content/pm/Signature.aidl deleted file mode 100644 index 36c127ad0384..000000000000 --- a/core/java/android/content/pm/Signature.aidl +++ /dev/null @@ -1,32 +0,0 @@ -/* //device/java/android/android/view/WindowManager.aidl -** -** Copyright 2007, 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.content.pm; - -/* For the key attestation application id provider service we needed a native implementation - * of the Signature parcelable because the service is used by the native keystore. - * The native implementation is now located at - * system/security/keystore/Signature.cpp - * and - * system/security/keystore/include/keystore/Signature.h. - * and can be used by linking against libkeystore_binder. - * - * This is not the best arrangement. If you, dear reader, happen to implement native implementations - * for the package manager's parcelables, consider moving Signature.cpp/.h to your library and - * adjust keystore's dependencies accordingly. Thank you. - */ -parcelable Signature cpp_header "keystore/Signature.h"; diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl index a6d8cafe8263..0c95c2ec7a7a 100644 --- a/core/java/android/nfc/INfcAdapter.aidl +++ b/core/java/android/nfc/INfcAdapter.aidl @@ -79,4 +79,9 @@ interface INfcAdapter Map getTagIntentAppPreferenceForUser(int userId); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)") int setTagIntentAppPreferenceForUser(int userId, String pkg, boolean allow); + + boolean isReaderOptionEnabled(); + boolean isReaderOptionSupported(); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)") + boolean enableReaderOption(boolean enable); } diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index 1307dfc2665e..46586308e3cf 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -17,6 +17,7 @@ package android.nfc; import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -1826,6 +1827,97 @@ public final class NfcAdapter { } /** + * Sets NFC Reader option feature. + * <p>This API is for the Settings application. + * @return True if successful + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_ENABLE_NFC_READER_OPTION) + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) + public boolean enableReaderOption(boolean enable) { + if (!sHasNfcFeature) { + throw new UnsupportedOperationException(); + } + try { + return sService.enableReaderOption(enable); + } catch (RemoteException e) { + attemptDeadServiceRecovery(e); + // Try one more time + if (sService == null) { + Log.e(TAG, "Failed to recover NFC Service."); + return false; + } + try { + return sService.enableReaderOption(enable); + } catch (RemoteException ee) { + Log.e(TAG, "Failed to recover NFC Service."); + } + return false; + } + } + + /** + * Checks if the device supports NFC Reader option functionality. + * + * @return True if device supports NFC Reader option, false otherwise + * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_READER_OPTION) + public boolean isReaderOptionSupported() { + if (!sHasNfcFeature) { + throw new UnsupportedOperationException(); + } + try { + return sService.isReaderOptionSupported(); + } catch (RemoteException e) { + attemptDeadServiceRecovery(e); + // Try one more time + if (sService == null) { + Log.e(TAG, "Failed to recover NFC Service."); + return false; + } + try { + return sService.isReaderOptionSupported(); + } catch (RemoteException ee) { + Log.e(TAG, "Failed to recover NFC Service."); + } + return false; + } + } + + /** + * Checks NFC Reader option feature is enabled. + * + * @return True if NFC Reader option is enabled, false otherwise + * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. + * @throws UnsupportedOperationException if device doesn't support + * NFC Reader option functionality. {@link #isReaderOptionSupported} + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_READER_OPTION) + public boolean isReaderOptionEnabled() { + if (!sHasNfcFeature) { + throw new UnsupportedOperationException(); + } + try { + return sService.isReaderOptionEnabled(); + } catch (RemoteException e) { + attemptDeadServiceRecovery(e); + // Try one more time + if (sService == null) { + Log.e(TAG, "Failed to recover NFC Service."); + return false; + } + try { + return sService.isReaderOptionEnabled(); + } catch (RemoteException ee) { + Log.e(TAG, "Failed to recover NFC Service."); + } + return false; + } + } + + /** * Enable NDEF Push feature. * <p>This API is for the Settings application. * @hide diff --git a/core/java/android/nfc/flags.aconfig b/core/java/android/nfc/flags.aconfig index e3faf3978856..55b0b4261763 100644 --- a/core/java/android/nfc/flags.aconfig +++ b/core/java/android/nfc/flags.aconfig @@ -6,3 +6,10 @@ flag { description: "Flag for NFC mainline changes" bug: "292140387" } + +flag { + name: "enable_nfc_reader_option" + namespace: "nfc" + description: "Flag for NFC reader option API changes" + bug: "291187960" +} diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java index 092923e927a3..6a4ec9b7605a 100644 --- a/core/java/android/os/BatteryManager.java +++ b/core/java/android/os/BatteryManager.java @@ -16,7 +16,10 @@ package android.os; +import static android.os.Flags.FLAG_STATE_OF_HEALTH_PUBLIC; + import android.Manifest.permission; +import android.annotation.FlaggedApi; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; @@ -354,17 +357,11 @@ public class BatteryManager { public static final int BATTERY_PROPERTY_CHARGING_POLICY = 9; /** - * - * Percentage representing the measured battery state of health (remaining - * estimated full charge capacity relative to the rated capacity in %). - * - * <p class="note"> - * The sender must hold the {@link android.Manifest.permission#BATTERY_STATS} permission. - * - * @hide + * Percentage representing the measured battery state of health. + * This is the remaining estimated full charge capacity relative + * to the rated capacity in %. */ - @RequiresPermission(permission.BATTERY_STATS) - @SystemApi + @FlaggedApi(FLAG_STATE_OF_HEALTH_PUBLIC) public static final int BATTERY_PROPERTY_STATE_OF_HEALTH = 10; private final Context mContext; diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig index 851aa6dce560..77229c44cc8d 100644 --- a/core/java/android/os/flags.aconfig +++ b/core/java/android/os/flags.aconfig @@ -1,6 +1,13 @@ package: "android.os" flag { + name: "state_of_health_public" + namespace: "system_sw_battery" + description: "Feature flag for making state_of_health a public api." + bug: "288842045" +} + +flag { name: "disallow_cellular_null_ciphers_restriction" namespace: "cellular_security" description: "Guards a new UserManager user restriction that admins can use to require cellular encryption on their managed devices." diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index baeb1aa71207..9cdb9cefdc43 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -10442,20 +10442,6 @@ public final class Settings { "assist_long_press_home_enabled"; /** - * Control whether Trust Agents are in active unlock or extend unlock mode. - * @hide - */ - @Readable - public static final String TRUST_AGENTS_EXTEND_UNLOCK = "trust_agents_extend_unlock"; - - /** - * Control whether the screen locks when trust is lost. - * @hide - */ - @Readable - public static final String LOCK_SCREEN_WHEN_TRUST_LOST = "lock_screen_when_trust_lost"; - - /** * Control whether Night display is currently activated. * @hide */ diff --git a/core/java/android/security/keymaster/KeyAttestationApplicationId.java b/core/java/android/security/keymaster/KeyAttestationApplicationId.java deleted file mode 100644 index 670f30e1b04b..000000000000 --- a/core/java/android/security/keymaster/KeyAttestationApplicationId.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.security.keymaster; - -import android.os.Parcel; -import android.os.Parcelable; - -/** - * @hide - * The information aggregated by this class is used by keystore to identify a caller of the - * keystore API toward a remote party. It aggregates multiple PackageInfos because keystore - * can only determine a caller by uid granularity, and a uid can be shared by multiple packages. - * The remote party must decide if it trusts all of the packages enough to consider the - * confidentiality of the key material in question intact. - */ -public class KeyAttestationApplicationId implements Parcelable { - private final KeyAttestationPackageInfo[] mAttestationPackageInfos; - - /** - * @param mAttestationPackageInfos - */ - public KeyAttestationApplicationId(KeyAttestationPackageInfo[] mAttestationPackageInfos) { - super(); - this.mAttestationPackageInfos = mAttestationPackageInfos; - } - - /** - * @return the mAttestationPackageInfos - */ - public KeyAttestationPackageInfo[] getAttestationPackageInfos() { - return mAttestationPackageInfos; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeTypedArray(mAttestationPackageInfos, flags); - } - - public static final @android.annotation.NonNull Parcelable.Creator<KeyAttestationApplicationId> CREATOR - = new Parcelable.Creator<KeyAttestationApplicationId>() { - @Override - public KeyAttestationApplicationId createFromParcel(Parcel source) { - return new KeyAttestationApplicationId(source); - } - - @Override - public KeyAttestationApplicationId[] newArray(int size) { - return new KeyAttestationApplicationId[size]; - } - }; - - KeyAttestationApplicationId(Parcel source) { - mAttestationPackageInfos = source.createTypedArray(KeyAttestationPackageInfo.CREATOR); - } -} diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.java b/core/java/android/security/keymaster/KeyAttestationPackageInfo.java deleted file mode 100644 index c0b8d8dfd4d9..000000000000 --- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.security.keymaster; - -import android.content.pm.Signature; -import android.os.Parcel; -import android.os.Parcelable; - -/** - * @hide - * This class constitutes and excerpt from the PackageManager's PackageInfo for the purpose of - * key attestation. It is part of the KeyAttestationApplicationId, which is used by - * keystore to identify the caller of the keystore API towards a remote party. - */ -public class KeyAttestationPackageInfo implements Parcelable { - private final String mPackageName; - private final long mPackageVersionCode; - private final Signature[] mPackageSignatures; - - /** - * @param mPackageName - * @param mPackageVersionCode - * @param mPackageSignatures - */ - public KeyAttestationPackageInfo( - String mPackageName, long mPackageVersionCode, Signature[] mPackageSignatures) { - super(); - this.mPackageName = mPackageName; - this.mPackageVersionCode = mPackageVersionCode; - this.mPackageSignatures = mPackageSignatures; - } - /** - * @return the mPackageName - */ - public String getPackageName() { - return mPackageName; - } - /** - * @return the mPackageVersionCode - */ - public long getPackageVersionCode() { - return mPackageVersionCode; - } - /** - * @return the mPackageSignatures - */ - public Signature[] getPackageSignatures() { - return mPackageSignatures; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(mPackageName); - dest.writeLong(mPackageVersionCode); - dest.writeTypedArray(mPackageSignatures, flags); - } - - public static final @android.annotation.NonNull Parcelable.Creator<KeyAttestationPackageInfo> CREATOR - = new Parcelable.Creator<KeyAttestationPackageInfo>() { - @Override - public KeyAttestationPackageInfo createFromParcel(Parcel source) { - return new KeyAttestationPackageInfo(source); - } - - @Override - public KeyAttestationPackageInfo[] newArray(int size) { - return new KeyAttestationPackageInfo[size]; - } - }; - - private KeyAttestationPackageInfo(Parcel source) { - mPackageName = source.readString(); - mPackageVersionCode = source.readLong(); - mPackageSignatures = source.createTypedArray(Signature.CREATOR); - } -} diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 0d0bfe3b8ab9..d70296353ff6 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -2536,7 +2536,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final int childrenCount = mChildrenCount; if (childrenCount != 0) { final float x = event.getXDispatchLocation(0); - final float y = event.getXDispatchLocation(0); + final float y = event.getYDispatchLocation(0); final ArrayList<View> preorderedList = buildOrderedChildList(); final boolean customOrder = preorderedList == null diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 490ec35d1482..a1f8de41cfce 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -96,7 +96,7 @@ public class LockPatternUtils { public static final int MIN_LOCK_PATTERN_SIZE = 4; /** - * The minimum size of a valid password. + * The minimum size of a valid password or PIN. */ public static final int MIN_LOCK_PASSWORD_SIZE = 4; @@ -171,7 +171,6 @@ public class LockPatternUtils { */ public static final int USER_FRP = UserHandle.USER_NULL + 1; - public final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen"; public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type"; @Deprecated public final static String PASSWORD_TYPE_ALTERNATE_KEY = "lockscreen.password_type_alternate"; @@ -597,16 +596,6 @@ public class LockPatternUtils { } /** - * Return true if the user has ever chosen a pattern. This is true even if the pattern is - * currently cleared. - * - * @return True if the user has ever chosen a pattern. - */ - public boolean isPatternEverChosen(int userId) { - return getBoolean(PATTERN_EVER_CHOSEN_KEY, false, userId); - } - - /** * Returns the length of the PIN set by a particular user. * @param userId user id of the user whose pin length we have to return * @return @@ -639,13 +628,6 @@ public class LockPatternUtils { return false; } } - /** - * Records that the user has chosen a pattern at some time, even if the pattern is - * currently cleared. - */ - public void reportPatternWasChosen(int userId) { - setBoolean(PATTERN_EVER_CHOSEN_KEY, true, userId); - } /** * Used by device policy manager to validate the current password @@ -771,7 +753,6 @@ public class LockPatternUtils { * and return false if the given credential is wrong. * @throws RuntimeException if password change encountered an unrecoverable error. * @throws UnsupportedOperationException secure lockscreen is not supported on this device. - * @throws IllegalArgumentException if new credential is too short. */ public boolean setLockCredential(@NonNull LockscreenCredential newCredential, @NonNull LockscreenCredential savedCredential, int userHandle) { @@ -779,7 +760,6 @@ public class LockPatternUtils { throw new UnsupportedOperationException( "This operation requires the lock screen feature."); } - newCredential.checkLength(); try { if (!getLockSettings().setLockCredential(newCredential, savedCredential, userHandle)) { @@ -1543,7 +1523,6 @@ public class LockPatternUtils { throw new UnsupportedOperationException( "This operation requires the lock screen feature."); } - credential.checkLength(); LockSettingsInternal localService = getLockSettingsInternal(); return localService.setLockCredentialWithToken(credential, tokenHandle, token, userHandle); diff --git a/core/java/com/android/internal/widget/LockscreenCredential.java b/core/java/com/android/internal/widget/LockscreenCredential.java index 03e7fd1c7403..c88763ce6c97 100644 --- a/core/java/com/android/internal/widget/LockscreenCredential.java +++ b/core/java/com/android/internal/widget/LockscreenCredential.java @@ -60,10 +60,24 @@ import java.util.Objects; public class LockscreenCredential implements Parcelable, AutoCloseable { private final int mType; - // Stores raw credential bytes, or null if credential has been zeroized. An empty password + // Stores raw credential bytes, or null if credential has been zeroized. A none credential // is represented as a byte array of length 0. private byte[] mCredential; + // This indicates that the credential used characters outside ASCII 32–127. + // + // Such credentials were never intended to be allowed. However, Android 10–14 had a bug where + // conversion from the chars the user entered to the credential bytes used a simple truncation. + // Thus, any 'char' whose remainder mod 256 was in the range 32–127 was accepted and was + // equivalent to some ASCII character. For example, ™, which is U+2122, was truncated to ASCII + // 0x22 which is the double-quote character ". + // + // We have to continue to allow a LockscreenCredential to be constructed with this bug, so that + // existing devices can be unlocked if their password used this bug. However, we prevent new + // passwords that use this bug from being set. The boolean below keeps track of the information + // needed to do that check, since the conversion to mCredential may have been lossy. + private final boolean mHasInvalidChars; + /** * Private constructor, use static builder methods instead. * @@ -71,7 +85,7 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { * LockscreenCredential will only store the reference internally without copying. This is to * minimize the number of extra copies introduced. */ - private LockscreenCredential(int type, byte[] credential) { + private LockscreenCredential(int type, byte[] credential, boolean hasInvalidChars) { Objects.requireNonNull(credential); if (type == CREDENTIAL_TYPE_NONE) { Preconditions.checkArgument(credential.length == 0); @@ -80,17 +94,28 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { Preconditions.checkArgument(type == CREDENTIAL_TYPE_PIN || type == CREDENTIAL_TYPE_PASSWORD || type == CREDENTIAL_TYPE_PATTERN); - Preconditions.checkArgument(credential.length > 0); + // Do not validate credential.length yet. All non-none credentials have a minimum + // length requirement; however, one of the uses of LockscreenCredential is to represent + // a proposed credential that might be too short. For example, a LockscreenCredential + // with type CREDENTIAL_TYPE_PIN and length 0 represents an attempt to set an empty PIN. + // This differs from an actual attempt to set a none credential. We have to allow the + // LockscreenCredential object to be constructed so that the validation logic can run, + // even though the validation logic will ultimately reject the credential as too short. } mType = type; mCredential = credential; + mHasInvalidChars = hasInvalidChars; + } + + private LockscreenCredential(int type, CharSequence credential) { + this(type, charsToBytesTruncating(credential), hasInvalidChars(credential)); } /** - * Creates a LockscreenCredential object representing empty password. + * Creates a LockscreenCredential object representing a none credential. */ public static LockscreenCredential createNone() { - return new LockscreenCredential(CREDENTIAL_TYPE_NONE, new byte[0]); + return new LockscreenCredential(CREDENTIAL_TYPE_NONE, new byte[0], false); } /** @@ -98,15 +123,14 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { */ public static LockscreenCredential createPattern(@NonNull List<LockPatternView.Cell> pattern) { return new LockscreenCredential(CREDENTIAL_TYPE_PATTERN, - LockPatternUtils.patternToByteArray(pattern)); + LockPatternUtils.patternToByteArray(pattern), /* hasInvalidChars= */ false); } /** * Creates a LockscreenCredential object representing the given alphabetic password. */ public static LockscreenCredential createPassword(@NonNull CharSequence password) { - return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD, - charSequenceToByteArray(password)); + return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD, password); } /** @@ -117,20 +141,19 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { */ public static LockscreenCredential createManagedPassword(@NonNull byte[] password) { return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD, - Arrays.copyOf(password, password.length)); + Arrays.copyOf(password, password.length), /* hasInvalidChars= */ false); } /** * Creates a LockscreenCredential object representing the given numeric PIN. */ public static LockscreenCredential createPin(@NonNull CharSequence pin) { - return new LockscreenCredential(CREDENTIAL_TYPE_PIN, - charSequenceToByteArray(pin)); + return new LockscreenCredential(CREDENTIAL_TYPE_PIN, pin); } /** * Creates a LockscreenCredential object representing the given alphabetic password. - * If the supplied password is empty, create an empty credential object. + * If the supplied password is empty, create a none credential object. */ public static LockscreenCredential createPasswordOrNone(@Nullable CharSequence password) { if (TextUtils.isEmpty(password)) { @@ -142,7 +165,7 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { /** * Creates a LockscreenCredential object representing the given numeric PIN. - * If the supplied password is empty, create an empty credential object. + * If the supplied password is empty, create a none credential object. */ public static LockscreenCredential createPinOrNone(@Nullable CharSequence pin) { if (TextUtils.isEmpty(pin)) { @@ -175,7 +198,7 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { return mCredential; } - /** Returns whether this is an empty credential */ + /** Returns whether this is a none credential */ public boolean isNone() { ensureNotZeroized(); return mType == CREDENTIAL_TYPE_NONE; @@ -205,10 +228,17 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { return mCredential.length; } + /** Returns true if this credential was constructed with any chars outside the allowed range */ + public boolean hasInvalidChars() { + ensureNotZeroized(); + return mHasInvalidChars; + } + /** Create a copy of the credential */ public LockscreenCredential duplicate() { return new LockscreenCredential(mType, - mCredential != null ? Arrays.copyOf(mCredential, mCredential.length) : null); + mCredential != null ? Arrays.copyOf(mCredential, mCredential.length) : null, + mHasInvalidChars); } /** @@ -222,27 +252,37 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { } /** - * Check if the credential meets minimal length requirement. + * Checks whether the credential meets basic requirements for setting it as a new credential. * - * @throws IllegalArgumentException if the credential is too short. + * This is redundant if {@link android.app.admin.PasswordMetrics#validateCredential()}, which + * does more comprehensive checks, is correctly called first (which it should be). + * + * @throws IllegalArgumentException if the credential contains invalid characters or is too + * short */ - public void checkLength() { - if (isNone()) { - return; + public void validateBasicRequirements() { + if (mHasInvalidChars) { + throw new IllegalArgumentException("credential contains invalid characters"); } - if (isPattern()) { - if (size() < LockPatternUtils.MIN_LOCK_PATTERN_SIZE) { - throw new IllegalArgumentException("pattern must not be null and at least " - + LockPatternUtils.MIN_LOCK_PATTERN_SIZE + " dots long."); - } - return; - } - if (isPassword() || isPin()) { - if (size() < LockPatternUtils.MIN_LOCK_PASSWORD_SIZE) { - throw new IllegalArgumentException("password must not be null and at least " - + "of length " + LockPatternUtils.MIN_LOCK_PASSWORD_SIZE); - } - return; + switch (getType()) { + case CREDENTIAL_TYPE_PATTERN: + if (size() < LockPatternUtils.MIN_LOCK_PATTERN_SIZE) { + throw new IllegalArgumentException("pattern must be at least " + + LockPatternUtils.MIN_LOCK_PATTERN_SIZE + " dots long."); + } + break; + case CREDENTIAL_TYPE_PIN: + if (size() < LockPatternUtils.MIN_LOCK_PASSWORD_SIZE) { + throw new IllegalArgumentException("PIN must be at least " + + LockPatternUtils.MIN_LOCK_PASSWORD_SIZE + " digits long."); + } + break; + case CREDENTIAL_TYPE_PASSWORD: + if (size() < LockPatternUtils.MIN_LOCK_PASSWORD_SIZE) { + throw new IllegalArgumentException("password must be at least " + + LockPatternUtils.MIN_LOCK_PASSWORD_SIZE + " characters long."); + } + break; } } @@ -317,6 +357,7 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mType); dest.writeByteArray(mCredential); + dest.writeBoolean(mHasInvalidChars); } public static final Parcelable.Creator<LockscreenCredential> CREATOR = @@ -324,7 +365,8 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { @Override public LockscreenCredential createFromParcel(Parcel source) { - return new LockscreenCredential(source.readInt(), source.createByteArray()); + return new LockscreenCredential(source.readInt(), source.createByteArray(), + source.readBoolean()); } @Override @@ -346,7 +388,7 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { @Override public int hashCode() { // Effective Java — Always override hashCode when you override equals - return Objects.hash(mType, Arrays.hashCode(mCredential)); + return Objects.hash(mType, Arrays.hashCode(mCredential), mHasInvalidChars); } @Override @@ -354,20 +396,45 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { if (o == this) return true; if (!(o instanceof LockscreenCredential)) return false; final LockscreenCredential other = (LockscreenCredential) o; - return mType == other.mType && Arrays.equals(mCredential, other.mCredential); + return mType == other.mType && Arrays.equals(mCredential, other.mCredential) + && mHasInvalidChars == other.mHasInvalidChars; + } + + private static boolean hasInvalidChars(CharSequence chars) { + // + // Consider the password to have invalid characters if it contains any non-ASCII characters + // or control characters. There are multiple reasons for this restriction: + // + // - Non-ASCII characters might only be possible to enter on a third-party keyboard app + // (IME) that is available when setting the password but not when verifying it after a + // reboot. This can happen if the keyboard is not direct boot aware or gets uninstalled. + // + // - Unicode strings that look identical to the user can map to different byte[]. Yet, only + // one byte[] can be accepted. Unicode normalization can solve this problem to some + // extent, but still many Unicode characters look similar and could cause confusion. + // + // - For backwards compatibility reasons, the upper 8 bits of the 16-bit 'chars' are + // discarded by charsToBytesTruncating(). Thus, as-is passwords with characters above + // U+00FF (255) are not as secure as they should be. IMPORTANT: Do not change the below + // code to allow characters above U+00FF (255) without fixing this issue! + // + for (int i = 0; i < chars.length(); i++) { + char c = chars.charAt(i); + if (c < 32 || c > 127) { + return true; + } + } + return false; } /** - * Converts a CharSequence to a byte array without requiring a toString(), which creates an - * additional copy. + * Converts a CharSequence to a byte array, intentionally truncating chars greater than 255 for + * backwards compatibility reasons. See {@link #mHasInvalidChars}. * * @param chars The CharSequence to convert * @return A byte array representing the input */ - private static byte[] charSequenceToByteArray(CharSequence chars) { - if (chars == null) { - return new byte[0]; - } + private static byte[] charsToBytesTruncating(CharSequence chars) { byte[] bytes = new byte[chars.length()]; for (int i = 0; i < chars.length(); i++) { bytes[i] = (byte) chars.charAt(i); diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index 55382cc1d380..bfd80a9e4f74 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -157,12 +157,8 @@ static struct thread_dispatch_offsets_t // **************************************************************************** // **************************************************************************** -static constexpr int32_t PROXY_WARN_INTERVAL = 5000; static constexpr uint32_t GC_INTERVAL = 1000; -static std::atomic<uint32_t> gNumProxies(0); -static std::atomic<uint32_t> gProxiesWarned(0); - // Number of GlobalRefs held by JavaBBinders. static std::atomic<uint32_t> gNumLocalRefsCreated(0); static std::atomic<uint32_t> gNumLocalRefsDeleted(0); @@ -842,19 +838,7 @@ jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val) return NULL; } BinderProxyNativeData* actualNativeData = getBPNativeData(env, object); - if (actualNativeData == nativeData) { - // Created a new Proxy - uint32_t numProxies = gNumProxies.fetch_add(1, std::memory_order_relaxed); - uint32_t numLastWarned = gProxiesWarned.load(std::memory_order_relaxed); - if (numProxies >= numLastWarned + PROXY_WARN_INTERVAL) { - // Multiple threads can get here, make sure only one of them gets to - // update the warn counter. - if (gProxiesWarned.compare_exchange_strong(numLastWarned, - numLastWarned + PROXY_WARN_INTERVAL, std::memory_order_relaxed)) { - ALOGW("Unexpectedly many live BinderProxies: %d\n", numProxies); - } - } - } else { + if (actualNativeData != nativeData) { delete nativeData; } @@ -1209,7 +1193,7 @@ jint android_os_Debug_getLocalObjectCount(JNIEnv* env, jobject clazz) jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz) { - return gNumProxies.load(); + return BpBinder::getBinderProxyCount(); } jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz) @@ -1494,7 +1478,6 @@ static void BinderProxy_destroy(void* rawNativeData) nativeData->mObject.get(), nativeData->mOrgue.get()); delete nativeData; IPCThreadState::self()->flushCommands(); - --gNumProxies; } JNIEXPORT jlong JNICALL android_os_BinderProxy_getNativeFinalizer(JNIEnv*, jclass) { diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 58f97b08f2e3..93048ea4fc85 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1022,7 +1022,7 @@ requires swapping ROTATION_90 and ROTATION_270. TODO(b/265991392): This should eventually be configured and parsed in display_settings.xml --> - <bool name="config_matchSecondaryInternalDisplaysOrientationToReverseDefaultDisplay">true</bool> + <bool name="config_matchSecondaryInternalDisplaysOrientationToReverseDefaultDisplay">false</bool> <!-- Indicate available ColorDisplayManager.COLOR_MODE_xxx. --> <integer-array name="config_availableColorModes"> diff --git a/core/tests/coretests/src/android/app/KeyguardManagerTest.java b/core/tests/coretests/src/android/app/KeyguardManagerTest.java index 958906c36d05..ed8b2887ac79 100644 --- a/core/tests/coretests/src/android/app/KeyguardManagerTest.java +++ b/core/tests/coretests/src/android/app/KeyguardManagerTest.java @@ -174,6 +174,22 @@ public class KeyguardManagerTest { } @Test + public void setLock_validatesCredential() { + // setLock() should validate the credential before setting it. Test one example, which is + // that PINs must contain only ASCII digits 0-9, i.e. bytes 48-57. Using bytes 0-9 is + // incorrect and should *not* be accepted. + byte[] invalidPin = new byte[] { 1, 2, 3, 4 }; + byte[] validPin = "1234".getBytes(); + + assertFalse(mKeyguardManager.setLock(KeyguardManager.PIN, invalidPin, -1, null)); + assertFalse(mKeyguardManager.isDeviceSecure()); + + assertTrue(mKeyguardManager.setLock(KeyguardManager.PIN, validPin, -1, null)); + assertTrue(mKeyguardManager.isDeviceSecure()); + assertTrue(mKeyguardManager.setLock(-1, null, KeyguardManager.PIN, validPin)); + } + + @Test public void checkLock_correctCredentials() { // Set to `true` to behave as if SET_INITIAL_LOCK permission had been granted. doReturn(true).when(mKeyguardManager).checkInitialLockMethodUsage(); diff --git a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java index c9e02f8a998d..33e81c163b4e 100644 --- a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java +++ b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java @@ -25,6 +25,7 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; import static android.app.admin.PasswordMetrics.complexityLevelToMinQuality; import static android.app.admin.PasswordMetrics.sanitizeComplexityLevel; +import static android.app.admin.PasswordMetrics.validateCredential; import static android.app.admin.PasswordMetrics.validatePasswordMetrics; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; @@ -41,6 +42,8 @@ import android.platform.test.annotations.Presubmit; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.internal.widget.LockPatternUtils; +import com.android.internal.widget.LockscreenCredential; import com.android.internal.widget.PasswordValidationError; import org.junit.Test; @@ -94,8 +97,7 @@ public class PasswordMetricsTest { @Test public void testComputeForPassword_metrics() { - final PasswordMetrics metrics = PasswordMetrics.computeForPasswordOrPin( - "6B~0z1Z3*8A".getBytes(), /* isPin */ false); + final PasswordMetrics metrics = metricsForPassword("6B~0z1Z3*8A"); assertEquals(11, metrics.length); assertEquals(4, metrics.letters); assertEquals(3, metrics.upperCase); @@ -133,72 +135,54 @@ public class PasswordMetricsTest { @Test public void testDetermineComplexity_lowNumeric() { - assertEquals(PASSWORD_COMPLEXITY_LOW, - PasswordMetrics.computeForPasswordOrPin("1234".getBytes(), - /* isPin */true).determineComplexity()); + assertEquals(PASSWORD_COMPLEXITY_LOW, metricsForPin("1234").determineComplexity()); } @Test public void testDetermineComplexity_lowNumericComplex() { - assertEquals(PASSWORD_COMPLEXITY_LOW, - PasswordMetrics.computeForPasswordOrPin("124".getBytes(), - /* isPin */ true).determineComplexity()); + assertEquals(PASSWORD_COMPLEXITY_LOW, metricsForPin("124").determineComplexity()); } @Test public void testDetermineComplexity_lowAlphabetic() { - assertEquals(PASSWORD_COMPLEXITY_LOW, - PasswordMetrics.computeForPasswordOrPin("a!".getBytes(), - /* isPin */ false).determineComplexity()); + assertEquals(PASSWORD_COMPLEXITY_LOW, metricsForPassword("a!").determineComplexity()); } @Test public void testDetermineComplexity_lowAlphanumeric() { - assertEquals(PASSWORD_COMPLEXITY_LOW, - PasswordMetrics.computeForPasswordOrPin("a!1".getBytes(), - /* isPin */ false).determineComplexity()); + assertEquals(PASSWORD_COMPLEXITY_LOW, metricsForPassword("a!1").determineComplexity()); } @Test public void testDetermineComplexity_mediumNumericComplex() { - assertEquals(PASSWORD_COMPLEXITY_MEDIUM, - PasswordMetrics.computeForPasswordOrPin("1238".getBytes(), - /* isPin */ true).determineComplexity()); + assertEquals(PASSWORD_COMPLEXITY_MEDIUM, metricsForPin("1238").determineComplexity()); } @Test public void testDetermineComplexity_mediumAlphabetic() { - assertEquals(PASSWORD_COMPLEXITY_MEDIUM, - PasswordMetrics.computeForPasswordOrPin("ab!c".getBytes(), - /* isPin */ false).determineComplexity()); + assertEquals(PASSWORD_COMPLEXITY_MEDIUM, metricsForPassword("ab!c").determineComplexity()); } @Test public void testDetermineComplexity_mediumAlphanumeric() { - assertEquals(PASSWORD_COMPLEXITY_MEDIUM, - PasswordMetrics.computeForPasswordOrPin("ab!1".getBytes(), - /* isPin */ false).determineComplexity()); + assertEquals(PASSWORD_COMPLEXITY_MEDIUM, metricsForPassword("ab!1").determineComplexity()); } @Test public void testDetermineComplexity_highNumericComplex() { - assertEquals(PASSWORD_COMPLEXITY_HIGH, - PasswordMetrics.computeForPasswordOrPin("12389647!".getBytes(), - /* isPin */ true).determineComplexity()); + assertEquals(PASSWORD_COMPLEXITY_HIGH, metricsForPin("12389647!").determineComplexity()); } @Test public void testDetermineComplexity_highAlphabetic() { assertEquals(PASSWORD_COMPLEXITY_HIGH, - PasswordMetrics.computeForPasswordOrPin("alphabetic!".getBytes(), - /* isPin */ false).determineComplexity()); + metricsForPassword("alphabetic!").determineComplexity()); } @Test public void testDetermineComplexity_highAlphanumeric() { assertEquals(PASSWORD_COMPLEXITY_HIGH, - PasswordMetrics.computeForPasswordOrPin("alphanumeric123!".getBytes(), - /* isPin */ false).determineComplexity()); + metricsForPassword("alphanumeric123!").determineComplexity()); } @Test @@ -374,8 +358,74 @@ public class PasswordMetricsTest { PasswordValidationError.NOT_ENOUGH_NON_DIGITS, 1); } + @Test + public void testValidateCredential_none() { + PasswordMetrics adminMetrics; + LockscreenCredential none = LockscreenCredential.createNone(); + + adminMetrics = new PasswordMetrics(CREDENTIAL_TYPE_NONE); + assertValidationErrors( + validateCredential(adminMetrics, PASSWORD_COMPLEXITY_NONE, none)); + + adminMetrics = new PasswordMetrics(CREDENTIAL_TYPE_PIN); + assertValidationErrors( + validateCredential(adminMetrics, PASSWORD_COMPLEXITY_NONE, none), + PasswordValidationError.WEAK_CREDENTIAL_TYPE, 0); + } + + @Test + public void testValidateCredential_password() { + PasswordMetrics adminMetrics; + LockscreenCredential password; + + adminMetrics = new PasswordMetrics(CREDENTIAL_TYPE_NONE); + password = LockscreenCredential.createPassword("password"); + assertValidationErrors( + validateCredential(adminMetrics, PASSWORD_COMPLEXITY_LOW, password)); + + // Test that validateCredential() checks LockscreenCredential#hasInvalidChars(). + adminMetrics = new PasswordMetrics(CREDENTIAL_TYPE_NONE); + password = LockscreenCredential.createPassword("™™™™"); + assertTrue(password.hasInvalidChars()); + assertValidationErrors( + validateCredential(adminMetrics, PASSWORD_COMPLEXITY_LOW, password), + PasswordValidationError.CONTAINS_INVALID_CHARACTERS, 0); + + // Test one more case where validateCredential() should reject the password. Beyond this, + // the unit tests for the lower-level method validatePasswordMetrics() should be sufficient. + adminMetrics = new PasswordMetrics(CREDENTIAL_TYPE_NONE); + adminMetrics.length = 6; + password = LockscreenCredential.createPassword("pass"); + assertValidationErrors( + validateCredential(adminMetrics, PASSWORD_COMPLEXITY_LOW, password), + PasswordValidationError.TOO_SHORT, 6); + } + + private LockscreenCredential createPattern(String patternString) { + return LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern( + patternString.getBytes())); + } + + private static PasswordMetrics metricsForPassword(String password) { + return PasswordMetrics.computeForCredential(LockscreenCredential.createPassword(password)); + } + + private static PasswordMetrics metricsForPin(String pin) { + return PasswordMetrics.computeForCredential(LockscreenCredential.createPin(pin)); + } + + @Test + public void testValidateCredential_pattern() { + PasswordMetrics adminMetrics = new PasswordMetrics(CREDENTIAL_TYPE_NONE); + assertValidationErrors( + validateCredential(adminMetrics, PASSWORD_COMPLEXITY_NONE, createPattern("123")), + PasswordValidationError.TOO_SHORT, 4); + assertValidationErrors( + validateCredential(adminMetrics, PASSWORD_COMPLEXITY_NONE, createPattern("1234"))); + } + /** - * @param expected sequense of validation error codes followed by requirement values, must have + * @param expected sequence of validation error codes followed by requirement values, must have * even number of elements. Empty means no errors. */ private void assertValidationErrors( diff --git a/core/tests/coretests/src/android/view/ViewGroupTest.java b/core/tests/coretests/src/android/view/ViewGroupTest.java index b37c8fd8c34e..bce3f3e8f2e1 100644 --- a/core/tests/coretests/src/android/view/ViewGroupTest.java +++ b/core/tests/coretests/src/android/view/ViewGroupTest.java @@ -49,11 +49,11 @@ public class ViewGroupTest { public void testDispatchMouseEventsUnderCursor() { final Context context = getInstrumentation().getContext(); final TestView viewGroup = new TestView(context, 0 /* left */, 0 /* top */, - 200 /* right */, 200 /* bottom */); + 200 /* right */, 100 /* bottom */); final TestView viewA = spy(new TestView(context, 0 /* left */, 0 /* top */, - 100 /* right */, 200 /* bottom */)); + 100 /* right */, 100 /* bottom */)); final TestView viewB = spy(new TestView(context, 100 /* left */, 0 /* top */, - 200 /* right */, 200 /* bottom */)); + 200 /* right */, 100 /* bottom */)); viewGroup.addView(viewA); viewGroup.addView(viewB); @@ -73,10 +73,10 @@ public class ViewGroupTest { MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[2]; coords[0] = new MotionEvent.PointerCoords(); coords[0].x = 80; - coords[0].y = 100; + coords[0].y = 50; coords[1] = new MotionEvent.PointerCoords(); coords[1].x = 240; - coords[1].y = 100; + coords[1].y = 50; MotionEvent event; // Make sure the down event is active with a pointer which coordinate is different from the @@ -91,6 +91,10 @@ public class ViewGroupTest { viewGroup.onResolvePointerIcon(event, 0 /* pointerIndex */); verify(viewB).onResolvePointerIcon(event, 0); + event.setAction(MotionEvent.ACTION_SCROLL); + viewGroup.dispatchGenericMotionEvent(event); + verify(viewB).dispatchGenericMotionEvent(event); + event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */, MotionEvent.ACTION_POINTER_DOWN | (1 << MotionEvent.ACTION_POINTER_INDEX_SHIFT), 2 /* pointerCount */, properties, coords, 0 /* metaState */, 0 /* buttonState */, @@ -102,8 +106,13 @@ public class ViewGroupTest { viewGroup.onResolvePointerIcon(event, 1 /* pointerIndex */); verify(viewB).onResolvePointerIcon(event, 1); + event.setAction(MotionEvent.ACTION_SCROLL); + viewGroup.dispatchGenericMotionEvent(event); + verify(viewB).dispatchGenericMotionEvent(event); + verify(viewA, never()).dispatchTouchEvent(any()); verify(viewA, never()).onResolvePointerIcon(any(), anyInt()); + verify(viewA, never()).dispatchGenericMotionEvent(any()); } /** diff --git a/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java b/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java index a47868d0a524..5692742f7248 100644 --- a/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java +++ b/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java @@ -16,52 +16,71 @@ package com.android.internal.widget; - import static com.google.common.truth.Truth.assertThat; -import android.test.AndroidTestCase; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; -import java.util.Arrays; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import org.junit.Test; +import org.junit.runner.RunWith; -public class LockscreenCredentialTest extends AndroidTestCase { +import java.util.Arrays; - public void testEmptyCredential() { - LockscreenCredential empty = LockscreenCredential.createNone(); +@RunWith(AndroidJUnit4.class) +public class LockscreenCredentialTest { - assertTrue(empty.isNone()); - assertEquals(0, empty.size()); - assertNotNull(empty.getCredential()); + @Test + public void testNoneCredential() { + LockscreenCredential none = LockscreenCredential.createNone(); - assertFalse(empty.isPin()); - assertFalse(empty.isPassword()); - assertFalse(empty.isPattern()); + assertTrue(none.isNone()); + assertEquals(0, none.size()); + assertArrayEquals(new byte[0], none.getCredential()); + + assertFalse(none.isPin()); + assertFalse(none.isPassword()); + assertFalse(none.isPattern()); + assertFalse(none.hasInvalidChars()); + none.validateBasicRequirements(); } + @Test public void testPinCredential() { LockscreenCredential pin = LockscreenCredential.createPin("3456"); assertTrue(pin.isPin()); assertEquals(4, pin.size()); - assertTrue(Arrays.equals("3456".getBytes(), pin.getCredential())); + assertArrayEquals("3456".getBytes(), pin.getCredential()); assertFalse(pin.isNone()); assertFalse(pin.isPassword()); assertFalse(pin.isPattern()); + assertFalse(pin.hasInvalidChars()); + pin.validateBasicRequirements(); } + @Test public void testPasswordCredential() { LockscreenCredential password = LockscreenCredential.createPassword("password"); assertTrue(password.isPassword()); assertEquals(8, password.size()); - assertTrue(Arrays.equals("password".getBytes(), password.getCredential())); + assertArrayEquals("password".getBytes(), password.getCredential()); assertFalse(password.isNone()); assertFalse(password.isPin()); assertFalse(password.isPattern()); + assertFalse(password.hasInvalidChars()); + password.validateBasicRequirements(); } + @Test public void testPatternCredential() { LockscreenCredential pattern = LockscreenCredential.createPattern(Arrays.asList( LockPatternView.Cell.of(0, 0), @@ -73,13 +92,34 @@ public class LockscreenCredentialTest extends AndroidTestCase { assertTrue(pattern.isPattern()); assertEquals(5, pattern.size()); - assertTrue(Arrays.equals("12369".getBytes(), pattern.getCredential())); + assertArrayEquals("12369".getBytes(), pattern.getCredential()); assertFalse(pattern.isNone()); assertFalse(pattern.isPin()); assertFalse(pattern.isPassword()); + assertFalse(pattern.hasInvalidChars()); + pattern.validateBasicRequirements(); } + // Constructing a LockscreenCredential with a too-short length, even 0, should not throw an + // exception. This is because LockscreenCredential needs to be able to represent a request to + // set a credential that is too short. + @Test + public void testZeroLengthCredential() { + LockscreenCredential credential = LockscreenCredential.createPin(""); + assertTrue(credential.isPin()); + assertEquals(0, credential.size()); + + credential = createPattern(""); + assertTrue(credential.isPattern()); + assertEquals(0, credential.size()); + + credential = LockscreenCredential.createPassword(""); + assertTrue(credential.isPassword()); + assertEquals(0, credential.size()); + } + + @Test public void testPasswordOrNoneCredential() { assertEquals(LockscreenCredential.createNone(), LockscreenCredential.createPasswordOrNone(null)); @@ -89,6 +129,7 @@ public class LockscreenCredentialTest extends AndroidTestCase { LockscreenCredential.createPasswordOrNone("abcd")); } + @Test public void testPinOrNoneCredential() { assertEquals(LockscreenCredential.createNone(), LockscreenCredential.createPinOrNone(null)); @@ -98,6 +139,35 @@ public class LockscreenCredentialTest extends AndroidTestCase { LockscreenCredential.createPinOrNone("1357")); } + // Test that passwords containing invalid characters that were incorrectly allowed in + // Android 10–14 are still interpreted in the same way, but are not allowed for new passwords. + @Test + public void testPasswordWithInvalidChars() { + // ™ is U+2122, which was truncated to ASCII 0x22 which is double quote. + String[] passwords = new String[] { "foo™", "™™™™", "™foo" }; + String[] equivalentAsciiPasswords = new String[] { "foo\"", "\"\"\"\"", "\"foo" }; + for (int i = 0; i < passwords.length; i++) { + LockscreenCredential credential = LockscreenCredential.createPassword(passwords[i]); + assertTrue(credential.hasInvalidChars()); + assertArrayEquals(equivalentAsciiPasswords[i].getBytes(), credential.getCredential()); + try { + credential.validateBasicRequirements(); + fail("should not be able to set password with invalid chars"); + } catch (IllegalArgumentException expected) { } + } + } + + @Test + public void testPinWithInvalidChars() { + LockscreenCredential pin = LockscreenCredential.createPin("\n\n\n\n"); + assertTrue(pin.hasInvalidChars()); + try { + pin.validateBasicRequirements(); + fail("should not be able to set PIN with invalid chars"); + } catch (IllegalArgumentException expected) { } + } + + @Test public void testSanitize() { LockscreenCredential password = LockscreenCredential.createPassword("password"); password.zeroize(); @@ -123,11 +193,16 @@ public class LockscreenCredentialTest extends AndroidTestCase { fail("Sanitized credential still accessible"); } catch (IllegalStateException expected) { } try { + password.hasInvalidChars(); + fail("Sanitized credential still accessible"); + } catch (IllegalStateException expected) { } + try { password.getCredential(); fail("Sanitized credential still accessible"); } catch (IllegalStateException expected) { } } + @Test public void testEquals() { assertEquals(LockscreenCredential.createNone(), LockscreenCredential.createNone()); assertEquals(LockscreenCredential.createPassword("1234"), @@ -136,34 +211,40 @@ public class LockscreenCredentialTest extends AndroidTestCase { LockscreenCredential.createPin("4321")); assertEquals(createPattern("1234"), createPattern("1234")); - assertNotSame(LockscreenCredential.createPassword("1234"), + assertNotEquals(LockscreenCredential.createPassword("1234"), LockscreenCredential.createNone()); - assertNotSame(LockscreenCredential.createPassword("1234"), + assertNotEquals(LockscreenCredential.createPassword("1234"), LockscreenCredential.createPassword("4321")); - assertNotSame(LockscreenCredential.createPassword("1234"), + assertNotEquals(LockscreenCredential.createPassword("1234"), createPattern("1234")); - assertNotSame(LockscreenCredential.createPassword("1234"), + assertNotEquals(LockscreenCredential.createPassword("1234"), LockscreenCredential.createPin("1234")); - assertNotSame(LockscreenCredential.createPin("1111"), + assertNotEquals(LockscreenCredential.createPin("1111"), LockscreenCredential.createNone()); - assertNotSame(LockscreenCredential.createPin("1111"), + assertNotEquals(LockscreenCredential.createPin("1111"), LockscreenCredential.createPin("2222")); - assertNotSame(LockscreenCredential.createPin("1111"), + assertNotEquals(LockscreenCredential.createPin("1111"), createPattern("1111")); - assertNotSame(LockscreenCredential.createPin("1111"), + assertNotEquals(LockscreenCredential.createPin("1111"), LockscreenCredential.createPassword("1111")); - assertNotSame(createPattern("5678"), + assertNotEquals(createPattern("5678"), LockscreenCredential.createNone()); - assertNotSame(createPattern("5678"), + assertNotEquals(createPattern("5678"), createPattern("1234")); - assertNotSame(createPattern("5678"), + assertNotEquals(createPattern("5678"), LockscreenCredential.createPassword("5678")); - assertNotSame(createPattern("5678"), + assertNotEquals(createPattern("5678"), LockscreenCredential.createPin("5678")); + + // Test that mHasInvalidChars is compared. To do this, compare two passwords that map to + // the same byte[] (due to the truncation bug) but different values of mHasInvalidChars. + assertNotEquals(LockscreenCredential.createPassword("™™™™"), + LockscreenCredential.createPassword("\"\"\"\"")); } + @Test public void testDuplicate() { LockscreenCredential credential; @@ -175,8 +256,13 @@ public class LockscreenCredentialTest extends AndroidTestCase { assertEquals(credential, credential.duplicate()); credential = createPattern("5678"); assertEquals(credential, credential.duplicate()); + + // Test that mHasInvalidChars is duplicated. + credential = LockscreenCredential.createPassword("™™™™"); + assertEquals(credential, credential.duplicate()); } + @Test public void testPasswordToHistoryHash() { String password = "1234"; LockscreenCredential credential = LockscreenCredential.createPassword(password); @@ -193,6 +279,7 @@ public class LockscreenCredentialTest extends AndroidTestCase { .isEqualTo(expectedHash); } + @Test public void testPasswordToHistoryHashInvalidInput() { String password = "1234"; LockscreenCredential credential = LockscreenCredential.createPassword(password); @@ -221,6 +308,7 @@ public class LockscreenCredentialTest extends AndroidTestCase { .isNull(); } + @Test public void testLegacyPasswordToHash() { String password = "1234"; String salt = "6d5331dd120077a0"; @@ -233,6 +321,7 @@ public class LockscreenCredentialTest extends AndroidTestCase { .isEqualTo(expectedHash); } + @Test public void testLegacyPasswordToHashInvalidInput() { String password = "1234"; String salt = "6d5331dd120077a0"; diff --git a/keystore/aaid/aidl/Android.bp b/keystore/aaid/aidl/Android.bp new file mode 100644 index 000000000000..97acfb4ea4c3 --- /dev/null +++ b/keystore/aaid/aidl/Android.bp @@ -0,0 +1,31 @@ +// Copyright 2020, The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +aidl_interface { + name: "android.security.aaid_aidl", + srcs: ["android/security/keystore/*.aidl"], + unstable: true, + backend: { + rust: { + enabled: true, + }, + cpp: { + enabled: true, + }, + }, +} diff --git a/core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl b/keystore/aaid/aidl/android/security/keystore/IKeyAttestationApplicationIdProvider.aidl index dbffd5f57ce2..c360cb8f281a 100644 --- a/core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl +++ b/keystore/aaid/aidl/android/security/keystore/IKeyAttestationApplicationIdProvider.aidl @@ -1,5 +1,5 @@ /** - * Copyright (c) 2016, The Android Open Source Project + * Copyright (c) 2023, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,19 +14,15 @@ * limitations under the License. */ -package android.security.keymaster; +package android.security.keystore; -import android.security.keymaster.KeyAttestationApplicationId; -import android.security.keymaster.KeyAttestationPackageInfo; -import android.content.pm.Signature; +import android.security.keystore.KeyAttestationApplicationId; -/** - * This must be kept manually in sync with system/security/keystore until AIDL - * can generate both Java and C++ bindings. - * - * @hide - */ +/** @hide */ interface IKeyAttestationApplicationIdProvider { - /* keep in sync with /system/security/keystore/keystore_attestation_id.cpp */ + /** + * Provides information describing the possible applications identified by a UID. + * @hide + */ KeyAttestationApplicationId getKeyAttestationApplicationId(int uid); } diff --git a/keystore/aaid/aidl/android/security/keystore/KeyAttestationApplicationId.aidl b/keystore/aaid/aidl/android/security/keystore/KeyAttestationApplicationId.aidl new file mode 100644 index 000000000000..c33e8309b2f2 --- /dev/null +++ b/keystore/aaid/aidl/android/security/keystore/KeyAttestationApplicationId.aidl @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.keystore; + +import android.security.keystore.KeyAttestationPackageInfo; + +/** + * @hide + * The information aggregated by this parcelable is used by keystore to identify a caller of the + * keystore API toward a remote party. It aggregates multiple PackageInfos because keystore + * can only determine a caller by uid granularity, and a uid can be shared by multiple packages. + * The remote party must decide if it trusts all of the packages enough to consider the + * confidentiality of the key material in question intact. + */ +parcelable KeyAttestationApplicationId { + KeyAttestationPackageInfo[] packageInfos; +} diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/keystore/aaid/aidl/android/security/keystore/KeyAttestationPackageInfo.aidl index f8b843bc032f..5f647d0b1abe 100644 --- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl +++ b/keystore/aaid/aidl/android/security/keystore/KeyAttestationPackageInfo.aidl @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Android Open Source Project + * Copyright (c) 2023, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,9 +14,20 @@ * limitations under the License. */ -package android.security.keymaster; +package android.security.keystore; -/* The cpp_header is relative to system/security/keystore/include - * Link against libkeystore_binder to make use of the native implementation of this Parcelable. +import android.security.keystore.Signature; + +/** + * @hide + * This parcelable constitutes and excerpt from the PackageManager's PackageInfo for the purpose of + * key attestation. It is part of the KeyAttestationApplicationId, which is used by + * keystore to identify the caller of the keystore API towards a remote party. */ -parcelable KeyAttestationPackageInfo cpp_header "keystore/KeyAttestationPackageInfo.h"; +parcelable KeyAttestationPackageInfo { + String packageName; + + long versionCode; + + Signature[] signatures; +} diff --git a/core/java/android/security/keymaster/KeyAttestationApplicationId.aidl b/keystore/aaid/aidl/android/security/keystore/Signature.aidl index 9f6ff58ed5ce..800499a13355 100644 --- a/core/java/android/security/keymaster/KeyAttestationApplicationId.aidl +++ b/keystore/aaid/aidl/android/security/keystore/Signature.aidl @@ -14,9 +14,18 @@ * limitations under the License. */ -package android.security.keymaster; +package android.security.keystore; -/* The cpp_header is relative to system/security/keystore/include - * Link against libkeystore_binder to make use of the native implementation of this Parcelable. +/** + * @hide + * Represents a signature data read from the package file. Extracted from from the PackageManager's + * PackageInfo for the purpose of key attestation. It is part of the KeyAttestationPackageInfo, + * which is used by keystore to identify the caller of the keystore API towards a remote party. */ -parcelable KeyAttestationApplicationId cpp_header "keystore/KeyAttestationApplicationId.h"; +parcelable Signature { + /** + * Represents signing certificate data associated with application package, signatures are + * expected to be a hex-encoded ASCII string representing valid X509 certificate. + */ + byte[] data; +} diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java index 0f3488bbe8d1..31c2eb2efaed 100644 --- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java +++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java @@ -28,8 +28,8 @@ import android.system.keystore2.ResponseCode; import android.util.Log; /** - * @hide This is the client side for IKeystoreUserManager AIDL. - * It shall only be used by the LockSettingsService. + * @hide This is the client side for IKeystoreMaintenance AIDL. + * It is used mainly by LockSettingsService. */ public class AndroidKeyStoreMaintenance { private static final String TAG = "AndroidKeyStoreMaintenance"; @@ -66,7 +66,7 @@ public class AndroidKeyStoreMaintenance { } /** - * Informs Keystore 2.0 about removing a usergit mer + * Informs Keystore 2.0 about removing a user * * @param userId - Android user id of the user being removed * @return 0 if successful or a {@code ResponseCode} @@ -91,7 +91,7 @@ public class AndroidKeyStoreMaintenance { * * @param userId - Android user id of the user * @param password - a secret derived from the synthetic password provided by the - * LockSettingService + * LockSettingsService * @return 0 if successful or a {@code ResponseCode} * @hide */ @@ -110,7 +110,7 @@ public class AndroidKeyStoreMaintenance { } /** - * Informs Keystore 2.0 that an app was uninstalled and the corresponding namspace is to + * Informs Keystore 2.0 that an app was uninstalled and the corresponding namespace is to * be cleared. */ public static int clearNamespace(@Domain int domain, long namespace) { @@ -172,10 +172,10 @@ public class AndroidKeyStoreMaintenance { * namespace. * * @return * 0 on success - * * KEY_NOT_FOUND if the source did not exists. + * * KEY_NOT_FOUND if the source did not exist. * * PERMISSION_DENIED if any of the required permissions was missing. * * INVALID_ARGUMENT if the destination was occupied or any domain value other than - * the allowed once were specified. + * the allowed ones was specified. * * SYSTEM_ERROR if an unexpected error occurred. */ public static int migrateKeyNamespace(KeyDescriptor source, KeyDescriptor destination) { diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java index b0cdb0554c11..1d668681f80c 100644 --- a/media/java/android/media/AudioAttributes.java +++ b/media/java/android/media/AudioAttributes.java @@ -875,18 +875,7 @@ public final class AudioAttributes implements Parcelable { /** * Sets the attribute describing what is the intended use of the audio signal, * such as alarm or ringtone. - * @param usage one of {@link AttributeSdkUsage#USAGE_UNKNOWN}, - * {@link AttributeSdkUsage#USAGE_MEDIA}, - * {@link AttributeSdkUsage#USAGE_VOICE_COMMUNICATION}, - * {@link AttributeSdkUsage#USAGE_VOICE_COMMUNICATION_SIGNALLING}, - * {@link AttributeSdkUsage#USAGE_ALARM}, {@link AudioAttributes#USAGE_NOTIFICATION}, - * {@link AttributeSdkUsage#USAGE_NOTIFICATION_RINGTONE}, - * {@link AttributeSdkUsage#USAGE_NOTIFICATION_EVENT}, - * {@link AttributeSdkUsage#USAGE_ASSISTANT}, - * {@link AttributeSdkUsage#USAGE_ASSISTANCE_ACCESSIBILITY}, - * {@link AttributeSdkUsage#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE}, - * {@link AttributeSdkUsage#USAGE_ASSISTANCE_SONIFICATION}, - * {@link AttributeSdkUsage#USAGE_GAME}. + * @param usage the usage to set. * @return the same Builder instance. */ public Builder setUsage(@AttributeSdkUsage int usage) { diff --git a/media/java/android/media/AudioMetadata.java b/media/java/android/media/AudioMetadata.java index 0f962f9e9d4b..4e61549a5e5a 100644 --- a/media/java/android/media/AudioMetadata.java +++ b/media/java/android/media/AudioMetadata.java @@ -226,16 +226,15 @@ public final class AudioMetadata { * * An Integer value representing presentation content classifier. * - * @see AudioPresentation.ContentClassifier - * One of {@link AudioPresentation#CONTENT_UNKNOWN}, - * {@link AudioPresentation#CONTENT_MAIN}, - * {@link AudioPresentation#CONTENT_MUSIC_AND_EFFECTS}, - * {@link AudioPresentation#CONTENT_VISUALLY_IMPAIRED}, - * {@link AudioPresentation#CONTENT_HEARING_IMPAIRED}, - * {@link AudioPresentation#CONTENT_DIALOG}, - * {@link AudioPresentation#CONTENT_COMMENTARY}, - * {@link AudioPresentation#CONTENT_EMERGENCY}, - * {@link AudioPresentation#CONTENT_VOICEOVER}. + * @see AudioPresentation#CONTENT_UNKNOWN + * @see AudioPresentation#CONTENT_MAIN + * @see AudioPresentation#CONTENT_MUSIC_AND_EFFECTS + * @see AudioPresentation#CONTENT_VISUALLY_IMPAIRED + * @see AudioPresentation#CONTENT_HEARING_IMPAIRED + * @see AudioPresentation#CONTENT_DIALOG + * @see AudioPresentation#CONTENT_COMMENTARY + * @see AudioPresentation#CONTENT_EMERGENCY + * @see AudioPresentation#CONTENT_VOICEOVER */ @NonNull public static final Key<Integer> KEY_PRESENTATION_CONTENT_CLASSIFIER = createKey("presentation-content-classifier", Integer.class); diff --git a/media/java/android/media/tv/SectionRequest.java b/media/java/android/media/tv/SectionRequest.java index 078e83222e4e..ec0d7f7a2ce4 100644 --- a/media/java/android/media/tv/SectionRequest.java +++ b/media/java/android/media/tv/SectionRequest.java @@ -81,7 +81,7 @@ public final class SectionRequest extends BroadcastInfoRequest implements Parcel /** * Gets the version number of requested session. If it is null, value will be -1. * <p>The consistency of version numbers between request and response depends on - * {@link BroadcastInfoRequest.RequestOption}. If the request has RequestOption value + * {@link BroadcastInfoRequest#getOption()}. If the request has RequestOption value * REQUEST_OPTION_AUTO_UPDATE, then the response may be set to the latest version which may be * different from the version of the request. Otherwise, response with a different version from * its request will be considered invalid. diff --git a/media/java/android/media/tv/SectionResponse.java b/media/java/android/media/tv/SectionResponse.java index f38ea9dfac99..10333fe424a6 100644 --- a/media/java/android/media/tv/SectionResponse.java +++ b/media/java/android/media/tv/SectionResponse.java @@ -76,7 +76,7 @@ public final class SectionResponse extends BroadcastInfoResponse implements Parc /** * Gets the Version number of requested session. If it is null, value will be -1. * <p>The consistency of version numbers between request and response depends on - * {@link BroadcastInfoRequest.RequestOption}. If the request has RequestOption value + * {@link BroadcastInfoRequest#getOption()}. If the request has RequestOption value * REQUEST_OPTION_AUTO_UPDATE, then the response may be set to the latest version which may be * different from the version of the request. Otherwise, response with a different version from * its request will be considered invalid. diff --git a/media/java/android/media/tv/TableRequest.java b/media/java/android/media/tv/TableRequest.java index d9587f6ac089..06df07fbc899 100644 --- a/media/java/android/media/tv/TableRequest.java +++ b/media/java/android/media/tv/TableRequest.java @@ -129,7 +129,7 @@ public final class TableRequest extends BroadcastInfoRequest implements Parcelab /** * Gets the version number of requested table. If it is null, value will be -1. * <p>The consistency of version numbers between request and response depends on - * {@link BroadcastInfoRequest.RequestOption}. If the request has RequestOption value + * {@link BroadcastInfoRequest#getOption()}. If the request has RequestOption value * REQUEST_OPTION_AUTO_UPDATE, then the response may be set to the latest version which may be * different from the version of the request. Otherwise, response with a different version from * its request will be considered invalid. diff --git a/media/java/android/media/tv/TableResponse.java b/media/java/android/media/tv/TableResponse.java index c4fc26ef1932..1daf452fa422 100644 --- a/media/java/android/media/tv/TableResponse.java +++ b/media/java/android/media/tv/TableResponse.java @@ -269,7 +269,7 @@ public final class TableResponse extends BroadcastInfoResponse implements Parcel /** * Gets the version number of requested table. If it is null, value will be -1. * <p>The consistency of version numbers between request and response depends on - * {@link BroadcastInfoRequest.RequestOption}. If the request has RequestOption value + * {@link BroadcastInfoRequest#getOption()}. If the request has RequestOption value * REQUEST_OPTION_AUTO_UPDATE, then the response may be set to the latest version which may be * different from the version of the request. Otherwise, response with a different version from * its request will be considered invalid. diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp index c616b84fa6fb..1c25080939da 100644 --- a/media/jni/android_media_MediaDrm.cpp +++ b/media/jni/android_media_MediaDrm.cpp @@ -38,6 +38,8 @@ #include <mediadrm/IDrmMetricsConsumer.h> #include <mediadrm/IDrm.h> #include <utils/Vector.h> +#include <map> +#include <string> using ::android::os::PersistableBundle; namespace drm = ::android::hardware::drm; @@ -193,6 +195,11 @@ struct LogMessageFields { jclass classId; }; +struct DrmExceptionFields { + jmethodID init; + jclass classId; +}; + struct fields_t { jfieldID context; jmethodID post_event; @@ -215,6 +222,7 @@ struct fields_t { jclass parcelCreatorClassId; KeyStatusFields keyStatus; LogMessageFields logMessage; + std::map<std::string, DrmExceptionFields> exceptionCtors; }; static fields_t gFields; @@ -245,18 +253,32 @@ jobject hidlLogMessagesToJavaList(JNIEnv *env, const Vector<drm::V1_4::LogMessag return arrayList; } -int drmThrowException(JNIEnv* env, const char *className, const DrmStatus &err, const char *msg) { +void resolveDrmExceptionCtor(JNIEnv *env, const char *className) { + jclass clazz; + jmethodID init; + FIND_CLASS(clazz, className); + GET_METHOD_ID(init, clazz, "<init>", "(Ljava/lang/String;III)V"); + gFields.exceptionCtors[std::string(className)] = { + .init = init, + .classId = static_cast<jclass>(env->NewGlobalRef(clazz)) + }; +} + +void drmThrowException(JNIEnv* env, const char *className, const DrmStatus &err, const char *msg) { using namespace android::jnihelp; - jstring _detailMessage = CreateExceptionMsg(env, msg); - int _status = ThrowException(env, className, "(Ljava/lang/String;III)V", - _detailMessage, - err.getCdmErr(), - err.getOemErr(), - err.getContext()); - if (_detailMessage != NULL) { - env->DeleteLocalRef(_detailMessage); + + if (gFields.exceptionCtors.count(std::string(className)) == 0) { + jniThrowException(env, className, msg); + } else { + jstring _detailMessage = CreateExceptionMsg(env, msg); + jobject exception = env->NewObject(gFields.exceptionCtors[std::string(className)].classId, + gFields.exceptionCtors[std::string(className)].init, _detailMessage, + err.getCdmErr(), err.getOemErr(), err.getContext()); + env->Throw(static_cast<jthrowable>(exception)); + if (_detailMessage != NULL) { + env->DeleteLocalRef(_detailMessage); + } } - return _status; } } // namespace anonymous @@ -952,6 +974,10 @@ static void android_media_MediaDrm_native_init(JNIEnv *env) { FIND_CLASS(clazz, "android/media/MediaDrm$LogMessage"); gFields.logMessage.classId = static_cast<jclass>(env->NewGlobalRef(clazz)); GET_METHOD_ID(gFields.logMessage.init, clazz, "<init>", "(JILjava/lang/String;)V"); + + resolveDrmExceptionCtor(env, "android/media/NotProvisionedException"); + resolveDrmExceptionCtor(env, "android/media/ResourceBusyException"); + resolveDrmExceptionCtor(env, "android/media/DeniedByServerException"); } static void android_media_MediaDrm_native_setup( @@ -2192,4 +2218,4 @@ static const JNINativeMethod gMethods[] = { int register_android_media_Drm(JNIEnv *env) { return AndroidRuntime::registerNativeMethods(env, "android/media/MediaDrm", gMethods, NELEM(gMethods)); -} +}
\ No newline at end of file diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index 3efb41dbfe5c..cf26937cbd88 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -163,12 +163,10 @@ public class SecureSettings { Settings.Secure.CHARGING_VIBRATION_ENABLED, Settings.Secure.ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS, Settings.Secure.ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS, - Settings.Secure.TRUST_AGENTS_EXTEND_UNLOCK, Settings.Secure.UI_NIGHT_MODE, Settings.Secure.UI_NIGHT_MODE_CUSTOM_TYPE, Settings.Secure.DARK_THEME_CUSTOM_START_TIME, Settings.Secure.DARK_THEME_CUSTOM_END_TIME, - Settings.Secure.LOCK_SCREEN_WHEN_TRUST_LOST, Settings.Secure.SKIP_DIRECTION, Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES, Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index f6c2f6918739..f78f2028d7b6 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -242,9 +242,7 @@ public class SecureSettingsValidators { Secure.ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS, NON_NEGATIVE_INTEGER_VALIDATOR); VALIDATORS.put(Secure.USER_SETUP_COMPLETE, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ASSIST_GESTURE_SETUP_COMPLETE, BOOLEAN_VALIDATOR); - VALIDATORS.put(Secure.TRUST_AGENTS_EXTEND_UNLOCK, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE, JSON_OBJECT_VALIDATOR); - VALIDATORS.put(Secure.LOCK_SCREEN_WHEN_TRUST_LOST, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.SKIP_GESTURE, BOOLEAN_VALIDATOR); /* * Only used if FeatureFlag "settings_skip_direction_mutable" is enabled. diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java index b404465a8cce..b19d3495c217 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java @@ -961,7 +961,6 @@ public class SettingsBackupAgent extends BackupAgentHelper { lockPatternUtils.setOwnerInfo(value, userId); break; case KEY_LOCK_SETTINGS_VISIBLE_PATTERN_ENABLED: - lockPatternUtils.reportPatternWasChosen(userId); lockPatternUtils.setVisiblePatternEnabled("1".equals(value), userId); break; case KEY_LOCK_SETTINGS_POWER_BUTTON_INSTANTLY_LOCKS: diff --git a/services/core/Android.bp b/services/core/Android.bp index 4e412bbe67df..9ac30f334d0b 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -180,6 +180,7 @@ java_library_static { "android.hidl.manager-V1.2-java", "cbor-java", "icu4j_calendar_astronomer", + "android.security.aaid_aidl-java", "netd-client", "overlayable_policy_aidl-java", "SurfaceFlingerProperties", diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index d94f4f22f2c9..5b496f4766b3 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -16,6 +16,7 @@ package com.android.server; +import static android.os.Flags.stateOfHealthPublic; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent; import static com.android.server.health.Utils.copyV1Battery; @@ -27,7 +28,6 @@ import android.app.BroadcastOptions; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.database.ContentObserver; import android.hardware.health.HealthInfo; import android.hardware.health.V2_1.BatteryCapacityLevel; @@ -1316,10 +1316,14 @@ public final class BatteryService extends SystemService { @Override public int getProperty(int id, final BatteryProperty prop) throws RemoteException { switch (id) { + case BatteryManager.BATTERY_PROPERTY_STATE_OF_HEALTH: + if (stateOfHealthPublic()) { + break; + } + case BatteryManager.BATTERY_PROPERTY_MANUFACTURING_DATE: case BatteryManager.BATTERY_PROPERTY_FIRST_USAGE_DATE: case BatteryManager.BATTERY_PROPERTY_CHARGING_POLICY: - case BatteryManager.BATTERY_PROPERTY_STATE_OF_HEALTH: mContext.enforceCallingPermission( android.Manifest.permission.BATTERY_STATS, null); break; diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java index b890bbd65b95..eae417e1e838 100644 --- a/services/core/java/com/android/server/content/SyncStorageEngine.java +++ b/services/core/java/com/android/server/content/SyncStorageEngine.java @@ -1845,7 +1845,7 @@ public class SyncStorageEngine { private void parseListenForTickles(TypedXmlPullParser parser) { int userId = 0; try { - parser.getAttributeInt(null, XML_ATTR_USER); + userId = parser.getAttributeInt(null, XML_ATTR_USER); } catch (XmlPullParserException e) { Slog.e(TAG, "error parsing the user for listen-for-tickles", e); } diff --git a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java index 46f486d875b6..f572845dc214 100644 --- a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java +++ b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java @@ -311,7 +311,7 @@ public class BiometricDeferredQueue { @Nullable private static synchronized IGateKeeperService getGatekeeperService() { - final IBinder service = ServiceManager.getService(Context.GATEKEEPER_SERVICE); + final IBinder service = ServiceManager.waitForService(Context.GATEKEEPER_SERVICE); if (service == null) { Slog.e(TAG, "Unable to acquire GateKeeperService"); return null; diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 0a02c49192b9..8547c96e13af 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -115,7 +115,6 @@ import android.system.keystore2.Domain; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; -import android.util.EventLog; import android.util.Log; import android.util.LongSparseArray; import android.util.Slog; @@ -861,15 +860,11 @@ public class LockSettingsService extends ILockSettings.Stub { @Override // binder interface public void systemReady() { - if (mContext.checkCallingOrSelfPermission(PERMISSION) != PERMISSION_GRANTED) { - EventLog.writeEvent(0x534e4554, "28251513", getCallingUid(), ""); // SafetyNet - } checkWritePermission(); mHasSecureLockScreen = mContext.getPackageManager() .hasSystemFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN); migrateOldData(); - getGateKeeperService(); getAuthSecretHal(); mDeviceProvisionedObserver.onSystemReady(); @@ -1109,9 +1104,6 @@ public class LockSettingsService extends ILockSettings.Stub { } private final void checkPasswordHavePermission() { - if (mContext.checkCallingOrSelfPermission(PERMISSION) != PERMISSION_GRANTED) { - EventLog.writeEvent(0x534e4554, "28251513", getCallingUid(), ""); // SafetyNet - } mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsHave"); } @@ -1676,6 +1668,7 @@ public class LockSettingsService extends ILockSettings.Stub { + PERMISSION); } } + credential.validateBasicRequirements(); final long identity = Binder.clearCallingIdentity(); try { @@ -1774,10 +1767,6 @@ public class LockSettingsService extends ILockSettings.Stub { } private void onPostPasswordChanged(LockscreenCredential newCredential, int userHandle) { - if (newCredential.isPattern()) { - setBoolean(LockPatternUtils.PATTERN_EVER_CHOSEN_KEY, true, userHandle); - } - updatePasswordHistory(newCredential, userHandle); mContext.getSystemService(TrustManager.class).reportEnabledTrustAgentsChanged(userHandle); } @@ -2239,17 +2228,6 @@ public class LockSettingsService extends ILockSettings.Stub { // credential has matched mBiometricDeferredQueue.addPendingLockoutResetForUser(userId, authResult.syntheticPassword.deriveGkPassword()); - - // perform verifyChallenge with synthetic password which generates the real GK auth - // token and response for the current user - response = mSpManager.verifyChallenge(getGateKeeperService(), - authResult.syntheticPassword, 0L /* challenge */, userId); - if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) { - // This shouldn't really happen: the unwrapping of SP succeeds, but SP doesn't - // match the recorded GK password handle. - Slog.wtf(TAG, "verifyChallenge with SP failed."); - return VerifyCredentialResponse.ERROR; - } } } if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { @@ -2645,7 +2623,7 @@ public class LockSettingsService extends ILockSettings.Stub { return mGateKeeperService; } - final IBinder service = ServiceManager.getService(Context.GATEKEEPER_SERVICE); + final IBinder service = ServiceManager.waitForService(Context.GATEKEEPER_SERVICE); if (service != null) { try { service.linkToDeath(new GateKeeperDiedRecipient(), 0); @@ -2883,7 +2861,7 @@ public class LockSettingsService extends ILockSettings.Stub { * * Also maintains the invariants described in {@link SyntheticPasswordManager} by * setting/clearing the protection (by the SP) on the user's auth-bound Keystore keys when the - * LSKF is added/removed, respectively. If the new LSKF is nonempty, then the Gatekeeper auth + * LSKF is added/removed, respectively. If an LSKF is being added, then the Gatekeeper auth * token is also refreshed. */ @GuardedBy("mSpManager") @@ -2900,9 +2878,7 @@ public class LockSettingsService extends ILockSettings.Stub { // not needed by synchronizeUnifiedWorkChallengeForProfiles() profilePasswords = null; - if (mSpManager.hasSidForUser(userId)) { - mSpManager.verifyChallenge(getGateKeeperService(), sp, 0L, userId); - } else { + if (!mSpManager.hasSidForUser(userId)) { mSpManager.newSidForUser(getGateKeeperService(), sp, userId); mSpManager.verifyChallenge(getGateKeeperService(), sp, 0L, userId); setKeystorePassword(sp.deriveKeyStorePassword(), userId); @@ -3118,6 +3094,7 @@ public class LockSettingsService extends ILockSettings.Stub { private boolean setLockCredentialWithToken(LockscreenCredential credential, long tokenHandle, byte[] token, int userId) { boolean result; + credential.validateBasicRequirements(); synchronized (mSpManager) { if (!mSpManager.hasEscrowData(userId)) { throw new SecurityException("Escrow token is disabled on the current user"); diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java index f107d0bf9932..df95c69e7271 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java @@ -16,8 +16,6 @@ package com.android.server.locksettings; -import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; -import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; import android.app.ActivityManager; @@ -313,16 +311,8 @@ class LockSettingsShellCommand extends ShellCommand { mLockPatternUtils.getRequestedPasswordMetrics(mCurrentUserId); final int requiredComplexity = mLockPatternUtils.getRequestedPasswordComplexity(mCurrentUserId); - final List<PasswordValidationError> errors; - if (credential.isPassword() || credential.isPin()) { - errors = PasswordMetrics.validatePassword(requiredMetrics, requiredComplexity, - credential.isPin(), credential.getCredential()); - } else { - PasswordMetrics metrics = new PasswordMetrics( - credential.isPattern() ? CREDENTIAL_TYPE_PATTERN : CREDENTIAL_TYPE_NONE); - errors = PasswordMetrics.validatePasswordMetrics( - requiredMetrics, requiredComplexity, metrics); - } + final List<PasswordValidationError> errors = + PasswordMetrics.validateCredential(requiredMetrics, requiredComplexity, credential); if (!errors.isEmpty()) { getOutPrintWriter().println( "New credential doesn't satisfy admin policies: " + errors.get(0)); diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java index e8fd6f88359c..7fec377b3e9e 100644 --- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java @@ -1602,7 +1602,7 @@ class SyntheticPasswordManager { /** Destroy all weak token-based SP protectors for the given user. */ public void destroyAllWeakTokenBasedProtectors(int userId) { List<Long> protectorIds = - mStorage.listSyntheticPasswordProtectorsForUser(SECDISCARDABLE_NAME, userId); + mStorage.listSyntheticPasswordProtectorsForUser(SP_BLOB_NAME, userId); for (long protectorId : protectorIds) { SyntheticPasswordBlob blob = SyntheticPasswordBlob.fromBytes(loadState(SP_BLOB_NAME, protectorId, userId)); diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index 2f9536ff6427..23532c1b4eb3 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -261,6 +261,8 @@ public final class OverlayManagerService extends SystemService { private final OverlayActorEnforcer mActorEnforcer; + private int mPrevStartedUserId = -1; + public OverlayManagerService(@NonNull final Context context) { super(context); try { @@ -338,6 +340,10 @@ public final class OverlayManagerService extends SystemService { } private void onStartUser(@UserIdInt int newUserId) { + // Do nothing when start a user that is the same as the one started previously. + if (newUserId == mPrevStartedUserId) { + return; + } try { traceBegin(TRACE_TAG_RRO, "OMS#onStartUser " + newUserId); // ensure overlays in the settings are up-to-date, and propagate @@ -348,6 +354,7 @@ public final class OverlayManagerService extends SystemService { } finally { traceEnd(TRACE_TAG_RRO); } + mPrevStartedUserId = newUserId; } private static String[] getDefaultOverlayPackages() { diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java index 39cd88810961..8bd2982d1ead 100644 --- a/services/core/java/com/android/server/pm/DexOptHelper.java +++ b/services/core/java/com/android/server/pm/DexOptHelper.java @@ -1050,7 +1050,7 @@ public final class DexOptHelper { context.unregisterReceiver(this); artManager.scheduleBackgroundDexoptJob(); } - }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); + }, new IntentFilter(Intent.ACTION_LOCKED_BOOT_COMPLETED)); } /** diff --git a/services/core/java/com/android/server/pm/PackageMetrics.java b/services/core/java/com/android/server/pm/PackageMetrics.java index 80d6ebbd90b3..85e11d66737a 100644 --- a/services/core/java/com/android/server/pm/PackageMetrics.java +++ b/services/core/java/com/android/server/pm/PackageMetrics.java @@ -34,8 +34,11 @@ import java.io.File; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Stream; @@ -155,10 +158,27 @@ final class PackageMetrics { private long getApksSize(File apkDir) { // TODO(b/249294752): also count apk sizes for failed installs final AtomicLong apksSize = new AtomicLong(); - try (Stream<Path> walkStream = Files.walk(apkDir.toPath())) { - walkStream.filter(p -> p.toFile().isFile() - && ApkLiteParseUtils.isApkFile(p.toFile())).forEach( - f -> apksSize.addAndGet(f.toFile().length())); + try { + Files.walkFileTree(apkDir.toPath(), new SimpleFileVisitor<>() { + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) + throws IOException { + if (dir.equals(apkDir.toPath())) { + return FileVisitResult.CONTINUE; + } else { + return FileVisitResult.SKIP_SUBTREE; + } + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) + throws IOException { + if (file.toFile().isFile() && ApkLiteParseUtils.isApkFile(file.toFile())) { + apksSize.addAndGet(file.toFile().length()); + } + return FileVisitResult.CONTINUE; + } + }); } catch (IOException e) { // ignore } diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java index f0bf1ea80570..d0c346a63889 100644 --- a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java +++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java @@ -334,7 +334,10 @@ public class ArtStatsLogUtils { ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__STATUS__STATUS_UNKNOWN), cancellationReason, durationMs, - 0); // deprecated, used to be durationIncludingSleepMs + 0, // deprecated, used to be durationIncludingSleepMs + 0, // optimizedPackagesCount + 0, // packagesDependingOnBootClasspathCount + 0); // totalPackagesCount } } } diff --git a/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java b/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java index c908acdd1d6c..d5bc91278aa8 100644 --- a/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java +++ b/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java @@ -24,9 +24,10 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.os.Binder; import android.os.RemoteException; import android.os.UserHandle; -import android.security.keymaster.IKeyAttestationApplicationIdProvider; -import android.security.keymaster.KeyAttestationApplicationId; -import android.security.keymaster.KeyAttestationPackageInfo; +import android.security.keystore.IKeyAttestationApplicationIdProvider; +import android.security.keystore.KeyAttestationApplicationId; +import android.security.keystore.KeyAttestationPackageInfo; +import android.security.keystore.Signature; /** * @hide @@ -64,14 +65,25 @@ public class KeyAttestationApplicationIdProviderService for (int i = 0; i < packageNames.length; ++i) { PackageInfo packageInfo = mPackageManager.getPackageInfoAsUser(packageNames[i], PackageManager.GET_SIGNATURES, userId); - keyAttestationPackageInfos[i] = new KeyAttestationPackageInfo(packageNames[i], - packageInfo.getLongVersionCode(), packageInfo.signatures); + KeyAttestationPackageInfo pInfo = new KeyAttestationPackageInfo(); + pInfo.packageName = new String(packageNames[i]); + pInfo.versionCode = packageInfo.getLongVersionCode(); + pInfo.signatures = new Signature[packageInfo.signatures.length]; + for (int index = 0; index < packageInfo.signatures.length; index++) { + Signature sign = new Signature(); + sign.data = packageInfo.signatures[index].toByteArray(); + pInfo.signatures[index] = sign; + } + + keyAttestationPackageInfos[i] = pInfo; } } catch (NameNotFoundException nnfe) { throw new RemoteException(nnfe.getMessage()); } finally { Binder.restoreCallingIdentity(token); } - return new KeyAttestationApplicationId(keyAttestationPackageInfos); + KeyAttestationApplicationId attestAppId = new KeyAttestationApplicationId(); + attestAppId.packageInfos = keyAttestationPackageInfos; + return attestAppId; } } diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index 36529d824f41..635e11be3a16 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -31,7 +31,6 @@ import android.app.trust.ITrustListener; import android.app.trust.ITrustManager; import android.content.BroadcastReceiver; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -42,11 +41,9 @@ import android.content.pm.UserInfo; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; -import android.database.ContentObserver; import android.graphics.drawable.Drawable; import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricSourceType; -import android.net.Uri; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -237,7 +234,6 @@ public class TrustManagerService extends SystemService { mIdleTrustableTimeoutAlarmListenerForUser = new SparseArray<>(); private AlarmManager mAlarmManager; private final Object mAlarmLock = new Object(); - private final SettingsObserver mSettingsObserver; private final StrongAuthTracker mStrongAuthTracker; @@ -279,7 +275,6 @@ public class TrustManagerService extends SystemService { mLockPatternUtils = injector.getLockPatternUtils(); mStrongAuthTracker = new StrongAuthTracker(context, injector.getLooper()); mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); - mSettingsObserver = new SettingsObserver(mHandler); } @Override @@ -307,103 +302,10 @@ public class TrustManagerService extends SystemService { } } - // Extend unlock config and logic - private final class SettingsObserver extends ContentObserver { - private final Uri TRUST_AGENTS_EXTEND_UNLOCK = - Settings.Secure.getUriFor(Settings.Secure.TRUST_AGENTS_EXTEND_UNLOCK); - - private final Uri LOCK_SCREEN_WHEN_TRUST_LOST = - Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_WHEN_TRUST_LOST); - - private final boolean mIsAutomotive; - private final ContentResolver mContentResolver; - private boolean mTrustAgentsNonrenewableTrust; - private boolean mLockWhenTrustLost; - - /** - * Creates a settings observer - * - * @param handler The handler to run {@link #onChange} on, or null if none. - */ - SettingsObserver(Handler handler) { - super(handler); - - PackageManager packageManager = getContext().getPackageManager(); - mIsAutomotive = packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); - - mContentResolver = getContext().getContentResolver(); - updateContentObserver(); - } - - void updateContentObserver() { - mContentResolver.unregisterContentObserver(this); - mContentResolver.registerContentObserver(TRUST_AGENTS_EXTEND_UNLOCK, - false /* notifyForDescendents */, - this /* observer */, - mCurrentUser); - mContentResolver.registerContentObserver(LOCK_SCREEN_WHEN_TRUST_LOST, - false /* notifyForDescendents */, - this /* observer */, - mCurrentUser); - - // Update the value immediately - onChange(true /* selfChange */, TRUST_AGENTS_EXTEND_UNLOCK); - onChange(true /* selfChange */, LOCK_SCREEN_WHEN_TRUST_LOST); - } - - @Override - public void onChange(boolean selfChange, Uri uri) { - if (TRUST_AGENTS_EXTEND_UNLOCK.equals(uri)) { - // Smart lock should only grant non-renewable trust. The only exception is for - // automotive, where it can actively unlock the head unit. - int defaultValue = mIsAutomotive ? 0 : 1; - - mTrustAgentsNonrenewableTrust = - Settings.Secure.getIntForUser( - mContentResolver, - Settings.Secure.TRUST_AGENTS_EXTEND_UNLOCK, - defaultValue, - mCurrentUser) != 0; - } else if (LOCK_SCREEN_WHEN_TRUST_LOST.equals(uri)) { - mLockWhenTrustLost = - Settings.Secure.getIntForUser( - mContentResolver, - Settings.Secure.LOCK_SCREEN_WHEN_TRUST_LOST, - 0 /* default */, - mCurrentUser) != 0; - } - } - - boolean getTrustAgentsNonrenewableTrust() { - return mTrustAgentsNonrenewableTrust; - } - - boolean getLockWhenTrustLost() { - return mLockWhenTrustLost; - } - } - - private void maybeLockScreen(int userId) { - if (userId != mCurrentUser) { - return; - } - - if (mSettingsObserver.getLockWhenTrustLost()) { - if (DEBUG) Slog.d(TAG, "Locking device because trust was lost"); - try { - WindowManagerGlobal.getWindowManagerService().lockNow(null); - } catch (RemoteException e) { - Slog.e(TAG, "Error locking screen when trust was lost"); - } - - // If active unlocking is not allowed, cancel any pending trust timeouts because the - // screen is already locked. - TrustedTimeoutAlarmListener alarm = mTrustTimeoutAlarmListenerForUser.get(userId); - if (alarm != null && mSettingsObserver.getTrustAgentsNonrenewableTrust()) { - mAlarmManager.cancel(alarm); - alarm.setQueued(false /* isQueued */); - } - } + // Automotive head units can be unlocked by a trust agent, even when the agent doesn't use + // FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE. + private boolean isAutomotive() { + return getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); } private void scheduleTrustTimeout(boolean override, boolean isTrustableTimeout) { @@ -600,12 +502,10 @@ public class TrustManagerService extends SystemService { synchronized (mUserTrustState) { wasTrusted = (mUserTrustState.get(userId) == TrustState.TRUSTED); wasTrustable = (mUserTrustState.get(userId) == TrustState.TRUSTABLE); - boolean isAutomotive = getContext().getPackageManager().hasSystemFeature( - PackageManager.FEATURE_AUTOMOTIVE); boolean renewingTrust = wasTrustable && ( (flags & TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) != 0); boolean canMoveToTrusted = - alreadyUnlocked || isFromUnlock || renewingTrust || isAutomotive; + alreadyUnlocked || isFromUnlock || renewingTrust || isAutomotive(); boolean upgradingTrustForCurrentUser = (userId == mCurrentUser); if (trustedByAtLeastOneAgent && wasTrusted) { @@ -632,9 +532,7 @@ public class TrustManagerService extends SystemService { isNowTrusted, newlyUnlocked, userId, flags, getTrustGrantedMessages(userId)); if (isNowTrusted != wasTrusted) { refreshDeviceLockedForUser(userId); - if (!isNowTrusted) { - maybeLockScreen(userId); - } else { + if (isNowTrusted) { boolean isTrustableTimeout = (flags & FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) != 0; // Every time we grant renewable trust we should override the idle trustable @@ -1357,7 +1255,7 @@ public class TrustManagerService extends SystemService { for (int i = 0; i < mActiveAgents.size(); i++) { AgentInfo info = mActiveAgents.valueAt(i); if (info.userId == userId) { - if (info.agent.isTrustableOrWaitingForDowngrade()) { + if (info.agent.isManagingTrust()) { return true; } } @@ -1855,9 +1753,7 @@ public class TrustManagerService extends SystemService { synchronized(mUsersUnlockedByBiometric) { mUsersUnlockedByBiometric.put(userId, true); } - // In non-renewable trust mode we need to refresh trust state here, which will call - // refreshDeviceLockedForUser() - int updateTrustOnUnlock = mSettingsObserver.getTrustAgentsNonrenewableTrust() ? 1 : 0; + int updateTrustOnUnlock = isAutomotive() ? 0 : 1; mHandler.obtainMessage(MSG_REFRESH_DEVICE_LOCKED_FOR_USER, userId, updateTrustOnUnlock).sendToTarget(); mHandler.obtainMessage(MSG_REFRESH_TRUSTABLE_TIMERS_AFTER_AUTH, userId).sendToTarget(); @@ -1966,7 +1862,6 @@ public class TrustManagerService extends SystemService { break; case MSG_SWITCH_USER: mCurrentUser = msg.arg1; - mSettingsObserver.updateContentObserver(); refreshDeviceLockedForUser(UserHandle.USER_ALL); break; case MSG_STOP_USER: @@ -2196,7 +2091,6 @@ public class TrustManagerService extends SystemService { mLockPatternUtils.requireStrongAuth( mStrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED, mUserId); } - maybeLockScreen(mUserId); } protected abstract void handleAlarm(); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 34170fa4c8b5..ab218a671823 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -5127,8 +5127,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { boolean deviceWideOnly) { final CallerIdentity caller = getCallerIdentity(); Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle) - && (isSystemUid(caller) || hasCallingOrSelfPermission( - permission.SET_INITIAL_LOCK))); + && (isSystemUid(caller) + // Accept any permission that ILockSettings#setLockCredential() accepts. + || hasCallingOrSelfPermission(permission.SET_INITIAL_LOCK) + || hasCallingOrSelfPermission(permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS) + || hasCallingOrSelfPermission(permission.ACCESS_KEYGUARD_SECURE_STORAGE))); return getPasswordMinimumMetricsUnchecked(userHandle, deviceWideOnly); } @@ -5725,20 +5728,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final int callingUid = caller.getUid(); final int userHandle = UserHandle.getUserId(callingUid); final boolean isPin = PasswordMetrics.isNumericOnly(password); + final LockscreenCredential newCredential; + if (isPin) { + newCredential = LockscreenCredential.createPin(password); + } else { + newCredential = LockscreenCredential.createPasswordOrNone(password); + } synchronized (getLockObject()) { final PasswordMetrics minMetrics = getPasswordMinimumMetricsUnchecked(userHandle); - final List<PasswordValidationError> validationErrors; final int complexity = getAggregatedPasswordComplexityLocked(userHandle); - // TODO: Consider changing validation API to take LockscreenCredential. - if (password.isEmpty()) { - validationErrors = PasswordMetrics.validatePasswordMetrics( - minMetrics, complexity, new PasswordMetrics(CREDENTIAL_TYPE_NONE)); - } else { - // TODO(b/120484642): remove getBytes() below - validationErrors = PasswordMetrics.validatePassword( - minMetrics, complexity, isPin, password.getBytes()); - } - + final List<PasswordValidationError> validationErrors = + PasswordMetrics.validateCredential(minMetrics, complexity, newCredential); if (!validationErrors.isEmpty()) { Slogf.w(LOG_TAG, "Failed to reset password due to constraint violation: %s", validationErrors.get(0)); @@ -5762,12 +5762,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Don't do this with the lock held, because it is going to call // back in to the service. final long ident = mInjector.binderClearCallingIdentity(); - final LockscreenCredential newCredential; - if (isPin) { - newCredential = LockscreenCredential.createPin(password); - } else { - newCredential = LockscreenCredential.createPasswordOrNone(password); - } try { if (tokenHandle == 0 || token == null) { if (!mLockPatternUtils.setLockCredential(newCredential, diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 99a3b808e082..cc07ccefa119 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -46,7 +46,6 @@ import static android.app.admin.DevicePolicyManager.WIFI_SECURITY_ENTERPRISE_EAP import static android.app.admin.DevicePolicyManager.WIFI_SECURITY_OPEN; import static android.app.admin.DevicePolicyManager.WIFI_SECURITY_PERSONAL; import static android.app.admin.DevicePolicyManager.WIPE_EUICC; -import static android.app.admin.PasswordMetrics.computeForPasswordOrPin; import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE; import static android.location.LocationManager.FUSED_PROVIDER; import static android.location.LocationManager.GPS_PROVIDER; @@ -5479,8 +5478,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { reset(mContext.spiedContext); - PasswordMetrics passwordMetricsNoSymbols = computeForPasswordOrPin( - "abcdXYZ5".getBytes(), /* isPin */ false); + PasswordMetrics passwordMetricsNoSymbols = metricsForPassword("abcdXYZ5"); setActivePasswordState(passwordMetricsNoSymbols); assertThat(dpm.isActivePasswordSufficient()).isTrue(); @@ -5507,8 +5505,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { reset(mContext.spiedContext); assertThat(dpm.isActivePasswordSufficient()).isFalse(); - PasswordMetrics passwordMetricsWithSymbols = computeForPasswordOrPin( - "abcd.XY5".getBytes(), /* isPin */ false); + PasswordMetrics passwordMetricsWithSymbols = metricsForPassword("abcd.XY5"); setActivePasswordState(passwordMetricsWithSymbols); assertThat(dpm.isActivePasswordSufficient()).isTrue(); @@ -5564,7 +5561,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { parentDpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_MEDIUM); when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM)) - .thenReturn(computeForPasswordOrPin("184342".getBytes(), /* isPin */ true)); + .thenReturn(metricsForPin("184342")); // Numeric password is compliant with current requirement (QUALITY_NUMERIC set explicitly // on the parent admin) @@ -5685,7 +5682,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Set a work challenge and verify profile.isActivePasswordSufficient is now true when(getServices().lockSettingsInternal.getUserPasswordMetrics(managedProfileUserId)) - .thenReturn(computeForPasswordOrPin("abcdXYZ5".getBytes(), /* isPin */ false)); + .thenReturn(metricsForPassword("abcdXYZ5")); assertThat(dpm.isActivePasswordSufficient()).isTrue(); assertThat(parentDpm.isActivePasswordSufficient()).isTrue(); } @@ -5710,7 +5707,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Set a work challenge and verify profile.isActivePasswordSufficient is now true when(getServices().lockSettingsInternal.getUserPasswordMetrics(managedProfileUserId)) - .thenReturn(computeForPasswordOrPin("5156".getBytes(), /* isPin */ true)); + .thenReturn(metricsForPin("5156")); assertThat(dpm.isActivePasswordSufficient()).isTrue(); assertThat(parentDpm.isActivePasswordSufficient()).isTrue(); } @@ -5735,7 +5732,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Set a device lockscreen and verify parent.isActivePasswordSufficient is now true when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM)) - .thenReturn(computeForPasswordOrPin("acbdXYZ5".getBytes(), /* isPin */ false)); + .thenReturn(metricsForPassword("acbdXYZ5")); assertThat(dpm.isActivePasswordSufficient()).isTrue(); assertThat(parentDpm.isActivePasswordSufficient()).isTrue(); } @@ -5758,7 +5755,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Set a device lockscreen and verify parent.isActivePasswordSufficient is now true when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM)) - .thenReturn(computeForPasswordOrPin("1234".getBytes(), /* isPin */ true)); + .thenReturn(metricsForPin("1234")); assertThat(dpm.isActivePasswordSufficient()).isTrue(); assertThat(parentDpm.isActivePasswordSufficient()).isTrue(); } @@ -5783,7 +5780,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Set a device lockscreen and verify {profile, parent}.isActivePasswordSufficient is true when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM)) - .thenReturn(computeForPasswordOrPin("abcdXYZ5".getBytes(), /* isPin */ false)); + .thenReturn(metricsForPassword("abcdXYZ5")); assertThat(dpm.isActivePasswordSufficient()).isTrue(); assertThat(parentDpm.isActivePasswordSufficient()).isTrue(); } @@ -5806,7 +5803,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Set a device lockscreen and verify {profile, parent}.isActivePasswordSufficient is true when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM)) - .thenReturn(computeForPasswordOrPin("51567548".getBytes(), /* isPin */ true)); + .thenReturn(metricsForPin("51567548")); assertThat(dpm.isActivePasswordSufficient()).isTrue(); assertThat(parentDpm.isActivePasswordSufficient()).isTrue(); } @@ -5831,7 +5828,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Set a device lockscreen and verify {profile, parent}.isActivePasswordSufficient is true when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM)) - .thenReturn(computeForPasswordOrPin("abcdXYZ5".getBytes(), /* isPin */ false)); + .thenReturn(metricsForPassword("abcdXYZ5")); assertThat(dpm.isActivePasswordSufficient()).isTrue(); assertThat(parentDpm.isActivePasswordSufficient()).isTrue(); } @@ -5854,7 +5851,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Set a device lockscreen and verify {profile, parent}.isActivePasswordSufficient is true when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM)) - .thenReturn(computeForPasswordOrPin("5156".getBytes(), /* isPin */ true)); + .thenReturn(metricsForPin("5156")); assertThat(dpm.isActivePasswordSufficient()).isTrue(); assertThat(parentDpm.isActivePasswordSufficient()).isTrue(); } @@ -6909,7 +6906,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { .thenReturn(CALLER_USER_HANDLE); when(getServices().lockSettingsInternal .getUserPasswordMetrics(CALLER_USER_HANDLE)) - .thenReturn(computeForPasswordOrPin("asdf".getBytes(), /* isPin */ false)); + .thenReturn(metricsForPassword("asdf")); assertThat(dpm.getPasswordComplexity()).isEqualTo(PASSWORD_COMPLEXITY_MEDIUM); } @@ -6929,10 +6926,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { when(getServices().lockSettingsInternal .getUserPasswordMetrics(CALLER_USER_HANDLE)) - .thenReturn(computeForPasswordOrPin("asdf".getBytes(), /* isPin */ false)); + .thenReturn(metricsForPassword("asdf")); when(getServices().lockSettingsInternal .getUserPasswordMetrics(parentUser.id)) - .thenReturn(computeForPasswordOrPin("parentUser".getBytes(), /* isPin */ false)); + .thenReturn(metricsForPassword("parentUser")); assertThat(dpm.getPasswordComplexity()).isEqualTo(PASSWORD_COMPLEXITY_HIGH); } @@ -7654,15 +7651,13 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertThat(dpm.getPasswordComplexity()).isEqualTo(PASSWORD_COMPLEXITY_NONE); reset(mContext.spiedContext); - PasswordMetrics passwordMetricsNoSymbols = computeForPasswordOrPin( - "1234".getBytes(), /* isPin */ true); + PasswordMetrics passwordMetricsNoSymbols = metricsForPin("1234"); setActivePasswordState(passwordMetricsNoSymbols); assertThat(dpm.getPasswordComplexity()).isEqualTo(PASSWORD_COMPLEXITY_LOW); assertThat(dpm.isActivePasswordSufficient()).isFalse(); reset(mContext.spiedContext); - passwordMetricsNoSymbols = computeForPasswordOrPin( - "84125312943a".getBytes(), /* isPin */ false); + passwordMetricsNoSymbols = metricsForPassword("84125312943a"); setActivePasswordState(passwordMetricsNoSymbols); assertThat(dpm.getPasswordComplexity()).isEqualTo(PASSWORD_COMPLEXITY_HIGH); // using isActivePasswordSufficient @@ -8838,4 +8833,12 @@ public class DevicePolicyManagerTest extends DpmTestBase { assumeTrue("device doesn't support deprecated password APIs", isDeprecatedPasswordApisSupported()); } + + private static PasswordMetrics metricsForPassword(String password) { + return PasswordMetrics.computeForCredential(LockscreenCredential.createPassword(password)); + } + + private static PasswordMetrics metricsForPin(String pin) { + return PasswordMetrics.computeForCredential(LockscreenCredential.createPin(pin)); + } } diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java index d8e4fdae5a34..7189383d180e 100644 --- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java @@ -285,6 +285,7 @@ public final class DeviceStateManagerServiceTest { assertEquals(info.currentState, DEFAULT_DEVICE_STATE.getIdentifier()); } + @FlakyTest(bugId = 297949293) @Test public void getDeviceStateInfo_baseStateAndCommittedStateNotSet() throws RemoteException { // Create a provider and a service without an initial base state. diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java index 60a033fde427..5a62d92e8e12 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java @@ -77,6 +77,26 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { testSetCredentialFailsWithoutLockScreen(PRIMARY_USER_ID, newPassword("password")); } + @Test(expected = IllegalArgumentException.class) + public void testSetTooShortPatternFails() throws RemoteException { + mService.setLockCredential(newPattern("123"), nonePassword(), PRIMARY_USER_ID); + } + + @Test(expected = IllegalArgumentException.class) + public void testSetTooShortPinFails() throws RemoteException { + mService.setLockCredential(newPin("123"), nonePassword(), PRIMARY_USER_ID); + } + + @Test(expected = IllegalArgumentException.class) + public void testSetTooShortPassword() throws RemoteException { + mService.setLockCredential(newPassword("123"), nonePassword(), PRIMARY_USER_ID); + } + + @Test(expected = IllegalArgumentException.class) + public void testSetPasswordWithInvalidChars() throws RemoteException { + mService.setLockCredential(newPassword("§µ¿¶¥£"), nonePassword(), PRIMARY_USER_ID); + } + @Test public void testSetPatternPrimaryUser() throws RemoteException { setAndVerifyCredential(PRIMARY_USER_ID, newPattern("123456789")); @@ -94,7 +114,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { @Test public void testChangePatternPrimaryUser() throws RemoteException { - testChangeCredential(PRIMARY_USER_ID, newPassword("!£$%^&*(())"), newPattern("1596321")); + testChangeCredential(PRIMARY_USER_ID, newPassword("password"), newPattern("1596321")); } @Test @@ -185,7 +205,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { assertNotNull(mGateKeeperService.getAuthToken(MANAGED_PROFILE_USER_ID)); assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID)); - setCredential(PRIMARY_USER_ID, newPassword("pwd"), primaryPassword); + setCredential(PRIMARY_USER_ID, newPassword("password"), primaryPassword); assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( profilePassword, MANAGED_PROFILE_USER_ID, 0 /* flags */) .getResponseCode()); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java index ce0347dbe4ac..dee77806e4f3 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java @@ -215,12 +215,12 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { @Test public void testChangeCredentialKeepsAuthSecret() throws RemoteException { LockscreenCredential password = newPassword("password"); - LockscreenCredential badPassword = newPassword("new"); + LockscreenCredential newPassword = newPassword("newPassword"); initSpAndSetCredential(PRIMARY_USER_ID, password); - mService.setLockCredential(badPassword, password, PRIMARY_USER_ID); + mService.setLockCredential(newPassword, password, PRIMARY_USER_ID); assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - badPassword, PRIMARY_USER_ID, 0 /* flags */).getResponseCode()); + newPassword, PRIMARY_USER_ID, 0 /* flags */).getResponseCode()); // Check the same secret was passed each time ArgumentCaptor<byte[]> secret = ArgumentCaptor.forClass(byte[].class); diff --git a/services/tests/servicestests/src/com/android/server/net/LockdownVpnTrackerTest.java b/services/tests/servicestests/src/com/android/server/net/LockdownVpnTrackerTest.java new file mode 100644 index 000000000000..949f8e7a6ab0 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/net/LockdownVpnTrackerTest.java @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.net; + +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static android.net.VpnManager.NOTIFICATION_CHANNEL_VPN; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import android.app.Notification; +import android.app.NotificationManager; +import android.content.Context; +import android.content.ContextWrapper; +import android.net.ConnectivityManager; +import android.net.ConnectivityManager.NetworkCallback; +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkInfo; +import android.os.Handler; +import android.os.HandlerThread; +import android.text.TextUtils; + +import androidx.test.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.android.internal.R; +import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; +import com.android.internal.net.VpnConfig; +import com.android.internal.net.VpnProfile; +import com.android.server.connectivity.Vpn; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class LockdownVpnTrackerTest { + private static final NetworkCapabilities TEST_CELL_NC = new NetworkCapabilities.Builder() + .addTransportType(TRANSPORT_CELLULAR) + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED) + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) + .build(); + private static final LinkProperties TEST_CELL_LP = new LinkProperties(); + + static { + TEST_CELL_LP.setInterfaceName("rmnet0"); + TEST_CELL_LP.addLinkAddress(new LinkAddress("192.0.2.2/25")); + } + + // Use a context wrapper instead of a mock since LockdownVpnTracker builds notifications which + // is tedious and currently unnecessary to mock. + private final Context mContext = new ContextWrapper(InstrumentationRegistry.getContext()) { + @Override + public Object getSystemService(String name) { + if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm; + if (Context.NOTIFICATION_SERVICE.equals(name)) return mNotificationManager; + + return super.getSystemService(name); + } + }; + @Mock private ConnectivityManager mCm; + @Mock private Vpn mVpn; + @Mock private NotificationManager mNotificationManager; + @Mock private NetworkInfo mVpnNetworkInfo; + @Mock private VpnConfig mVpnConfig; + @Mock private Network mNetwork; + @Mock private Network mNetwork2; + @Mock private Network mVpnNetwork; + + private HandlerThread mHandlerThread; + private Handler mHandler; + private VpnProfile mProfile; + + private VpnProfile createTestVpnProfile() { + final String profileName = "testVpnProfile"; + final VpnProfile profile = new VpnProfile(profileName); + profile.name = "My VPN"; + profile.server = "192.0.2.1"; + profile.dnsServers = "8.8.8.8"; + profile.ipsecIdentifier = "My ipsecIdentifier"; + profile.ipsecSecret = "My PSK"; + profile.type = VpnProfile.TYPE_IKEV2_IPSEC_PSK; + + return profile; + } + + private NetworkCallback getDefaultNetworkCallback() { + final ArgumentCaptor<NetworkCallback> callbackCaptor = + ArgumentCaptor.forClass(NetworkCallback.class); + verify(mCm).registerSystemDefaultNetworkCallback(callbackCaptor.capture(), eq(mHandler)); + return callbackCaptor.getValue(); + } + + private NetworkCallback getVpnNetworkCallback() { + final ArgumentCaptor<NetworkCallback> callbackCaptor = + ArgumentCaptor.forClass(NetworkCallback.class); + verify(mCm).registerNetworkCallback(any(), callbackCaptor.capture(), eq(mHandler)); + return callbackCaptor.getValue(); + } + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + mHandlerThread = new HandlerThread("LockdownVpnTrackerTest"); + mHandlerThread.start(); + mHandler = mHandlerThread.getThreadHandler(); + + doReturn(mVpnNetworkInfo).when(mVpn).getNetworkInfo(); + doReturn(false).when(mVpnNetworkInfo).isConnectedOrConnecting(); + doReturn(mVpnConfig).when(mVpn).getLegacyVpnConfig(); + // mVpnConfig is a mock but the production code will try to add addresses in this array + // assuming it's non-null, so it needs to be initialized. + mVpnConfig.addresses = new ArrayList<>(); + + mProfile = createTestVpnProfile(); + } + + @After + public void tearDown() throws Exception { + if (mHandlerThread != null) { + mHandlerThread.quitSafely(); + mHandlerThread.join(); + } + } + + private LockdownVpnTracker initAndVerifyLockdownVpnTracker() { + final LockdownVpnTracker lockdownVpnTracker = + new LockdownVpnTracker(mContext, mHandler, mVpn, mProfile); + lockdownVpnTracker.init(); + verify(mVpn).setEnableTeardown(false); + verify(mVpn).setLockdown(true); + verify(mCm).setLegacyLockdownVpnEnabled(true); + verify(mVpn).stopVpnRunnerPrivileged(); + verify(mNotificationManager).cancel(any(), eq(SystemMessage.NOTE_VPN_STATUS)); + + return lockdownVpnTracker; + } + + private void callCallbacksForNetworkConnect(NetworkCallback callback, Network network, + NetworkCapabilities nc, LinkProperties lp, boolean blocked) { + callback.onAvailable(network); + callback.onCapabilitiesChanged(network, nc); + callback.onLinkPropertiesChanged(network, lp); + callback.onBlockedStatusChanged(network, blocked); + } + + private void callCallbacksForNetworkConnect(NetworkCallback callback, Network network) { + callCallbacksForNetworkConnect( + callback, network, TEST_CELL_NC, TEST_CELL_LP, true /* blocked */); + } + + private boolean isExpectedNotification(Notification notification, int titleRes, int iconRes) { + if (!NOTIFICATION_CHANNEL_VPN.equals(notification.getChannelId())) { + return false; + } + final CharSequence expectedTitle = mContext.getString(titleRes); + final CharSequence actualTitle = notification.extras.getCharSequence( + Notification.EXTRA_TITLE); + if (!TextUtils.equals(expectedTitle, actualTitle)) { + return false; + } + return notification.getSmallIcon().getResId() == iconRes; + } + + @Test + public void testShutdown() { + final LockdownVpnTracker lockdownVpnTracker = initAndVerifyLockdownVpnTracker(); + final NetworkCallback defaultCallback = getDefaultNetworkCallback(); + final NetworkCallback vpnCallback = getVpnNetworkCallback(); + clearInvocations(mVpn, mCm, mNotificationManager); + + lockdownVpnTracker.shutdown(); + verify(mVpn).stopVpnRunnerPrivileged(); + verify(mVpn).setLockdown(false); + verify(mCm).setLegacyLockdownVpnEnabled(false); + verify(mNotificationManager).cancel(any(), eq(SystemMessage.NOTE_VPN_STATUS)); + verify(mVpn).setEnableTeardown(true); + verify(mCm).unregisterNetworkCallback(defaultCallback); + verify(mCm).unregisterNetworkCallback(vpnCallback); + } + + @Test + public void testDefaultNetworkConnected() { + initAndVerifyLockdownVpnTracker(); + final NetworkCallback defaultCallback = getDefaultNetworkCallback(); + clearInvocations(mVpn, mCm, mNotificationManager); + + // mNetwork connected and available. + callCallbacksForNetworkConnect(defaultCallback, mNetwork); + + // Vpn is starting + verify(mVpn).startLegacyVpnPrivileged(mProfile, mNetwork, TEST_CELL_LP); + verify(mNotificationManager).notify(any(), eq(SystemMessage.NOTE_VPN_STATUS), + argThat(notification -> isExpectedNotification(notification, + R.string.vpn_lockdown_connecting, R.drawable.vpn_disconnected))); + } + + private void doTestDefaultLpChanged(LinkProperties startingLp, LinkProperties newLp) { + initAndVerifyLockdownVpnTracker(); + final NetworkCallback defaultCallback = getDefaultNetworkCallback(); + callCallbacksForNetworkConnect( + defaultCallback, mNetwork, TEST_CELL_NC, startingLp, true /* blocked */); + clearInvocations(mVpn, mCm, mNotificationManager); + + // LockdownVpnTracker#handleStateChangedLocked() is not called on the same network even if + // the LinkProperties change. + defaultCallback.onLinkPropertiesChanged(mNetwork, newLp); + + // Ideally the VPN should start if it hasn't already, but it doesn't because nothing calls + // LockdownVpnTracker#handleStateChangedLocked. This is a bug. + // TODO: consider fixing this. + verify(mVpn, never()).stopVpnRunnerPrivileged(); + verify(mVpn, never()).startLegacyVpnPrivileged(any(), any(), any()); + verify(mNotificationManager, never()).cancel(any(), eq(SystemMessage.NOTE_VPN_STATUS)); + } + + @Test + public void testDefaultLPChanged_V4AddLinkAddressV4() { + final LinkProperties lp = new LinkProperties(TEST_CELL_LP); + lp.setInterfaceName("rmnet0"); + lp.addLinkAddress(new LinkAddress("192.0.2.3/25")); + doTestDefaultLpChanged(TEST_CELL_LP, lp); + } + + @Test + public void testDefaultLPChanged_V4AddLinkAddressV6() { + final LinkProperties lp = new LinkProperties(); + lp.setInterfaceName("rmnet0"); + lp.addLinkAddress(new LinkAddress("192.0.2.3/25")); + final LinkProperties newLp = new LinkProperties(lp); + newLp.addLinkAddress(new LinkAddress("2001:db8::1/64")); + doTestDefaultLpChanged(lp, newLp); + } + + @Test + public void testDefaultLPChanged_V6AddLinkAddressV4() { + final LinkProperties lp = new LinkProperties(); + lp.setInterfaceName("rmnet0"); + lp.addLinkAddress(new LinkAddress("2001:db8::1/64")); + final LinkProperties newLp = new LinkProperties(lp); + newLp.addLinkAddress(new LinkAddress("192.0.2.3/25")); + doTestDefaultLpChanged(lp, newLp); + } + + @Test + public void testDefaultLPChanged_AddLinkAddressV4() { + final LinkProperties lp = new LinkProperties(); + lp.setInterfaceName("rmnet0"); + doTestDefaultLpChanged(lp, TEST_CELL_LP); + } + + @Test + public void testDefaultNetworkChanged() { + initAndVerifyLockdownVpnTracker(); + final NetworkCallback defaultCallback = getDefaultNetworkCallback(); + final NetworkCallback vpnCallback = getVpnNetworkCallback(); + callCallbacksForNetworkConnect(defaultCallback, mNetwork); + clearInvocations(mVpn, mCm, mNotificationManager); + + // New network and LinkProperties received + final NetworkCapabilities wifiNc = new NetworkCapabilities.Builder() + .addTransportType(TRANSPORT_WIFI) + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED) + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) + .build(); + final LinkProperties wifiLp = new LinkProperties(); + wifiLp.setInterfaceName("wlan0"); + callCallbacksForNetworkConnect( + defaultCallback, mNetwork2, wifiNc, wifiLp, true /* blocked */); + + // Vpn is restarted. + verify(mVpn).stopVpnRunnerPrivileged(); + verify(mVpn).startLegacyVpnPrivileged(mProfile, mNetwork2, wifiLp); + verify(mNotificationManager, never()).cancel(any(), eq(SystemMessage.NOTE_VPN_STATUS)); + verify(mNotificationManager).notify(any(), eq(SystemMessage.NOTE_VPN_STATUS), + argThat(notification -> isExpectedNotification(notification, + R.string.vpn_lockdown_connecting, R.drawable.vpn_disconnected))); + + // Vpn is Connected + doReturn(true).when(mVpnNetworkInfo).isConnectedOrConnecting(); + doReturn(true).when(mVpnNetworkInfo).isConnected(); + vpnCallback.onAvailable(mVpnNetwork); + verify(mNotificationManager).notify(any(), eq(SystemMessage.NOTE_VPN_STATUS), + argThat(notification -> isExpectedNotification(notification, + R.string.vpn_lockdown_connected, R.drawable.vpn_connected))); + + } + + @Test + public void testSystemDefaultLost() { + initAndVerifyLockdownVpnTracker(); + final NetworkCallback defaultCallback = getDefaultNetworkCallback(); + // mNetwork connected + callCallbacksForNetworkConnect(defaultCallback, mNetwork); + clearInvocations(mVpn, mCm, mNotificationManager); + + defaultCallback.onLost(mNetwork); + + // Vpn is stopped + verify(mVpn).stopVpnRunnerPrivileged(); + verify(mNotificationManager).cancel(any(), eq(SystemMessage.NOTE_VPN_STATUS)); + } +} diff --git a/tests/TrustTests/src/android/trust/test/CanUnlockWithActiveUnlockTest.kt b/tests/TrustTests/src/android/trust/test/IsActiveUnlockRunningTest.kt index 7b68a829e23b..7b68a829e23b 100644 --- a/tests/TrustTests/src/android/trust/test/CanUnlockWithActiveUnlockTest.kt +++ b/tests/TrustTests/src/android/trust/test/IsActiveUnlockRunningTest.kt |