diff options
39 files changed, 1333 insertions, 312 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index 341047dd1802..f0b759878e20 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -14,6 +14,7 @@ aconfig_srcjars = [ ":android.hardware.biometrics.flags-aconfig-java{.generated_srcjars}", + ":android.nfc.flags-aconfig-java{.generated_srcjars}", ":android.os.flags-aconfig-java{.generated_srcjars}", ":android.security.flags-aconfig-java{.generated_srcjars}", ":com.android.hardware.camera2-aconfig-java{.generated_srcjars}", @@ -110,6 +111,19 @@ java_aconfig_library { defaults: ["framework-minus-apex-aconfig-java-defaults"], } +// NFC +aconfig_declarations { + name: "android.nfc.flags-aconfig", + package: "android.nfc", + srcs: ["core/java/android/nfc/*.aconfig"], +} + +java_aconfig_library { + name: "android.nfc.flags-aconfig-java", + aconfig_declarations: "android.nfc.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} + // Security aconfig_declarations { name: "android.security.flags-aconfig", diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp index c05d300fddb8..d15b44f16621 100644 --- a/api/StubLibraries.bp +++ b/api/StubLibraries.bp @@ -66,6 +66,7 @@ droidstubs { tag: ".removed-api.txt", }, ], + api_surface: "public", } priv_apps = " --show-annotation android.annotation.SystemApi\\(" + @@ -119,6 +120,7 @@ droidstubs { tag: ".removed-api.txt", }, ], + api_surface: "system", } droidstubs { @@ -164,6 +166,7 @@ droidstubs { tag: ".removed-api.txt", }, ], + api_surface: "test", } droidstubs { @@ -203,6 +206,7 @@ droidstubs { tag: ".removed-api.txt", }, ], + api_surface: "module-lib", } ///////////////////////////////////////////////////////////////////// @@ -379,8 +383,8 @@ java_defaults { java_api_library { name: "android-non-updatable.stubs.from-text", api_surface: "public", - api_files: [ - ":non-updatable-current.txt", + api_contributions: [ + "api-stubs-docs-non-updatable.api.contribution", ], defaults: ["android-non-updatable_from_text_defaults"], full_api_surface_stub: "android_stubs_current.from-text", @@ -389,9 +393,9 @@ java_api_library { java_api_library { name: "android-non-updatable.stubs.system.from-text", api_surface: "system", - api_files: [ - ":non-updatable-current.txt", - ":non-updatable-system-current.txt", + api_contributions: [ + "api-stubs-docs-non-updatable.api.contribution", + "system-api-stubs-docs-non-updatable.api.contribution", ], defaults: ["android-non-updatable_from_text_defaults"], full_api_surface_stub: "android_system_stubs_current.from-text", @@ -400,10 +404,10 @@ java_api_library { java_api_library { name: "android-non-updatable.stubs.test.from-text", api_surface: "test", - api_files: [ - ":non-updatable-current.txt", - ":non-updatable-system-current.txt", - ":non-updatable-test-current.txt", + api_contributions: [ + "api-stubs-docs-non-updatable.api.contribution", + "system-api-stubs-docs-non-updatable.api.contribution", + "test-api-stubs-docs-non-updatable.api.contribution", ], defaults: ["android-non-updatable_from_text_defaults"], full_api_surface_stub: "android_test_stubs_current.from-text", @@ -412,10 +416,10 @@ java_api_library { java_api_library { name: "android-non-updatable.stubs.module_lib.from-text", api_surface: "module_lib", - api_files: [ - ":non-updatable-current.txt", - ":non-updatable-system-current.txt", - ":non-updatable-module-lib-current.txt", + api_contributions: [ + "api-stubs-docs-non-updatable.api.contribution", + "system-api-stubs-docs-non-updatable.api.contribution", + "module-lib-api-stubs-docs-non-updatable.api.contribution", ], defaults: ["android-non-updatable_from_text_defaults"], full_api_surface_stub: "android_module_lib_stubs_current_full.from-text", @@ -598,7 +602,6 @@ java_defaults { name: "android_test_stubs_current_contributions", api_surface: "test", api_contributions: [ - "test-api-stubs-docs-non-updatable.api.contribution", "framework-virtualization.stubs.source.test.api.contribution", ], } @@ -664,6 +667,7 @@ java_api_library { api_contributions: [ "api-stubs-docs-non-updatable.api.contribution", "system-api-stubs-docs-non-updatable.api.contribution", + "test-api-stubs-docs-non-updatable.api.contribution", ], visibility: ["//visibility:public"], } diff --git a/core/api/current.txt b/core/api/current.txt index cba935fadb55..d399e349de0c 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -37935,6 +37935,7 @@ package android.security.keystore { method @Nullable public java.util.Date getKeyValidityStart(); method @NonNull public String getKeystoreAlias(); method public int getMaxUsageCount(); + method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public java.util.Set<java.lang.String> getMgf1Digests(); method public int getPurposes(); method @NonNull public String[] getSignaturePaddings(); method public int getUserAuthenticationType(); @@ -37942,6 +37943,7 @@ package android.security.keystore { method public boolean isDevicePropertiesAttestationIncluded(); method @NonNull public boolean isDigestsSpecified(); method public boolean isInvalidatedByBiometricEnrollment(); + method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public boolean isMgf1DigestsSpecified(); method public boolean isRandomizedEncryptionRequired(); method public boolean isStrongBoxBacked(); method public boolean isUnlockedDeviceRequired(); @@ -37973,6 +37975,7 @@ package android.security.keystore { method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityForOriginationEnd(java.util.Date); method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityStart(java.util.Date); method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setMaxUsageCount(int); + method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setMgf1Digests(@Nullable java.lang.String...); method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setRandomizedEncryptionRequired(boolean); method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setSignaturePaddings(java.lang.String...); method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUnlockedDeviceRequired(boolean); @@ -38077,12 +38080,14 @@ package android.security.keystore { method @Nullable public java.util.Date getKeyValidityForOriginationEnd(); method @Nullable public java.util.Date getKeyValidityStart(); method public int getMaxUsageCount(); + method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public java.util.Set<java.lang.String> getMgf1Digests(); method public int getPurposes(); method @NonNull public String[] getSignaturePaddings(); method public int getUserAuthenticationType(); method public int getUserAuthenticationValidityDurationSeconds(); method public boolean isDigestsSpecified(); method public boolean isInvalidatedByBiometricEnrollment(); + method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public boolean isMgf1DigestsSpecified(); method public boolean isRandomizedEncryptionRequired(); method public boolean isUnlockedDeviceRequired(); method public boolean isUserAuthenticationRequired(); @@ -38104,6 +38109,7 @@ package android.security.keystore { method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityForOriginationEnd(java.util.Date); method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityStart(java.util.Date); method @NonNull public android.security.keystore.KeyProtection.Builder setMaxUsageCount(int); + method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public android.security.keystore.KeyProtection.Builder setMgf1Digests(@Nullable java.lang.String...); method @NonNull public android.security.keystore.KeyProtection.Builder setRandomizedEncryptionRequired(boolean); method @NonNull public android.security.keystore.KeyProtection.Builder setSignaturePaddings(java.lang.String...); method @NonNull public android.security.keystore.KeyProtection.Builder setUnlockedDeviceRequired(boolean); diff --git a/core/api/system-current.txt b/core/api/system-current.txt index c1c27884891b..8cf921cb8d36 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -9140,6 +9140,73 @@ package android.nfc { } +package android.nfc.cardemulation { + + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public final class AidGroup implements android.os.Parcelable { + ctor @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public AidGroup(@NonNull java.util.List<java.lang.String>, @Nullable String); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Nullable public static android.nfc.cardemulation.AidGroup createFromXml(@NonNull org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int describeContents(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dump(@NonNull android.util.proto.ProtoOutputStream); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<java.lang.String> getAids(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getCategory(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void writeAsXml(@NonNull org.xmlpull.v1.XmlSerializer) throws java.io.IOException; + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void writeToParcel(@NonNull android.os.Parcel, int); + field @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.AidGroup> CREATOR; + } + + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public final class ApduServiceInfo implements android.os.Parcelable { + ctor @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public ApduServiceInfo(@NonNull android.content.pm.PackageManager, @NonNull android.content.pm.ResolveInfo, boolean) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int describeContents(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dump(@NonNull android.os.ParcelFileDescriptor, @NonNull java.io.PrintWriter, @NonNull String[]); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dumpDebug(@NonNull android.util.proto.ProtoOutputStream); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<android.nfc.cardemulation.AidGroup> getAidGroups(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<java.lang.String> getAids(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getCategoryForAid(@NonNull String); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.content.ComponentName getComponent(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getDescription(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.nfc.cardemulation.AidGroup getDynamicAidGroupForCategory(@NonNull String); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Nullable public String getOffHostSecureElement(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<java.lang.String> getPrefixAids(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getSettingsActivityName(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<java.lang.String> getSubsetAids(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int getUid(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean hasCategory(@NonNull String); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean isOnHost(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public CharSequence loadAppLabel(@NonNull android.content.pm.PackageManager); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.graphics.drawable.Drawable loadBanner(@NonNull android.content.pm.PackageManager); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.graphics.drawable.Drawable loadIcon(@NonNull android.content.pm.PackageManager); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public CharSequence loadLabel(@NonNull android.content.pm.PackageManager); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public boolean removeDynamicAidGroupForCategory(@NonNull String); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean requiresScreenOn(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean requiresUnlock(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void resetOffHostSecureElement(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void setDynamicAidGroup(@NonNull android.nfc.cardemulation.AidGroup); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void setOffHostSecureElement(@NonNull String); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void writeToParcel(@NonNull android.os.Parcel, int); + field @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.ApduServiceInfo> CREATOR; + } + + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public final class NfcFServiceInfo implements android.os.Parcelable { + ctor @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public NfcFServiceInfo(@NonNull android.content.pm.PackageManager, @NonNull android.content.pm.ResolveInfo) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int describeContents(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dump(@NonNull android.os.ParcelFileDescriptor, @NonNull java.io.PrintWriter, @NonNull String[]); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dumpDebug(@NonNull android.util.proto.ProtoOutputStream); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.content.ComponentName getComponent(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getDescription(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getNfcid2(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getSystemCode(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getT3tPmm(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int getUid(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.graphics.drawable.Drawable loadIcon(@NonNull android.content.pm.PackageManager); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public CharSequence loadLabel(@NonNull android.content.pm.PackageManager); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void setDynamicNfcid2(@NonNull String); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void setDynamicSystemCode(@NonNull String); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void writeToParcel(@NonNull android.os.Parcel, int); + field @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.NfcFServiceInfo> CREATOR; + } + +} + package android.os { public class BatteryManager { diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt index 0100f0e76285..71c02dc42c62 100644 --- a/core/api/system-lint-baseline.txt +++ b/core/api/system-lint-baseline.txt @@ -225,3 +225,16 @@ SamShouldBeLast: android.view.accessibility.AccessibilityManager#addTouchExplora SAM-compatible parameters (such as parameter 1, "listener", in android.view.accessibility.AccessibilityManager.addTouchExplorationStateChangeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.webkit.WebChromeClient#onShowFileChooser(android.webkit.WebView, android.webkit.ValueCallback<android.net.Uri[]>, android.webkit.WebChromeClient.FileChooserParams): SAM-compatible parameters (such as parameter 2, "filePathCallback", in android.webkit.WebChromeClient.onShowFileChooser) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions + +UnflaggedApi: android.nfc.cardemulation.AidGroup#CONTENTS_FILE_DESCRIPTOR: + New API must be flagged with @FlaggedApi: field android.nfc.cardemulation.AidGroup.CONTENTS_FILE_DESCRIPTOR +UnflaggedApi: android.nfc.cardemulation.AidGroup#PARCELABLE_WRITE_RETURN_VALUE: + New API must be flagged with @FlaggedApi: field android.nfc.cardemulation.AidGroup.PARCELABLE_WRITE_RETURN_VALUE +UnflaggedApi: android.nfc.cardemulation.ApduServiceInfo#CONTENTS_FILE_DESCRIPTOR: + New API must be flagged with @FlaggedApi: field android.nfc.cardemulation.ApduServiceInfo.CONTENTS_FILE_DESCRIPTOR +UnflaggedApi: android.nfc.cardemulation.ApduServiceInfo#PARCELABLE_WRITE_RETURN_VALUE: + New API must be flagged with @FlaggedApi: field android.nfc.cardemulation.ApduServiceInfo.PARCELABLE_WRITE_RETURN_VALUE +UnflaggedApi: android.nfc.cardemulation.NfcFServiceInfo#CONTENTS_FILE_DESCRIPTOR: + New API must be flagged with @FlaggedApi: field android.nfc.cardemulation.NfcFServiceInfo.CONTENTS_FILE_DESCRIPTOR +UnflaggedApi: android.nfc.cardemulation.NfcFServiceInfo#PARCELABLE_WRITE_RETURN_VALUE: + New API must be flagged with @FlaggedApi: field android.nfc.cardemulation.NfcFServiceInfo.PARCELABLE_WRITE_RETURN_VALUE diff --git a/core/java/android/nfc/cardemulation/AidGroup.java b/core/java/android/nfc/cardemulation/AidGroup.java index 2436e57b74bc..958669ee5852 100644 --- a/core/java/android/nfc/cardemulation/AidGroup.java +++ b/core/java/android/nfc/cardemulation/AidGroup.java @@ -16,7 +16,11 @@ package android.nfc.cardemulation; -import android.compat.annotation.UnsupportedAppUsage; +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.nfc.Flags; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; @@ -29,6 +33,11 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Locale; + +/********************************************************************** + * This file is not a part of the NFC mainline module * + * *******************************************************************/ /** * The AidGroup class represents a group of Application Identifiers (AIDs). @@ -39,28 +48,30 @@ import java.util.List; * * @hide */ +@SystemApi +@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public final class AidGroup implements Parcelable { /** * The maximum number of AIDs that can be present in any one group. */ - public static final int MAX_NUM_AIDS = 256; + private static final int MAX_NUM_AIDS = 256; + + private static final String TAG = "AidGroup"; - static final String TAG = "AidGroup"; - @UnsupportedAppUsage - final List<String> aids; - @UnsupportedAppUsage - final String category; - @UnsupportedAppUsage - final String description; + private final List<String> mAids; + private final String mCategory; + @SuppressWarnings("unused") // Unused as of now, but part of the XML input. + private final String mDescription; /** * Creates a new AidGroup object. * - * @param aids The list of AIDs present in the group - * @param category The category of this group, e.g. {@link CardEmulation#CATEGORY_PAYMENT} + * @param aids list of AIDs present in the group + * @param category category of this group, e.g. {@link CardEmulation#CATEGORY_PAYMENT} */ - public AidGroup(List<String> aids, String category) { + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public AidGroup(@NonNull List<String> aids, @Nullable String category) { if (aids == null || aids.size() == 0) { throw new IllegalArgumentException("No AIDS in AID group."); } @@ -73,45 +84,55 @@ public final class AidGroup implements Parcelable { } } if (isValidCategory(category)) { - this.category = category; + this.mCategory = category; } else { - this.category = CardEmulation.CATEGORY_OTHER; + this.mCategory = CardEmulation.CATEGORY_OTHER; } - this.aids = new ArrayList<String>(aids.size()); + this.mAids = new ArrayList<String>(aids.size()); for (String aid : aids) { - this.aids.add(aid.toUpperCase()); + this.mAids.add(aid.toUpperCase(Locale.US)); } - this.description = null; + this.mDescription = null; } - @UnsupportedAppUsage - AidGroup(String category, String description) { - this.aids = new ArrayList<String>(); - this.category = category; - this.description = description; + /** + * Creates a new AidGroup object. + * + * @param category category of this group, e.g. {@link CardEmulation#CATEGORY_PAYMENT} + * @param description description of this group + */ + AidGroup(@NonNull String category, @NonNull String description) { + this.mAids = new ArrayList<String>(); + this.mCategory = category; + this.mDescription = description; } /** + * Returns the category of this group. * @return the category of this AID group */ - @UnsupportedAppUsage + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull public String getCategory() { - return category; + return mCategory; } /** + * Returns the list of AIDs in this group. + * * @return the list of AIDs in this group */ - @UnsupportedAppUsage + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull public List<String> getAids() { - return aids; + return mAids; } @Override public String toString() { - StringBuilder out = new StringBuilder("Category: " + category + - ", AIDs:"); - for (String aid : aids) { + StringBuilder out = new StringBuilder("Category: " + mCategory + + ", AIDs:"); + for (String aid : mAids) { out.append(aid); out.append(", "); } @@ -119,41 +140,44 @@ public final class AidGroup implements Parcelable { } /** - * Dump debugging info as AidGroupProto + * Dump debugging info as AidGroupProto. * * If the output belongs to a sub message, the caller is responsible for wrapping this function * between {@link ProtoOutputStream#start(long)} and {@link ProtoOutputStream#end(long)}. * * @param proto the ProtoOutputStream to write to */ - public void dump(ProtoOutputStream proto) { - proto.write(AidGroupProto.CATEGORY, category); - for (String aid : aids) { + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void dump(@NonNull ProtoOutputStream proto) { + proto.write(AidGroupProto.CATEGORY, mCategory); + for (String aid : mAids) { proto.write(AidGroupProto.AIDS, aid); } } + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Override public int describeContents() { return 0; } + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(category); - dest.writeInt(aids.size()); - if (aids.size() > 0) { - dest.writeStringList(aids); + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString8(mCategory); + dest.writeInt(mAids.size()); + if (mAids.size() > 0) { + dest.writeStringList(mAids); } } - @UnsupportedAppUsage - public static final @android.annotation.NonNull Parcelable.Creator<AidGroup> CREATOR = + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public static final @NonNull Parcelable.Creator<AidGroup> CREATOR = new Parcelable.Creator<AidGroup>() { @Override public AidGroup createFromParcel(Parcel source) { - String category = source.readString(); + String category = source.readString8(); int listSize = source.readInt(); ArrayList<String> aidList = new ArrayList<String>(); if (listSize > 0) { @@ -168,8 +192,17 @@ public final class AidGroup implements Parcelable { } }; - @UnsupportedAppUsage - static public AidGroup createFromXml(XmlPullParser parser) throws XmlPullParserException, IOException { + /** + * Create an instance of AID group from XML file. + * + * @param parser input xml parser stream + * @throws XmlPullParserException If an error occurs parsing the element. + * @throws IOException If an error occurs reading the element. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @Nullable + public static AidGroup createFromXml(@NonNull XmlPullParser parser) + throws XmlPullParserException, IOException { String category = null; ArrayList<String> aids = new ArrayList<String>(); AidGroup group = null; @@ -210,11 +243,16 @@ public final class AidGroup implements Parcelable { return group; } - @UnsupportedAppUsage - public void writeAsXml(XmlSerializer out) throws IOException { + /** + * Serialize instance of AID group to XML file. + * @param out XML serializer stream + * @throws IOException If an error occurs reading the element. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void writeAsXml(@NonNull XmlSerializer out) throws IOException { out.startTag(null, "aid-group"); - out.attribute(null, "category", category); - for (String aid : aids) { + out.attribute(null, "category", mCategory); + for (String aid : mAids) { out.startTag(null, "aid"); out.attribute(null, "value", aid); out.endTag(null, "aid"); @@ -222,7 +260,7 @@ public final class AidGroup implements Parcelable { out.endTag(null, "aid-group"); } - static boolean isValidCategory(String category) { + private static boolean isValidCategory(String category) { return CardEmulation.CATEGORY_PAYMENT.equals(category) || CardEmulation.CATEGORY_OTHER.equals(category); } diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java index 793a70e93f56..18ec914860fb 100644 --- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java +++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java @@ -14,10 +14,16 @@ * limitations under the License. */ +/********************************************************************** + * This file is not a part of the NFC mainline module * + * *******************************************************************/ + package android.nfc.cardemulation; +import android.annotation.FlaggedApi; +import android.annotation.NonNull; import android.annotation.Nullable; -import android.compat.annotation.UnsupportedAppUsage; +import android.annotation.SystemApi; import android.content.ComponentName; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -28,7 +34,9 @@ import android.content.res.Resources.NotFoundException; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; +import android.nfc.Flags; import android.os.Parcel; +import android.os.ParcelFileDescriptor; import android.os.Parcelable; import android.util.AttributeSet; import android.util.Log; @@ -38,7 +46,6 @@ import android.util.proto.ProtoOutputStream; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; @@ -47,82 +54,82 @@ import java.util.List; import java.util.Map; /** + * Class holding APDU service info. + * * @hide */ +@SystemApi +@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public final class ApduServiceInfo implements Parcelable { - static final String TAG = "ApduServiceInfo"; + private static final String TAG = "ApduServiceInfo"; /** * The service that implements this */ - @UnsupportedAppUsage - final ResolveInfo mService; + private final ResolveInfo mService; /** * Description of the service */ - final String mDescription; + private final String mDescription; /** * Whether this service represents AIDs running on the host CPU */ - final boolean mOnHost; + private final boolean mOnHost; /** * Offhost reader name. * eg: SIM, eSE etc */ - String mOffHostName; + private String mOffHostName; /** * Offhost reader name from manifest file. - * Used for unsetOffHostSecureElement() + * Used for resetOffHostSecureElement() */ - final String mStaticOffHostName; + private final String mStaticOffHostName; /** * Mapping from category to static AID group */ - @UnsupportedAppUsage - final HashMap<String, AidGroup> mStaticAidGroups; + private final HashMap<String, AidGroup> mStaticAidGroups; /** * Mapping from category to dynamic AID group */ - @UnsupportedAppUsage - final HashMap<String, AidGroup> mDynamicAidGroups; + private final HashMap<String, AidGroup> mDynamicAidGroups; /** * Whether this service should only be started when the device is unlocked. */ - final boolean mRequiresDeviceUnlock; + private final boolean mRequiresDeviceUnlock; /** * Whether this service should only be started when the device is screen on. */ - final boolean mRequiresDeviceScreenOn; + private final boolean mRequiresDeviceScreenOn; /** * The id of the service banner specified in XML. */ - final int mBannerResourceId; + private final int mBannerResourceId; /** * The uid of the package the service belongs to */ - final int mUid; + private final int mUid; /** * Settings Activity for this service */ - final String mSettingsActivityName; + private final String mSettingsActivityName; /** * @hide */ - @UnsupportedAppUsage public ApduServiceInfo(ResolveInfo info, boolean onHost, String description, - ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups, + List<AidGroup> staticAidGroups, List<AidGroup> dynamicAidGroups, boolean requiresUnlock, int bannerResource, int uid, String settingsActivityName, String offHost, String staticOffHost) { this(info, onHost, description, staticAidGroups, dynamicAidGroups, @@ -134,7 +141,7 @@ public final class ApduServiceInfo implements Parcelable { * @hide */ public ApduServiceInfo(ResolveInfo info, boolean onHost, String description, - ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups, + List<AidGroup> staticAidGroups, List<AidGroup> dynamicAidGroups, boolean requiresUnlock, boolean requiresScreenOn, int bannerResource, int uid, String settingsActivityName, String offHost, String staticOffHost) { this.mService = info; @@ -147,19 +154,28 @@ public final class ApduServiceInfo implements Parcelable { this.mRequiresDeviceUnlock = requiresUnlock; this.mRequiresDeviceScreenOn = requiresScreenOn; for (AidGroup aidGroup : staticAidGroups) { - this.mStaticAidGroups.put(aidGroup.category, aidGroup); + this.mStaticAidGroups.put(aidGroup.getCategory(), aidGroup); } for (AidGroup aidGroup : dynamicAidGroups) { - this.mDynamicAidGroups.put(aidGroup.category, aidGroup); + this.mDynamicAidGroups.put(aidGroup.getCategory(), aidGroup); } this.mBannerResourceId = bannerResource; this.mUid = uid; this.mSettingsActivityName = settingsActivityName; } - @UnsupportedAppUsage - public ApduServiceInfo(PackageManager pm, ResolveInfo info, boolean onHost) throws - XmlPullParserException, IOException { + /** + * Creates a new ApduServiceInfo object. + * + * @param pm packageManager instance + * @param info app component info + * @param onHost whether service is on host or not (secure element) + * @throws XmlPullParserException If an error occurs parsing the element. + * @throws IOException If an error occurs reading the element. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public ApduServiceInfo(@NonNull PackageManager pm, @NonNull ResolveInfo info, boolean onHost) + throws XmlPullParserException, IOException { ServiceInfo si = info.serviceInfo; XmlResourceParser parser = null; try { @@ -277,9 +293,9 @@ public final class ApduServiceInfo implements Parcelable { groupAttrs.recycle(); } else if (eventType == XmlPullParser.END_TAG && "aid-group".equals(tagName) && currentGroup != null) { - if (currentGroup.aids.size() > 0) { - if (!mStaticAidGroups.containsKey(currentGroup.category)) { - mStaticAidGroups.put(currentGroup.category, currentGroup); + if (currentGroup.getAids().size() > 0) { + if (!mStaticAidGroups.containsKey(currentGroup.getCategory())) { + mStaticAidGroups.put(currentGroup.getCategory(), currentGroup); } } else { Log.e(TAG, "Not adding <aid-group> with empty or invalid AIDs"); @@ -291,8 +307,8 @@ public final class ApduServiceInfo implements Parcelable { com.android.internal.R.styleable.AidFilter); String aid = a.getString(com.android.internal.R.styleable.AidFilter_name). toUpperCase(); - if (CardEmulation.isValidAid(aid) && !currentGroup.aids.contains(aid)) { - currentGroup.aids.add(aid); + if (CardEmulation.isValidAid(aid) && !currentGroup.getAids().contains(aid)) { + currentGroup.getAids().add(aid); } else { Log.e(TAG, "Ignoring invalid or duplicate aid: " + aid); } @@ -305,8 +321,8 @@ public final class ApduServiceInfo implements Parcelable { toUpperCase(); // Add wildcard char to indicate prefix aid = aid.concat("*"); - if (CardEmulation.isValidAid(aid) && !currentGroup.aids.contains(aid)) { - currentGroup.aids.add(aid); + if (CardEmulation.isValidAid(aid) && !currentGroup.getAids().contains(aid)) { + currentGroup.getAids().add(aid); } else { Log.e(TAG, "Ignoring invalid or duplicate aid: " + aid); } @@ -319,8 +335,8 @@ public final class ApduServiceInfo implements Parcelable { toUpperCase(); // Add wildcard char to indicate suffix aid = aid.concat("#"); - if (CardEmulation.isValidAid(aid) && !currentGroup.aids.contains(aid)) { - currentGroup.aids.add(aid); + if (CardEmulation.isValidAid(aid) && !currentGroup.getAids().contains(aid)) { + currentGroup.getAids().add(aid); } else { Log.e(TAG, "Ignoring invalid or duplicate aid: " + aid); } @@ -336,11 +352,25 @@ public final class ApduServiceInfo implements Parcelable { mUid = si.applicationInfo.uid; } + /** + * Returns the app component corresponding to this APDU service. + * + * @return app component for this service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull public ComponentName getComponent() { return new ComponentName(mService.serviceInfo.packageName, mService.serviceInfo.name); } + /** + * Returns the offhost secure element name (if the service is offhost). + * + * @return offhost secure element name for offhost services + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @Nullable public String getOffHostSecureElement() { return mOffHostName; } @@ -353,18 +383,30 @@ public final class ApduServiceInfo implements Parcelable { * for that category. * @return List of AIDs registered by the service */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull public List<String> getAids() { final ArrayList<String> aids = new ArrayList<String>(); for (AidGroup group : getAidGroups()) { - aids.addAll(group.aids); + aids.addAll(group.getAids()); } return aids; } + /** + * Returns a consolidated list of AIDs with prefixes from the AID groups + * registered by this service. Note that if a service has both + * a static (manifest-based) AID group for a category and a dynamic + * AID group, only the dynamically registered AIDs will be returned + * for that category. + * @return List of prefix AIDs registered by the service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull public List<String> getPrefixAids() { final ArrayList<String> prefixAids = new ArrayList<String>(); for (AidGroup group : getAidGroups()) { - for (String aid : group.aids) { + for (String aid : group.getAids()) { if (aid.endsWith("*")) { prefixAids.add(aid); } @@ -373,10 +415,20 @@ public final class ApduServiceInfo implements Parcelable { return prefixAids; } + /** + * Returns a consolidated list of AIDs with subsets from the AID groups + * registered by this service. Note that if a service has both + * a static (manifest-based) AID group for a category and a dynamic + * AID group, only the dynamically registered AIDs will be returned + * for that category. + * @return List of prefix AIDs registered by the service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull public List<String> getSubsetAids() { final ArrayList<String> subsetAids = new ArrayList<String>(); for (AidGroup group : getAidGroups()) { - for (String aid : group.aids) { + for (String aid : group.getAids()) { if (aid.endsWith("#")) { subsetAids.add(aid); } @@ -384,14 +436,28 @@ public final class ApduServiceInfo implements Parcelable { } return subsetAids; } + /** * Returns the registered AID group for this category. + * + * @param category category name + * @return {@link AidGroup} instance for the provided category */ - public AidGroup getDynamicAidGroupForCategory(String category) { + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public AidGroup getDynamicAidGroupForCategory(@NonNull String category) { return mDynamicAidGroups.get(category); } - public boolean removeDynamicAidGroupForCategory(String category) { + /** + * Removes the registered AID group for this category. + * + * @param category category name + * @return {@code true} if an AID group existed + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public boolean removeDynamicAidGroupForCategory(@NonNull String category) { return (mDynamicAidGroups.remove(category) != null); } @@ -403,7 +469,9 @@ public final class ApduServiceInfo implements Parcelable { * for that category. * @return List of AIDs registered by the service */ - public ArrayList<AidGroup> getAidGroups() { + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public List<AidGroup> getAidGroups() { final ArrayList<AidGroup> groups = new ArrayList<AidGroup>(); for (Map.Entry<String, AidGroup> entry : mDynamicAidGroups.entrySet()) { groups.add(entry.getValue()); @@ -421,49 +489,83 @@ public final class ApduServiceInfo implements Parcelable { /** * Returns the category to which this service has attributed the AID that is passed in, * or null if we don't know this AID. + * @param aid AID to lookup for + * @return category name corresponding to this AID */ - public String getCategoryForAid(String aid) { - ArrayList<AidGroup> groups = getAidGroups(); + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public String getCategoryForAid(@NonNull String aid) { + List<AidGroup> groups = getAidGroups(); for (AidGroup group : groups) { - if (group.aids.contains(aid.toUpperCase())) { - return group.category; + if (group.getAids().contains(aid.toUpperCase())) { + return group.getCategory(); } } return null; } - public boolean hasCategory(String category) { + /** + * Returns whether there is any AID group for this category. + * @param category category name + * @return {@code true} if an AID group exists + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public boolean hasCategory(@NonNull String category) { return (mStaticAidGroups.containsKey(category) || mDynamicAidGroups.containsKey(category)); } - @UnsupportedAppUsage + /** + * Returns whether the service is on host or not. + * @return true if the service is on host (not secure element) + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean isOnHost() { return mOnHost; } - @UnsupportedAppUsage + /** + * Returns whether the service requires device unlock. + * @return whether the service requires device unlock + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean requiresUnlock() { return mRequiresDeviceUnlock; } /** * Returns whether this service should only be started when the device is screen on. + * @return whether the service requires screen on */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean requiresScreenOn() { return mRequiresDeviceScreenOn; } - @UnsupportedAppUsage + /** + * Returns description of service. + * @return user readable description of service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull public String getDescription() { return mDescription; } - @UnsupportedAppUsage + /** + * Returns uid of service. + * @return uid of the service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int getUid() { return mUid; } - public void setOrReplaceDynamicAidGroup(AidGroup aidGroup) { + /** + * Add or replace an AID group to this service. + * @param aidGroup instance of aid group to set or replace + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void setDynamicAidGroup(@NonNull AidGroup aidGroup) { mDynamicAidGroups.put(aidGroup.getCategory(), aidGroup); } @@ -476,7 +578,8 @@ public final class ApduServiceInfo implements Parcelable { * TS26_NFC_REQ_070: For embedded SE, Secure Element Name SHALL be eSE[number] * (e.g. eSE/eSE1, eSE2, etc.). */ - public void setOffHostSecureElement(String offHost) { + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void setOffHostSecureElement(@NonNull String offHost) { mOffHostName = offHost; } @@ -484,15 +587,30 @@ public final class ApduServiceInfo implements Parcelable { * Resets the off host Secure Element to statically defined * by the service in the manifest file. */ - public void unsetOffHostSecureElement() { + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void resetOffHostSecureElement() { mOffHostName = mStaticOffHostName; } - public CharSequence loadLabel(PackageManager pm) { + /** + * Load label for this service. + * @param pm packagemanager instance + * @return label name corresponding to service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public CharSequence loadLabel(@NonNull PackageManager pm) { return mService.loadLabel(pm); } - public CharSequence loadAppLabel(PackageManager pm) { + /** + * Load application label for this service. + * @param pm packagemanager instance + * @return app label name corresponding to service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public CharSequence loadAppLabel(@NonNull PackageManager pm) { try { return pm.getApplicationLabel(pm.getApplicationInfo( mService.resolvePackageName, PackageManager.GET_META_DATA)); @@ -501,12 +619,25 @@ public final class ApduServiceInfo implements Parcelable { } } - public Drawable loadIcon(PackageManager pm) { + /** + * Load application icon for this service. + * @param pm packagemanager instance + * @return app icon corresponding to service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public Drawable loadIcon(@NonNull PackageManager pm) { return mService.loadIcon(pm); } - @UnsupportedAppUsage - public Drawable loadBanner(PackageManager pm) { + /** + * Load application banner for this service. + * @param pm packagemanager instance + * @return app banner corresponding to service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public Drawable loadBanner(@NonNull PackageManager pm) { Resources res; try { res = pm.getResourcesForApplication(mService.serviceInfo.packageName); @@ -521,7 +652,12 @@ public final class ApduServiceInfo implements Parcelable { } } - @UnsupportedAppUsage + /** + * Load activity name for this service. + * @return activity name for this service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull public String getSettingsActivityName() { return mSettingsActivityName; } @Override @@ -556,14 +692,15 @@ public final class ApduServiceInfo implements Parcelable { return getComponent().hashCode(); } - + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Override public int describeContents() { return 0; } + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Override - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { mService.writeToParcel(dest, flags); dest.writeString(mDescription); dest.writeInt(mOnHost ? 1 : 0); @@ -584,8 +721,8 @@ public final class ApduServiceInfo implements Parcelable { dest.writeString(mSettingsActivityName); }; - @UnsupportedAppUsage - public static final @android.annotation.NonNull Parcelable.Creator<ApduServiceInfo> CREATOR = + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public static final @NonNull Parcelable.Creator<ApduServiceInfo> CREATOR = new Parcelable.Creator<ApduServiceInfo>() { @Override public ApduServiceInfo createFromParcel(Parcel source) { @@ -620,7 +757,15 @@ public final class ApduServiceInfo implements Parcelable { } }; - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + /** + * Dump contents for debugging. + * @param fd parcelfiledescriptor instance + * @param pw printwriter instance + * @param args args for dumping + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void dump(@NonNull ParcelFileDescriptor fd, @NonNull PrintWriter pw, + @NonNull String[] args) { pw.println(" " + getComponent() + " (Description: " + getDescription() + ")" + " (UID: " + getUid() + ")"); @@ -633,15 +778,15 @@ public final class ApduServiceInfo implements Parcelable { } pw.println(" Static AID groups:"); for (AidGroup group : mStaticAidGroups.values()) { - pw.println(" Category: " + group.category); - for (String aid : group.aids) { + pw.println(" Category: " + group.getCategory()); + for (String aid : group.getAids()) { pw.println(" AID: " + aid); } } pw.println(" Dynamic AID groups:"); for (AidGroup group : mDynamicAidGroups.values()) { - pw.println(" Category: " + group.category); - for (String aid : group.aids) { + pw.println(" Category: " + group.getCategory()); + for (String aid : group.getAids()) { pw.println(" AID: " + aid); } } @@ -651,7 +796,7 @@ public final class ApduServiceInfo implements Parcelable { } /** - * Dump debugging info as ApduServiceInfoProto + * Dump debugging info as ApduServiceInfoProto. * * If the output belongs to a sub message, the caller is responsible for wrapping this function * between {@link ProtoOutputStream#start(long)} and {@link ProtoOutputStream#end(long)}. @@ -659,7 +804,8 @@ public final class ApduServiceInfo implements Parcelable { * * @param proto the ProtoOutputStream to write to */ - public void dumpDebug(ProtoOutputStream proto) { + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void dumpDebug(@NonNull ProtoOutputStream proto) { Utils.dumpDebugComponentName(getComponent(), proto, ApduServiceInfoProto.COMPONENT_NAME); proto.write(ApduServiceInfoProto.DESCRIPTION, getDescription()); proto.write(ApduServiceInfoProto.ON_HOST, mOnHost); diff --git a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java b/core/java/android/nfc/cardemulation/NfcFServiceInfo.java index 7a36b269240c..ec919e4d66bc 100644 --- a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java +++ b/core/java/android/nfc/cardemulation/NfcFServiceInfo.java @@ -14,9 +14,16 @@ * limitations under the License. */ +/********************************************************************** + * This file is not a part of the NFC mainline module * + * *******************************************************************/ + package android.nfc.cardemulation; +import android.annotation.FlaggedApi; +import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.content.ComponentName; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -26,7 +33,9 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; +import android.nfc.Flags; import android.os.Parcel; +import android.os.ParcelFileDescriptor; import android.os.Parcelable; import android.util.AttributeSet; import android.util.Log; @@ -36,13 +45,16 @@ import android.util.proto.ProtoOutputStream; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; /** + * Class to hold NfcF service info. + * * @hide */ +@SystemApi +@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public final class NfcFServiceInfo implements Parcelable { static final String TAG = "NfcFServiceInfo"; @@ -51,42 +63,42 @@ public final class NfcFServiceInfo implements Parcelable { /** * The service that implements this */ - final ResolveInfo mService; + private final ResolveInfo mService; /** * Description of the service */ - final String mDescription; + private final String mDescription; /** * System Code of the service */ - final String mSystemCode; + private final String mSystemCode; /** * System Code of the service registered by API */ - String mDynamicSystemCode; + private String mDynamicSystemCode; /** * NFCID2 of the service */ - final String mNfcid2; + private final String mNfcid2; /** * NFCID2 of the service registered by API */ - String mDynamicNfcid2; + private String mDynamicNfcid2; /** * The uid of the package the service belongs to */ - final int mUid; + private final int mUid; /** * LF_T3T_PMM of the service */ - final String mT3tPmm; + private final String mT3tPmm; /** * @hide @@ -104,7 +116,16 @@ public final class NfcFServiceInfo implements Parcelable { this.mT3tPmm = t3tPmm; } - public NfcFServiceInfo(PackageManager pm, ResolveInfo info) + /** + * Creates a new NfcFServiceInfo object. + * + * @param pm packageManager instance + * @param info app component info + * @throws XmlPullParserException If an error occurs parsing the element. + * @throws IOException If an error occurs reading the element. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public NfcFServiceInfo(@NonNull PackageManager pm, @NonNull ResolveInfo info) throws XmlPullParserException, IOException { ServiceInfo si = info.serviceInfo; XmlResourceParser parser = null; @@ -192,44 +213,107 @@ public final class NfcFServiceInfo implements Parcelable { mUid = si.applicationInfo.uid; } + /** + * Returns the app component corresponding to this NFCF service. + * + * @return app component for this service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull public ComponentName getComponent() { return new ComponentName(mService.serviceInfo.packageName, mService.serviceInfo.name); } + /** + * Returns the system code corresponding to this service. + * + * @return system code for this service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull public String getSystemCode() { return (mDynamicSystemCode == null ? mSystemCode : mDynamicSystemCode); } - public void setOrReplaceDynamicSystemCode(String systemCode) { + /** + * Add or replace a system code to this service. + * @param systemCode system code to set or replace + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void setDynamicSystemCode(@NonNull String systemCode) { mDynamicSystemCode = systemCode; } + /** + * Returns NFC ID2. + * + * @return nfc id2 to return + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull public String getNfcid2() { return (mDynamicNfcid2 == null ? mNfcid2 : mDynamicNfcid2); } - public void setOrReplaceDynamicNfcid2(String nfcid2) { + /** + * Set or replace NFC ID2 + * + * @param nfcid2 NFC ID2 string + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void setDynamicNfcid2(@NonNull String nfcid2) { mDynamicNfcid2 = nfcid2; } + /** + * Returns description of service. + * @return user readable description of service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull public String getDescription() { return mDescription; } + /** + * Returns uid of service. + * @return uid of the service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int getUid() { return mUid; } + /** + * Returns LF_T3T_PMM of the service + * @return returns LF_T3T_PMM of the service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull public String getT3tPmm() { return mT3tPmm; } - public CharSequence loadLabel(PackageManager pm) { + /** + * Load application label for this service. + * @param pm packagemanager instance + * @return label name corresponding to service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public CharSequence loadLabel(@NonNull PackageManager pm) { return mService.loadLabel(pm); } - public Drawable loadIcon(PackageManager pm) { + /** + * Load application icon for this service. + * @param pm packagemanager instance + * @return app icon corresponding to service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public Drawable loadIcon(@NonNull PackageManager pm) { return mService.loadIcon(pm); } @@ -270,13 +354,15 @@ public final class NfcFServiceInfo implements Parcelable { return getComponent().hashCode(); } + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Override public int describeContents() { return 0; } + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Override - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { mService.writeToParcel(dest, flags); dest.writeString(mDescription); dest.writeString(mSystemCode); @@ -293,7 +379,8 @@ public final class NfcFServiceInfo implements Parcelable { dest.writeString(mT3tPmm); }; - public static final @android.annotation.NonNull Parcelable.Creator<NfcFServiceInfo> CREATOR = + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public static final @NonNull Parcelable.Creator<NfcFServiceInfo> CREATOR = new Parcelable.Creator<NfcFServiceInfo>() { @Override public NfcFServiceInfo createFromParcel(Parcel source) { @@ -322,7 +409,15 @@ public final class NfcFServiceInfo implements Parcelable { } }; - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + /** + * Dump contents of the service for debugging. + * @param fd parcelfiledescriptor instance + * @param pw printwriter instance + * @param args args for dumping + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void dump(@NonNull ParcelFileDescriptor fd, @NonNull PrintWriter pw, + @NonNull String[] args) { pw.println(" " + getComponent() + " (Description: " + getDescription() + ")" + " (UID: " + getUid() + ")"); @@ -332,14 +427,15 @@ public final class NfcFServiceInfo implements Parcelable { } /** - * Dump debugging info as NfcFServiceInfoProto + * Dump debugging info as NfcFServiceInfoProto. * * If the output belongs to a sub message, the caller is responsible for wrapping this function * between {@link ProtoOutputStream#start(long)} and {@link ProtoOutputStream#end(long)}. * * @param proto the ProtoOutputStream to write to */ - public void dumpDebug(ProtoOutputStream proto) { + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void dumpDebug(@NonNull ProtoOutputStream proto) { Utils.dumpDebugComponentName(getComponent(), proto, NfcFServiceInfoProto.COMPONENT_NAME); proto.write(NfcFServiceInfoProto.DESCRIPTION, getDescription()); proto.write(NfcFServiceInfoProto.SYSTEM_CODE, getSystemCode()); @@ -347,4 +443,3 @@ public final class NfcFServiceInfo implements Parcelable { proto.write(NfcFServiceInfoProto.T3T_PMM, getT3tPmm()); } } - diff --git a/core/java/android/nfc/flags.aconfig b/core/java/android/nfc/flags.aconfig new file mode 100644 index 000000000000..e3faf3978856 --- /dev/null +++ b/core/java/android/nfc/flags.aconfig @@ -0,0 +1,8 @@ +package: "android.nfc" + +flag { + name: "enable_nfc_mainline" + namespace: "nfc" + description: "Flag for NFC mainline changes" + bug: "292140387" +} diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java index 1929a4d562d4..5196b1702cc0 100644 --- a/core/java/android/os/BinderProxy.java +++ b/core/java/android/os/BinderProxy.java @@ -32,7 +32,9 @@ import java.io.FileDescriptor; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -613,15 +615,35 @@ public final class BinderProxy implements IBinder { */ public native boolean transactNative(int code, Parcel data, Parcel reply, int flags) throws RemoteException; + + /* This list is to hold strong reference to the death recipients that are waiting for the death + * of binder that this proxy references. Previously, the death recipients were strongy + * referenced from JNI, but that can cause memory leak (b/298374304) when the application has a + * strong reference from the death recipient to the proxy object. The JNI reference is now weak. + * And this strong reference is to keep death recipients at least until the proxy is GC'ed. */ + private List<DeathRecipient> mDeathRecipients = Collections.synchronizedList(new ArrayList<>()); + /** * See {@link IBinder#linkToDeath(DeathRecipient, int)} */ - public native void linkToDeath(DeathRecipient recipient, int flags) - throws RemoteException; + public void linkToDeath(DeathRecipient recipient, int flags) + throws RemoteException { + linkToDeathNative(recipient, flags); + mDeathRecipients.add(recipient); + } + /** * See {@link IBinder#unlinkToDeath} */ - public native boolean unlinkToDeath(DeathRecipient recipient, int flags); + public boolean unlinkToDeath(DeathRecipient recipient, int flags) { + mDeathRecipients.remove(recipient); + return unlinkToDeathNative(recipient, flags); + } + + private native void linkToDeathNative(DeathRecipient recipient, int flags) + throws RemoteException; + + private native boolean unlinkToDeathNative(DeathRecipient recipient, int flags); /** * Perform a dump on the remote object diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 73c93ac9d06b..81c32550cf14 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -15,7 +15,19 @@ license { ], } -cc_library_shared { +soong_config_module_type { + name: "cc_library_shared_for_libandroid_runtime", + module_type: "cc_library_shared", + config_namespace: "ANDROID", + bool_variables: [ + "release_binder_death_recipient_weak_from_jni", + ], + properties: [ + "cflags", + ], +} + +cc_library_shared_for_libandroid_runtime { name: "libandroid_runtime", host_supported: true, cflags: [ @@ -46,6 +58,12 @@ cc_library_shared { }, }, + soong_config_variables: { + release_binder_death_recipient_weak_from_jni: { + cflags: ["-DBINDER_DEATH_RECIPIENT_WEAK_FROM_JNI"], + }, + }, + cpp_std: "gnu++20", srcs: [ diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index 6ed0a8a047f5..55382cc1d380 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -17,19 +17,8 @@ #define LOG_TAG "JavaBinder" //#define LOG_NDEBUG 0 -#include "android_os_Parcel.h" #include "android_util_Binder.h" -#include <atomic> -#include <fcntl.h> -#include <inttypes.h> -#include <mutex> -#include <stdio.h> -#include <string> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - #include <android-base/stringprintf.h> #include <binder/BpBinder.h> #include <binder/IInterface.h> @@ -40,7 +29,16 @@ #include <binder/Stability.h> #include <binderthreadstate/CallerUtils.h> #include <cutils/atomic.h> +#include <fcntl.h> +#include <inttypes.h> #include <log/log.h> +#include <nativehelper/JNIHelp.h> +#include <nativehelper/ScopedLocalRef.h> +#include <nativehelper/ScopedUtfChars.h> +#include <stdio.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> #include <utils/KeyedVector.h> #include <utils/List.h> #include <utils/Log.h> @@ -48,10 +46,11 @@ #include <utils/SystemClock.h> #include <utils/threads.h> -#include <nativehelper/JNIHelp.h> -#include <nativehelper/ScopedLocalRef.h> -#include <nativehelper/ScopedUtfChars.h> +#include <atomic> +#include <mutex> +#include <string> +#include "android_os_Parcel.h" #include "core_jni_helpers.h" //#undef ALOGV @@ -557,14 +556,48 @@ public: }; // ---------------------------------------------------------------------------- +#ifdef BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI +#if __BIONIC__ +#include <android/api-level.h> +static bool target_sdk_is_at_least_vic() { + return android_get_application_target_sdk_version() >= __ANDROID_API_V__; +} +#else +static constexpr bool target_sdk_is_at_least_vic() { + // If not built for Android (i.e. glibc host), follow the latest behavior as there's no compat + // requirement there. + return true; +} +#endif // __BIONIC__ +#endif // BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI class JavaDeathRecipient : public IBinder::DeathRecipient { public: JavaDeathRecipient(JNIEnv* env, jobject object, const sp<DeathRecipientList>& list) - : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object)), - mObjectWeak(NULL), mList(list) - { + : mVM(jnienv_to_javavm(env)), mObject(NULL), mObjectWeak(NULL), mList(list) { + // b/298374304: For apps targeting Android V or beyond, we no longer hold the global JNI ref + // to the death recipient objects. This is to prevent the memory leak which can happen when + // the death recipient object internally has a strong reference to the proxy object. Under + // the old behavior, you were unable to kill the binder service by dropping all references + // to the proxy object - because it is still strong referenced from JNI (here). The only way + // to cut the strong reference was to call unlinkDeath(), but it was easy to forget. + // + // Now, the strong reference to the death recipient is held in the Java-side proxy object. + // See BinderProxy.mDeathRecipients. From JNI, only the weak reference is kept. An + // implication of this is that you may not receive binderDied() if you drop all references + // to the proxy object before the service dies. This should be okay for most cases because + // you normally are not interested in the death of a binder service which you don't have any + // reference to. If however you want to get binderDied() regardless of the proxy object's + // lifecycle, keep a strong reference to the death recipient object by yourself. +#ifdef BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI + if (target_sdk_is_at_least_vic()) { + mObjectWeak = env->NewWeakGlobalRef(object); + } else +#endif + { + mObject = env->NewGlobalRef(object); + } // These objects manage their own lifetimes so are responsible for final bookkeeping. // The list holds a strong reference to this object. LOGDEATH("Adding JDR %p to DRL %p", this, list.get()); @@ -577,26 +610,49 @@ public: void binderDied(const wp<IBinder>& who) { LOGDEATH("Receiving binderDied() on JavaDeathRecipient %p\n", this); - if (mObject != NULL) { - JNIEnv* env = javavm_to_jnienv(mVM); - ScopedLocalRef<jobject> jBinderProxy(env, javaObjectForIBinder(env, who.promote())); - env->CallStaticVoidMethod(gBinderProxyOffsets.mClass, - gBinderProxyOffsets.mSendDeathNotice, mObject, - jBinderProxy.get()); - if (env->ExceptionCheck()) { - jthrowable excep = env->ExceptionOccurred(); - binder_report_exception(env, excep, - "*** Uncaught exception returned from death notification!"); - } + if (mObject == NULL && mObjectWeak == NULL) { + return; + } + JNIEnv* env = javavm_to_jnienv(mVM); + ScopedLocalRef<jobject> jBinderProxy(env, javaObjectForIBinder(env, who.promote())); + + // Hold a local reference to the recipient. This may fail if the recipient is weakly + // referenced, in which case we can't deliver the death notice. + ScopedLocalRef<jobject> jRecipient(env, + env->NewLocalRef(mObject != NULL ? mObject + : mObjectWeak)); + if (jRecipient.get() == NULL) { + ALOGW("Binder died, but death recipient is already garbage collected. If your target " + "sdk level is at or above 35, this can happen when you dropped all references to " + "the binder service before it died. If you want to get a death notice for a " + "binder service which you have no reference to, keep a strong reference to the " + "death recipient by yourself."); + return; + } - // Serialize with our containing DeathRecipientList so that we can't - // delete the global ref on mObject while the list is being iterated. + if (mFired) { + ALOGW("Received multiple death notices for the same binder object. Binder driver bug?"); + return; + } + mFired = true; + + env->CallStaticVoidMethod(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mSendDeathNotice, + jRecipient.get(), jBinderProxy.get()); + if (env->ExceptionCheck()) { + jthrowable excep = env->ExceptionOccurred(); + binder_report_exception(env, excep, + "*** Uncaught exception returned from death notification!"); + } + + // Demote from strong ref (if exists) to weak after binderDied() has been delivered, to + // allow the DeathRecipient and BinderProxy to be GC'd if no longer needed. Do this in sync + // with our containing DeathRecipientList so that we can't delete the global ref on mObject + // while the list is being iterated. + if (mObject != NULL) { sp<DeathRecipientList> list = mList.promote(); if (list != NULL) { AutoMutex _l(list->lock()); - // Demote from strong ref to weak after binderDied() has been delivered, - // to allow the DeathRecipient and BinderProxy to be GC'd if no longer needed. mObjectWeak = env->NewWeakGlobalRef(mObject); env->DeleteGlobalRef(mObject); mObject = NULL; @@ -663,9 +719,19 @@ protected: private: JavaVM* const mVM; - jobject mObject; // Initial strong ref to Java-side DeathRecipient. Cleared on binderDied(). - jweak mObjectWeak; // Weak ref to the same Java-side DeathRecipient after binderDied(). + + // If target sdk version < 35, the Java-side DeathRecipient is strongly referenced from mObject + // upon linkToDeath() and then after binderDied() is called, the strong reference is demoted to + // a weak reference (mObjectWeak). + // If target sdk version >= 35, the strong reference is never made here (i.e. mObject == NULL + // always). Instead, the strong reference to the Java-side DeathRecipient is made in + // BinderProxy.mDeathRecipients. In the native world, only the weak reference is kept. + jobject mObject; + jweak mObjectWeak; wp<DeathRecipientList> mList; + + // Whether binderDied was called or not. + bool mFired = false; }; // ---------------------------------------------------------------------------- @@ -1452,17 +1518,19 @@ static jobject android_os_BinderProxy_getExtension(JNIEnv* env, jobject obj) { // ---------------------------------------------------------------------------- +// clang-format off static const JNINativeMethod gBinderProxyMethods[] = { /* name, signature, funcPtr */ {"pingBinder", "()Z", (void*)android_os_BinderProxy_pingBinder}, {"isBinderAlive", "()Z", (void*)android_os_BinderProxy_isBinderAlive}, {"getInterfaceDescriptor", "()Ljava/lang/String;", (void*)android_os_BinderProxy_getInterfaceDescriptor}, {"transactNative", "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void*)android_os_BinderProxy_transact}, - {"linkToDeath", "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath}, - {"unlinkToDeath", "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath}, + {"linkToDeathNative", "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath}, + {"unlinkToDeathNative", "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath}, {"getNativeFinalizer", "()J", (void*)android_os_BinderProxy_getNativeFinalizer}, {"getExtension", "()Landroid/os/IBinder;", (void*)android_os_BinderProxy_getExtension}, }; +// clang-format on const char* const kBinderProxyPathName = "android/os/BinderProxy"; diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index 9eed904d52be..6db1832d0508 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -16,6 +16,7 @@ package android.security.keystore; +import android.annotation.FlaggedApi; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; @@ -34,7 +35,10 @@ import java.security.KeyPairGenerator; import java.security.Signature; import java.security.cert.Certificate; import java.security.spec.AlgorithmParameterSpec; +import java.util.Collections; import java.util.Date; +import java.util.HashSet; +import java.util.Set; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; @@ -300,6 +304,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu private final Date mKeyValidityForConsumptionEnd; private final @KeyProperties.PurposeEnum int mPurposes; private final @KeyProperties.DigestEnum String[] mDigests; + private final @NonNull @KeyProperties.DigestEnum Set<String> mMgf1Digests; private final @KeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings; private final @KeyProperties.SignaturePaddingEnum String[] mSignaturePaddings; private final @KeyProperties.BlockModeEnum String[] mBlockModes; @@ -343,6 +348,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu Date keyValidityForConsumptionEnd, @KeyProperties.PurposeEnum int purposes, @KeyProperties.DigestEnum String[] digests, + @KeyProperties.DigestEnum Set<String> mgf1Digests, @KeyProperties.EncryptionPaddingEnum String[] encryptionPaddings, @KeyProperties.SignaturePaddingEnum String[] signaturePaddings, @KeyProperties.BlockModeEnum String[] blockModes, @@ -401,6 +407,9 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd); mPurposes = purposes; mDigests = ArrayUtils.cloneIfNotEmpty(digests); + // No need to copy the input parameter because the Builder class passes in an immutable + // collection. + mMgf1Digests = mgf1Digests != null ? mgf1Digests : Collections.emptySet(); mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(encryptionPaddings)); mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(signaturePaddings)); @@ -562,7 +571,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu /** * Returns the set of digest algorithms (e.g., {@code SHA-256}, {@code SHA-384} with which the - * key can be used or {@code null} if not specified. + * key can be used. * * <p>See {@link KeyProperties}.{@code DIGEST} constants. * @@ -590,6 +599,40 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu } /** + * Returns the set of digests that can be used by the MGF1 mask generation function + * (e.g., {@code SHA-256}, {@code SHA-384}) with the key. Useful with the {@code RSA-OAEP} + * scheme. + * If not explicitly specified during key generation, the default {@code SHA-1} digest is + * used and may be specified when using the key. + * + * <p>See {@link KeyProperties}.{@code DIGEST} constants. + * + * @throws IllegalStateException if this set has not been specified. + * + * @see #isMgf1DigestsSpecified() + */ + @NonNull + @FlaggedApi("MGF1_DIGEST_SETTER") + public @KeyProperties.DigestEnum Set<String> getMgf1Digests() { + if (mMgf1Digests.isEmpty()) { + throw new IllegalStateException("Mask generation function (MGF) not specified"); + } + return new HashSet(mMgf1Digests); + } + + /** + * Returns {@code true} if the set of digests for the MGF1 mask generation function, + * with which the key can be used, has been specified. Useful with the {@code RSA-OAEP} scheme. + * + * @see #getMgf1Digests() + */ + @NonNull + @FlaggedApi("MGF1_DIGEST_SETTER") + public boolean isMgf1DigestsSpecified() { + return !mMgf1Digests.isEmpty(); + } + + /** * Returns the set of padding schemes (e.g., {@code PKCS7Padding}, {@code OEAPPadding}, * {@code PKCS1Padding}, {@code NoPadding}) with which the key can be used when * encrypting/decrypting. Attempts to use the key with any other padding scheme will be @@ -899,6 +942,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu private Date mKeyValidityForOriginationEnd; private Date mKeyValidityForConsumptionEnd; private @KeyProperties.DigestEnum String[] mDigests; + private @NonNull @KeyProperties.DigestEnum Set<String> mMgf1Digests = + Collections.emptySet(); private @KeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings; private @KeyProperties.SignaturePaddingEnum String[] mSignaturePaddings; private @KeyProperties.BlockModeEnum String[] mBlockModes; @@ -968,6 +1013,9 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu if (sourceSpec.isDigestsSpecified()) { mDigests = sourceSpec.getDigests(); } + if (sourceSpec.isMgf1DigestsSpecified()) { + mMgf1Digests = sourceSpec.getMgf1Digests(); + } mEncryptionPaddings = sourceSpec.getEncryptionPaddings(); mSignaturePaddings = sourceSpec.getSignaturePaddings(); mBlockModes = sourceSpec.getBlockModes(); @@ -1214,6 +1262,30 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu } /** + * Sets the set of hash functions (e.g., {@code SHA-256}, {@code SHA-384}) which could be + * used by the mask generation function MGF1 (which is used for certain operations with + * the key). Attempts to use the key with any other digest for the mask generation + * function will be rejected. + * + * <p>This can only be specified for signing/verification keys and RSA encryption/decryption + * keys used with RSA OAEP padding scheme because these operations involve a mask generation + * function (MGF1) with a digest. + * The default digest for MGF1 is {@code SHA-1}, which will be specified during key creation + * time if no digests have been explicitly provided. + * When using the key, the caller may not specify any digests that were not provided during + * key creation time. The caller may specify the default digest, {@code SHA-1}, if no + * digests were explicitly provided during key creation (but it is not necessary to do so). + * + * <p>See {@link KeyProperties}.{@code DIGEST} constants. + */ + @NonNull + @FlaggedApi("MGF1_DIGEST_SETTER") + public Builder setMgf1Digests(@Nullable @KeyProperties.DigestEnum String... mgf1Digests) { + mMgf1Digests = Set.of(mgf1Digests); + return this; + } + + /** * Sets the set of padding schemes (e.g., {@code PKCS7Padding}, {@code OAEPPadding}, * {@code PKCS1Padding}, {@code NoPadding}) with which the key can be used when * encrypting/decrypting. Attempts to use the key with any other padding scheme will be @@ -1745,6 +1817,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu mKeyValidityForConsumptionEnd, mPurposes, mDigests, + mMgf1Digests, mEncryptionPaddings, mSignaturePaddings, mBlockModes, diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java index c14c3c534cf4..1262a5303f38 100644 --- a/keystore/java/android/security/keystore/KeyProtection.java +++ b/keystore/java/android/security/keystore/KeyProtection.java @@ -16,6 +16,7 @@ package android.security.keystore; +import android.annotation.FlaggedApi; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; @@ -30,7 +31,10 @@ import java.security.Key; import java.security.KeyStore.ProtectionParameter; import java.security.Signature; import java.security.cert.Certificate; +import java.util.Collections; import java.util.Date; +import java.util.HashSet; +import java.util.Set; import javax.crypto.Cipher; import javax.crypto.Mac; @@ -223,6 +227,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { private final @KeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings; private final @KeyProperties.SignaturePaddingEnum String[] mSignaturePaddings; private final @KeyProperties.DigestEnum String[] mDigests; + private final @NonNull @KeyProperties.DigestEnum Set<String> mMgf1Digests; private final @KeyProperties.BlockModeEnum String[] mBlockModes; private final boolean mRandomizedEncryptionRequired; private final boolean mUserAuthenticationRequired; @@ -246,6 +251,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { @KeyProperties.EncryptionPaddingEnum String[] encryptionPaddings, @KeyProperties.SignaturePaddingEnum String[] signaturePaddings, @KeyProperties.DigestEnum String[] digests, + @KeyProperties.DigestEnum Set<String> mgf1Digests, @KeyProperties.BlockModeEnum String[] blockModes, boolean randomizedEncryptionRequired, boolean userAuthenticationRequired, @@ -269,6 +275,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(signaturePaddings)); mDigests = ArrayUtils.cloneIfNotEmpty(digests); + mMgf1Digests = mgf1Digests; mBlockModes = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(blockModes)); mRandomizedEncryptionRequired = randomizedEncryptionRequired; mUserAuthenticationRequired = userAuthenticationRequired; @@ -378,6 +385,40 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { } /** + * Returns the set of digests that can be used by the MGF1 mask generation function + * (e.g., {@code SHA-256}, {@code SHA-384}) with the key. Useful with the {@code RSA-OAEP} + * scheme. + * If not explicitly specified during key generation, the default {@code SHA-1} digest is + * used and may be specified. + * + * <p>See {@link KeyProperties}.{@code DIGEST} constants. + * + * @throws IllegalStateException if this set has not been specified. + * + * @see #isMgf1DigestsSpecified() + */ + @NonNull + @FlaggedApi("MGF1_DIGEST_SETTER") + public @KeyProperties.DigestEnum Set<String> getMgf1Digests() { + if (mMgf1Digests.isEmpty()) { + throw new IllegalStateException("Mask generation function (MGF) not specified"); + } + return new HashSet(mMgf1Digests); + } + + /** + * Returns {@code true} if the set of digests for the MGF1 mask generation function, + * with which the key can be used, has been specified. Useful with the {@code RSA-OAEP} scheme. + * + * @see #getMgf1Digests() + */ + @NonNull + @FlaggedApi("MGF1_DIGEST_SETTER") + public boolean isMgf1DigestsSpecified() { + return !mMgf1Digests.isEmpty(); + } + + /** * Gets the set of block modes (e.g., {@code GCM}, {@code CBC}) with which the key can be used * when encrypting/decrypting. Attempts to use the key with any other block modes will be * rejected. @@ -574,6 +615,8 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { private @KeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings; private @KeyProperties.SignaturePaddingEnum String[] mSignaturePaddings; private @KeyProperties.DigestEnum String[] mDigests; + private @NonNull @KeyProperties.DigestEnum Set<String> mMgf1Digests = + Collections.emptySet(); private @KeyProperties.BlockModeEnum String[] mBlockModes; private boolean mRandomizedEncryptionRequired = true; private boolean mUserAuthenticationRequired; @@ -724,6 +767,30 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { } /** + * Sets the set of hash functions (e.g., {@code SHA-256}, {@code SHA-384}) which could be + * used by the mask generation function MGF1 (which is used for certain operations with + * the key). Attempts to use the key with any other digest for the mask generation + * function will be rejected. + * + * <p>This can only be specified for signing/verification keys and RSA encryption/decryption + * keys used with RSA OAEP padding scheme because these operations involve a mask generation + * function (MGF1) with a digest. + * The default digest for MGF1 is {@code SHA-1}, which will be specified during key import + * time if no digests have been explicitly provided. + * When using the key, the caller may not specify any digests that were not provided during + * key import time. The caller may specify the default digest, {@code SHA-1}, if no + * digests were explicitly provided during key import (but it is not necessary to do so). + * + * <p>See {@link KeyProperties}.{@code DIGEST} constants. + */ + @NonNull + @FlaggedApi("MGF1_DIGEST_SETTER") + public Builder setMgf1Digests(@Nullable @KeyProperties.DigestEnum String... mgf1Digests) { + mMgf1Digests = Set.of(mgf1Digests); + return this; + } + + /** * Sets the set of block modes (e.g., {@code GCM}, {@code CBC}) with which the key can be * used when encrypting/decrypting. Attempts to use the key with any other block modes will * be rejected. @@ -1111,6 +1178,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { mEncryptionPaddings, mSignaturePaddings, mDigests, + mMgf1Digests, mBlockModes, mRandomizedEncryptionRequired, mUserAuthenticationRequired, diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java index a6e33664f2b1..30b3b5ce0ead 100644 --- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java @@ -23,7 +23,11 @@ import java.math.BigInteger; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.ECGenParameterSpec; import java.security.spec.RSAKeyGenParameterSpec; +import java.util.ArrayList; +import java.util.Collections; import java.util.Date; +import java.util.List; +import java.util.Set; import javax.security.auth.x500.X500Principal; @@ -91,6 +95,11 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { } else { out.writeStringArray(null); } + if (mSpec.isMgf1DigestsSpecified()) { + out.writeStringList(List.copyOf(mSpec.getMgf1Digests())); + } else { + out.writeStringList(null); + } out.writeStringArray(mSpec.getEncryptionPaddings()); out.writeStringArray(mSpec.getSignaturePaddings()); out.writeStringArray(mSpec.getBlockModes()); @@ -152,6 +161,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { final Date keyValidityForOriginationEnd = readDateOrNull(in); final Date keyValidityForConsumptionEnd = readDateOrNull(in); final String[] digests = in.createStringArray(); + final ArrayList<String> mgf1Digests = in.createStringArrayList(); final String[] encryptionPaddings = in.createStringArray(); final String[] signaturePaddings = in.createStringArray(); final String[] blockModes = in.createStringArray(); @@ -189,6 +199,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { keyValidityForConsumptionEnd, purposes, digests, + mgf1Digests != null ? Set.copyOf(mgf1Digests) : Collections.emptySet(), encryptionPaddings, signaturePaddings, blockModes, diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java index 9ac0f6d304f6..101a10e3d312 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java @@ -24,6 +24,7 @@ import android.os.StrictMode; import android.security.KeyStoreException; import android.security.KeyStoreOperation; import android.security.keymaster.KeymasterDefs; +import android.security.keystore.KeyProperties; import android.security.keystore.KeyStoreCryptoOperation; import android.system.keystore2.Authorization; @@ -71,7 +72,7 @@ import javax.crypto.spec.SecretKeySpec; */ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStoreCryptoOperation { private static final String TAG = "AndroidKeyStoreCipherSpiBase"; - public static final String DEFAULT_MGF1_DIGEST = "SHA-1"; + public static final String DEFAULT_MGF1_DIGEST = KeyProperties.DIGEST_SHA1; // Fields below are populated by Cipher.init and KeyStore.begin and should be preserved after // doFinal finishes. diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java index 1398da3f5ef2..ed4b485f3927 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -188,6 +188,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato private int[] mKeymasterEncryptionPaddings; private int[] mKeymasterSignaturePaddings; private int[] mKeymasterDigests; + private int[] mKeymasterMgf1Digests; private Long mRSAPublicExponent; @@ -323,6 +324,21 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato } else { mKeymasterDigests = EmptyArray.INT; } + if (spec.isMgf1DigestsSpecified()) { + // User-specified digests: Add all of them and do _not_ add the SHA-1 + // digest by default (stick to what the user provided). + Set<String> mgfDigests = spec.getMgf1Digests(); + mKeymasterMgf1Digests = new int[mgfDigests.size()]; + int offset = 0; + for (String digest : mgfDigests) { + mKeymasterMgf1Digests[offset] = KeyProperties.Digest.toKeymaster(digest); + offset++; + } + } else { + // No user-specified digests: Add the SHA-1 default. + mKeymasterMgf1Digests = new int[]{ + KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST)}; + } // Check that user authentication related parameters are acceptable. This method // will throw an IllegalStateException if there are issues (e.g., secure lock screen @@ -544,6 +560,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato mKeymasterEncryptionPaddings = null; mKeymasterSignaturePaddings = null; mKeymasterDigests = null; + mKeymasterMgf1Digests = null; mKeySizeBits = 0; mSpec = null; mRSAPublicExponent = null; @@ -831,24 +848,11 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato KeymasterDefs.KM_TAG_PADDING, padding )); if (padding == KeymasterDefs.KM_PAD_RSA_OAEP) { - final boolean[] hasDefaultMgf1DigestBeenAdded = {false}; - ArrayUtils.forEach(mKeymasterDigests, (digest) -> { + ArrayUtils.forEach(mKeymasterMgf1Digests, (mgf1Digest) -> { params.add(KeyStore2ParameterUtils.makeEnum( - KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, digest + KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, mgf1Digest )); - hasDefaultMgf1DigestBeenAdded[0] |= - digest.equals(KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST)); }); - /* Because of default MGF1 digest is SHA-1. It has to be added in Key - * characteristics. Otherwise, crypto operations will fail with Incompatible - * MGF1 digest. - */ - if (!hasDefaultMgf1DigestBeenAdded[0]) { - params.add(KeyStore2ParameterUtils.makeEnum( - KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, - KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST) - )); - } } }); ArrayUtils.forEach(mKeymasterSignaturePaddings, (padding) -> { diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java index eef817902ade..d3d970153cf3 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java @@ -526,25 +526,22 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { padding )); if (padding == KeymasterDefs.KM_PAD_RSA_OAEP) { - if (spec.isDigestsSpecified()) { - boolean hasDefaultMgf1DigestBeenAdded = false; - for (String digest : spec.getDigests()) { + if (spec.isMgf1DigestsSpecified()) { + for (String mgf1Digest : spec.getMgf1Digests()) { importArgs.add(KeyStore2ParameterUtils.makeEnum( KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, - KeyProperties.Digest.toKeymaster(digest) + KeyProperties.Digest.toKeymaster(mgf1Digest) )); - hasDefaultMgf1DigestBeenAdded |= digest.equals(DEFAULT_MGF1_DIGEST); } + } else { /* Because of default MGF1 digest is SHA-1. It has to be added in Key * characteristics. Otherwise, crypto operations will fail with Incompatible * MGF1 digest. */ - if (!hasDefaultMgf1DigestBeenAdded) { - importArgs.add(KeyStore2ParameterUtils.makeEnum( - KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, - KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST) - )); - } + importArgs.add(KeyStore2ParameterUtils.makeEnum( + KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, + KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST) + )); } } } diff --git a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java index 2ae61ab3b38d..d4e2dbc81509 100644 --- a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java +++ b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java @@ -17,6 +17,7 @@ package android.security; import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; @@ -101,6 +102,7 @@ public final class ParcelableKeyGenParameterSpecTest { assertThat(spec.getKeyValidityForOriginationEnd(), is(KEY_VALIDITY_FOR_ORIG_END)); assertThat(spec.getKeyValidityForConsumptionEnd(), is(KEY_VALIDITY_FOR_CONSUMPTION_END)); assertThat(spec.getDigests(), is(new String[] {DIGEST})); + assertThat(spec.isMgf1DigestsSpecified(), is(false)); assertThat(spec.getEncryptionPaddings(), is(new String[] {ENCRYPTION_PADDING})); assertThat(spec.getSignaturePaddings(), is(new String[] {SIGNATURE_PADDING})); assertThat(spec.getBlockModes(), is(new String[] {BLOCK_MODE})); @@ -189,4 +191,19 @@ public final class ParcelableKeyGenParameterSpecTest { ECGenParameterSpec parcelSpec = (ECGenParameterSpec) fromParcel.getAlgorithmParameterSpec(); assertEquals(parcelSpec.getName(), ecSpec.getName()); } + + @Test + public void testParcelingMgf1Digests() { + String[] mgf1Digests = + new String[] {KeyProperties.DIGEST_SHA1, KeyProperties.DIGEST_SHA256}; + + ParcelableKeyGenParameterSpec spec = new ParcelableKeyGenParameterSpec( + new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES) + .setMgf1Digests(mgf1Digests) + .build()); + Parcel parcel = parcelForReading(spec); + KeyGenParameterSpec fromParcel = + ParcelableKeyGenParameterSpec.CREATOR.createFromParcel(parcel).getSpec(); + assertArrayEquals(fromParcel.getMgf1Digests().toArray(), mgf1Digests); + } } diff --git a/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java b/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java index ddbb1d8c097c..da5e8bf84191 100644 --- a/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java +++ b/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java @@ -16,9 +16,12 @@ package android.security.keystore; +import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertThrows; import android.security.ParcelableKeyGenParameterSpecTest; @@ -61,4 +64,54 @@ public final class KeyGenParameterSpecTest { assertEquals(copiedSpec.getAttestationChallenge(), null); } + + @Test + public void testMgf1DigestsNotSpecifiedByDefault() { + KeyGenParameterSpec spec = ParcelableKeyGenParameterSpecTest.configureDefaultSpec(); + assertThat(spec.isMgf1DigestsSpecified(), is(false)); + assertThrows(IllegalStateException.class, () -> { + spec.getMgf1Digests(); + }); + } + + @Test + public void testMgf1DigestsCanBeSpecified() { + String[] mgf1Digests = + new String[] {KeyProperties.DIGEST_SHA1, KeyProperties.DIGEST_SHA256}; + KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES) + .setMgf1Digests(mgf1Digests) + .build(); + assertThat(spec.isMgf1DigestsSpecified(), is(true)); + assertThat(spec.getMgf1Digests(), containsInAnyOrder(mgf1Digests)); + + KeyGenParameterSpec copiedSpec = new KeyGenParameterSpec.Builder(spec).build(); + assertThat(copiedSpec.isMgf1DigestsSpecified(), is(true)); + assertThat(copiedSpec.getMgf1Digests(), containsInAnyOrder(mgf1Digests)); + } + + @Test + public void testMgf1DigestsAreNotModified() { + String[] mgf1Digests = + new String[] {KeyProperties.DIGEST_SHA1, KeyProperties.DIGEST_SHA256}; + KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES) + .setMgf1Digests(mgf1Digests); + + KeyGenParameterSpec firstSpec = builder.build(); + assertArrayEquals(mgf1Digests, firstSpec.getMgf1Digests().toArray()); + + String[] otherDigests = new String[] {KeyProperties.DIGEST_SHA224}; + KeyGenParameterSpec secondSpec = builder.setMgf1Digests(otherDigests).build(); + assertThat(secondSpec.getMgf1Digests(), containsInAnyOrder(otherDigests)); + + // Now check that the first spec created hasn't changed. + assertThat(firstSpec.getMgf1Digests(), containsInAnyOrder(mgf1Digests)); + } + + @Test + public void testEmptyMgf1DigestsCanBeSet() { + KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES) + .setMgf1Digests(new String[] {}).build(); + + assertThat(spec.isMgf1DigestsSpecified(), is(false)); + } } diff --git a/mime/Android.bp b/mime/Android.bp index a3ea65cb2efe..757862b998b4 100644 --- a/mime/Android.bp +++ b/mime/Android.bp @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. - package { // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import @@ -125,6 +124,6 @@ java_genrule { srcs: [ "java-res/vendor.mime.types", ], - // strip comments normalize whitepace drop empty lines prepend ? to fields that are missing it - cmd: "awk '{gsub(/#.*$$/,\"\"); $$1=$$1; print;}' $(in) | grep ' ' | awk '{for(i=1;i<=NF;i++) { sub(/^\\??/, \"?\", $$i); }; print}' > $(out)", + // strip comments normalize whitepace drop empty lines prepend ? to fields that are missing it + cmd: "awk '{gsub(/#.*$$/,\"\"); $$1=$$1; print;}' $(in) | (grep ' ' || echo -n '') | awk '{for(i=1;i<=NF;i++) { sub(/^\\??/, \"?\", $$i); }; print}' > $(out)", } diff --git a/services/Android.bp b/services/Android.bp index eb56c250e224..5b15392fde27 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -255,4 +255,5 @@ droidstubs { tag: ".removed-api.txt", }, ], + api_surface: "system-server", } diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 7f929604d684..cb5885586d26 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -266,17 +266,17 @@ class StorageManagerService extends IStorageManager.Stub @Override public void onUserUnlocking(@NonNull TargetUser user) { - mStorageManagerService.onUnlockUser(user.getUserIdentifier()); + mStorageManagerService.onUserUnlocking(user.getUserIdentifier()); } @Override public void onUserStopped(@NonNull TargetUser user) { - mStorageManagerService.onCleanupUser(user.getUserIdentifier()); + mStorageManagerService.onUserStopped(user.getUserIdentifier()); } @Override public void onUserStopping(@NonNull TargetUser user) { - mStorageManagerService.onStopUser(user.getUserIdentifier()); + mStorageManagerService.onUserStopping(user.getUserIdentifier()); } @Override @@ -1247,8 +1247,8 @@ class StorageManagerService extends IStorageManager.Stub } } - private void onUnlockUser(int userId) { - Slog.d(TAG, "onUnlockUser " + userId); + private void onUserUnlocking(int userId) { + Slog.d(TAG, "onUserUnlocking " + userId); if (userId != UserHandle.USER_SYSTEM) { // Check if this user shares media with another user @@ -1311,8 +1311,8 @@ class StorageManagerService extends IStorageManager.Stub } } - private void onCleanupUser(int userId) { - Slog.d(TAG, "onCleanupUser " + userId); + private void onUserStopped(int userId) { + Slog.d(TAG, "onUserStopped " + userId); try { mVold.onUserStopped(userId); @@ -1326,8 +1326,8 @@ class StorageManagerService extends IStorageManager.Stub } } - private void onStopUser(int userId) { - Slog.i(TAG, "onStopUser " + userId); + private void onUserStopping(int userId) { + Slog.i(TAG, "onUserStopping " + userId); try { mStorageSessionController.onUserStopping(userId); } catch (Exception e) { diff --git a/services/foldables/devicestateprovider/OWNERS b/services/foldables/devicestateprovider/OWNERS new file mode 100644 index 000000000000..573284419a48 --- /dev/null +++ b/services/foldables/devicestateprovider/OWNERS @@ -0,0 +1,6 @@ +akulian@google.com +jiamingliu@google.com +kchyn@google.com +kennethford@google.com +nickchameyev@google.com +nicomazz@google.com
\ No newline at end of file diff --git a/tests/BinderLeakTest/Android.bp b/tests/BinderLeakTest/Android.bp new file mode 100644 index 000000000000..78b0ede76d4e --- /dev/null +++ b/tests/BinderLeakTest/Android.bp @@ -0,0 +1,40 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +filegroup { + name: "binder_leak_test_aidl", + srcs: ["**/*.aidl"], + path: "aidl", +} + +java_defaults { + name: "BinderTest.defaults", + srcs: [ + "**/*.java", + ":binder_leak_test_aidl", + ], + static_libs: [ + "androidx.test.ext.junit", + "androidx.test.rules", + "androidx.test.runner", + ], +} + +// Built with target_sdk_version: current +android_test { + name: "BinderLeakTest", + defaults: ["BinderTest.defaults"], +} + +// Built with target_sdk_version: 33 +android_test { + name: "BinderLeakTest_legacy", + defaults: ["BinderTest.defaults"], + manifest: "AndroidManifest_legacy.xml", +} diff --git a/tests/BinderLeakTest/AndroidManifest.xml b/tests/BinderLeakTest/AndroidManifest.xml new file mode 100644 index 000000000000..756def7ac29d --- /dev/null +++ b/tests/BinderLeakTest/AndroidManifest.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.test.binder"> + <application> + <service + android:name=".MyService" + android:enabled="true" + android:exported="true" + android:process=":service"> + </service> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.test.binder" + android:label="Binder leak test"> + </instrumentation> +</manifest> diff --git a/tests/BinderLeakTest/AndroidManifest_legacy.xml b/tests/BinderLeakTest/AndroidManifest_legacy.xml new file mode 100644 index 000000000000..03d1dfd1fd83 --- /dev/null +++ b/tests/BinderLeakTest/AndroidManifest_legacy.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.test.binder"> + <uses-sdk android:minSdkVersion="33" + android:targetSdkVersion="33" + android:maxSdkVersion="33" /> + <application> + <service + android:name=".MyService" + android:enabled="true" + android:exported="true" + android:process=":service"> + </service> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.test.binder" + android:label="Binder leak test"> + </instrumentation> +</manifest> diff --git a/tests/BinderLeakTest/aidl/com/android/test/binder/IFoo.aidl b/tests/BinderLeakTest/aidl/com/android/test/binder/IFoo.aidl new file mode 100644 index 000000000000..a721959d19b4 --- /dev/null +++ b/tests/BinderLeakTest/aidl/com/android/test/binder/IFoo.aidl @@ -0,0 +1,5 @@ +package com.android.test.binder; + +interface IFoo { + +} diff --git a/tests/BinderLeakTest/aidl/com/android/test/binder/IFooProvider.aidl b/tests/BinderLeakTest/aidl/com/android/test/binder/IFooProvider.aidl new file mode 100644 index 000000000000..b487f51f812c --- /dev/null +++ b/tests/BinderLeakTest/aidl/com/android/test/binder/IFooProvider.aidl @@ -0,0 +1,10 @@ +package com.android.test.binder; +import com.android.test.binder.IFoo; + +interface IFooProvider { + IFoo createFoo(); + + boolean isFooGarbageCollected(); + + oneway void killProcess(); +} diff --git a/tests/BinderLeakTest/java/com/android/test/binder/BinderTest.java b/tests/BinderLeakTest/java/com/android/test/binder/BinderTest.java new file mode 100644 index 000000000000..f07317f7d5f3 --- /dev/null +++ b/tests/BinderLeakTest/java/com/android/test/binder/BinderTest.java @@ -0,0 +1,153 @@ +/* + * 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.test.binder; + +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 android.content.Intent; +import android.os.Build; +import android.os.IBinder; +import android.os.RemoteException; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ServiceTestRule; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.lang.ref.PhantomReference; +import java.lang.ref.ReferenceQueue; +import java.util.concurrent.TimeoutException; + +@RunWith(AndroidJUnit4.class) +public class BinderTest { + @Rule + public final ServiceTestRule serviceRule = new ServiceTestRule(); + + @Test + public void testDeathRecipientLeaksOrNot() + throws RemoteException, TimeoutException, InterruptedException { + Intent intent = new Intent(ApplicationProvider.getApplicationContext(), MyService.class); + IFooProvider provider = IFooProvider.Stub.asInterface(serviceRule.bindService(intent)); + FooHolder holder = new FooHolder(provider.createFoo()); + + // ref will get enqueued right after holder is finalized for gc. + ReferenceQueue<FooHolder> refQueue = new ReferenceQueue<>(); + PhantomReference<FooHolder> ref = new PhantomReference<>(holder, refQueue); + + DeathRecorder deathRecorder = new DeathRecorder(); + holder.registerDeathRecorder(deathRecorder); + + if (getSdkVersion() >= Build.VERSION_CODES.VANILLA_ICE_CREAM) { + ///////////////////////////////////////////// + // New behavior + // + // Reference chain at this moment: + // holder --(java strong ref)--> FooHolder + // FooHolder.mProxy --(java strong ref)--> IFoo.Proxy + // IFoo.Proxy.mRemote --(java strong ref)--> BinderProxy + // BinderProxy --(binder ref)--> Foo.Stub + // In other words, the variable "holder" is the root of the reference chain. + + // By setting the variable to null, we make FooHolder, IFoo.Proxy, BinderProxy, and even + // Foo.Stub unreachable. + holder = null; + + // Ensure that the objects are garbage collected + forceGc(); + assertEquals(ref, refQueue.poll()); + assertTrue(provider.isFooGarbageCollected()); + + // The binder has died, but we don't get notified since the death recipient is GC'ed. + provider.killProcess(); + Thread.sleep(1000); // give some time for the service process to die and reaped + assertFalse(deathRecorder.deathRecorded); + } else { + ///////////////////////////////////////////// + // Legacy behavior + // + // Reference chain at this moment: + // JavaDeathRecipient --(JNI strong ref)--> FooHolder + // holder --(java strong ref)--> FooHolder + // FooHolder.mProxy --(java strong ref)--> IFoo.Proxy + // IFoo.Proxy.mRemote --(java strong ref)--> BinderProxy + // BinderProxy --(binder ref)--> Foo.Stub + // So, BOTH JavaDeathRecipient and holder are roots of the reference chain. + + // Even if we set holder to null, it doesn't make other objects unreachable; they are + // still reachable via the JNI strong ref. + holder = null; + + // Check that objects are not garbage collected + forceGc(); + assertNotEquals(ref, refQueue.poll()); + assertFalse(provider.isFooGarbageCollected()); + + // The legacy behavior is getting notified even when there's no reference + provider.killProcess(); + Thread.sleep(1000); // give some time for the service process to die and reaped + assertTrue(deathRecorder.deathRecorded); + } + } + + static class FooHolder implements IBinder.DeathRecipient { + private IFoo mProxy; + private DeathRecorder mDeathRecorder; + + FooHolder(IFoo proxy) throws RemoteException { + proxy.asBinder().linkToDeath(this, 0); + + // A strong reference from DeathRecipient(this) to the binder proxy is created here + mProxy = proxy; + } + + public void registerDeathRecorder(DeathRecorder dr) { + mDeathRecorder = dr; + } + + @Override + public void binderDied() { + if (mDeathRecorder != null) { + mDeathRecorder.deathRecorded = true; + } + } + } + + static class DeathRecorder { + public boolean deathRecorded = false; + } + + // Try calling System.gc() until an orphaned object is confirmed to be finalized + private static void forceGc() { + Object obj = new Object(); + ReferenceQueue<Object> refQueue = new ReferenceQueue<>(); + PhantomReference<Object> ref = new PhantomReference<>(obj, refQueue); + obj = null; // make it an orphan + while (refQueue.poll() != ref) { + System.gc(); + } + } + + private static int getSdkVersion() { + return ApplicationProvider.getApplicationContext().getApplicationInfo().targetSdkVersion; + } +} diff --git a/tests/BinderLeakTest/java/com/android/test/binder/MyService.java b/tests/BinderLeakTest/java/com/android/test/binder/MyService.java new file mode 100644 index 000000000000..c701253446f4 --- /dev/null +++ b/tests/BinderLeakTest/java/com/android/test/binder/MyService.java @@ -0,0 +1,63 @@ +/* + * 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.test.binder; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; +import android.os.RemoteException; + +import java.lang.ref.PhantomReference; +import java.lang.ref.ReferenceQueue; + +public class MyService extends Service { + @Override + public IBinder onBind(Intent intent) { + return new IFooProvider.Stub() { + ReferenceQueue<IFoo> mRefQueue = new ReferenceQueue<>(); + PhantomReference<IFoo> mRef; + + @Override + public IFoo createFoo() throws RemoteException { + IFoo binder = new IFoo.Stub() {}; + mRef = new PhantomReference<>(binder, mRefQueue); + return binder; + } + + @Override + public boolean isFooGarbageCollected() throws RemoteException { + forceGc(); + return mRefQueue.poll() == mRef; + } + + @Override + public void killProcess() throws RemoteException { + android.os.Process.killProcess(android.os.Process.myPid()); + } + }; + } + + private static void forceGc() { + Object obj = new Object(); + ReferenceQueue<Object> refQueue = new ReferenceQueue<>(); + PhantomReference<Object> ref = new PhantomReference<>(obj, refQueue); + obj = null; // make it an orphan + while (refQueue.poll() != ref) { + System.gc(); + } + } +} diff --git a/tests/NetworkSecurityConfigTest/res/raw/ca_certs_der.der b/tests/NetworkSecurityConfigTest/res/raw/ca_certs_der.der Binary files differindex 235bd4797b78..fd888ec600cd 100644 --- a/tests/NetworkSecurityConfigTest/res/raw/ca_certs_der.der +++ b/tests/NetworkSecurityConfigTest/res/raw/ca_certs_der.der diff --git a/tests/NetworkSecurityConfigTest/res/raw/ca_certs_pem.pem b/tests/NetworkSecurityConfigTest/res/raw/ca_certs_pem.pem index 413e3c07d2ab..66f7bfd21289 100644 --- a/tests/NetworkSecurityConfigTest/res/raw/ca_certs_pem.pem +++ b/tests/NetworkSecurityConfigTest/res/raw/ca_certs_pem.pem @@ -1,35 +1,31 @@ -----BEGIN CERTIFICATE----- -MIIDfTCCAuagAwIBAgIDErvmMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT -MRAwDgYDVQQKEwdFcXVpZmF4MS0wKwYDVQQLEyRFcXVpZmF4IFNlY3VyZSBDZXJ0 -aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDIwNTIxMDQwMDAwWhcNMTgwODIxMDQwMDAw -WjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UE -AxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9m -OSm9BXiLnTjoBbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIu -T8rxh0PBFpVXLVDviS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6c -JmTM386DGXHKTubU1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmR -Cw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5asz -PeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo4HwMIHtMB8GA1UdIwQYMBaAFEjm -aPkr0rKV10fYIyAQTzOYkJ/UMB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrM -TjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjA6BgNVHR8EMzAxMC+g -LaArhilodHRwOi8vY3JsLmdlb3RydXN0LmNvbS9jcmxzL3NlY3VyZWNhLmNybDBO -BgNVHSAERzBFMEMGBFUdIAAwOzA5BggrBgEFBQcCARYtaHR0cHM6Ly93d3cuZ2Vv -dHJ1c3QuY29tL3Jlc291cmNlcy9yZXBvc2l0b3J5MA0GCSqGSIb3DQEBBQUAA4GB -AHbhEm5OSxYShjAGsoEIz/AIx8dxfmbuwu3UOx//8PDITtZDOLC5MH0Y0FWDomrL -NhGc6Ehmo21/uBPUR/6LWlxz/K7ZGzIZOKuXNBSqltLroxwUCEm2u+WR74M26x1W -b8ravHNjkOR/ez4iyz0H7V84dJzjA1BOoa+Y7mHyhD8S ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkG -A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz -cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2 -MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV -BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt -YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN -ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE -BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is -I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G -CSqGSIb3DQEBBQUAA4GBABByUqkFFBkyCEHwxWsKzH4PIRnN5GfcX6kb5sroc50i -2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWXbj9T/UWZYB2oK0z5XqcJ -2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/D/xwzoiQ +MIIFYjCCBEqgAwIBAgIQd70NbNs2+RrqIQ/E8FjTDTANBgkqhkiG9w0BAQsFADBX +MQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEQMA4GA1UE +CxMHUm9vdCBDQTEbMBkGA1UEAxMSR2xvYmFsU2lnbiBSb290IENBMB4XDTIwMDYx +OTAwMDA0MloXDTI4MDEyODAwMDA0MlowRzELMAkGA1UEBhMCVVMxIjAgBgNVBAoT +GUdvb2dsZSBUcnVzdCBTZXJ2aWNlcyBMTEMxFDASBgNVBAMTC0dUUyBSb290IFIx +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAthECix7joXebO9y/lD63 +ladAPKH9gvl9MgaCcfb2jH/76Nu8ai6Xl6OMS/kr9rH5zoQdsfnFl97vufKj6bwS +iV6nqlKr+CMny6SxnGPb15l+8Ape62im9MZaRw1NEDPjTrETo8gYbEvs/AmQ351k +KSUjB6G00j0uYODP0gmHu81I8E3CwnqIiru6z1kZ1q+PsAewnjHxgsHA3y6mbWwZ +DrXYfiYaRQM9sHmklCitD38m5agI/pboPGiUU+6DOogrFZYJsuB6jC511pzrp1Zk +j5ZPaK49l8KEj8C8QMALXL32h7M1bKwYUH+E4EzNktMg6TO8UpmvMrUpsyUqtEj5 +cuHKZPfmghCN6J3Cioj6OGaK/GP5Afl4/Xtcd/p2h/rs37EOeZVXtL0m79YB0esW +CruOC7XFxYpVq9Os6pFLKcwZpDIlTirxZUTQAs6qzkm06p98g7BAe+dDq6dso499 +iYH6TKX/1Y7DzkvgtdizjkXPdsDtQCv9Uw+wp9U7DbGKogPeMa3Md+pvez7W35Ei +Eua++tgy/BBjFFFy3l3WFpO9KWgz7zpm7AeKJt8T11dleCfeXkkUAKIAf5qoIbap +sZWwpbkNFhHax2xIPEDgfg1azVY80ZcFuctL7TlLnMQ/0lUTbiSw1nH69MG6zO0b +9f6BQdgAmD06yK56mDcYBZUCAwEAAaOCATgwggE0MA4GA1UdDwEB/wQEAwIBhjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTkrysmcRorSCeFL1JmLO/wiRNxPjAf +BgNVHSMEGDAWgBRge2YaRQ2XyolQL30EzTSo//z9SzBgBggrBgEFBQcBAQRUMFIw +JQYIKwYBBQUHMAGGGWh0dHA6Ly9vY3NwLnBraS5nb29nL2dzcjEwKQYIKwYBBQUH +MAKGHWh0dHA6Ly9wa2kuZ29vZy9nc3IxL2dzcjEuY3J0MDIGA1UdHwQrMCkwJ6Al +oCOGIWh0dHA6Ly9jcmwucGtpLmdvb2cvZ3NyMS9nc3IxLmNybDA7BgNVHSAENDAy +MAgGBmeBDAECATAIBgZngQwBAgIwDQYLKwYBBAHWeQIFAwIwDQYLKwYBBAHWeQIF +AwMwDQYJKoZIhvcNAQELBQADggEBADSkHrEoo9C0dhemMXoh6dFSPsjbdBZBiLg9 +NR3t5P+T4Vxfq7vqfM/b5A3Ri1fyJm9bvhdGaJQ3b2t6yMAYN/olUazsaL+yyEn9 +WprKASOshIArAoyZl+tJaox118fessmXn1hIVw41oeQa1v1vg4Fv74zPl6/AhSrw +9U5pCZEt4Wi4wStz6dTZ/CLANx8LZh1J7QJVj2fhMtfTJr9w4z30Z209fOU0iOMy ++qduBmpvvYuR7hZL6Dupszfnw0Skfths18dG9ZKb59UhvmaSGZRVbNQpsg3BZlvi +d0lIKO2d1xozclOzgjXPYovJJIultzkMu34qQb9Sz/yilrbCgj8= -----END CERTIFICATE----- diff --git a/tests/NetworkSecurityConfigTest/res/xml/domain_whitespace.xml b/tests/NetworkSecurityConfigTest/res/xml/domain_whitespace.xml index 5d23d36e1dbf..99106ad37783 100644 --- a/tests/NetworkSecurityConfigTest/res/xml/domain_whitespace.xml +++ b/tests/NetworkSecurityConfigTest/res/xml/domain_whitespace.xml @@ -5,7 +5,7 @@ </domain> <domain> developer.android.com </domain> <pin-set> - <pin digest="SHA-256"> 7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y= </pin> + <pin digest="SHA-256"> zCTnfLwLKbS9S2sbp+uFz4KZOocFvXxkV06Ce9O5M2w= </pin> </pin-set> </domain-config> </network-security-config> diff --git a/tests/NetworkSecurityConfigTest/res/xml/nested_domains.xml b/tests/NetworkSecurityConfigTest/res/xml/nested_domains.xml index d45fd77a5f0f..232f88ff6cc9 100644 --- a/tests/NetworkSecurityConfigTest/res/xml/nested_domains.xml +++ b/tests/NetworkSecurityConfigTest/res/xml/nested_domains.xml @@ -9,7 +9,7 @@ <domain-config> <domain>developer.android.com</domain> <pin-set> - <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin> + <pin digest="SHA-256">zCTnfLwLKbS9S2sbp+uFz4KZOocFvXxkV06Ce9O5M2w=</pin> </pin-set> </domain-config> </domain-config> diff --git a/tests/NetworkSecurityConfigTest/res/xml/pins1.xml b/tests/NetworkSecurityConfigTest/res/xml/pins1.xml index 1773d28094a3..7cc81b0101f1 100644 --- a/tests/NetworkSecurityConfigTest/res/xml/pins1.xml +++ b/tests/NetworkSecurityConfigTest/res/xml/pins1.xml @@ -3,7 +3,7 @@ <domain-config> <domain>android.com</domain> <pin-set> - <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin> + <pin digest="SHA-256">zCTnfLwLKbS9S2sbp+uFz4KZOocFvXxkV06Ce9O5M2w=</pin> </pin-set> </domain-config> </network-security-config> diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java index 047be162e642..0494f174f191 100644 --- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java +++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java @@ -22,23 +22,17 @@ import android.os.Build; import android.test.ActivityUnitTestCase; import android.util.ArraySet; import android.util.Pair; + +import com.android.org.conscrypt.TrustedCertificateStore; + import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.net.Socket; -import java.net.URL; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Collections; import java.util.HashSet; import java.util.Set; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLHandshakeException; -import javax.net.ssl.TrustManager; -import com.android.org.conscrypt.TrustedCertificateStore; +import javax.net.ssl.SSLContext; public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> { @@ -46,9 +40,9 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> { super(Activity.class); } - // SHA-256 of the G2 intermediate CA for android.com (as of 10/2015). - private static final byte[] G2_SPKI_SHA256 - = hexToBytes("ec722969cb64200ab6638f68ac538e40abab5b19a6485661042a1061c4612776"); + // SHA-256 of the GTS intermediate CA (CN = GTS CA 1C3) for android.com (as of 09/2023). + private static final byte[] GTS_INTERMEDIATE_SPKI_SHA256 = + hexToBytes("cc24e77cbc0b29b4bd4b6b1ba7eb85cf82993a8705bd7c64574e827bd3b9336c"); private static final byte[] TEST_CA_BYTES = hexToBytes( @@ -161,7 +155,7 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> { public void testGoodPin() throws Exception { ArraySet<Pin> pins = new ArraySet<Pin>(); - pins.add(new Pin("SHA-256", G2_SPKI_SHA256)); + pins.add(new Pin("SHA-256", GTS_INTERMEDIATE_SPKI_SHA256)); NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder() .setPinSet(new PinSet(pins, Long.MAX_VALUE)) .addCertificatesEntryRef( @@ -247,7 +241,7 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> { public void testWithUrlConnection() throws Exception { ArraySet<Pin> pins = new ArraySet<Pin>(); - pins.add(new Pin("SHA-256", G2_SPKI_SHA256)); + pins.add(new Pin("SHA-256", GTS_INTERMEDIATE_SPKI_SHA256)); NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder() .setPinSet(new PinSet(pins, Long.MAX_VALUE)) .addCertificatesEntryRef( @@ -304,7 +298,7 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> { } finally { // Delete the user added CA. We don't know the alias so just delete them all. for (String alias : store.aliases()) { - if (store.isUser(alias)) { + if (TrustedCertificateStore.isUser(alias)) { try { store.deleteCertificateEntry(alias); } catch (Exception ignored) { diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestUtils.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestUtils.java index 9dec21be7f37..39b5cb4c4f0d 100644 --- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestUtils.java +++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestUtils.java @@ -16,19 +16,20 @@ package android.security.net.config; +import static org.junit.Assert.fail; + import android.content.pm.ApplicationInfo; import android.os.Build; -import java.net.Socket; + import java.net.URL; + import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLHandshakeException; -import javax.net.ssl.TrustManager; +import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManagerFactory; -import junit.framework.Assert; - -public final class TestUtils extends Assert { +public final class TestUtils { private TestUtils() { } @@ -36,8 +37,8 @@ public final class TestUtils extends Assert { public static void assertConnectionFails(SSLContext context, String host, int port) throws Exception { try { - Socket s = context.getSocketFactory().createSocket(host, port); - s.getInputStream(); + SSLSocket s = (SSLSocket) context.getSocketFactory().createSocket(host, port); + s.startHandshake(); fail("Expected connection to " + host + ":" + port + " to fail."); } catch (SSLHandshakeException expected) { } @@ -45,7 +46,8 @@ public final class TestUtils extends Assert { public static void assertConnectionSucceeds(SSLContext context, String host, int port) throws Exception { - Socket s = context.getSocketFactory().createSocket(host, port); + SSLSocket s = (SSLSocket) context.getSocketFactory().createSocket(host, port); + s.startHandshake(); s.getInputStream(); } diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java index 4b7a014f25dc..81e05c1d4e42 100644 --- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java +++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java @@ -16,26 +16,18 @@ package android.security.net.config; -import android.content.Context; import android.content.pm.ApplicationInfo; import android.test.AndroidTestCase; import android.test.MoreAsserts; -import android.util.ArraySet; -import android.util.Pair; + import java.io.IOException; import java.net.InetAddress; -import java.net.Socket; -import java.net.URL; import java.security.KeyStore; import java.security.Provider; -import java.security.Security; import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Collections; import java.util.Set; -import javax.net.ssl.HttpsURLConnection; + import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; @@ -52,7 +44,7 @@ public class XmlConfigTests extends AndroidTestCase { NetworkSecurityConfig config = appConfig.getConfigForHostname(""); assertNotNull(config); // Check defaults. - assertTrue(config.isCleartextTrafficPermitted()); + assertFalse(config.isCleartextTrafficPermitted()); assertFalse(config.isHstsEnforced()); assertFalse(config.getTrustAnchors().isEmpty()); PinSet pinSet = config.getPins(); @@ -72,7 +64,7 @@ public class XmlConfigTests extends AndroidTestCase { NetworkSecurityConfig config = appConfig.getConfigForHostname(""); assertNotNull(config); // Check defaults. - assertTrue(config.isCleartextTrafficPermitted()); + assertFalse(config.isCleartextTrafficPermitted()); assertFalse(config.isHstsEnforced()); assertTrue(config.getTrustAnchors().isEmpty()); PinSet pinSet = config.getPins(); @@ -91,14 +83,14 @@ public class XmlConfigTests extends AndroidTestCase { NetworkSecurityConfig config = appConfig.getConfigForHostname(""); assertNotNull(config); // Check defaults. - assertTrue(config.isCleartextTrafficPermitted()); + assertFalse(config.isCleartextTrafficPermitted()); assertFalse(config.isHstsEnforced()); assertTrue(config.getTrustAnchors().isEmpty()); PinSet pinSet = config.getPins(); assertTrue(pinSet.pins.isEmpty()); // Check android.com. config = appConfig.getConfigForHostname("android.com"); - assertTrue(config.isCleartextTrafficPermitted()); + assertFalse(config.isCleartextTrafficPermitted()); assertFalse(config.isHstsEnforced()); assertFalse(config.getTrustAnchors().isEmpty()); pinSet = config.getPins(); @@ -188,7 +180,7 @@ public class XmlConfigTests extends AndroidTestCase { ApplicationConfig appConfig = new ApplicationConfig(source); assertTrue(appConfig.hasPerDomainConfigs()); NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com"); - assertTrue(config.isCleartextTrafficPermitted()); + assertFalse(config.isCleartextTrafficPermitted()); assertFalse(config.isHstsEnforced()); assertFalse(config.getTrustAnchors().isEmpty()); PinSet pinSet = config.getPins(); @@ -250,9 +242,9 @@ public class XmlConfigTests extends AndroidTestCase { ApplicationConfig appConfig = new ApplicationConfig(source); // Check android.com. NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com"); - assertTrue(config.isCleartextTrafficPermitted()); + assertFalse(config.isCleartextTrafficPermitted()); assertFalse(config.isHstsEnforced()); - assertEquals(2, config.getTrustAnchors().size()); + assertEquals(1, config.getTrustAnchors().size()); // Try connections. SSLContext context = TestUtils.getSSLContext(source); TestUtils.assertConnectionSucceeds(context, "android.com", 443); @@ -267,9 +259,9 @@ public class XmlConfigTests extends AndroidTestCase { ApplicationConfig appConfig = new ApplicationConfig(source); // Check android.com. NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com"); - assertTrue(config.isCleartextTrafficPermitted()); + assertFalse(config.isCleartextTrafficPermitted()); assertFalse(config.isHstsEnforced()); - assertEquals(2, config.getTrustAnchors().size()); + assertEquals(1, config.getTrustAnchors().size()); // Try connections. SSLContext context = TestUtils.getSSLContext(source); TestUtils.assertConnectionSucceeds(context, "android.com", 443); |