summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/aapt/SdkConstants.h1
-rw-r--r--tools/aapt2/SdkConstants.h1
-rw-r--r--tools/aapt2/optimize/VersionCollapser.cpp23
-rw-r--r--tools/app_metadata_bundles/Android.bp42
-rw-r--r--tools/app_metadata_bundles/OWNERS2
-rw-r--r--tools/app_metadata_bundles/README.md9
-rw-r--r--tools/app_metadata_bundles/src/aslgen/aslgen.mf1
-rw-r--r--tools/app_metadata_bundles/src/aslgen/java/com/android/aslgen/Main.java113
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/AslConverter.java124
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AndroidSafetyLabel.java81
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AndroidSafetyLabelFactory.java87
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AppInfo.java200
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AppInfoFactory.java124
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AslMarshallable.java31
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AslMarshallableFactory.java32
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategory.java76
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategoryFactory.java55
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabels.java146
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabelsFactory.java194
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataType.java218
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataTypeFactory.java91
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DeveloperInfo.java173
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DeveloperInfoFactory.java96
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SafetyLabels.java91
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SafetyLabelsFactory.java102
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SecurityLabels.java66
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SecurityLabelsFactory.java60
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabel.java54
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabelFactory.java57
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/ThirdPartyVerification.java51
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/ThirdPartyVerificationFactory.java57
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/TransparencyInfo.java73
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/TransparencyInfoFactory.java76
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/AslgenUtil.java26
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/DataCategoryConstants.java77
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/DataTypeConstants.java197
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/MalformedXmlException.java33
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/XmlUtils.java496
-rw-r--r--tools/app_metadata_bundles/src/test/java/com/android/asllib/AllTests.java48
-rw-r--r--tools/app_metadata_bundles/src/test/java/com/android/asllib/AslgenTests.java89
-rw-r--r--tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AndroidSafetyLabelTest.java112
-rw-r--r--tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AppInfoTest.java152
-rw-r--r--tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataLabelsTest.java325
-rw-r--r--tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataTypeEqualityTest.java170
-rw-r--r--tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DeveloperInfoTest.java132
-rw-r--r--tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SafetyLabelsTest.java108
-rw-r--r--tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SecurityLabelsTest.java96
-rw-r--r--tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SystemAppSafetyLabelTest.java87
-rw-r--r--tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/ThirdPartyVerificationTest.java87
-rw-r--r--tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/TransparencyInfoTest.java83
-rw-r--r--tools/app_metadata_bundles/src/test/java/com/android/asllib/testutils/TestUtils.java230
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/missing-version.xml3
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/valid-empty.xml1
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/with-safety-labels.xml4
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/with-system-app-safety-label.xml4
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/with-transparency-info.xml4
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/missing-version.xml2
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/valid-empty.xml3
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/with-safety-labels.xml6
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/with-system-app-safety-label.xml6
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/with-transparency-info.xml4
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/appinfo/hr/all-fields-valid.xml14
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/appinfo/od/all-fields-valid.xml25
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-actions-in-app.xml17
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-app-performance.xml11
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-audio.xml11
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-calendar.xml5
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-contacts.xml5
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-email-text-message.xml11
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-financial.xml14
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-health-fitness.xml8
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-identifiers.xml5
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-location.xml8
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-personal-empty-purpose.xml5
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-personal-missing-purpose.xml4
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-personal-partial.xml8
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-personal-unrecognized-type.xml5
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-personal.xml31
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-photo-video.xml8
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-search-and-browsing.xml5
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-storage.xml5
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-unrecognized.xml5
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-actions-in-app.xml27
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-app-performance.xml17
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-audio.xml17
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-calendar.xml7
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-contacts.xml7
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-email-text-message.xml17
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-financial.xml22
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-health-fitness.xml12
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-identifiers.xml7
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-location.xml12
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-personal-partial.xml13
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-personal.xml50
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-photo-video.xml12
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-search-and-browsing.xml7
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-storage.xml7
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-actions-in-app.xml12
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-app-performance.xml8
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-audio.xml8
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-calendar.xml4
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-contacts.xml4
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-email-text-message.xml8
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-financial.xml10
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-health-fitness.xml6
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-identifiers.xml4
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-location.xml6
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-empty-purpose.xml4
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-missing-purpose.xml3
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-partial.xml6
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-unrecognized-type.xml4
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal.xml21
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-photo-video.xml6
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-search-and-browsing.xml4
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-storage.xml4
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized-type.xml4
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized.xml4
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-collected-shared.xml8
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-invalid-bool.xml5
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-valid-bool.xml4
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-ephemeral-collision.xml14
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-ephemeral.xml14
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-invalid-bool.xml5
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-valid-bool.xml5
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-shared-invalid-bool.xml5
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-shared-valid-bool.xml5
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-actions-in-app.xml31
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-app-performance.xml21
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-audio.xml21
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-calendar.xml11
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-contacts.xml11
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-email-text-message.xml21
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-financial.xml26
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-health-fitness.xml16
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-identifiers.xml11
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-location.xml16
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-empty-purpose.xml11
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-missing-purpose.xml8
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-partial.xml17
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal.xml53
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-photo-video.xml16
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-search-and-browsing.xml11
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-storage.xml11
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-unrecognized-type.xml11
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-unrecognized.xml11
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-accessed-collected-shared.xml29
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-accessed-valid-bool.xml11
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-collected-ephemeral.xml38
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-collected-invalid-bool.xml13
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-collected-valid-bool.xml13
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-shared-valid-bool.xml12
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/developerinfo/hr/all-fields-valid.xml8
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/developerinfo/od/all-fields-valid.xml9
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/missing-version.xml2
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/valid-empty.xml1
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-data-labels.xml7
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-security-labels.xml6
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-third-party-verification.xml4
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/missing-version.xml2
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/valid-empty.xml3
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-data-labels.xml15
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-security-labels.xml7
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-third-party-verification.xml6
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/securitylabels/hr/all-fields-valid.xml4
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/securitylabels/od/all-fields-valid.xml4
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/hr/missing-bool.xml1
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/hr/valid.xml1
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/missing-bool.xml2
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/valid.xml3
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/thirdpartyverification/hr/missing-url.xml1
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/thirdpartyverification/hr/valid.xml1
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/thirdpartyverification/od/missing-url.xml2
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/thirdpartyverification/od/valid.xml3
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/hr/valid-empty.xml4
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/hr/with-app-info.xml4
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/hr/with-developer-info.xml11
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/valid-empty.xml1
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/with-app-info.xml26
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/with-developer-info.xml12
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/contacts/hr.xml11
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/contacts/od.xml19
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/hr.xml33
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/od.xml69
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/location/hr.xml16
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/location/od.xml27
-rw-r--r--tools/app_metadata_bundles/src/test/resources/test.xml16
-rw-r--r--[-rwxr-xr-x]tools/codegen/src/com/android/codegen/Main.kt0
-rw-r--r--tools/hoststubgen/TEST_MAPPING11
-rw-r--r--tools/hoststubgen/hoststubgen/Android.bp23
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt136
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt)12
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt21
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt49
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt12
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt59
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt113
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt202
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt11
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt4
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt26
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt61
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt95
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapper.kt59
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt58
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt36
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/Helper.kt35
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt12
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt281
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt246
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt304
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt246
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt400
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt17
-rwxr-xr-xtools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py1
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java22
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.java3
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java2
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java4
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java28
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java34
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java22
-rw-r--r--tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt47
-rw-r--r--tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/visitors/HelperTest.kt26
-rwxr-xr-xtools/hoststubgen/scripts/dump-jar4
-rw-r--r--tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt4
-rw-r--r--tools/processors/intdef_mappings/test/android/processor/IntDefProcessorTest.kt4
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt79
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt16
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt14
-rw-r--r--tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt13
-rw-r--r--tools/systemfeatures/Android.bp4
-rw-r--r--tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt119
-rw-r--r--tools/systemfeatures/tests/PackageManager.java1
-rw-r--r--tools/systemfeatures/tests/SystemFeaturesGeneratorTest.java17
238 files changed, 9402 insertions, 289 deletions
diff --git a/tools/aapt/SdkConstants.h b/tools/aapt/SdkConstants.h
index e2c161482857..ebb82ce1e2b7 100644
--- a/tools/aapt/SdkConstants.h
+++ b/tools/aapt/SdkConstants.h
@@ -50,6 +50,7 @@ enum {
SDK_S_V2 = 32,
SDK_TIRAMISU = 33,
SDK_UPSIDE_DOWN_CAKE = 34,
+ SDK_VANILLA_ICE_CREAM = 35,
SDK_CUR_DEVELOPMENT = 10000,
};
diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h
index e47704ea2725..f131cc6d7553 100644
--- a/tools/aapt2/SdkConstants.h
+++ b/tools/aapt2/SdkConstants.h
@@ -60,6 +60,7 @@ enum : ApiVersion {
SDK_S_V2 = 32,
SDK_TIRAMISU = 33,
SDK_UPSIDE_DOWN_CAKE = 34,
+ SDK_VANILLA_ICE_CREAM = 35,
SDK_CUR_DEVELOPMENT = 10000,
};
diff --git a/tools/aapt2/optimize/VersionCollapser.cpp b/tools/aapt2/optimize/VersionCollapser.cpp
index cd791bda250b..27fff9a05334 100644
--- a/tools/aapt2/optimize/VersionCollapser.cpp
+++ b/tools/aapt2/optimize/VersionCollapser.cpp
@@ -70,7 +70,7 @@ FilterIterator<Iterator, Pred> make_filter_iterator(Iterator begin,
* exception is when there is no exact matching resource for the minSdk. The next smallest one will
* be kept.
*/
-static void CollapseVersions(int min_sdk, ResourceEntry* entry) {
+static void CollapseVersions(IAaptContext* context, int min_sdk, ResourceEntry* entry) {
// First look for all sdks less than minSdk.
for (auto iter = entry->values.rbegin(); iter != entry->values.rend();
++iter) {
@@ -102,7 +102,14 @@ static void CollapseVersions(int min_sdk, ResourceEntry* entry) {
auto filter_iter =
make_filter_iterator(iter + 1, entry->values.rend(), pred);
while (filter_iter.HasNext()) {
- filter_iter.Next() = {};
+ auto& next = filter_iter.Next();
+ if (context->IsVerbose()) {
+ context->GetDiagnostics()->Note(android::DiagMessage()
+ << "removing configuration " << next->config.to_string()
+ << " for entry: " << entry->name
+ << ", because its SDK version is smaller than minSdk");
+ }
+ next = {};
}
}
}
@@ -126,6 +133,12 @@ static void CollapseVersions(int min_sdk, ResourceEntry* entry) {
util::make_unique<ResourceConfigValue>(
config_value->config.CopyWithoutSdkVersion(),
config_value->product);
+ if (context->IsVerbose()) {
+ context->GetDiagnostics()->Note(android::DiagMessage()
+ << "overriding resource: " << entry->name
+ << ", removing SDK version from configuration "
+ << config_value->config.to_string());
+ }
new_value->value = std::move(config_value->value);
config_value = std::move(new_value);
@@ -147,10 +160,14 @@ static void CollapseVersions(int min_sdk, ResourceEntry* entry) {
bool VersionCollapser::Consume(IAaptContext* context, ResourceTable* table) {
TRACE_NAME("VersionCollapser::Consume");
const int min_sdk = context->GetMinSdkVersion();
+ if (context->IsVerbose()) {
+ context->GetDiagnostics()->Note(android::DiagMessage()
+ << "Running VersionCollapser with minSdk = " << min_sdk);
+ }
for (auto& package : table->packages) {
for (auto& type : package->types) {
for (auto& entry : type->entries) {
- CollapseVersions(min_sdk, entry.get());
+ CollapseVersions(context, min_sdk, entry.get());
}
}
}
diff --git a/tools/app_metadata_bundles/Android.bp b/tools/app_metadata_bundles/Android.bp
new file mode 100644
index 000000000000..dced50d7ee3b
--- /dev/null
+++ b/tools/app_metadata_bundles/Android.bp
@@ -0,0 +1,42 @@
+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"],
+ default_team: "trendy_team_preload_safety",
+}
+
+java_library_host {
+ name: "asllib",
+ srcs: [
+ "src/lib/java/**/*.java",
+ ],
+ static_libs: [
+ "guava",
+ ],
+}
+
+java_binary_host {
+ name: "aslgen",
+ manifest: "src/aslgen/aslgen.mf",
+ srcs: [
+ "src/aslgen/java/**/*.java",
+ ],
+ static_libs: [
+ "asllib",
+ ],
+}
+
+java_test_host {
+ name: "aslgen-test",
+ srcs: ["src/test/java/**/*.java"],
+ exclude_srcs: [
+ ],
+ java_resource_dirs: ["src/test/resources"],
+ static_libs: [
+ "aslgen",
+ "junit",
+ ],
+}
diff --git a/tools/app_metadata_bundles/OWNERS b/tools/app_metadata_bundles/OWNERS
new file mode 100644
index 000000000000..a2a250b2d5b7
--- /dev/null
+++ b/tools/app_metadata_bundles/OWNERS
@@ -0,0 +1,2 @@
+wenhaowang@google.com
+mloh@google.com
diff --git a/tools/app_metadata_bundles/README.md b/tools/app_metadata_bundles/README.md
new file mode 100644
index 000000000000..6e8d287b41dd
--- /dev/null
+++ b/tools/app_metadata_bundles/README.md
@@ -0,0 +1,9 @@
+# App metadata bundles
+
+This project delivers a comprehensive toolchain solution for developers
+to efficiently manage app metadata bundles.
+
+The project consists of two subprojects:
+
+ * A pure Java library, and
+ * A pure Java command-line tool.
diff --git a/tools/app_metadata_bundles/src/aslgen/aslgen.mf b/tools/app_metadata_bundles/src/aslgen/aslgen.mf
new file mode 100644
index 000000000000..fc656e2155a7
--- /dev/null
+++ b/tools/app_metadata_bundles/src/aslgen/aslgen.mf
@@ -0,0 +1 @@
+Main-Class: com.android.aslgen.Main \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/aslgen/java/com/android/aslgen/Main.java b/tools/app_metadata_bundles/src/aslgen/java/com/android/aslgen/Main.java
new file mode 100644
index 000000000000..d7edfd44019c
--- /dev/null
+++ b/tools/app_metadata_bundles/src/aslgen/java/com/android/aslgen/Main.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2024 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.aslgen;
+
+import com.android.asllib.AslConverter;
+import com.android.asllib.AslConverter.Format;
+import com.android.asllib.util.MalformedXmlException;
+
+import org.xml.sax.SAXException;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.TransformerException;
+
+public class Main {
+
+ /** Takes the options to make file conversion. */
+ public static void main(String[] args)
+ throws IOException,
+ ParserConfigurationException,
+ SAXException,
+ TransformerException,
+ MalformedXmlException {
+
+ String inFile = null;
+ String outFile = null;
+ Format inFormat = AslConverter.Format.NULL;
+ Format outFormat = AslConverter.Format.NULL;
+
+ // Except for "--help", all arguments require a value currently.
+ // So just make sure we have an even number and
+ // then process them all two at a time.
+ if (args.length == 1 && "--help".equals(args[0])) {
+ showUsage();
+ return;
+ }
+ if (args.length % 2 != 0) {
+ throw new IllegalArgumentException("Argument is missing corresponding value");
+ }
+ for (int i = 0; i < args.length - 1; i += 2) {
+ final String arg = args[i].trim();
+ final String argValue = args[i + 1].trim();
+ if ("--in-path".equals(arg)) {
+ inFile = argValue;
+ } else if ("--out-path".equals(arg)) {
+ outFile = argValue;
+ } else if ("--in-format".equals(arg)) {
+ inFormat = getFormat(argValue);
+ } else if ("--out-format".equals(arg)) {
+ outFormat = getFormat(argValue);
+ } else {
+ throw new IllegalArgumentException("Unknown argument: " + arg);
+ }
+ }
+
+ if (inFile == null) {
+ throw new IllegalArgumentException("input file is required");
+ }
+
+ if (outFile == null) {
+ throw new IllegalArgumentException("output file is required");
+ }
+
+ if (inFormat == AslConverter.Format.NULL) {
+ throw new IllegalArgumentException("input format is required");
+ }
+
+ if (outFormat == AslConverter.Format.NULL) {
+ throw new IllegalArgumentException("output format is required");
+ }
+
+ System.out.println("in path: " + inFile);
+ System.out.println("out path: " + outFile);
+ System.out.println("in format: " + inFormat);
+ System.out.println("out format: " + outFormat);
+
+ var asl = AslConverter.readFromStream(new FileInputStream(inFile), inFormat);
+ AslConverter.writeToStream(new FileOutputStream(outFile), asl, outFormat);
+ }
+
+ private static Format getFormat(String argValue) {
+ if ("hr".equals(argValue)) {
+ return AslConverter.Format.HUMAN_READABLE;
+ } else if ("od".equals(argValue)) {
+ return AslConverter.Format.ON_DEVICE;
+ } else {
+ return AslConverter.Format.NULL;
+ }
+ }
+
+ private static void showUsage() {
+ System.err.println(
+ "Usage: aslgen --in-path [input-file] --out-path [output-file] --in-format [hr|od]"
+ + " --out-format [hr|od]");
+ }
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AslConverter.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AslConverter.java
new file mode 100644
index 000000000000..191f38d3df80
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AslConverter.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2024 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.asllib;
+
+import com.android.asllib.marshallable.AndroidSafetyLabel;
+import com.android.asllib.marshallable.AndroidSafetyLabelFactory;
+import com.android.asllib.util.MalformedXmlException;
+import com.android.asllib.util.XmlUtils;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+public class AslConverter {
+ public enum Format {
+ NULL,
+ HUMAN_READABLE,
+ ON_DEVICE;
+ }
+
+ /** Reads a {@link AndroidSafetyLabel} from an {@link InputStream}. */
+ // TODO(b/329902686): Support parsing from on-device.
+ public static AndroidSafetyLabel readFromStream(InputStream in, Format format)
+ throws IOException, ParserConfigurationException, SAXException, MalformedXmlException {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(true);
+ Document document = factory.newDocumentBuilder().parse(in);
+
+ switch (format) {
+ case HUMAN_READABLE:
+ Element appMetadataBundles =
+ XmlUtils.getSingleChildElement(
+ document, XmlUtils.HR_TAG_APP_METADATA_BUNDLES, true);
+
+ return new AndroidSafetyLabelFactory()
+ .createFromHrElements(XmlUtils.listOf(appMetadataBundles));
+ case ON_DEVICE:
+ Element bundleEle =
+ XmlUtils.getSingleChildElement(document, XmlUtils.OD_TAG_BUNDLE, true);
+ return new AndroidSafetyLabelFactory()
+ .createFromOdElements(XmlUtils.listOf(bundleEle));
+ default:
+ throw new IllegalStateException("Unrecognized input format.");
+ }
+ }
+
+ /** Reads a {@link AndroidSafetyLabel} from a String. */
+ public static AndroidSafetyLabel readFromString(String in, Format format)
+ throws IOException, ParserConfigurationException, SAXException, MalformedXmlException {
+ InputStream stream = new ByteArrayInputStream(in.getBytes(StandardCharsets.UTF_8));
+ return readFromStream(stream, format);
+ }
+
+ /** Write the content of the {@link AndroidSafetyLabel} to a {@link OutputStream}. */
+ // TODO(b/329902686): Support outputting human-readable format.
+ public static void writeToStream(
+ OutputStream out, AndroidSafetyLabel asl, AslConverter.Format format)
+ throws IOException, ParserConfigurationException, TransformerException {
+ var docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ var document = docBuilder.newDocument();
+
+ switch (format) {
+ case HUMAN_READABLE:
+ for (var child : asl.toHrDomElements(document)) {
+ document.appendChild(child);
+ }
+ break;
+ case ON_DEVICE:
+ for (var child : asl.toOdDomElements(document)) {
+ document.appendChild(child);
+ }
+ break;
+ default:
+ throw new IllegalStateException("Unrecognized input format.");
+ }
+
+ TransformerFactory transformerFactory = TransformerFactory.newInstance();
+ Transformer transformer = transformerFactory.newTransformer();
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+ transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+ StreamResult streamResult = new StreamResult(out); // out
+ DOMSource domSource = new DOMSource(document);
+ transformer.transform(domSource, streamResult);
+ }
+
+ /** Get the content of the {@link AndroidSafetyLabel} as String. */
+ public static String getXmlAsString(AndroidSafetyLabel asl, AslConverter.Format format)
+ throws IOException, ParserConfigurationException, TransformerException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ writeToStream(out, asl, format);
+ return out.toString(StandardCharsets.UTF_8);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AndroidSafetyLabel.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AndroidSafetyLabel.java
new file mode 100644
index 000000000000..72140a17297c
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AndroidSafetyLabel.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 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.asllib.marshallable;
+
+import com.android.asllib.util.XmlUtils;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.util.List;
+
+public class AndroidSafetyLabel implements AslMarshallable {
+
+ private final Long mVersion;
+ private final SystemAppSafetyLabel mSystemAppSafetyLabel;
+ private final SafetyLabels mSafetyLabels;
+ private final TransparencyInfo mTransparencyInfo;
+
+ public SafetyLabels getSafetyLabels() {
+ return mSafetyLabels;
+ }
+
+ public AndroidSafetyLabel(
+ Long version,
+ SystemAppSafetyLabel systemAppSafetyLabel,
+ SafetyLabels safetyLabels,
+ TransparencyInfo transparencyInfo) {
+ this.mVersion = version;
+ this.mSystemAppSafetyLabel = systemAppSafetyLabel;
+ this.mSafetyLabels = safetyLabels;
+ this.mTransparencyInfo = transparencyInfo;
+ }
+
+ /** Creates an on-device DOM element from an {@link AndroidSafetyLabel} */
+ @Override
+ public List<Element> toOdDomElements(Document doc) {
+ Element aslEle = doc.createElement(XmlUtils.OD_TAG_BUNDLE);
+ aslEle.appendChild(XmlUtils.createOdLongEle(doc, XmlUtils.OD_NAME_VERSION, mVersion));
+ if (mSafetyLabels != null) {
+ XmlUtils.appendChildren(aslEle, mSafetyLabels.toOdDomElements(doc));
+ }
+ if (mSystemAppSafetyLabel != null) {
+ XmlUtils.appendChildren(aslEle, mSystemAppSafetyLabel.toOdDomElements(doc));
+ }
+ if (mTransparencyInfo != null) {
+ XmlUtils.appendChildren(aslEle, mTransparencyInfo.toOdDomElements(doc));
+ }
+ return XmlUtils.listOf(aslEle);
+ }
+
+ /** Creates the human-readable DOM elements from the AslMarshallable Java Object. */
+ @Override
+ public List<Element> toHrDomElements(Document doc) {
+ Element aslEle = doc.createElement(XmlUtils.HR_TAG_APP_METADATA_BUNDLES);
+ aslEle.setAttribute(XmlUtils.HR_ATTR_VERSION, String.valueOf(mVersion));
+ if (mSafetyLabels != null) {
+ XmlUtils.appendChildren(aslEle, mSafetyLabels.toHrDomElements(doc));
+ }
+ if (mSystemAppSafetyLabel != null) {
+ XmlUtils.appendChildren(aslEle, mSystemAppSafetyLabel.toHrDomElements(doc));
+ }
+ if (mTransparencyInfo != null) {
+ XmlUtils.appendChildren(aslEle, mTransparencyInfo.toHrDomElements(doc));
+ }
+ return XmlUtils.listOf(aslEle);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AndroidSafetyLabelFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AndroidSafetyLabelFactory.java
new file mode 100644
index 000000000000..c53cbbf99a46
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AndroidSafetyLabelFactory.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2024 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.asllib.marshallable;
+
+import com.android.asllib.util.MalformedXmlException;
+import com.android.asllib.util.XmlUtils;
+
+import org.w3c.dom.Element;
+
+import java.util.List;
+
+public class AndroidSafetyLabelFactory implements AslMarshallableFactory<AndroidSafetyLabel> {
+
+ /** Creates an {@link AndroidSafetyLabel} from human-readable DOM element */
+ @Override
+ public AndroidSafetyLabel createFromHrElements(List<Element> appMetadataBundles)
+ throws MalformedXmlException {
+ Element appMetadataBundlesEle = XmlUtils.getSingleElement(appMetadataBundles);
+ long version = XmlUtils.tryGetVersion(appMetadataBundlesEle);
+
+ Element safetyLabelsEle =
+ XmlUtils.getSingleChildElement(
+ appMetadataBundlesEle, XmlUtils.HR_TAG_SAFETY_LABELS, false);
+ SafetyLabels safetyLabels =
+ new SafetyLabelsFactory().createFromHrElements(XmlUtils.listOf(safetyLabelsEle));
+
+ Element systemAppSafetyLabelEle =
+ XmlUtils.getSingleChildElement(
+ appMetadataBundlesEle, XmlUtils.HR_TAG_SYSTEM_APP_SAFETY_LABEL, false);
+ SystemAppSafetyLabel systemAppSafetyLabel =
+ new SystemAppSafetyLabelFactory()
+ .createFromHrElements(XmlUtils.listOf(systemAppSafetyLabelEle));
+
+ Element transparencyInfoEle =
+ XmlUtils.getSingleChildElement(
+ appMetadataBundlesEle, XmlUtils.HR_TAG_TRANSPARENCY_INFO, false);
+ TransparencyInfo transparencyInfo =
+ new TransparencyInfoFactory()
+ .createFromHrElements(XmlUtils.listOf(transparencyInfoEle));
+
+ return new AndroidSafetyLabel(
+ version, systemAppSafetyLabel, safetyLabels, transparencyInfo);
+ }
+
+ /** Creates an {@link AndroidSafetyLabel} from on-device DOM elements */
+ @Override
+ public AndroidSafetyLabel createFromOdElements(List<Element> elements)
+ throws MalformedXmlException {
+ Element bundleEle = XmlUtils.getSingleElement(elements);
+ Long version = XmlUtils.getOdLongEle(bundleEle, XmlUtils.OD_NAME_VERSION, true);
+
+ Element safetyLabelsEle =
+ XmlUtils.getOdPbundleWithName(bundleEle, XmlUtils.OD_NAME_SAFETY_LABELS, false);
+ SafetyLabels safetyLabels =
+ new SafetyLabelsFactory().createFromOdElements(XmlUtils.listOf(safetyLabelsEle));
+
+ Element systemAppSafetyLabelEle =
+ XmlUtils.getOdPbundleWithName(
+ bundleEle, XmlUtils.OD_NAME_SYSTEM_APP_SAFETY_LABEL, false);
+ SystemAppSafetyLabel systemAppSafetyLabel =
+ new SystemAppSafetyLabelFactory()
+ .createFromOdElements(XmlUtils.listOf(systemAppSafetyLabelEle));
+
+ Element transparencyInfoEle =
+ XmlUtils.getOdPbundleWithName(bundleEle, XmlUtils.OD_NAME_TRANSPARENCY_INFO, false);
+ TransparencyInfo transparencyInfo =
+ new TransparencyInfoFactory()
+ .createFromOdElements(XmlUtils.listOf(transparencyInfoEle));
+
+ return new AndroidSafetyLabel(
+ version, systemAppSafetyLabel, safetyLabels, transparencyInfo);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AppInfo.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AppInfo.java
new file mode 100644
index 000000000000..129733ebc1b6
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AppInfo.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2024 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.asllib.marshallable;
+
+import com.android.asllib.util.XmlUtils;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.util.List;
+
+/** AppInfo representation */
+public class AppInfo implements AslMarshallable {
+ private final String mTitle;
+ private final String mDescription;
+ private final Boolean mContainsAds;
+ private final Boolean mObeyAps;
+ private final Boolean mAdsFingerprinting;
+ private final Boolean mSecurityFingerprinting;
+ private final String mPrivacyPolicy;
+ private final List<String> mSecurityEndpoints;
+ private final List<String> mFirstPartyEndpoints;
+ private final List<String> mServiceProviderEndpoints;
+ private final String mCategory;
+ private final String mEmail;
+ private final String mWebsite;
+
+ public AppInfo(
+ String title,
+ String description,
+ Boolean containsAds,
+ Boolean obeyAps,
+ Boolean adsFingerprinting,
+ Boolean securityFingerprinting,
+ String privacyPolicy,
+ List<String> securityEndpoints,
+ List<String> firstPartyEndpoints,
+ List<String> serviceProviderEndpoints,
+ String category,
+ String email,
+ String website) {
+ this.mTitle = title;
+ this.mDescription = description;
+ this.mContainsAds = containsAds;
+ this.mObeyAps = obeyAps;
+ this.mAdsFingerprinting = adsFingerprinting;
+ this.mSecurityFingerprinting = securityFingerprinting;
+ this.mPrivacyPolicy = privacyPolicy;
+ this.mSecurityEndpoints = securityEndpoints;
+ this.mFirstPartyEndpoints = firstPartyEndpoints;
+ this.mServiceProviderEndpoints = serviceProviderEndpoints;
+ this.mCategory = category;
+ this.mEmail = email;
+ this.mWebsite = website;
+ }
+
+ /** Creates an on-device DOM element from the {@link SafetyLabels}. */
+ @Override
+ public List<Element> toOdDomElements(Document doc) {
+ Element appInfoEle = XmlUtils.createPbundleEleWithName(doc, XmlUtils.OD_NAME_APP_INFO);
+ if (this.mTitle != null) {
+ appInfoEle.appendChild(XmlUtils.createOdStringEle(doc, XmlUtils.OD_NAME_TITLE, mTitle));
+ }
+ if (this.mDescription != null) {
+ appInfoEle.appendChild(
+ XmlUtils.createOdStringEle(doc, XmlUtils.OD_NAME_DESCRIPTION, mDescription));
+ }
+ if (this.mContainsAds != null) {
+ appInfoEle.appendChild(
+ XmlUtils.createOdBooleanEle(doc, XmlUtils.OD_NAME_CONTAINS_ADS, mContainsAds));
+ }
+ if (this.mObeyAps != null) {
+ appInfoEle.appendChild(
+ XmlUtils.createOdBooleanEle(doc, XmlUtils.OD_NAME_OBEY_APS, mObeyAps));
+ }
+ if (this.mAdsFingerprinting != null) {
+ appInfoEle.appendChild(
+ XmlUtils.createOdBooleanEle(
+ doc, XmlUtils.OD_NAME_ADS_FINGERPRINTING, mAdsFingerprinting));
+ }
+ if (this.mSecurityFingerprinting != null) {
+ appInfoEle.appendChild(
+ XmlUtils.createOdBooleanEle(
+ doc,
+ XmlUtils.OD_NAME_SECURITY_FINGERPRINTING,
+ mSecurityFingerprinting));
+ }
+ if (this.mPrivacyPolicy != null) {
+ appInfoEle.appendChild(
+ XmlUtils.createOdStringEle(
+ doc, XmlUtils.OD_NAME_PRIVACY_POLICY, mPrivacyPolicy));
+ }
+ if (this.mSecurityEndpoints != null) {
+ appInfoEle.appendChild(
+ XmlUtils.createOdArray(
+ doc,
+ XmlUtils.OD_TAG_STRING_ARRAY,
+ XmlUtils.OD_NAME_SECURITY_ENDPOINT,
+ mSecurityEndpoints));
+ }
+ if (this.mFirstPartyEndpoints != null) {
+ appInfoEle.appendChild(
+ XmlUtils.createOdArray(
+ doc,
+ XmlUtils.OD_TAG_STRING_ARRAY,
+ XmlUtils.OD_NAME_FIRST_PARTY_ENDPOINT,
+ mFirstPartyEndpoints));
+ }
+ if (this.mServiceProviderEndpoints != null) {
+ appInfoEle.appendChild(
+ XmlUtils.createOdArray(
+ doc,
+ XmlUtils.OD_TAG_STRING_ARRAY,
+ XmlUtils.OD_NAME_SERVICE_PROVIDER_ENDPOINT,
+ mServiceProviderEndpoints));
+ }
+ if (this.mCategory != null) {
+ appInfoEle.appendChild(
+ XmlUtils.createOdStringEle(doc, XmlUtils.OD_NAME_CATEGORY, this.mCategory));
+ }
+ if (this.mEmail != null) {
+ appInfoEle.appendChild(
+ XmlUtils.createOdStringEle(doc, XmlUtils.OD_NAME_EMAIL, this.mEmail));
+ }
+ if (this.mWebsite != null) {
+ appInfoEle.appendChild(
+ XmlUtils.createOdStringEle(doc, XmlUtils.OD_NAME_WEBSITE, this.mWebsite));
+ }
+ return XmlUtils.listOf(appInfoEle);
+ }
+
+ /** Creates the human-readable DOM elements from the AslMarshallable Java Object. */
+ @Override
+ public List<Element> toHrDomElements(Document doc) {
+ Element appInfoEle = doc.createElement(XmlUtils.HR_TAG_APP_INFO);
+ if (this.mTitle != null) {
+ appInfoEle.setAttribute(XmlUtils.HR_ATTR_TITLE, this.mTitle);
+ }
+ if (this.mDescription != null) {
+ appInfoEle.setAttribute(XmlUtils.HR_ATTR_DESCRIPTION, this.mDescription);
+ }
+ if (this.mContainsAds != null) {
+ appInfoEle.setAttribute(
+ XmlUtils.HR_ATTR_CONTAINS_ADS, String.valueOf(this.mContainsAds));
+ }
+ if (this.mObeyAps != null) {
+ appInfoEle.setAttribute(XmlUtils.HR_ATTR_OBEY_APS, String.valueOf(this.mObeyAps));
+ }
+ if (this.mAdsFingerprinting != null) {
+ appInfoEle.setAttribute(
+ XmlUtils.HR_ATTR_ADS_FINGERPRINTING, String.valueOf(this.mAdsFingerprinting));
+ }
+ if (this.mSecurityFingerprinting != null) {
+ appInfoEle.setAttribute(
+ XmlUtils.HR_ATTR_SECURITY_FINGERPRINTING,
+ String.valueOf(this.mSecurityFingerprinting));
+ }
+ if (this.mPrivacyPolicy != null) {
+ appInfoEle.setAttribute(XmlUtils.HR_ATTR_PRIVACY_POLICY, this.mPrivacyPolicy);
+ }
+ if (this.mSecurityEndpoints != null) {
+ appInfoEle.setAttribute(
+ XmlUtils.HR_ATTR_SECURITY_ENDPOINTS, String.join("|", this.mSecurityEndpoints));
+ }
+ if (this.mFirstPartyEndpoints != null) {
+ appInfoEle.setAttribute(
+ XmlUtils.HR_ATTR_FIRST_PARTY_ENDPOINTS,
+ String.join("|", this.mFirstPartyEndpoints));
+ }
+ if (this.mServiceProviderEndpoints != null) {
+ appInfoEle.setAttribute(
+ XmlUtils.HR_ATTR_SERVICE_PROVIDER_ENDPOINTS,
+ String.join("|", this.mServiceProviderEndpoints));
+ }
+ if (this.mCategory != null) {
+ appInfoEle.setAttribute(XmlUtils.HR_ATTR_CATEGORY, this.mCategory);
+ }
+ if (this.mEmail != null) {
+ appInfoEle.setAttribute(XmlUtils.HR_ATTR_EMAIL, this.mEmail);
+ }
+ if (this.mWebsite != null) {
+ appInfoEle.setAttribute(XmlUtils.HR_ATTR_WEBSITE, this.mWebsite);
+ }
+ return XmlUtils.listOf(appInfoEle);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AppInfoFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AppInfoFactory.java
new file mode 100644
index 000000000000..c5069619e069
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AppInfoFactory.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2024 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.asllib.marshallable;
+
+import com.android.asllib.util.AslgenUtil;
+import com.android.asllib.util.MalformedXmlException;
+import com.android.asllib.util.XmlUtils;
+
+import org.w3c.dom.Element;
+
+import java.util.List;
+
+public class AppInfoFactory implements AslMarshallableFactory<AppInfo> {
+
+ /** Creates a {@link AppInfo} from the human-readable DOM element. */
+ @Override
+ public AppInfo createFromHrElements(List<Element> elements) throws MalformedXmlException {
+ Element appInfoEle = XmlUtils.getSingleElement(elements);
+ if (appInfoEle == null) {
+ AslgenUtil.logI("No AppInfo found in hr format.");
+ return null;
+ }
+
+ String title = XmlUtils.getStringAttr(appInfoEle, XmlUtils.HR_ATTR_TITLE, true);
+ String description = XmlUtils.getStringAttr(appInfoEle, XmlUtils.HR_ATTR_DESCRIPTION, true);
+ Boolean containsAds = XmlUtils.getBoolAttr(appInfoEle, XmlUtils.HR_ATTR_CONTAINS_ADS, true);
+ Boolean obeyAps = XmlUtils.getBoolAttr(appInfoEle, XmlUtils.HR_ATTR_OBEY_APS, true);
+ Boolean adsFingerprinting =
+ XmlUtils.getBoolAttr(appInfoEle, XmlUtils.HR_ATTR_ADS_FINGERPRINTING, true);
+ Boolean securityFingerprinting =
+ XmlUtils.getBoolAttr(appInfoEle, XmlUtils.HR_ATTR_SECURITY_FINGERPRINTING, true);
+ String privacyPolicy =
+ XmlUtils.getStringAttr(appInfoEle, XmlUtils.HR_ATTR_PRIVACY_POLICY, true);
+ List<String> securityEndpoints =
+ XmlUtils.getPipelineSplitAttr(
+ appInfoEle, XmlUtils.HR_ATTR_SECURITY_ENDPOINTS, true);
+ List<String> firstPartyEndpoints =
+ XmlUtils.getPipelineSplitAttr(
+ appInfoEle, XmlUtils.HR_ATTR_FIRST_PARTY_ENDPOINTS, true);
+ List<String> serviceProviderEndpoints =
+ XmlUtils.getPipelineSplitAttr(
+ appInfoEle, XmlUtils.HR_ATTR_SERVICE_PROVIDER_ENDPOINTS, true);
+ String category = XmlUtils.getStringAttr(appInfoEle, XmlUtils.HR_ATTR_CATEGORY, true);
+ String email = XmlUtils.getStringAttr(appInfoEle, XmlUtils.HR_ATTR_EMAIL, true);
+ String website = XmlUtils.getStringAttr(appInfoEle, XmlUtils.HR_ATTR_WEBSITE, false);
+
+ return new AppInfo(
+ title,
+ description,
+ containsAds,
+ obeyAps,
+ adsFingerprinting,
+ securityFingerprinting,
+ privacyPolicy,
+ securityEndpoints,
+ firstPartyEndpoints,
+ serviceProviderEndpoints,
+ category,
+ email,
+ website);
+ }
+
+ /** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
+ @Override
+ public AppInfo createFromOdElements(List<Element> elements) throws MalformedXmlException {
+ Element appInfoEle = XmlUtils.getSingleElement(elements);
+ if (appInfoEle == null) {
+ AslgenUtil.logI("No AppInfo found in od format.");
+ return null;
+ }
+
+ String title = XmlUtils.getOdStringEle(appInfoEle, XmlUtils.OD_NAME_TITLE, true);
+ String description =
+ XmlUtils.getOdStringEle(appInfoEle, XmlUtils.OD_NAME_DESCRIPTION, true);
+ Boolean containsAds =
+ XmlUtils.getOdBoolEle(appInfoEle, XmlUtils.OD_NAME_CONTAINS_ADS, true);
+ Boolean obeyAps = XmlUtils.getOdBoolEle(appInfoEle, XmlUtils.OD_NAME_OBEY_APS, true);
+ Boolean adsFingerprinting =
+ XmlUtils.getOdBoolEle(appInfoEle, XmlUtils.OD_NAME_ADS_FINGERPRINTING, true);
+ Boolean securityFingerprinting =
+ XmlUtils.getOdBoolEle(appInfoEle, XmlUtils.OD_NAME_SECURITY_FINGERPRINTING, true);
+ String privacyPolicy =
+ XmlUtils.getOdStringEle(appInfoEle, XmlUtils.OD_NAME_PRIVACY_POLICY, true);
+ List<String> securityEndpoints =
+ XmlUtils.getOdStringArray(appInfoEle, XmlUtils.OD_NAME_SECURITY_ENDPOINT, true);
+ List<String> firstPartyEndpoints =
+ XmlUtils.getOdStringArray(appInfoEle, XmlUtils.OD_NAME_FIRST_PARTY_ENDPOINT, true);
+ List<String> serviceProviderEndpoints =
+ XmlUtils.getOdStringArray(
+ appInfoEle, XmlUtils.OD_NAME_SERVICE_PROVIDER_ENDPOINT, true);
+ String category = XmlUtils.getOdStringEle(appInfoEle, XmlUtils.OD_NAME_CATEGORY, true);
+ String email = XmlUtils.getOdStringEle(appInfoEle, XmlUtils.OD_NAME_EMAIL, true);
+ String website = XmlUtils.getOdStringEle(appInfoEle, XmlUtils.OD_NAME_WEBSITE, false);
+
+ return new AppInfo(
+ title,
+ description,
+ containsAds,
+ obeyAps,
+ adsFingerprinting,
+ securityFingerprinting,
+ privacyPolicy,
+ securityEndpoints,
+ firstPartyEndpoints,
+ serviceProviderEndpoints,
+ category,
+ email,
+ website);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AslMarshallable.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AslMarshallable.java
new file mode 100644
index 000000000000..0a70e7d0d74b
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AslMarshallable.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 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.asllib.marshallable;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.util.List;
+
+public interface AslMarshallable {
+
+ /** Creates the on-device DOM elements from the AslMarshallable Java Object. */
+ List<Element> toOdDomElements(Document doc);
+
+ /** Creates the human-readable DOM elements from the AslMarshallable Java Object. */
+ List<Element> toHrDomElements(Document doc);
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AslMarshallableFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AslMarshallableFactory.java
new file mode 100644
index 000000000000..39582900f3a0
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AslMarshallableFactory.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 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.asllib.marshallable;
+
+import com.android.asllib.util.MalformedXmlException;
+
+import org.w3c.dom.Element;
+
+import java.util.List;
+
+public interface AslMarshallableFactory<T extends AslMarshallable> {
+
+ /** Creates an {@link AslMarshallableFactory} from human-readable DOM elements */
+ T createFromHrElements(List<Element> elements) throws MalformedXmlException;
+
+ /** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
+ T createFromOdElements(List<Element> elements) throws MalformedXmlException;
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategory.java
new file mode 100644
index 000000000000..c16d18b34360
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategory.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 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.asllib.marshallable;
+
+import com.android.asllib.util.DataCategoryConstants;
+import com.android.asllib.util.DataTypeConstants;
+import com.android.asllib.util.XmlUtils;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Data usage category representation containing one or more {@link DataType}. Valid category keys
+ * are defined in {@link DataCategoryConstants}, each category has a valid set of types {@link
+ * DataType}, which are mapped in {@link DataTypeConstants}
+ */
+public class DataCategory implements AslMarshallable {
+ private final String mCategoryName;
+ private final Map<String, DataType> mDataTypes;
+
+ public DataCategory(String categoryName, Map<String, DataType> dataTypes) {
+ this.mCategoryName = categoryName;
+ this.mDataTypes = dataTypes;
+ }
+
+ public DataCategory(String categoryName) {
+ this.mCategoryName = categoryName;
+ this.mDataTypes = new LinkedHashMap<String, DataType>();
+ }
+
+ public String getCategoryName() {
+ return mCategoryName;
+ }
+
+ /** Return the type {@link Map} of String type key to {@link DataType} */
+
+ public Map<String, DataType> getDataTypes() {
+ return mDataTypes;
+ }
+
+ /** Creates on-device DOM element(s) from the {@link DataCategory}. */
+ @Override
+ public List<Element> toOdDomElements(Document doc) {
+ Element dataCategoryEle = XmlUtils.createPbundleEleWithName(doc, this.getCategoryName());
+ for (DataType dataType : mDataTypes.values()) {
+ XmlUtils.appendChildren(dataCategoryEle, dataType.toOdDomElements(doc));
+ }
+ return XmlUtils.listOf(dataCategoryEle);
+ }
+
+ /** Creates the human-readable DOM elements from the AslMarshallable Java Object. */
+ @Override
+ public List<Element> toHrDomElements(Document doc) {
+ throw new IllegalStateException(
+ "Turning DataCategory or DataType into human-readable DOM elements requires"
+ + " visibility into parent elements. The logic resides in DataLabels.");
+ }
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategoryFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategoryFactory.java
new file mode 100644
index 000000000000..724416285acd
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategoryFactory.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 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.asllib.marshallable;
+
+import com.android.asllib.util.DataTypeConstants;
+import com.android.asllib.util.MalformedXmlException;
+import com.android.asllib.util.XmlUtils;
+
+import org.w3c.dom.Element;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class DataCategoryFactory {
+ /** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
+ public DataCategory createFromOdElements(List<Element> elements) throws MalformedXmlException {
+ Element dataCategoryEle = XmlUtils.getSingleElement(elements);
+ Map<String, DataType> dataTypeMap = new LinkedHashMap<String, DataType>();
+ String categoryName = dataCategoryEle.getAttribute(XmlUtils.OD_ATTR_NAME);
+ var odDataTypes = XmlUtils.asElementList(dataCategoryEle.getChildNodes());
+ for (Element odDataTypeEle : odDataTypes) {
+ String dataTypeName = odDataTypeEle.getAttribute(XmlUtils.OD_ATTR_NAME);
+ if (!DataTypeConstants.getValidDataTypes().containsKey(categoryName)) {
+ throw new MalformedXmlException(
+ String.format("Unrecognized data category %s", categoryName));
+ }
+ if (!DataTypeConstants.getValidDataTypes().get(categoryName).contains(dataTypeName)) {
+ throw new MalformedXmlException(
+ String.format(
+ "Unrecognized data type name %s for category %s",
+ dataTypeName, categoryName));
+ }
+ dataTypeMap.put(
+ dataTypeName,
+ new DataTypeFactory().createFromOdElements(XmlUtils.listOf(odDataTypeEle)));
+ }
+
+ return new DataCategory(categoryName, dataTypeMap);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabels.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabels.java
new file mode 100644
index 000000000000..ba0e3db52027
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabels.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2024 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.asllib.marshallable;
+
+import com.android.asllib.util.DataCategoryConstants;
+import com.android.asllib.util.XmlUtils;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Data label representation with data shared and data collected maps containing zero or more {@link
+ * DataCategory}
+ */
+public class DataLabels implements AslMarshallable {
+ private final Map<String, DataCategory> mDataCollected;
+ private final Map<String, DataCategory> mDataShared;
+
+ public DataLabels(
+ Map<String, DataCategory> dataCollected,
+ Map<String, DataCategory> dataShared) {
+ mDataCollected = dataCollected;
+ mDataShared = dataShared;
+ }
+
+ /**
+ * Returns the data collected {@link Map} of {@link DataCategoryConstants} to {@link
+ * DataCategory}
+ */
+ public Map<String, DataCategory> getDataCollected() {
+ return mDataCollected;
+ }
+
+ /**
+ * Returns the data shared {@link Map} of {@link DataCategoryConstants} to {@link DataCategory}
+ */
+ public Map<String, DataCategory> getDataShared() {
+ return mDataShared;
+ }
+
+ /** Gets the on-device DOM element for the {@link DataLabels}. */
+ @Override
+ public List<Element> toOdDomElements(Document doc) {
+ Element dataLabelsEle =
+ XmlUtils.createPbundleEleWithName(doc, XmlUtils.OD_NAME_DATA_LABELS);
+
+ maybeAppendDataUsages(doc, dataLabelsEle, mDataCollected, XmlUtils.OD_NAME_DATA_COLLECTED);
+ maybeAppendDataUsages(doc, dataLabelsEle, mDataShared, XmlUtils.OD_NAME_DATA_SHARED);
+
+ return XmlUtils.listOf(dataLabelsEle);
+ }
+
+ /** Creates the human-readable DOM elements from the AslMarshallable Java Object. */
+ @Override
+ public List<Element> toHrDomElements(Document doc) {
+ Element dataLabelsEle = doc.createElement(XmlUtils.HR_TAG_DATA_LABELS);
+ maybeAppendHrDataUsages(
+ doc, dataLabelsEle, mDataCollected, XmlUtils.HR_TAG_DATA_COLLECTED, false);
+ maybeAppendHrDataUsages(
+ doc, dataLabelsEle, mDataCollected, XmlUtils.HR_TAG_DATA_COLLECTED_EPHEMERAL, true);
+ maybeAppendHrDataUsages(
+ doc, dataLabelsEle, mDataShared, XmlUtils.HR_TAG_DATA_SHARED, false);
+ return XmlUtils.listOf(dataLabelsEle);
+ }
+
+ private void maybeAppendDataUsages(
+ Document doc,
+ Element dataLabelsEle,
+ Map<String, DataCategory> dataCategoriesMap,
+ String dataUsageTypeName) {
+ if (dataCategoriesMap.isEmpty()) {
+ return;
+ }
+ Element dataUsageEle = XmlUtils.createPbundleEleWithName(doc, dataUsageTypeName);
+
+ for (String dataCategoryName : dataCategoriesMap.keySet()) {
+ Element dataCategoryEle = XmlUtils.createPbundleEleWithName(doc, dataCategoryName);
+ DataCategory dataCategory = dataCategoriesMap.get(dataCategoryName);
+ for (String dataTypeName : dataCategory.getDataTypes().keySet()) {
+ DataType dataType = dataCategory.getDataTypes().get(dataTypeName);
+ XmlUtils.appendChildren(dataCategoryEle, dataType.toOdDomElements(doc));
+ }
+ dataUsageEle.appendChild(dataCategoryEle);
+ }
+ dataLabelsEle.appendChild(dataUsageEle);
+ }
+
+ private void maybeAppendHrDataUsages(
+ Document doc,
+ Element dataLabelsEle,
+ Map<String, DataCategory> dataCategoriesMap,
+ String dataUsageTypeName,
+ boolean ephemeral) {
+ if (dataCategoriesMap.isEmpty()) {
+ return;
+ }
+ for (String dataCategoryName : dataCategoriesMap.keySet()) {
+ DataCategory dataCategory = dataCategoriesMap.get(dataCategoryName);
+ for (String dataTypeName : dataCategory.getDataTypes().keySet()) {
+ DataType dataType = dataCategory.getDataTypes().get(dataTypeName);
+ if (ephemeral
+ != (dataType.getEphemeral() != null ? dataType.getEphemeral() : false)) {
+ continue;
+ }
+
+ Element hrDataTypeEle = doc.createElement(dataUsageTypeName);
+ hrDataTypeEle.setAttribute(
+ XmlUtils.HR_ATTR_DATA_TYPE,
+ dataCategoryName + XmlUtils.DATA_TYPE_SEPARATOR + dataTypeName);
+ XmlUtils.maybeSetHrBoolAttr(
+ hrDataTypeEle,
+ XmlUtils.HR_ATTR_IS_COLLECTION_OPTIONAL,
+ dataType.getIsCollectionOptional());
+ XmlUtils.maybeSetHrBoolAttr(
+ hrDataTypeEle,
+ XmlUtils.HR_ATTR_IS_SHARING_OPTIONAL,
+ dataType.getIsSharingOptional());
+ hrDataTypeEle.setAttribute(
+ XmlUtils.HR_ATTR_PURPOSES,
+ String.join(
+ "|",
+ dataType.getPurposes().stream()
+ .map(DataType.Purpose::toString)
+ .toList()));
+ dataLabelsEle.appendChild(hrDataTypeEle);
+ }
+ }
+ }
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabelsFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabelsFactory.java
new file mode 100644
index 000000000000..c4d88761835a
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabelsFactory.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2024 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.asllib.marshallable;
+
+import com.android.asllib.util.AslgenUtil;
+import com.android.asllib.util.DataCategoryConstants;
+import com.android.asllib.util.DataTypeConstants;
+import com.android.asllib.util.MalformedXmlException;
+import com.android.asllib.util.XmlUtils;
+
+import org.w3c.dom.Element;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class DataLabelsFactory implements AslMarshallableFactory<DataLabels> {
+
+ /** Creates a {@link DataLabels} from the human-readable DOM element. */
+ @Override
+ public DataLabels createFromHrElements(List<Element> elements) throws MalformedXmlException {
+ Element ele = XmlUtils.getSingleElement(elements);
+ if (ele == null) {
+ AslgenUtil.logI("Found no DataLabels in hr format.");
+ return null;
+ }
+ Map<String, DataCategory> dataCollected =
+ getDataCategoriesWithTag(ele, XmlUtils.HR_TAG_DATA_COLLECTED, false);
+ Map<String, DataCategory> dataCollectedEphemeral =
+ getDataCategoriesWithTag(ele, XmlUtils.HR_TAG_DATA_COLLECTED_EPHEMERAL, true);
+ Map<String, DataCategory> dataShared =
+ getDataCategoriesWithTag(ele, XmlUtils.HR_TAG_DATA_SHARED, null);
+
+ for (String dataCollectedEphemeralDataCategoryKey : dataCollectedEphemeral.keySet()) {
+ DataCategory dataCategoryEphemeral =
+ dataCollectedEphemeral.get(dataCollectedEphemeralDataCategoryKey);
+ for (String dataCollectedEphemeralDataTypeKey :
+ dataCategoryEphemeral.getDataTypes().keySet()) {
+ if (dataCollected.containsKey(dataCollectedEphemeralDataCategoryKey)
+ && dataCollected
+ .get(dataCollectedEphemeralDataCategoryKey)
+ .getDataTypes()
+ .containsKey(dataCollectedEphemeralDataTypeKey)) {
+ throw new MalformedXmlException(
+ String.format(
+ "Duplicate entries in data-collected and"
+ + " data-collected-ephemeral: %s %s",
+ dataCollectedEphemeralDataCategoryKey,
+ dataCollectedEphemeralDataTypeKey));
+ }
+
+ if (!dataCollected.containsKey(dataCollectedEphemeralDataCategoryKey)) {
+ dataCollected.put(
+ dataCollectedEphemeralDataCategoryKey,
+ new DataCategory(dataCollectedEphemeralDataCategoryKey));
+ }
+ DataType dataTypeEphemeral =
+ dataCategoryEphemeral.getDataTypes().get(dataCollectedEphemeralDataTypeKey);
+ dataCollected
+ .get(dataCollectedEphemeralDataCategoryKey)
+ .getDataTypes()
+ .put(dataCollectedEphemeralDataTypeKey, dataTypeEphemeral);
+ }
+ }
+ DataLabels dataLabels = new DataLabels(dataCollected, dataShared);
+
+ validateIsXOptional(dataLabels);
+ return dataLabels;
+ }
+
+ /** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
+ @Override
+ public DataLabels createFromOdElements(List<Element> elements) throws MalformedXmlException {
+ Element dataLabelsEle = XmlUtils.getSingleElement(elements);
+ if (dataLabelsEle == null) {
+ AslgenUtil.logI("Found no DataLabels in od format.");
+ return null;
+ }
+ Map<String, DataCategory> dataCollected =
+ getOdDataCategoriesWithTag(dataLabelsEle, XmlUtils.OD_NAME_DATA_COLLECTED);
+ Map<String, DataCategory> dataShared =
+ getOdDataCategoriesWithTag(dataLabelsEle, XmlUtils.OD_NAME_DATA_SHARED);
+ DataLabels dataLabels = new DataLabels(dataCollected, dataShared);
+ validateIsXOptional(dataLabels);
+ return dataLabels;
+ }
+
+ private static Map<String, DataCategory> getOdDataCategoriesWithTag(
+ Element dataLabelsEle, String dataCategoryUsageTypeTag) throws MalformedXmlException {
+ Map<String, DataCategory> dataCategoryMap = new LinkedHashMap<String, DataCategory>();
+ Element dataUsageEle =
+ XmlUtils.getOdPbundleWithName(dataLabelsEle, dataCategoryUsageTypeTag, false);
+ if (dataUsageEle == null) {
+ return dataCategoryMap;
+ }
+ List<Element> dataCategoryEles = XmlUtils.asElementList(dataUsageEle.getChildNodes());
+ for (Element dataCategoryEle : dataCategoryEles) {
+ String dataCategoryName = dataCategoryEle.getAttribute(XmlUtils.OD_ATTR_NAME);
+ DataCategory dataCategory =
+ new DataCategoryFactory().createFromOdElements(List.of(dataCategoryEle));
+ dataCategoryMap.put(dataCategoryName, dataCategory);
+ }
+ return dataCategoryMap;
+ }
+
+ private static Map<String, DataCategory> getDataCategoriesWithTag(
+ Element dataLabelsEle, String dataCategoryUsageTypeTag, Boolean ephemeral)
+ throws MalformedXmlException {
+ List<Element> dataUsedElements =
+ XmlUtils.getChildrenByTagName(dataLabelsEle, dataCategoryUsageTypeTag);
+ Map<String, DataCategory> dataCategoryMap = new LinkedHashMap<String, DataCategory>();
+
+ for (int i = 0; i < dataUsedElements.size(); i++) {
+ Element dataUsedEle = dataUsedElements.get(i);
+ String dataCategoryAndTypeCombinedStr =
+ dataUsedEle.getAttribute(XmlUtils.HR_ATTR_DATA_TYPE);
+ String[] strs = dataCategoryAndTypeCombinedStr.split(XmlUtils.DATA_TYPE_SEPARATOR);
+ if (strs.length != 2) {
+ throw new MalformedXmlException(
+ String.format(
+ "Could not parse human-readable data type string (expecting"
+ + " substring of _data_type_): %s",
+ dataCategoryAndTypeCombinedStr));
+ }
+ String dataCategoryName = strs[0];
+ String dataTypeName = strs[1];
+
+ if (!DataCategoryConstants.getValidDataCategories().contains(dataCategoryName)) {
+ throw new MalformedXmlException(
+ String.format("Unrecognized category name: %s", dataCategoryName));
+ }
+ if (!DataTypeConstants.getValidDataTypes()
+ .get(dataCategoryName)
+ .contains(dataTypeName)) {
+ throw new MalformedXmlException(
+ String.format(
+ "Unrecognized data type name %s for category %s",
+ dataTypeName, dataCategoryName));
+ }
+
+ if (!dataCategoryMap.containsKey(dataCategoryName)) {
+ dataCategoryMap.put(dataCategoryName, new DataCategory(dataCategoryName));
+ }
+ dataCategoryMap
+ .get(dataCategoryName)
+ .getDataTypes()
+ .put(
+ dataTypeName,
+ new DataTypeFactory().createFromHrElements(dataUsedEle, ephemeral));
+ }
+
+ return dataCategoryMap;
+ }
+
+ private void validateIsXOptional(DataLabels dataLabels) throws MalformedXmlException {
+ // Validate booleans such as isCollectionOptional, isSharingOptional.
+ for (DataCategory dataCategory : dataLabels.getDataCollected().values()) {
+ for (DataType dataType : dataCategory.getDataTypes().values()) {
+ if (dataType.getIsSharingOptional() != null) {
+ throw new MalformedXmlException(
+ String.format(
+ "isSharingOptional was unexpectedly defined on a DataType"
+ + " belonging to data collected: %s",
+ dataType.getDataTypeName()));
+ }
+ }
+ }
+ for (DataCategory dataCategory : dataLabels.getDataShared().values()) {
+ for (DataType dataType : dataCategory.getDataTypes().values()) {
+ if (dataType.getIsCollectionOptional() != null) {
+ throw new MalformedXmlException(
+ String.format(
+ "isCollectionOptional was unexpectedly defined on a DataType"
+ + " belonging to data shared: %s",
+ dataType.getDataTypeName()));
+ }
+ }
+ }
+ }
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataType.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataType.java
new file mode 100644
index 000000000000..d2326d10e176
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataType.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2024 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.asllib.marshallable;
+
+import com.android.asllib.util.XmlUtils;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Data usage type representation. Types are specific to a {@link DataCategory} and contains
+ * metadata related to the data usage purpose.
+ */
+public class DataType implements AslMarshallable {
+
+ public enum Purpose {
+ APP_FUNCTIONALITY(1),
+ ANALYTICS(2),
+ DEVELOPER_COMMUNICATIONS(3),
+ FRAUD_PREVENTION_SECURITY(4),
+ ADVERTISING(5),
+ PERSONALIZATION(6),
+ ACCOUNT_MANAGEMENT(7);
+
+ private final int mValue;
+
+ Purpose(int value) {
+ this.mValue = value;
+ }
+
+ /** Get the int value associated with the Purpose. */
+ public int getValue() {
+ return mValue;
+ }
+
+ /** Get the Purpose associated with the int value. */
+ public static Purpose forValue(int value) {
+ for (Purpose e : values()) {
+ if (e.getValue() == value) {
+ return e;
+ }
+ }
+ throw new IllegalArgumentException("No Purpose enum for value: " + value);
+ }
+
+ /** Get the Purpose associated with the human-readable String. */
+ public static Purpose forString(String s) {
+ for (Purpose e : values()) {
+ if (e.toString().equals(s)) {
+ return e;
+ }
+ }
+ throw new IllegalArgumentException("No Purpose enum for str: " + s);
+ }
+
+ /** Human-readable String representation of Purpose. */
+ public String toString() {
+ return this.name().toLowerCase();
+ }
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || obj.getClass() != this.getClass()) {
+ return false;
+ }
+ DataType objAsDataType = (DataType) obj;
+ return Objects.equals(this.mDataTypeName, objAsDataType.mDataTypeName)
+ && Objects.equals(
+ new HashSet<>(this.mPurposes), new HashSet<>(objAsDataType.mPurposes))
+ && Objects.equals(this.mIsCollectionOptional, objAsDataType.mIsCollectionOptional)
+ && Objects.equals(this.mIsSharingOptional, objAsDataType.mIsSharingOptional)
+ && Objects.equals(this.mEphemeral, objAsDataType.mEphemeral);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 1;
+ int prime = 31;
+ result =
+ (prime * result) + (this.mDataTypeName != null ? this.mDataTypeName.hashCode() : 0);
+ result =
+ (prime * result)
+ + (this.mPurposes != null ? new HashSet<>(this.mPurposes).hashCode() : 0);
+ result =
+ (prime * result)
+ + (this.mIsCollectionOptional != null
+ ? this.mIsCollectionOptional.hashCode()
+ : 0);
+ result =
+ (prime * result)
+ + (this.mIsSharingOptional != null
+ ? this.mIsSharingOptional.hashCode()
+ : 0);
+ result = (prime * result) + (this.mEphemeral != null ? this.mEphemeral.hashCode() : 0);
+ return result;
+ }
+
+ private final String mDataTypeName;
+
+ private final List<Purpose> mPurposes;
+ private final Boolean mIsCollectionOptional;
+ private final Boolean mIsSharingOptional;
+ private final Boolean mEphemeral;
+
+ public DataType(
+ String dataTypeName,
+ List<Purpose> purposes,
+ Boolean isCollectionOptional,
+ Boolean isSharingOptional,
+ Boolean ephemeral) {
+ this.mDataTypeName = dataTypeName;
+ this.mPurposes = purposes;
+ this.mIsCollectionOptional = isCollectionOptional;
+ this.mIsSharingOptional = isSharingOptional;
+ this.mEphemeral = ephemeral;
+ }
+
+ public String getDataTypeName() {
+ return mDataTypeName;
+ }
+
+ /**
+ * Returns {@link Set} of valid {@link Integer} purposes for using the associated data category
+ * and type
+ */
+ public List<Purpose> getPurposes() {
+ return mPurposes;
+ }
+
+ /**
+ * For data-collected, returns {@code true} if data usage is user optional and {@code false} if
+ * data usage is required. Should return {@code null} for data-accessed and data-shared.
+ */
+ public Boolean getIsCollectionOptional() {
+ return mIsCollectionOptional;
+ }
+
+ /**
+ * For data-shared, returns {@code true} if data usage is user optional and {@code false} if
+ * data usage is required. Should return {@code null} for data-accessed and data-collected.
+ */
+ public Boolean getIsSharingOptional() {
+ return mIsSharingOptional;
+ }
+
+ /**
+ * For data-collected, returns {@code true} if data usage is user optional and {@code false} if
+ * data usage is processed ephemerally. Should return {@code null} for data-shared.
+ */
+ public Boolean getEphemeral() {
+ return mEphemeral;
+ }
+
+ @Override
+ public List<Element> toOdDomElements(Document doc) {
+ Element dataTypeEle = XmlUtils.createPbundleEleWithName(doc, this.getDataTypeName());
+ if (!this.getPurposes().isEmpty()) {
+ dataTypeEle.appendChild(
+ XmlUtils.createOdArray(
+ doc,
+ XmlUtils.OD_TAG_INT_ARRAY,
+ XmlUtils.OD_NAME_PURPOSES,
+ this.getPurposes().stream()
+ .map(p -> String.valueOf(p.getValue()))
+ .toList()));
+ }
+
+ maybeAddBoolToOdElement(
+ doc,
+ dataTypeEle,
+ this.getIsCollectionOptional(),
+ XmlUtils.OD_NAME_IS_COLLECTION_OPTIONAL);
+ maybeAddBoolToOdElement(
+ doc,
+ dataTypeEle,
+ this.getIsSharingOptional(),
+ XmlUtils.OD_NAME_IS_SHARING_OPTIONAL);
+ maybeAddBoolToOdElement(doc, dataTypeEle, this.getEphemeral(), XmlUtils.OD_NAME_EPHEMERAL);
+ return XmlUtils.listOf(dataTypeEle);
+ }
+
+ /** Creates the human-readable DOM elements from the AslMarshallable Java Object. */
+ @Override
+ public List<Element> toHrDomElements(Document doc) {
+ throw new IllegalStateException(
+ "Turning DataCategory or DataType into human-readable DOM elements requires"
+ + " visibility into parent elements. The logic resides in DataLabels.");
+ }
+
+ private static void maybeAddBoolToOdElement(
+ Document doc, Element parentEle, Boolean b, String odName) {
+ if (b == null) {
+ return;
+ }
+ Element ele = XmlUtils.createOdBooleanEle(doc, odName, b);
+ parentEle.appendChild(ele);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataTypeFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataTypeFactory.java
new file mode 100644
index 000000000000..a5559d801349
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataTypeFactory.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2024 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.asllib.marshallable;
+
+import com.android.asllib.util.MalformedXmlException;
+import com.android.asllib.util.XmlUtils;
+
+import org.w3c.dom.Element;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class DataTypeFactory {
+ /** Creates a {@link DataType} from the human-readable DOM element. */
+ public DataType createFromHrElements(Element hrDataTypeEle, Boolean ephemeral)
+ throws MalformedXmlException {
+ String dataCategoryAndTypeCombinedStr =
+ hrDataTypeEle.getAttribute(XmlUtils.HR_ATTR_DATA_TYPE);
+ String[] strs = dataCategoryAndTypeCombinedStr.split(XmlUtils.DATA_TYPE_SEPARATOR);
+ if (strs.length != 2) {
+ throw new MalformedXmlException(
+ String.format(
+ "Could not parse human-readable data type string (expecting substring"
+ + " of _data_type_): %s",
+ dataCategoryAndTypeCombinedStr));
+ }
+ String dataTypeName = strs[1];
+
+ List<DataType.Purpose> purposes =
+ XmlUtils.getPipelineSplitAttr(hrDataTypeEle, XmlUtils.HR_ATTR_PURPOSES, true)
+ .stream()
+ .map(DataType.Purpose::forString)
+ .collect(Collectors.toList());
+ if (purposes.isEmpty()) {
+ throw new MalformedXmlException(String.format("Found no purpose in: %s", dataTypeName));
+ }
+ if (new HashSet<>(purposes).size() != purposes.size()) {
+ throw new MalformedXmlException(
+ String.format("Found non-unique purposes in: %s", dataTypeName));
+ }
+ Boolean isCollectionOptional =
+ XmlUtils.getBoolAttr(hrDataTypeEle, XmlUtils.HR_ATTR_IS_COLLECTION_OPTIONAL, false);
+ Boolean isSharingOptional =
+ XmlUtils.getBoolAttr(hrDataTypeEle, XmlUtils.HR_ATTR_IS_SHARING_OPTIONAL, false);
+ // Boolean ephemeral = XmlUtils.getBoolAttr(hrDataTypeEle, XmlUtils.HR_ATTR_EPHEMERAL,
+ // false);
+ return new DataType(
+ dataTypeName, purposes, isCollectionOptional, isSharingOptional, ephemeral);
+ }
+
+ /** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
+ public DataType createFromOdElements(List<Element> elements) throws MalformedXmlException {
+ Element odDataTypeEle = XmlUtils.getSingleElement(elements);
+ String dataTypeName = odDataTypeEle.getAttribute(XmlUtils.OD_ATTR_NAME);
+ List<Integer> purposeInts =
+ XmlUtils.getOdIntArray(odDataTypeEle, XmlUtils.OD_NAME_PURPOSES, true);
+ List<DataType.Purpose> purposes =
+ purposeInts.stream().map(DataType.Purpose::forValue).collect(Collectors.toList());
+ if (purposes.isEmpty()) {
+ throw new MalformedXmlException(String.format("Found no purpose in: %s", dataTypeName));
+ }
+ if (new HashSet<>(purposes).size() != purposes.size()) {
+ throw new MalformedXmlException(
+ String.format("Found non-unique purposes in: %s", dataTypeName));
+ }
+ Boolean isCollectionOptional =
+ XmlUtils.getOdBoolEle(
+ odDataTypeEle, XmlUtils.OD_NAME_IS_COLLECTION_OPTIONAL, false);
+ Boolean isSharingOptional =
+ XmlUtils.getOdBoolEle(odDataTypeEle, XmlUtils.OD_NAME_IS_SHARING_OPTIONAL, false);
+ Boolean ephemeral = XmlUtils.getOdBoolEle(odDataTypeEle, XmlUtils.OD_NAME_EPHEMERAL, false);
+
+ return new DataType(
+ dataTypeName, purposes, isCollectionOptional, isSharingOptional, ephemeral);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DeveloperInfo.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DeveloperInfo.java
new file mode 100644
index 000000000000..94fad9607880
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DeveloperInfo.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2024 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.asllib.marshallable;
+
+import com.android.asllib.util.XmlUtils;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.util.List;
+
+/** DeveloperInfo representation */
+public class DeveloperInfo implements AslMarshallable {
+ public enum DeveloperRelationship {
+ OEM(0),
+ ODM(1),
+ SOC(2),
+ OTA(3),
+ CARRIER(4),
+ AOSP(5),
+ OTHER(6);
+
+ private final int mValue;
+
+ DeveloperRelationship(int value) {
+ this.mValue = value;
+ }
+
+ /** Get the int value associated with the DeveloperRelationship. */
+ public int getValue() {
+ return mValue;
+ }
+
+ /** Get the DeveloperRelationship associated with the int value. */
+ public static DeveloperInfo.DeveloperRelationship forValue(int value) {
+ for (DeveloperInfo.DeveloperRelationship e : values()) {
+ if (e.getValue() == value) {
+ return e;
+ }
+ }
+ throw new IllegalArgumentException("No DeveloperRelationship enum for value: " + value);
+ }
+
+ /** Get the DeveloperRelationship associated with the human-readable String. */
+ public static DeveloperInfo.DeveloperRelationship forString(String s) {
+ for (DeveloperInfo.DeveloperRelationship e : values()) {
+ if (e.toString().equals(s)) {
+ return e;
+ }
+ }
+ throw new IllegalArgumentException("No DeveloperRelationship enum for str: " + s);
+ }
+
+ /** Human-readable String representation of DeveloperRelationship. */
+ public String toString() {
+ return this.name().toLowerCase();
+ }
+ }
+
+ private final String mName;
+ private final String mEmail;
+ private final String mAddress;
+ private final String mCountryRegion;
+ private final DeveloperRelationship mDeveloperRelationship;
+ private final String mWebsite;
+ private final String mAppDeveloperRegistryId;
+
+ public DeveloperInfo(
+ String name,
+ String email,
+ String address,
+ String countryRegion,
+ DeveloperRelationship developerRelationship,
+ String website,
+ String appDeveloperRegistryId) {
+ this.mName = name;
+ this.mEmail = email;
+ this.mAddress = address;
+ this.mCountryRegion = countryRegion;
+ this.mDeveloperRelationship = developerRelationship;
+ this.mWebsite = website;
+ this.mAppDeveloperRegistryId = appDeveloperRegistryId;
+ }
+
+ /** Creates an on-device DOM element from the {@link SafetyLabels}. */
+ @Override
+ public List<Element> toOdDomElements(Document doc) {
+ Element developerInfoEle =
+ XmlUtils.createPbundleEleWithName(doc, XmlUtils.OD_NAME_DEVELOPER_INFO);
+ if (mName != null) {
+ developerInfoEle.appendChild(
+ XmlUtils.createOdStringEle(doc, XmlUtils.OD_NAME_NAME, mName));
+ }
+ if (mEmail != null) {
+ developerInfoEle.appendChild(
+ XmlUtils.createOdStringEle(doc, XmlUtils.OD_NAME_EMAIL, mEmail));
+ }
+ if (mAddress != null) {
+ developerInfoEle.appendChild(
+ XmlUtils.createOdStringEle(doc, XmlUtils.OD_NAME_ADDRESS, mAddress));
+ }
+ if (mCountryRegion != null) {
+ developerInfoEle.appendChild(
+ XmlUtils.createOdStringEle(
+ doc, XmlUtils.OD_NAME_COUNTRY_REGION, mCountryRegion));
+ }
+ if (mDeveloperRelationship != null) {
+ developerInfoEle.appendChild(
+ XmlUtils.createOdLongEle(
+ doc,
+ XmlUtils.OD_NAME_DEVELOPER_RELATIONSHIP,
+ mDeveloperRelationship.getValue()));
+ }
+ if (mWebsite != null) {
+ developerInfoEle.appendChild(
+ XmlUtils.createOdStringEle(doc, XmlUtils.OD_NAME_WEBSITE, mWebsite));
+ }
+ if (mAppDeveloperRegistryId != null) {
+ developerInfoEle.appendChild(
+ XmlUtils.createOdStringEle(
+ doc,
+ XmlUtils.OD_NAME_APP_DEVELOPER_REGISTRY_ID,
+ mAppDeveloperRegistryId));
+ }
+
+ return XmlUtils.listOf(developerInfoEle);
+ }
+
+ /** Creates the human-readable DOM elements from the AslMarshallable Java Object. */
+ @Override
+ public List<Element> toHrDomElements(Document doc) {
+ Element developerInfoEle = doc.createElement(XmlUtils.HR_TAG_DEVELOPER_INFO);
+ if (mName != null) {
+ developerInfoEle.setAttribute(XmlUtils.HR_ATTR_NAME, mName);
+ }
+ if (mEmail != null) {
+ developerInfoEle.setAttribute(XmlUtils.HR_ATTR_EMAIL, mEmail);
+ }
+ if (mAddress != null) {
+ developerInfoEle.setAttribute(XmlUtils.HR_ATTR_ADDRESS, mAddress);
+ }
+ if (mCountryRegion != null) {
+ developerInfoEle.setAttribute(XmlUtils.HR_ATTR_COUNTRY_REGION, mCountryRegion);
+ }
+ if (mDeveloperRelationship != null) {
+ developerInfoEle.setAttribute(
+ XmlUtils.HR_ATTR_DEVELOPER_RELATIONSHIP, mDeveloperRelationship.toString());
+ }
+ if (mWebsite != null) {
+ developerInfoEle.setAttribute(XmlUtils.HR_ATTR_WEBSITE, mWebsite);
+ }
+ if (mAppDeveloperRegistryId != null) {
+ developerInfoEle.setAttribute(
+ XmlUtils.HR_ATTR_APP_DEVELOPER_REGISTRY_ID, mAppDeveloperRegistryId);
+ }
+
+ return XmlUtils.listOf(developerInfoEle);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DeveloperInfoFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DeveloperInfoFactory.java
new file mode 100644
index 000000000000..0f3b41cd5d1a
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DeveloperInfoFactory.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2024 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.asllib.marshallable;
+
+import com.android.asllib.util.AslgenUtil;
+import com.android.asllib.util.MalformedXmlException;
+import com.android.asllib.util.XmlUtils;
+
+import org.w3c.dom.Element;
+
+import java.util.List;
+
+public class DeveloperInfoFactory implements AslMarshallableFactory<DeveloperInfo> {
+
+ /** Creates a {@link DeveloperInfo} from the human-readable DOM element. */
+ @Override
+ public DeveloperInfo createFromHrElements(List<Element> elements) throws MalformedXmlException {
+ Element developerInfoEle = XmlUtils.getSingleElement(elements);
+ if (developerInfoEle == null) {
+ AslgenUtil.logI("No DeveloperInfo found in hr format.");
+ return null;
+ }
+ String name = XmlUtils.getStringAttr(developerInfoEle, XmlUtils.HR_ATTR_NAME, true);
+ String email = XmlUtils.getStringAttr(developerInfoEle, XmlUtils.HR_ATTR_EMAIL, true);
+ String address = XmlUtils.getStringAttr(developerInfoEle, XmlUtils.HR_ATTR_ADDRESS, true);
+ String countryRegion =
+ XmlUtils.getStringAttr(developerInfoEle, XmlUtils.HR_ATTR_COUNTRY_REGION, true);
+ DeveloperInfo.DeveloperRelationship developerRelationship =
+ DeveloperInfo.DeveloperRelationship.forString(
+ XmlUtils.getStringAttr(
+ developerInfoEle, XmlUtils.HR_ATTR_DEVELOPER_RELATIONSHIP, true));
+ String website = XmlUtils.getStringAttr(developerInfoEle, XmlUtils.HR_ATTR_WEBSITE, false);
+ String appDeveloperRegistryId =
+ XmlUtils.getStringAttr(
+ developerInfoEle, XmlUtils.HR_ATTR_APP_DEVELOPER_REGISTRY_ID, false);
+
+ return new DeveloperInfo(
+ name,
+ email,
+ address,
+ countryRegion,
+ developerRelationship,
+ website,
+ appDeveloperRegistryId);
+ }
+
+ /** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
+ @Override
+ public DeveloperInfo createFromOdElements(List<Element> elements) throws MalformedXmlException {
+ Element developerInfoEle = XmlUtils.getSingleElement(elements);
+ if (developerInfoEle == null) {
+ AslgenUtil.logI("No DeveloperInfo found in od format.");
+ return null;
+ }
+ String name = XmlUtils.getOdStringEle(developerInfoEle, XmlUtils.OD_NAME_NAME, true);
+ String email = XmlUtils.getOdStringEle(developerInfoEle, XmlUtils.OD_NAME_EMAIL, true);
+ String address = XmlUtils.getOdStringEle(developerInfoEle, XmlUtils.OD_NAME_ADDRESS, true);
+ String countryRegion =
+ XmlUtils.getOdStringEle(developerInfoEle, XmlUtils.OD_NAME_COUNTRY_REGION, true);
+ DeveloperInfo.DeveloperRelationship developerRelationship =
+ DeveloperInfo.DeveloperRelationship.forValue(
+ (int)
+ (long)
+ XmlUtils.getOdLongEle(
+ developerInfoEle,
+ XmlUtils.OD_NAME_DEVELOPER_RELATIONSHIP,
+ true));
+ String website = XmlUtils.getOdStringEle(developerInfoEle, XmlUtils.OD_NAME_WEBSITE, false);
+ String appDeveloperRegistryId =
+ XmlUtils.getOdStringEle(
+ developerInfoEle, XmlUtils.OD_NAME_APP_DEVELOPER_REGISTRY_ID, false);
+
+ return new DeveloperInfo(
+ name,
+ email,
+ address,
+ countryRegion,
+ developerRelationship,
+ website,
+ appDeveloperRegistryId);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SafetyLabels.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SafetyLabels.java
new file mode 100644
index 000000000000..6af80715f7c1
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SafetyLabels.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2024 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.asllib.marshallable;
+
+import com.android.asllib.util.XmlUtils;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.util.List;
+
+/** Safety Label representation containing zero or more {@link DataCategory} for data shared */
+public class SafetyLabels implements AslMarshallable {
+
+ private final Long mVersion;
+ private final DataLabels mDataLabels;
+ private final SecurityLabels mSecurityLabels;
+ private final ThirdPartyVerification mThirdPartyVerification;
+
+ public SafetyLabels(
+ Long version,
+ DataLabels dataLabels,
+ SecurityLabels securityLabels,
+ ThirdPartyVerification thirdPartyVerification) {
+ this.mVersion = version;
+ this.mDataLabels = dataLabels;
+ this.mSecurityLabels = securityLabels;
+ this.mThirdPartyVerification = thirdPartyVerification;
+ }
+
+ /** Returns the data label for the safety label */
+ public DataLabels getDataLabel() {
+ return mDataLabels;
+ }
+
+ /** Gets the version of the {@link SafetyLabels}. */
+ public Long getVersion() {
+ return mVersion;
+ }
+
+ /** Creates an on-device DOM element from the {@link SafetyLabels}. */
+ @Override
+ public List<Element> toOdDomElements(Document doc) {
+ Element safetyLabelsEle =
+ XmlUtils.createPbundleEleWithName(doc, XmlUtils.OD_NAME_SAFETY_LABELS);
+ safetyLabelsEle.appendChild(
+ XmlUtils.createOdLongEle(doc, XmlUtils.OD_NAME_VERSION, mVersion));
+ if (mDataLabels != null) {
+ XmlUtils.appendChildren(safetyLabelsEle, mDataLabels.toOdDomElements(doc));
+ }
+ if (mSecurityLabels != null) {
+ XmlUtils.appendChildren(safetyLabelsEle, mSecurityLabels.toOdDomElements(doc));
+ }
+ if (mThirdPartyVerification != null) {
+ XmlUtils.appendChildren(safetyLabelsEle, mThirdPartyVerification.toOdDomElements(doc));
+ }
+ return XmlUtils.listOf(safetyLabelsEle);
+ }
+
+ /** Creates the human-readable DOM elements from the AslMarshallable Java Object. */
+ @Override
+ public List<Element> toHrDomElements(Document doc) {
+ Element safetyLabelsEle = doc.createElement(XmlUtils.HR_TAG_SAFETY_LABELS);
+ safetyLabelsEle.setAttribute(XmlUtils.HR_ATTR_VERSION, String.valueOf(mVersion));
+
+ if (mDataLabels != null) {
+ XmlUtils.appendChildren(safetyLabelsEle, mDataLabels.toHrDomElements(doc));
+ }
+ if (mSecurityLabels != null) {
+ XmlUtils.appendChildren(safetyLabelsEle, mSecurityLabels.toHrDomElements(doc));
+ }
+ if (mThirdPartyVerification != null) {
+ XmlUtils.appendChildren(safetyLabelsEle, mThirdPartyVerification.toHrDomElements(doc));
+ }
+ return XmlUtils.listOf(safetyLabelsEle);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SafetyLabelsFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SafetyLabelsFactory.java
new file mode 100644
index 000000000000..2644b435311b
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SafetyLabelsFactory.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2024 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.asllib.marshallable;
+
+import com.android.asllib.util.AslgenUtil;
+import com.android.asllib.util.MalformedXmlException;
+import com.android.asllib.util.XmlUtils;
+
+import org.w3c.dom.Element;
+
+import java.util.List;
+
+public class SafetyLabelsFactory implements AslMarshallableFactory<SafetyLabels> {
+
+ /** Creates a {@link SafetyLabels} from the human-readable DOM element. */
+ @Override
+ public SafetyLabels createFromHrElements(List<Element> elements) throws MalformedXmlException {
+ Element safetyLabelsEle = XmlUtils.getSingleElement(elements);
+ if (safetyLabelsEle == null) {
+ AslgenUtil.logI("No SafetyLabels found in hr format.");
+ return null;
+ }
+ long version = XmlUtils.tryGetVersion(safetyLabelsEle);
+
+ DataLabels dataLabels =
+ new DataLabelsFactory()
+ .createFromHrElements(
+ XmlUtils.listOf(
+ XmlUtils.getSingleChildElement(
+ safetyLabelsEle,
+ XmlUtils.HR_TAG_DATA_LABELS,
+ false)));
+ SecurityLabels securityLabels =
+ new SecurityLabelsFactory()
+ .createFromHrElements(
+ XmlUtils.listOf(
+ XmlUtils.getSingleChildElement(
+ safetyLabelsEle,
+ XmlUtils.HR_TAG_SECURITY_LABELS,
+ false)));
+ ThirdPartyVerification thirdPartyVerification =
+ new ThirdPartyVerificationFactory()
+ .createFromHrElements(
+ XmlUtils.listOf(
+ XmlUtils.getSingleChildElement(
+ safetyLabelsEle,
+ XmlUtils.HR_TAG_THIRD_PARTY_VERIFICATION,
+ false)));
+ return new SafetyLabels(version, dataLabels, securityLabels, thirdPartyVerification);
+ }
+
+ /** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
+ @Override
+ public SafetyLabels createFromOdElements(List<Element> elements) throws MalformedXmlException {
+ Element safetyLabelsEle = XmlUtils.getSingleElement(elements);
+ if (safetyLabelsEle == null) {
+ AslgenUtil.logI("No SafetyLabels found in od format.");
+ return null;
+ }
+ Long version = XmlUtils.getOdLongEle(safetyLabelsEle, XmlUtils.OD_NAME_VERSION, true);
+
+ DataLabels dataLabels =
+ new DataLabelsFactory()
+ .createFromOdElements(
+ XmlUtils.listOf(
+ XmlUtils.getOdPbundleWithName(
+ safetyLabelsEle,
+ XmlUtils.OD_NAME_DATA_LABELS,
+ false)));
+ SecurityLabels securityLabels =
+ new SecurityLabelsFactory()
+ .createFromOdElements(
+ XmlUtils.listOf(
+ XmlUtils.getOdPbundleWithName(
+ safetyLabelsEle,
+ XmlUtils.OD_NAME_SECURITY_LABELS,
+ false)));
+ ThirdPartyVerification thirdPartyVerification =
+ new ThirdPartyVerificationFactory()
+ .createFromOdElements(
+ XmlUtils.listOf(
+ XmlUtils.getOdPbundleWithName(
+ safetyLabelsEle,
+ XmlUtils.OD_NAME_THIRD_PARTY_VERIFICATION,
+ false)));
+ return new SafetyLabels(version, dataLabels, securityLabels, thirdPartyVerification);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SecurityLabels.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SecurityLabels.java
new file mode 100644
index 000000000000..48643ba0e3ab
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SecurityLabels.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2024 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.asllib.marshallable;
+
+import com.android.asllib.util.XmlUtils;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.util.List;
+
+/** Security Labels representation */
+public class SecurityLabels implements AslMarshallable {
+
+ private final Boolean mIsDataDeletable;
+ private final Boolean mIsDataEncrypted;
+
+ public SecurityLabels(Boolean isDataDeletable, Boolean isDataEncrypted) {
+ this.mIsDataDeletable = isDataDeletable;
+ this.mIsDataEncrypted = isDataEncrypted;
+ }
+
+ /** Creates an on-device DOM element from the {@link SecurityLabels}. */
+ @Override
+ public List<Element> toOdDomElements(Document doc) {
+ Element ele = XmlUtils.createPbundleEleWithName(doc, XmlUtils.OD_NAME_SECURITY_LABELS);
+ if (mIsDataDeletable != null) {
+ ele.appendChild(
+ XmlUtils.createOdBooleanEle(
+ doc, XmlUtils.OD_NAME_IS_DATA_DELETABLE, mIsDataDeletable));
+ }
+ if (mIsDataEncrypted != null) {
+ ele.appendChild(
+ XmlUtils.createOdBooleanEle(
+ doc, XmlUtils.OD_NAME_IS_DATA_ENCRYPTED, mIsDataEncrypted));
+ }
+ return XmlUtils.listOf(ele);
+ }
+
+ /** Creates the human-readable DOM elements from the AslMarshallable Java Object. */
+ @Override
+ public List<Element> toHrDomElements(Document doc) {
+ Element ele = doc.createElement(XmlUtils.HR_TAG_SECURITY_LABELS);
+ if (mIsDataDeletable != null) {
+ ele.setAttribute(XmlUtils.HR_ATTR_IS_DATA_DELETABLE, String.valueOf(mIsDataDeletable));
+ }
+ if (mIsDataEncrypted != null) {
+ ele.setAttribute(XmlUtils.HR_ATTR_IS_DATA_ENCRYPTED, String.valueOf(mIsDataEncrypted));
+ }
+ return XmlUtils.listOf(ele);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SecurityLabelsFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SecurityLabelsFactory.java
new file mode 100644
index 000000000000..525a80388261
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SecurityLabelsFactory.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 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.asllib.marshallable;
+
+import com.android.asllib.util.AslgenUtil;
+import com.android.asllib.util.MalformedXmlException;
+import com.android.asllib.util.XmlUtils;
+
+import org.w3c.dom.Element;
+
+import java.util.List;
+
+public class SecurityLabelsFactory implements AslMarshallableFactory<SecurityLabels> {
+
+ /** Creates a {@link SecurityLabels} from the human-readable DOM element. */
+ @Override
+ public SecurityLabels createFromHrElements(List<Element> elements)
+ throws MalformedXmlException {
+ Element ele = XmlUtils.getSingleElement(elements);
+ if (ele == null) {
+ AslgenUtil.logI("No SecurityLabels found in hr format.");
+ return null;
+ }
+ Boolean isDataDeletable =
+ XmlUtils.getBoolAttr(ele, XmlUtils.HR_ATTR_IS_DATA_DELETABLE, false);
+ Boolean isDataEncrypted =
+ XmlUtils.getBoolAttr(ele, XmlUtils.HR_ATTR_IS_DATA_ENCRYPTED, false);
+ return new SecurityLabels(isDataDeletable, isDataEncrypted);
+ }
+
+ /** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
+ @Override
+ public SecurityLabels createFromOdElements(List<Element> elements)
+ throws MalformedXmlException {
+ Element ele = XmlUtils.getSingleElement(elements);
+ if (ele == null) {
+ AslgenUtil.logI("No SecurityLabels found in od format.");
+ return null;
+ }
+ Boolean isDataDeletable =
+ XmlUtils.getOdBoolEle(ele, XmlUtils.OD_NAME_IS_DATA_DELETABLE, false);
+ Boolean isDataEncrypted =
+ XmlUtils.getOdBoolEle(ele, XmlUtils.OD_NAME_IS_DATA_ENCRYPTED, false);
+ return new SecurityLabels(isDataDeletable, isDataEncrypted);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabel.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabel.java
new file mode 100644
index 000000000000..242e7be66d76
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabel.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 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.asllib.marshallable;
+
+import com.android.asllib.util.XmlUtils;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.util.List;
+
+/** Safety Label representation containing zero or more {@link DataCategory} for data shared */
+public class SystemAppSafetyLabel implements AslMarshallable {
+
+ private final Boolean mDeclaration;
+
+ public SystemAppSafetyLabel(Boolean d) {
+ this.mDeclaration = d;
+ }
+
+ /** Creates an on-device DOM element from the {@link SystemAppSafetyLabel}. */
+ @Override
+ public List<Element> toOdDomElements(Document doc) {
+ Element systemAppSafetyLabelEle =
+ XmlUtils.createPbundleEleWithName(doc, XmlUtils.OD_NAME_SYSTEM_APP_SAFETY_LABEL);
+ systemAppSafetyLabelEle.appendChild(
+ XmlUtils.createOdBooleanEle(doc, XmlUtils.OD_NAME_DECLARATION, mDeclaration));
+ return XmlUtils.listOf(systemAppSafetyLabelEle);
+ }
+
+ /** Creates the human-readable DOM elements from the AslMarshallable Java Object. */
+ @Override
+ public List<Element> toHrDomElements(Document doc) {
+ Element systemAppSafetyLabelEle =
+ doc.createElement(XmlUtils.HR_TAG_SYSTEM_APP_SAFETY_LABEL);
+ XmlUtils.maybeSetHrBoolAttr(
+ systemAppSafetyLabelEle, XmlUtils.HR_ATTR_DECLARATION, mDeclaration);
+ return XmlUtils.listOf(systemAppSafetyLabelEle);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabelFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabelFactory.java
new file mode 100644
index 000000000000..7f4aa7a3b690
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabelFactory.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2024 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.asllib.marshallable;
+
+import com.android.asllib.util.AslgenUtil;
+import com.android.asllib.util.MalformedXmlException;
+import com.android.asllib.util.XmlUtils;
+
+import org.w3c.dom.Element;
+
+import java.util.List;
+
+public class SystemAppSafetyLabelFactory implements AslMarshallableFactory<SystemAppSafetyLabel> {
+
+ /** Creates a {@link SystemAppSafetyLabel} from the human-readable DOM element. */
+ @Override
+ public SystemAppSafetyLabel createFromHrElements(List<Element> elements)
+ throws MalformedXmlException {
+ Element systemAppSafetyLabelEle = XmlUtils.getSingleElement(elements);
+ if (systemAppSafetyLabelEle == null) {
+ AslgenUtil.logI("No SystemAppSafetyLabel found in hr format.");
+ return null;
+ }
+
+ Boolean declaration =
+ XmlUtils.getBoolAttr(systemAppSafetyLabelEle, XmlUtils.HR_ATTR_DECLARATION, true);
+ return new SystemAppSafetyLabel(declaration);
+ }
+
+ /** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
+ @Override
+ public SystemAppSafetyLabel createFromOdElements(List<Element> elements)
+ throws MalformedXmlException {
+ Element systemAppSafetyLabelEle = XmlUtils.getSingleElement(elements);
+ if (systemAppSafetyLabelEle == null) {
+ AslgenUtil.logI("No SystemAppSafetyLabel found in od format.");
+ return null;
+ }
+ Boolean declaration =
+ XmlUtils.getOdBoolEle(systemAppSafetyLabelEle, XmlUtils.OD_NAME_DECLARATION, true);
+ return new SystemAppSafetyLabel(declaration);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/ThirdPartyVerification.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/ThirdPartyVerification.java
new file mode 100644
index 000000000000..d74f3f062513
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/ThirdPartyVerification.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 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.asllib.marshallable;
+
+import com.android.asllib.util.XmlUtils;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.util.List;
+
+/** ThirdPartyVerification representation. */
+public class ThirdPartyVerification implements AslMarshallable {
+
+ private final String mUrl;
+
+ public ThirdPartyVerification(String url) {
+ this.mUrl = url;
+ }
+
+ /** Creates an on-device DOM element from the {@link ThirdPartyVerification}. */
+ @Override
+ public List<Element> toOdDomElements(Document doc) {
+ Element ele =
+ XmlUtils.createPbundleEleWithName(doc, XmlUtils.OD_NAME_THIRD_PARTY_VERIFICATION);
+ ele.appendChild(XmlUtils.createOdStringEle(doc, XmlUtils.OD_NAME_URL, mUrl));
+ return XmlUtils.listOf(ele);
+ }
+
+ /** Creates the human-readable DOM elements from the AslMarshallable Java Object. */
+ @Override
+ public List<Element> toHrDomElements(Document doc) {
+ Element ele = doc.createElement(XmlUtils.HR_TAG_THIRD_PARTY_VERIFICATION);
+ ele.setAttribute(XmlUtils.HR_ATTR_URL, mUrl);
+ return XmlUtils.listOf(ele);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/ThirdPartyVerificationFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/ThirdPartyVerificationFactory.java
new file mode 100644
index 000000000000..197e7aa77743
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/ThirdPartyVerificationFactory.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2024 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.asllib.marshallable;
+
+import com.android.asllib.util.AslgenUtil;
+import com.android.asllib.util.MalformedXmlException;
+import com.android.asllib.util.XmlUtils;
+
+import org.w3c.dom.Element;
+
+import java.util.List;
+
+public class ThirdPartyVerificationFactory
+ implements AslMarshallableFactory<ThirdPartyVerification> {
+
+ /** Creates a {@link ThirdPartyVerification} from the human-readable DOM element. */
+ @Override
+ public ThirdPartyVerification createFromHrElements(List<Element> elements)
+ throws MalformedXmlException {
+ Element ele = XmlUtils.getSingleElement(elements);
+ if (ele == null) {
+ AslgenUtil.logI("No ThirdPartyVerification found in hr format.");
+ return null;
+ }
+
+ String url = XmlUtils.getStringAttr(ele, XmlUtils.HR_ATTR_URL, true);
+ return new ThirdPartyVerification(url);
+ }
+
+ /** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
+ @Override
+ public ThirdPartyVerification createFromOdElements(List<Element> elements)
+ throws MalformedXmlException {
+ Element ele = XmlUtils.getSingleElement(elements);
+ if (ele == null) {
+ AslgenUtil.logI("No ThirdPartyVerification found in od format.");
+ return null;
+ }
+
+ String url = XmlUtils.getOdStringEle(ele, XmlUtils.OD_NAME_URL, true);
+ return new ThirdPartyVerification(url);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/TransparencyInfo.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/TransparencyInfo.java
new file mode 100644
index 000000000000..6a8700a10d3f
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/TransparencyInfo.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 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.asllib.marshallable;
+
+import com.android.asllib.util.XmlUtils;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.util.List;
+
+/** TransparencyInfo representation containing {@link DeveloperInfo} and {@link AppInfo} */
+public class TransparencyInfo implements AslMarshallable {
+
+ private final DeveloperInfo mDeveloperInfo;
+ private final AppInfo mAppInfo;
+
+ public TransparencyInfo(DeveloperInfo developerInfo, AppInfo appInfo) {
+ this.mDeveloperInfo = developerInfo;
+ this.mAppInfo = appInfo;
+ }
+
+ /** Gets the {@link DeveloperInfo} of the {@link TransparencyInfo}. */
+ public DeveloperInfo getDeveloperInfo() {
+ return mDeveloperInfo;
+ }
+
+ /** Gets the {@link AppInfo} of the {@link TransparencyInfo}. */
+ public AppInfo getAppInfo() {
+ return mAppInfo;
+ }
+
+ /** Creates an on-device DOM element from the {@link TransparencyInfo}. */
+ @Override
+ public List<Element> toOdDomElements(Document doc) {
+ Element transparencyInfoEle =
+ XmlUtils.createPbundleEleWithName(doc, XmlUtils.OD_NAME_TRANSPARENCY_INFO);
+ if (mDeveloperInfo != null) {
+ XmlUtils.appendChildren(transparencyInfoEle, mDeveloperInfo.toOdDomElements(doc));
+ }
+ if (mAppInfo != null) {
+ XmlUtils.appendChildren(transparencyInfoEle, mAppInfo.toOdDomElements(doc));
+ }
+ return XmlUtils.listOf(transparencyInfoEle);
+ }
+
+ /** Creates the human-readable DOM elements from the AslMarshallable Java Object. */
+ @Override
+ public List<Element> toHrDomElements(Document doc) {
+ Element transparencyInfoEle = doc.createElement(XmlUtils.HR_TAG_TRANSPARENCY_INFO);
+ if (mDeveloperInfo != null) {
+ XmlUtils.appendChildren(transparencyInfoEle, mDeveloperInfo.toHrDomElements(doc));
+ }
+ if (mAppInfo != null) {
+ XmlUtils.appendChildren(transparencyInfoEle, mAppInfo.toHrDomElements(doc));
+ }
+ return XmlUtils.listOf(transparencyInfoEle);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/TransparencyInfoFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/TransparencyInfoFactory.java
new file mode 100644
index 000000000000..94c564087918
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/TransparencyInfoFactory.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 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.asllib.marshallable;
+
+import com.android.asllib.util.AslgenUtil;
+import com.android.asllib.util.MalformedXmlException;
+import com.android.asllib.util.XmlUtils;
+
+import org.w3c.dom.Element;
+
+import java.util.List;
+
+public class TransparencyInfoFactory implements AslMarshallableFactory<TransparencyInfo> {
+
+ /** Creates a {@link TransparencyInfo} from the human-readable DOM element. */
+ @Override
+ public TransparencyInfo createFromHrElements(List<Element> elements)
+ throws MalformedXmlException {
+ Element transparencyInfoEle = XmlUtils.getSingleElement(elements);
+ if (transparencyInfoEle == null) {
+ AslgenUtil.logI("No TransparencyInfo found in hr format.");
+ return null;
+ }
+
+ Element developerInfoEle =
+ XmlUtils.getSingleChildElement(
+ transparencyInfoEle, XmlUtils.HR_TAG_DEVELOPER_INFO, false);
+ DeveloperInfo developerInfo =
+ new DeveloperInfoFactory().createFromHrElements(XmlUtils.listOf(developerInfoEle));
+
+ Element appInfoEle =
+ XmlUtils.getSingleChildElement(
+ transparencyInfoEle, XmlUtils.HR_TAG_APP_INFO, false);
+ AppInfo appInfo = new AppInfoFactory().createFromHrElements(XmlUtils.listOf(appInfoEle));
+
+ return new TransparencyInfo(developerInfo, appInfo);
+ }
+
+ /** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
+ @Override
+ public TransparencyInfo createFromOdElements(List<Element> elements)
+ throws MalformedXmlException {
+ Element transparencyInfoEle = XmlUtils.getSingleElement(elements);
+ if (transparencyInfoEle == null) {
+ AslgenUtil.logI("No TransparencyInfo found in od format.");
+ return null;
+ }
+
+ Element developerInfoEle =
+ XmlUtils.getOdPbundleWithName(
+ transparencyInfoEle, XmlUtils.OD_NAME_DEVELOPER_INFO, false);
+ DeveloperInfo developerInfo =
+ new DeveloperInfoFactory().createFromOdElements(XmlUtils.listOf(developerInfoEle));
+
+ Element appInfoEle =
+ XmlUtils.getOdPbundleWithName(
+ transparencyInfoEle, XmlUtils.OD_NAME_APP_INFO, false);
+ AppInfo appInfo = new AppInfoFactory().createFromOdElements(XmlUtils.listOf(appInfoEle));
+
+ return new TransparencyInfo(developerInfo, appInfo);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/AslgenUtil.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/AslgenUtil.java
new file mode 100644
index 000000000000..7d5421545091
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/AslgenUtil.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 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.asllib.util;
+
+public class AslgenUtil {
+ private static final String ASLGEN_TAG = "ASLGEN";
+
+ /** Log info. */
+ public static void logI(String s) {
+ System.out.println(String.format("%s -- INFO: %s", ASLGEN_TAG, s));
+ }
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/DataCategoryConstants.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/DataCategoryConstants.java
new file mode 100644
index 000000000000..b5ae54c5bc00
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/DataCategoryConstants.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2024 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.asllib.util;
+
+import com.android.asllib.marshallable.DataCategory;
+import com.android.asllib.marshallable.DataType;
+import com.android.asllib.marshallable.SafetyLabels;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Constants for determining valid {@link String} data types for usage within {@link SafetyLabels},
+ * {@link DataCategory}, and {@link DataType}
+ */
+public class DataCategoryConstants {
+
+ public static final String CATEGORY_PERSONAL = "personal";
+ public static final String CATEGORY_FINANCIAL = "financial";
+ public static final String CATEGORY_LOCATION = "location";
+ public static final String CATEGORY_EMAIL_TEXT_MESSAGE = "email_text_message";
+ public static final String CATEGORY_PHOTO_VIDEO = "photo_video";
+ public static final String CATEGORY_AUDIO = "audio";
+ public static final String CATEGORY_STORAGE = "storage";
+ public static final String CATEGORY_HEALTH_FITNESS = "health_fitness";
+ public static final String CATEGORY_CONTACTS = "contacts";
+ public static final String CATEGORY_CALENDAR = "calendar";
+ public static final String CATEGORY_IDENTIFIERS = "identifiers";
+ public static final String CATEGORY_APP_PERFORMANCE = "app_performance";
+ public static final String CATEGORY_ACTIONS_IN_APP = "actions_in_app";
+ public static final String CATEGORY_SEARCH_AND_BROWSING = "search_and_browsing";
+
+ /** Set of valid categories */
+ public static final Set<String> VALID_CATEGORIES =
+ Collections.unmodifiableSet(
+ new HashSet<>(
+ Arrays.asList(
+ CATEGORY_PERSONAL,
+ CATEGORY_FINANCIAL,
+ CATEGORY_LOCATION,
+ CATEGORY_EMAIL_TEXT_MESSAGE,
+ CATEGORY_PHOTO_VIDEO,
+ CATEGORY_AUDIO,
+ CATEGORY_STORAGE,
+ CATEGORY_HEALTH_FITNESS,
+ CATEGORY_CONTACTS,
+ CATEGORY_CALENDAR,
+ CATEGORY_IDENTIFIERS,
+ CATEGORY_APP_PERFORMANCE,
+ CATEGORY_ACTIONS_IN_APP,
+ CATEGORY_SEARCH_AND_BROWSING)));
+
+ /** Returns {@link Set} of valid {@link String} category keys */
+ public static Set<String> getValidDataCategories() {
+ return VALID_CATEGORIES;
+ }
+
+ private DataCategoryConstants() {
+ /* do nothing - hide constructor */
+ }
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/DataTypeConstants.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/DataTypeConstants.java
new file mode 100644
index 000000000000..358d575c1d56
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/DataTypeConstants.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2024 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.asllib.util;
+
+import com.android.asllib.marshallable.DataCategory;
+import com.android.asllib.marshallable.DataType;
+import com.android.asllib.marshallable.SafetyLabels;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Constants for determining valid {@link String} data types for usage within {@link SafetyLabels},
+ * {@link DataCategory}, and {@link DataType}
+ */
+public class DataTypeConstants {
+ /** Data types for {@link DataCategoryConstants.CATEGORY_PERSONAL} */
+ public static final String TYPE_NAME = "name";
+
+ public static final String TYPE_EMAIL_ADDRESS = "email_address";
+ public static final String TYPE_PHYSICAL_ADDRESS = "physical_address";
+ public static final String TYPE_PHONE_NUMBER = "phone_number";
+ public static final String TYPE_RACE_ETHNICITY = "race_ethnicity";
+ public static final String TYPE_POLITICAL_OR_RELIGIOUS_BELIEFS =
+ "political_or_religious_beliefs";
+ public static final String TYPE_SEXUAL_ORIENTATION_OR_GENDER_IDENTITY =
+ "sexual_orientation_or_gender_identity";
+ public static final String TYPE_PERSONAL_IDENTIFIERS = "personal_identifiers";
+ public static final String TYPE_OTHER = "other";
+
+ /** Data types for {@link DataCategoryConstants.CATEGORY_FINANCIAL} */
+ public static final String TYPE_CARD_BANK_ACCOUNT = "card_bank_account";
+
+ public static final String TYPE_PURCHASE_HISTORY = "purchase_history";
+ public static final String TYPE_CREDIT_SCORE = "credit_score";
+ public static final String TYPE_FINANCIAL_OTHER = "other";
+
+ /** Data types for {@link DataCategoryConstants.CATEGORY_LOCATION} */
+ public static final String TYPE_APPROX_LOCATION = "approx_location";
+
+ public static final String TYPE_PRECISE_LOCATION = "precise_location";
+
+ /** Data types for {@link DataCategoryConstants.CATEGORY_EMAIL_TEXT_MESSAGE} */
+ public static final String TYPE_EMAILS = "emails";
+
+ public static final String TYPE_TEXT_MESSAGES = "text_messages";
+ public static final String TYPE_EMAIL_TEXT_MESSAGE_OTHER = "other";
+
+ /** Data types for {@link DataCategoryConstants.CATEGORY_PHOTO_VIDEO} */
+ public static final String TYPE_PHOTOS = "photos";
+
+ public static final String TYPE_VIDEOS = "videos";
+
+ /** Data types for {@link DataCategoryConstants.CATEGORY_AUDIO} */
+ public static final String TYPE_SOUND_RECORDINGS = "sound_recordings";
+
+ public static final String TYPE_MUSIC_FILES = "music_files";
+ public static final String TYPE_AUDIO_OTHER = "other";
+
+ /** Data types for {@link DataCategoryConstants.CATEGORY_STORAGE} */
+ public static final String TYPE_FILES_DOCS = "files_docs";
+
+ /** Data types for {@link DataCategoryConstants.CATEGORY_HEALTH_FITNESS} */
+ public static final String TYPE_HEALTH = "health";
+
+ public static final String TYPE_FITNESS = "fitness";
+
+ /** Data types for {@link DataCategoryConstants.CATEGORY_CONTACTS} */
+ public static final String TYPE_CONTACTS = "contacts";
+
+ /** Data types for {@link DataCategoryConstants.CATEGORY_CALENDAR} */
+ public static final String TYPE_CALENDAR = "calendar";
+
+ /** Data types for {@link DataCategoryConstants.CATEGORY_IDENTIFIERS} */
+ public static final String TYPE_IDENTIFIERS_OTHER = "other";
+
+ /** Data types for {@link DataCategoryConstants.CATEGORY_APP_PERFORMANCE} */
+ public static final String TYPE_CRASH_LOGS = "crash_logs";
+
+ public static final String TYPE_PERFORMANCE_DIAGNOSTICS = "performance_diagnostics";
+ public static final String TYPE_APP_PERFORMANCE_OTHER = "other";
+
+ /** Data types for {@link DataCategoryConstants.CATEGORY_ACTIONS_IN_APP} */
+ public static final String TYPE_USER_INTERACTION = "user_interaction";
+
+ public static final String TYPE_IN_APP_SEARCH_HISTORY = "in_app_search_history";
+ public static final String TYPE_INSTALLED_APPS = "installed_apps";
+ public static final String TYPE_USER_GENERATED_CONTENT = "user_generated_content";
+ public static final String TYPE_ACTIONS_IN_APP_OTHER = "other";
+
+ /** Data types for {@link DataCategoryConstants.CATEGORY_SEARCH_AND_BROWSING} */
+ public static final String TYPE_WEB_BROWSING_HISTORY = "web_browsing_history";
+
+ /** Set of valid categories */
+ private static final Map<String, Set<String>> VALID_DATA_TYPES =
+ new ImmutableMap.Builder<String, Set<String>>()
+ .put(
+ DataCategoryConstants.CATEGORY_PERSONAL,
+ ImmutableSet.of(
+ DataTypeConstants.TYPE_NAME,
+ DataTypeConstants.TYPE_EMAIL_ADDRESS,
+ DataTypeConstants.TYPE_PHYSICAL_ADDRESS,
+ DataTypeConstants.TYPE_PHONE_NUMBER,
+ DataTypeConstants.TYPE_RACE_ETHNICITY,
+ DataTypeConstants.TYPE_POLITICAL_OR_RELIGIOUS_BELIEFS,
+ DataTypeConstants.TYPE_SEXUAL_ORIENTATION_OR_GENDER_IDENTITY,
+ DataTypeConstants.TYPE_PERSONAL_IDENTIFIERS,
+ DataTypeConstants.TYPE_OTHER))
+ .put(
+ DataCategoryConstants.CATEGORY_FINANCIAL,
+ ImmutableSet.of(
+ DataTypeConstants.TYPE_CARD_BANK_ACCOUNT,
+ DataTypeConstants.TYPE_PURCHASE_HISTORY,
+ DataTypeConstants.TYPE_CREDIT_SCORE,
+ DataTypeConstants.TYPE_OTHER))
+ .put(
+ DataCategoryConstants.CATEGORY_LOCATION,
+ ImmutableSet.of(
+ DataTypeConstants.TYPE_APPROX_LOCATION,
+ DataTypeConstants.TYPE_PRECISE_LOCATION))
+ .put(
+ DataCategoryConstants.CATEGORY_EMAIL_TEXT_MESSAGE,
+ ImmutableSet.of(
+ DataTypeConstants.TYPE_EMAILS,
+ DataTypeConstants.TYPE_TEXT_MESSAGES,
+ DataTypeConstants.TYPE_OTHER))
+ .put(
+ DataCategoryConstants.CATEGORY_PHOTO_VIDEO,
+ ImmutableSet.of(
+ DataTypeConstants.TYPE_PHOTOS, DataTypeConstants.TYPE_VIDEOS))
+ .put(
+ DataCategoryConstants.CATEGORY_AUDIO,
+ ImmutableSet.of(
+ DataTypeConstants.TYPE_SOUND_RECORDINGS,
+ DataTypeConstants.TYPE_MUSIC_FILES,
+ DataTypeConstants.TYPE_OTHER))
+ .put(
+ DataCategoryConstants.CATEGORY_STORAGE,
+ ImmutableSet.of(DataTypeConstants.TYPE_FILES_DOCS))
+ .put(
+ DataCategoryConstants.CATEGORY_HEALTH_FITNESS,
+ ImmutableSet.of(
+ DataTypeConstants.TYPE_HEALTH, DataTypeConstants.TYPE_FITNESS))
+ .put(
+ DataCategoryConstants.CATEGORY_CONTACTS,
+ ImmutableSet.of(DataTypeConstants.TYPE_CONTACTS))
+ .put(
+ DataCategoryConstants.CATEGORY_CALENDAR,
+ ImmutableSet.of(DataTypeConstants.TYPE_CALENDAR))
+ .put(
+ DataCategoryConstants.CATEGORY_IDENTIFIERS,
+ ImmutableSet.of(DataTypeConstants.TYPE_OTHER))
+ .put(
+ DataCategoryConstants.CATEGORY_APP_PERFORMANCE,
+ ImmutableSet.of(
+ DataTypeConstants.TYPE_CRASH_LOGS,
+ DataTypeConstants.TYPE_PERFORMANCE_DIAGNOSTICS,
+ DataTypeConstants.TYPE_OTHER))
+ .put(
+ DataCategoryConstants.CATEGORY_ACTIONS_IN_APP,
+ ImmutableSet.of(
+ DataTypeConstants.TYPE_USER_INTERACTION,
+ DataTypeConstants.TYPE_IN_APP_SEARCH_HISTORY,
+ DataTypeConstants.TYPE_INSTALLED_APPS,
+ DataTypeConstants.TYPE_USER_GENERATED_CONTENT,
+ DataTypeConstants.TYPE_OTHER))
+ .put(
+ DataCategoryConstants.CATEGORY_SEARCH_AND_BROWSING,
+ ImmutableSet.of(DataTypeConstants.TYPE_WEB_BROWSING_HISTORY))
+ .buildOrThrow();
+
+ /** Returns {@link Set} of valid {@link String} category keys */
+ public static Map<String, Set<String>> getValidDataTypes() {
+ return VALID_DATA_TYPES;
+ }
+
+ private DataTypeConstants() {
+ /* do nothing - hide constructor */
+ }
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/MalformedXmlException.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/MalformedXmlException.java
new file mode 100644
index 000000000000..216df56c453e
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/MalformedXmlException.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 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.asllib.util;
+
+public class MalformedXmlException extends Exception {
+ /** Constructs an {@code MalformedXmlException} with no detail message. */
+ public MalformedXmlException() {
+ super();
+ }
+
+ /**
+ * Constructs an {@code MalformedXmlException} with the specified detail message.
+ *
+ * @param s the detail message.
+ */
+ public MalformedXmlException(String s) {
+ super(s);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/XmlUtils.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/XmlUtils.java
new file mode 100644
index 000000000000..97cbc3950490
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/XmlUtils.java
@@ -0,0 +1,496 @@
+/*
+ * Copyright (C) 2024 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.asllib.util;
+
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class XmlUtils {
+ public static final String DATA_TYPE_SEPARATOR = "_data_type_";
+
+ public static final String HR_TAG_APP_METADATA_BUNDLES = "app-metadata-bundles";
+ public static final String HR_TAG_SYSTEM_APP_SAFETY_LABEL = "system-app-safety-label";
+ public static final String HR_TAG_SAFETY_LABELS = "safety-labels";
+ public static final String HR_TAG_TRANSPARENCY_INFO = "transparency-info";
+ public static final String HR_TAG_DEVELOPER_INFO = "developer-info";
+ public static final String HR_TAG_APP_INFO = "app-info";
+ public static final String HR_TAG_DATA_LABELS = "data-labels";
+ public static final String HR_TAG_SECURITY_LABELS = "security-labels";
+ public static final String HR_TAG_THIRD_PARTY_VERIFICATION = "third-party-verification";
+ public static final String HR_TAG_DATA_ACCESSED = "data-accessed";
+ public static final String HR_TAG_DATA_COLLECTED = "data-collected";
+ public static final String HR_TAG_DATA_COLLECTED_EPHEMERAL = "data-collected-ephemeral";
+ public static final String HR_TAG_DATA_SHARED = "data-shared";
+ public static final String HR_ATTR_NAME = "name";
+ public static final String HR_ATTR_EMAIL = "email";
+ public static final String HR_ATTR_ADDRESS = "address";
+ public static final String HR_ATTR_COUNTRY_REGION = "countryRegion";
+ public static final String HR_ATTR_DEVELOPER_RELATIONSHIP = "relationship";
+ public static final String HR_ATTR_WEBSITE = "website";
+ public static final String HR_ATTR_APP_DEVELOPER_REGISTRY_ID = "registryId";
+ public static final String HR_ATTR_DATA_CATEGORY = "dataCategory";
+ public static final String HR_ATTR_DATA_TYPE = "dataType";
+ public static final String HR_ATTR_IS_COLLECTION_OPTIONAL = "isCollectionOptional";
+ public static final String HR_ATTR_IS_SHARING_OPTIONAL = "isSharingOptional";
+ public static final String HR_ATTR_IS_DATA_DELETABLE = "isDataDeletable";
+ public static final String HR_ATTR_IS_DATA_ENCRYPTED = "isDataEncrypted";
+ // public static final String HR_ATTR_EPHEMERAL = "ephemeral";
+ public static final String HR_ATTR_PURPOSES = "purposes";
+ public static final String HR_ATTR_VERSION = "version";
+ public static final String HR_ATTR_URL = "url";
+ public static final String HR_ATTR_DECLARATION = "declaration";
+ public static final String HR_ATTR_TITLE = "title";
+ public static final String HR_ATTR_DESCRIPTION = "description";
+ public static final String HR_ATTR_CONTAINS_ADS = "containsAds";
+ public static final String HR_ATTR_OBEY_APS = "obeyAps";
+ public static final String HR_ATTR_ADS_FINGERPRINTING = "adsFingerprinting";
+ public static final String HR_ATTR_SECURITY_FINGERPRINTING = "securityFingerprinting";
+ public static final String HR_ATTR_PRIVACY_POLICY = "privacyPolicy";
+ public static final String HR_ATTR_SECURITY_ENDPOINTS = "securityEndpoints";
+ public static final String HR_ATTR_FIRST_PARTY_ENDPOINTS = "firstPartyEndpoints";
+ public static final String HR_ATTR_SERVICE_PROVIDER_ENDPOINTS = "serviceProviderEndpoints";
+ public static final String HR_ATTR_CATEGORY = "category";
+
+ public static final String OD_TAG_BUNDLE = "bundle";
+ public static final String OD_TAG_PBUNDLE_AS_MAP = "pbundle_as_map";
+ public static final String OD_TAG_BOOLEAN = "boolean";
+ public static final String OD_TAG_LONG = "long";
+ public static final String OD_TAG_STRING = "string";
+ public static final String OD_TAG_INT_ARRAY = "int-array";
+ public static final String OD_TAG_STRING_ARRAY = "string-array";
+ public static final String OD_TAG_ITEM = "item";
+ public static final String OD_ATTR_NAME = "name";
+ public static final String OD_ATTR_VALUE = "value";
+ public static final String OD_ATTR_NUM = "num";
+ public static final String OD_NAME_SAFETY_LABELS = "safety_labels";
+ public static final String OD_NAME_TRANSPARENCY_INFO = "transparency_info";
+ public static final String OD_NAME_DEVELOPER_INFO = "developer_info";
+ public static final String OD_NAME_NAME = "name";
+ public static final String OD_NAME_EMAIL = "email";
+ public static final String OD_NAME_ADDRESS = "address";
+ public static final String OD_NAME_COUNTRY_REGION = "country_region";
+ public static final String OD_NAME_DEVELOPER_RELATIONSHIP = "relationship";
+ public static final String OD_NAME_WEBSITE = "website";
+ public static final String OD_NAME_APP_DEVELOPER_REGISTRY_ID = "app_developer_registry_id";
+ public static final String OD_NAME_APP_INFO = "app_info";
+ public static final String OD_NAME_TITLE = "title";
+ public static final String OD_NAME_DESCRIPTION = "description";
+ public static final String OD_NAME_CONTAINS_ADS = "contains_ads";
+ public static final String OD_NAME_OBEY_APS = "obey_aps";
+ public static final String OD_NAME_ADS_FINGERPRINTING = "ads_fingerprinting";
+ public static final String OD_NAME_SECURITY_FINGERPRINTING = "security_fingerprinting";
+ public static final String OD_NAME_PRIVACY_POLICY = "privacy_policy";
+ public static final String OD_NAME_SECURITY_ENDPOINT = "security_endpoint";
+ public static final String OD_NAME_FIRST_PARTY_ENDPOINT = "first_party_endpoint";
+ public static final String OD_NAME_SERVICE_PROVIDER_ENDPOINT = "service_provider_endpoint";
+ public static final String OD_NAME_CATEGORY = "category";
+ public static final String OD_NAME_VERSION = "version";
+ public static final String OD_NAME_URL = "url";
+ public static final String OD_NAME_DECLARATION = "declaration";
+ public static final String OD_NAME_SYSTEM_APP_SAFETY_LABEL = "system_app_safety_label";
+ public static final String OD_NAME_SECURITY_LABELS = "security_labels";
+ public static final String OD_NAME_THIRD_PARTY_VERIFICATION = "third_party_verification";
+ public static final String OD_NAME_DATA_LABELS = "data_labels";
+ public static final String OD_NAME_DATA_ACCESSED = "data_accessed";
+ public static final String OD_NAME_DATA_COLLECTED = "data_collected";
+ public static final String OD_NAME_DATA_SHARED = "data_shared";
+ public static final String OD_NAME_PURPOSES = "purposes";
+ public static final String OD_NAME_IS_COLLECTION_OPTIONAL = "is_collection_optional";
+ public static final String OD_NAME_IS_SHARING_OPTIONAL = "is_sharing_optional";
+ public static final String OD_NAME_IS_DATA_DELETABLE = "is_data_deletable";
+ public static final String OD_NAME_IS_DATA_ENCRYPTED = "is_data_encrypted";
+ public static final String OD_NAME_EPHEMERAL = "ephemeral";
+
+ public static final String TRUE_STR = "true";
+ public static final String FALSE_STR = "false";
+
+ /** Gets the top-level children with the tag name.. */
+ public static List<Element> getChildrenByTagName(Node parentEle, String tagName) {
+ var elements = XmlUtils.asElementList(parentEle.getChildNodes());
+ return elements.stream().filter(e -> e.getTagName().equals(tagName)).toList();
+ }
+
+ /**
+ * Gets the single {@link Element} within {@param parentEle} and having the {@param tagName}.
+ */
+ public static Element getSingleChildElement(Node parentEle, String tagName, boolean required)
+ throws MalformedXmlException {
+ String parentTagNameForErrorMsg =
+ (parentEle instanceof Element) ? ((Element) parentEle).getTagName() : "Node";
+ var elements = getChildrenByTagName(parentEle, tagName);
+
+ if (elements.size() > 1) {
+ throw new MalformedXmlException(
+ String.format(
+ "Expected 1 %s in %s but got %s.",
+ tagName, parentTagNameForErrorMsg, elements.size()));
+ } else if (elements.isEmpty()) {
+ if (required) {
+ throw new MalformedXmlException(
+ String.format(
+ "Expected 1 %s in %s but got 0.",
+ tagName, parentTagNameForErrorMsg));
+ } else {
+ return null;
+ }
+ }
+ return elements.get(0);
+ }
+
+ /** Gets the single {@link Element} within {@param elements}. */
+ public static Element getSingleElement(List<Element> elements) {
+ if (elements.size() != 1) {
+ throw new IllegalStateException(
+ String.format("Expected 1 element in list but got %s.", elements.size()));
+ }
+ return elements.get(0);
+ }
+
+ /** Converts {@param nodeList} into List of {@link Element}. */
+ public static List<Element> asElementList(NodeList nodeList) {
+ List<Element> elementList = new ArrayList<Element>();
+ for (int i = 0; i < nodeList.getLength(); i++) {
+ var elementAsNode = nodeList.item(i);
+ if (elementAsNode instanceof Element) {
+ elementList.add(((Element) elementAsNode));
+ }
+ }
+ return elementList;
+ }
+
+ /** Appends {@param children} to the {@param ele}. */
+ public static void appendChildren(Element ele, List<Element> children) {
+ for (Element c : children) {
+ ele.appendChild(c);
+ }
+ }
+
+ /** Gets the Boolean from the String value. */
+ private static Boolean fromString(String s) {
+ if (s == null) {
+ return null;
+ }
+ if (s.equals(TRUE_STR)) {
+ return true;
+ } else if (s.equals(FALSE_STR)) {
+ return false;
+ }
+ return null;
+ }
+
+ /** Creates an on-device PBundle DOM Element with the given attribute name. */
+ public static Element createPbundleEleWithName(Document doc, String name) {
+ var ele = doc.createElement(XmlUtils.OD_TAG_PBUNDLE_AS_MAP);
+ ele.setAttribute(XmlUtils.OD_ATTR_NAME, name);
+ return ele;
+ }
+
+ /** Create an on-device Boolean DOM Element with the given attribute name. */
+ public static Element createOdBooleanEle(Document doc, String name, boolean b) {
+ var ele = doc.createElement(XmlUtils.OD_TAG_BOOLEAN);
+ ele.setAttribute(XmlUtils.OD_ATTR_NAME, name);
+ ele.setAttribute(XmlUtils.OD_ATTR_VALUE, String.valueOf(b));
+ return ele;
+ }
+
+ /** Sets human-readable bool attribute if non-null. */
+ public static void maybeSetHrBoolAttr(Element ele, String attrName, Boolean b) {
+ if (b != null) {
+ ele.setAttribute(attrName, String.valueOf(b));
+ }
+ }
+
+ /** Create an on-device Long DOM Element with the given attribute name. */
+ public static Element createOdLongEle(Document doc, String name, long l) {
+ var ele = doc.createElement(XmlUtils.OD_TAG_LONG);
+ ele.setAttribute(XmlUtils.OD_ATTR_NAME, name);
+ ele.setAttribute(XmlUtils.OD_ATTR_VALUE, String.valueOf(l));
+ return ele;
+ }
+
+ /** Create an on-device Long DOM Element with the given attribute name. */
+ public static Element createOdStringEle(Document doc, String name, String val) {
+ var ele = doc.createElement(XmlUtils.OD_TAG_STRING);
+ ele.setAttribute(XmlUtils.OD_ATTR_NAME, name);
+ ele.setAttribute(XmlUtils.OD_ATTR_VALUE, val);
+ return ele;
+ }
+
+ /** Create OD style array DOM Element, which can represent any time but is stored as Strings. */
+ public static Element createOdArray(
+ Document doc, String arrayTag, String arrayName, List<String> arrayVals) {
+ Element arrEle = doc.createElement(arrayTag);
+ arrEle.setAttribute(XmlUtils.OD_ATTR_NAME, arrayName);
+ arrEle.setAttribute(XmlUtils.OD_ATTR_NUM, String.valueOf(arrayVals.size()));
+ for (String s : arrayVals) {
+ Element itemEle = doc.createElement(XmlUtils.OD_TAG_ITEM);
+ itemEle.setAttribute(XmlUtils.OD_ATTR_VALUE, s);
+ arrEle.appendChild(itemEle);
+ }
+ return arrEle;
+ }
+
+ /** Returns whether the String is null or empty. */
+ public static boolean isNullOrEmpty(String s) {
+ return s == null || s.isEmpty();
+ }
+
+ /** Tries getting required version attribute and throws exception if it doesn't exist */
+ public static Long tryGetVersion(Element ele) throws MalformedXmlException {
+ long version;
+ try {
+ version = Long.parseLong(ele.getAttribute(XmlUtils.HR_ATTR_VERSION));
+ } catch (Exception e) {
+ throw new MalformedXmlException(
+ String.format(
+ "Malformed or missing required version in: %s", ele.getTagName()));
+ }
+ return version;
+ }
+
+ /** Gets a pipeline-split attribute. */
+ public static List<String> getPipelineSplitAttr(Element ele, String attrName, boolean required)
+ throws MalformedXmlException {
+ List<String> list = Arrays.stream(ele.getAttribute(attrName).split("\\|")).toList();
+ if ((list.isEmpty() || list.get(0).isEmpty()) && required) {
+ throw new MalformedXmlException(
+ String.format(
+ "Delimited string %s was required but missing, in %s.",
+ attrName, ele.getTagName()));
+ }
+ return list;
+ }
+
+ /** Gets a Boolean attribute. */
+ public static Boolean getBoolAttr(Element ele, String attrName, boolean required)
+ throws MalformedXmlException {
+ Boolean b = XmlUtils.fromString(ele.getAttribute(attrName));
+ if (b == null && required) {
+ throw new MalformedXmlException(
+ String.format(
+ "Boolean %s was required but missing, in %s.",
+ attrName, ele.getTagName()));
+ }
+ return b;
+ }
+
+ /** Gets a Boolean attribute. */
+ public static Boolean getOdBoolEle(Element ele, String nameName, boolean required)
+ throws MalformedXmlException {
+ List<Element> boolEles =
+ XmlUtils.getChildrenByTagName(ele, XmlUtils.OD_TAG_BOOLEAN).stream()
+ .filter(e -> e.getAttribute(XmlUtils.OD_ATTR_NAME).equals(nameName))
+ .toList();
+ if (boolEles.size() > 1) {
+ throw new MalformedXmlException(
+ String.format(
+ "Found more than one boolean %s in %s.", nameName, ele.getTagName()));
+ }
+ if (boolEles.isEmpty()) {
+ if (required) {
+ throw new MalformedXmlException(
+ String.format("Found no boolean %s in %s.", nameName, ele.getTagName()));
+ }
+ return null;
+ }
+ Element boolEle = boolEles.get(0);
+
+ Boolean b = XmlUtils.fromString(boolEle.getAttribute(XmlUtils.OD_ATTR_VALUE));
+ if (b == null && required) {
+ throw new MalformedXmlException(
+ String.format(
+ "Boolean %s was required but missing, in %s.",
+ nameName, ele.getTagName()));
+ }
+ return b;
+ }
+
+ /** Gets an on-device Long attribute. */
+ public static Long getOdLongEle(Element ele, String nameName, boolean required)
+ throws MalformedXmlException {
+ List<Element> longEles =
+ XmlUtils.getChildrenByTagName(ele, XmlUtils.OD_TAG_LONG).stream()
+ .filter(e -> e.getAttribute(XmlUtils.OD_ATTR_NAME).equals(nameName))
+ .toList();
+ if (longEles.size() > 1) {
+ throw new MalformedXmlException(
+ String.format(
+ "Found more than one long %s in %s.", nameName, ele.getTagName()));
+ }
+ if (longEles.isEmpty()) {
+ if (required) {
+ throw new MalformedXmlException(
+ String.format("Found no long %s in %s.", nameName, ele.getTagName()));
+ }
+ return null;
+ }
+ Element longEle = longEles.get(0);
+ Long l = null;
+ try {
+ l = Long.parseLong(longEle.getAttribute(XmlUtils.OD_ATTR_VALUE));
+ } catch (NumberFormatException e) {
+ throw new MalformedXmlException(
+ String.format(
+ "%s in %s was not formatted as long", nameName, ele.getTagName()));
+ }
+ return l;
+ }
+
+ /** Gets an on-device String attribute. */
+ public static String getOdStringEle(Element ele, String nameName, boolean required)
+ throws MalformedXmlException {
+ List<Element> eles =
+ XmlUtils.getChildrenByTagName(ele, XmlUtils.OD_TAG_STRING).stream()
+ .filter(e -> e.getAttribute(XmlUtils.OD_ATTR_NAME).equals(nameName))
+ .toList();
+ if (eles.size() > 1) {
+ throw new MalformedXmlException(
+ String.format(
+ "Found more than one string %s in %s.", nameName, ele.getTagName()));
+ }
+ if (eles.isEmpty()) {
+ if (required) {
+ throw new MalformedXmlException(
+ String.format("Found no string %s in %s.", nameName, ele.getTagName()));
+ }
+ return null;
+ }
+ String str = eles.get(0).getAttribute(XmlUtils.OD_ATTR_VALUE);
+ if (XmlUtils.isNullOrEmpty(str) && required) {
+ throw new MalformedXmlException(
+ String.format(
+ "%s in %s was empty or missing value", nameName, ele.getTagName()));
+ }
+ return str;
+ }
+
+ /** Gets a OD Pbundle Element attribute with the specified name. */
+ public static Element getOdPbundleWithName(Element ele, String nameName, boolean required)
+ throws MalformedXmlException {
+ List<Element> eles =
+ XmlUtils.getChildrenByTagName(ele, XmlUtils.OD_TAG_PBUNDLE_AS_MAP).stream()
+ .filter(e -> e.getAttribute(XmlUtils.OD_ATTR_NAME).equals(nameName))
+ .toList();
+ if (eles.size() > 1) {
+ throw new MalformedXmlException(
+ String.format(
+ "Found more than one pbundle %s in %s.", nameName, ele.getTagName()));
+ }
+ if (eles.isEmpty()) {
+ if (required) {
+ throw new MalformedXmlException(
+ String.format("Found no pbundle %s in %s.", nameName, ele.getTagName()));
+ }
+ return null;
+ }
+ return eles.get(0);
+ }
+
+ /** Gets a required String attribute. */
+ public static String getStringAttr(Element ele, String attrName) throws MalformedXmlException {
+ return getStringAttr(ele, attrName, true);
+ }
+
+ /** Gets a String attribute; throws exception if required and non-existent. */
+ public static String getStringAttr(Element ele, String attrName, boolean required)
+ throws MalformedXmlException {
+ String s = ele.getAttribute(attrName);
+ if (isNullOrEmpty(s)) {
+ if (required) {
+ throw new MalformedXmlException(
+ String.format(
+ "Malformed or missing required %s in: %s",
+ attrName, ele.getTagName()));
+ } else {
+ return null;
+ }
+ }
+ return s;
+ }
+
+ /** Gets on-device style int array. */
+ public static List<Integer> getOdIntArray(Element ele, String nameName, boolean required)
+ throws MalformedXmlException {
+ List<Element> intArrayEles =
+ XmlUtils.getChildrenByTagName(ele, XmlUtils.OD_TAG_INT_ARRAY).stream()
+ .filter(e -> e.getAttribute(XmlUtils.OD_ATTR_NAME).equals(nameName))
+ .toList();
+ if (intArrayEles.size() > 1) {
+ throw new MalformedXmlException(
+ String.format("Found more than one %s in %s.", nameName, ele.getTagName()));
+ }
+ if (intArrayEles.isEmpty()) {
+ if (required) {
+ throw new MalformedXmlException(
+ String.format("Found no %s in %s.", nameName, ele.getTagName()));
+ }
+ return null;
+ }
+ Element intArrayEle = intArrayEles.get(0);
+ List<Element> itemEles = XmlUtils.getChildrenByTagName(intArrayEle, XmlUtils.OD_TAG_ITEM);
+ List<Integer> ints = new ArrayList<Integer>();
+ for (Element itemEle : itemEles) {
+ ints.add(Integer.parseInt(XmlUtils.getStringAttr(itemEle, XmlUtils.OD_ATTR_VALUE)));
+ }
+ return ints;
+ }
+
+ /** Gets on-device style String array. */
+ public static List<String> getOdStringArray(Element ele, String nameName, boolean required)
+ throws MalformedXmlException {
+ List<Element> arrayEles =
+ XmlUtils.getChildrenByTagName(ele, XmlUtils.OD_TAG_STRING_ARRAY).stream()
+ .filter(e -> e.getAttribute(XmlUtils.OD_ATTR_NAME).equals(nameName))
+ .toList();
+ if (arrayEles.size() > 1) {
+ throw new MalformedXmlException(
+ String.format(
+ "Found more than one string array %s in %s.",
+ nameName, ele.getTagName()));
+ }
+ if (arrayEles.isEmpty()) {
+ if (required) {
+ throw new MalformedXmlException(
+ String.format(
+ "Found no string array %s in %s.", nameName, ele.getTagName()));
+ }
+ return null;
+ }
+ Element arrayEle = arrayEles.get(0);
+ List<Element> itemEles = XmlUtils.getChildrenByTagName(arrayEle, XmlUtils.OD_TAG_ITEM);
+ List<String> strs = new ArrayList<String>();
+ for (Element itemEle : itemEles) {
+ strs.add(XmlUtils.getStringAttr(itemEle, XmlUtils.OD_ATTR_VALUE, true));
+ }
+ return strs;
+ }
+
+ /**
+ * Utility method for making a List from one element, to support easier refactoring if needed.
+ * For example, List.of() doesn't support null elements.
+ */
+ public static List<Element> listOf(Element e) {
+ return Arrays.asList(e);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/AllTests.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/AllTests.java
new file mode 100644
index 000000000000..dbeeb496144a
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/AllTests.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 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.asllib;
+
+import com.android.asllib.marshallable.AndroidSafetyLabelTest;
+import com.android.asllib.marshallable.AppInfoTest;
+import com.android.asllib.marshallable.DataLabelsTest;
+import com.android.asllib.marshallable.DataTypeEqualityTest;
+import com.android.asllib.marshallable.DeveloperInfoTest;
+import com.android.asllib.marshallable.SafetyLabelsTest;
+import com.android.asllib.marshallable.SecurityLabelsTest;
+import com.android.asllib.marshallable.SystemAppSafetyLabelTest;
+import com.android.asllib.marshallable.ThirdPartyVerificationTest;
+import com.android.asllib.marshallable.TransparencyInfoTest;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+ AslgenTests.class,
+ AndroidSafetyLabelTest.class,
+ AppInfoTest.class,
+ // DataCategoryTest.class,
+ DataLabelsTest.class,
+ DataTypeEqualityTest.class,
+ DeveloperInfoTest.class,
+ SafetyLabelsTest.class,
+ SecurityLabelsTest.class,
+ SystemAppSafetyLabelTest.class,
+ ThirdPartyVerificationTest.class,
+ TransparencyInfoTest.class
+})
+public class AllTests {}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/AslgenTests.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/AslgenTests.java
new file mode 100644
index 000000000000..5d1d45a9a29b
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/AslgenTests.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 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.asllib;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.asllib.marshallable.AndroidSafetyLabel;
+import com.android.asllib.testutils.TestUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public class AslgenTests {
+ private static final String VALID_MAPPINGS_PATH = "com/android/asllib/validmappings";
+ private static final List<String> VALID_MAPPINGS_SUBDIRS = List.of("general");
+ private static final String HR_XML_FILENAME = "hr.xml";
+ private static final String OD_XML_FILENAME = "od.xml";
+
+ /** Logic for setting up tests (empty if not yet needed). */
+ public static void main(String[] params) throws Exception {}
+
+ /** Tests valid mappings between HR and OD. */
+ @Test
+ public void testValidMappings() throws Exception {
+ System.out.println("start testing valid mappings.");
+
+ for (String subdir : VALID_MAPPINGS_SUBDIRS) {
+ Path hrPath = Paths.get(VALID_MAPPINGS_PATH, subdir, HR_XML_FILENAME);
+ Path odPath = Paths.get(VALID_MAPPINGS_PATH, subdir, OD_XML_FILENAME);
+
+ System.out.println("hr path: " + hrPath.toString());
+ System.out.println("od path: " + odPath.toString());
+
+ InputStream hrStream =
+ getClass().getClassLoader().getResourceAsStream(hrPath.toString());
+ String hrContents =
+ TestUtils.getFormattedXml(
+ new String(hrStream.readAllBytes(), StandardCharsets.UTF_8), false);
+ InputStream odStream =
+ getClass().getClassLoader().getResourceAsStream(odPath.toString());
+ String odContents =
+ TestUtils.getFormattedXml(
+ new String(odStream.readAllBytes(), StandardCharsets.UTF_8), false);
+ AndroidSafetyLabel aslFromHr =
+ AslConverter.readFromString(hrContents, AslConverter.Format.HUMAN_READABLE);
+ String aslToOdStr =
+ TestUtils.getFormattedXml(
+ AslConverter.getXmlAsString(aslFromHr, AslConverter.Format.ON_DEVICE),
+ false);
+ AndroidSafetyLabel aslFromOd =
+ AslConverter.readFromString(odContents, AslConverter.Format.ON_DEVICE);
+ String aslToHrStr =
+ TestUtils.getFormattedXml(
+ AslConverter.getXmlAsString(
+ aslFromOd, AslConverter.Format.HUMAN_READABLE),
+ false);
+
+ System.out.println("od expected: " + odContents);
+ System.out.println("asl to od: " + aslToOdStr);
+ assertEquals(odContents, aslToOdStr);
+
+ System.out.println("hr expected: " + hrContents);
+ System.out.println("asl to hr: " + aslToHrStr);
+ assertEquals(hrContents, aslToHrStr);
+ }
+ }
+}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AndroidSafetyLabelTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AndroidSafetyLabelTest.java
new file mode 100644
index 000000000000..61a78232801c
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AndroidSafetyLabelTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 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.asllib.marshallable;
+
+import com.android.asllib.testutils.TestUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class AndroidSafetyLabelTest {
+ private static final String ANDROID_SAFETY_LABEL_HR_PATH =
+ "com/android/asllib/androidsafetylabel/hr";
+ private static final String ANDROID_SAFETY_LABEL_OD_PATH =
+ "com/android/asllib/androidsafetylabel/od";
+
+ private static final String MISSING_VERSION_FILE_NAME = "missing-version.xml";
+ private static final String VALID_EMPTY_FILE_NAME = "valid-empty.xml";
+ private static final String WITH_SAFETY_LABELS_FILE_NAME = "with-safety-labels.xml";
+ private static final String WITH_SYSTEM_APP_SAFETY_LABEL_FILE_NAME =
+ "with-system-app-safety-label.xml";
+ private static final String WITH_TRANSPARENCY_INFO_FILE_NAME = "with-transparency-info.xml";
+
+ @Before
+ public void setUp() throws Exception {
+ System.out.println("set up.");
+ }
+
+ /** Test for android safety label missing version. */
+ @Test
+ public void testAndroidSafetyLabelMissingVersion() throws Exception {
+ System.out.println("starting testAndroidSafetyLabelMissingVersion.");
+ hrToOdExpectException(MISSING_VERSION_FILE_NAME);
+ odToHrExpectException(MISSING_VERSION_FILE_NAME);
+ }
+
+ /** Test for android safety label valid empty. */
+ @Test
+ public void testAndroidSafetyLabelValidEmptyFile() throws Exception {
+ System.out.println("starting testAndroidSafetyLabelValidEmptyFile.");
+ testHrToOdAndroidSafetyLabel(VALID_EMPTY_FILE_NAME);
+ testOdToHrAndroidSafetyLabel(VALID_EMPTY_FILE_NAME);
+ }
+
+ /** Test for android safety label with safety labels. */
+ @Test
+ public void testAndroidSafetyLabelWithSafetyLabels() throws Exception {
+ System.out.println("starting testAndroidSafetyLabelWithSafetyLabels.");
+ testHrToOdAndroidSafetyLabel(WITH_SAFETY_LABELS_FILE_NAME);
+ testOdToHrAndroidSafetyLabel(WITH_SAFETY_LABELS_FILE_NAME);
+ }
+
+ /** Test for android safety label with system app safety label. */
+ @Test
+ public void testAndroidSafetyLabelWithSystemAppSafetyLabel() throws Exception {
+ System.out.println("starting testAndroidSafetyLabelWithSystemAppSafetyLabel.");
+ testHrToOdAndroidSafetyLabel(WITH_SYSTEM_APP_SAFETY_LABEL_FILE_NAME);
+ testOdToHrAndroidSafetyLabel(WITH_SYSTEM_APP_SAFETY_LABEL_FILE_NAME);
+ }
+
+ /** Test for android safety label with transparency info. */
+ @Test
+ public void testAndroidSafetyLabelWithTransparencyInfo() throws Exception {
+ System.out.println("starting testAndroidSafetyLabelWithTransparencyInfo.");
+ testHrToOdAndroidSafetyLabel(WITH_TRANSPARENCY_INFO_FILE_NAME);
+ testOdToHrAndroidSafetyLabel(WITH_TRANSPARENCY_INFO_FILE_NAME);
+ }
+
+ private void hrToOdExpectException(String fileName) {
+ TestUtils.hrToOdExpectException(
+ new AndroidSafetyLabelFactory(), ANDROID_SAFETY_LABEL_HR_PATH, fileName);
+ }
+
+ private void odToHrExpectException(String fileName) {
+ TestUtils.odToHrExpectException(
+ new AndroidSafetyLabelFactory(), ANDROID_SAFETY_LABEL_OD_PATH, fileName);
+ }
+
+ private void testHrToOdAndroidSafetyLabel(String fileName) throws Exception {
+ TestUtils.testHrToOd(
+ TestUtils.document(),
+ new AndroidSafetyLabelFactory(),
+ ANDROID_SAFETY_LABEL_HR_PATH,
+ ANDROID_SAFETY_LABEL_OD_PATH,
+ fileName);
+ }
+
+ private void testOdToHrAndroidSafetyLabel(String fileName) throws Exception {
+ TestUtils.testOdToHr(
+ TestUtils.document(),
+ new AndroidSafetyLabelFactory(),
+ ANDROID_SAFETY_LABEL_OD_PATH,
+ ANDROID_SAFETY_LABEL_HR_PATH,
+ fileName);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AppInfoTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AppInfoTest.java
new file mode 100644
index 000000000000..9e91c6f22641
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AppInfoTest.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2017 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.asllib.marshallable;
+
+import static org.junit.Assert.assertThrows;
+
+import com.android.asllib.testutils.TestUtils;
+import com.android.asllib.util.MalformedXmlException;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.nio.file.Paths;
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public class AppInfoTest {
+ private static final String APP_INFO_HR_PATH = "com/android/asllib/appinfo/hr";
+ private static final String APP_INFO_OD_PATH = "com/android/asllib/appinfo/od";
+ public static final List<String> REQUIRED_FIELD_NAMES =
+ List.of(
+ "title",
+ "description",
+ "containsAds",
+ "obeyAps",
+ "adsFingerprinting",
+ "securityFingerprinting",
+ "privacyPolicy",
+ "securityEndpoints",
+ "firstPartyEndpoints",
+ "serviceProviderEndpoints",
+ "category",
+ "email");
+ public static final List<String> REQUIRED_FIELD_NAMES_OD =
+ List.of(
+ "title",
+ "description",
+ "contains_ads",
+ "obey_aps",
+ "ads_fingerprinting",
+ "security_fingerprinting",
+ "privacy_policy",
+ "security_endpoint",
+ "first_party_endpoint",
+ "service_provider_endpoint",
+ "category",
+ "email");
+ public static final List<String> OPTIONAL_FIELD_NAMES = List.of("website");
+ public static final List<String> OPTIONAL_FIELD_NAMES_OD = List.of("website");
+
+ private static final String ALL_FIELDS_VALID_FILE_NAME = "all-fields-valid.xml";
+
+ /** Logic for setting up tests (empty if not yet needed). */
+ public static void main(String[] params) throws Exception {}
+
+ @Before
+ public void setUp() throws Exception {
+ System.out.println("set up.");
+ }
+
+ /** Test for all fields valid. */
+ @Test
+ public void testAllFieldsValid() throws Exception {
+ System.out.println("starting testAllFieldsValid.");
+ testHrToOdAppInfo(ALL_FIELDS_VALID_FILE_NAME);
+ testOdToHrAppInfo(ALL_FIELDS_VALID_FILE_NAME);
+ }
+
+ /** Tests missing required fields fails. */
+ @Test
+ public void testMissingRequiredFields() throws Exception {
+ System.out.println("Starting testMissingRequiredFields");
+ for (String reqField : REQUIRED_FIELD_NAMES) {
+ System.out.println("testing missing required field hr: " + reqField);
+ var appInfoEle =
+ TestUtils.getElementsFromResource(
+ Paths.get(APP_INFO_HR_PATH, ALL_FIELDS_VALID_FILE_NAME));
+ appInfoEle.get(0).removeAttribute(reqField);
+
+ assertThrows(
+ MalformedXmlException.class,
+ () -> new AppInfoFactory().createFromHrElements(appInfoEle));
+ }
+
+ for (String reqField : REQUIRED_FIELD_NAMES_OD) {
+ System.out.println("testing missing required field od: " + reqField);
+ var appInfoEle =
+ TestUtils.getElementsFromResource(
+ Paths.get(APP_INFO_OD_PATH, ALL_FIELDS_VALID_FILE_NAME));
+ TestUtils.removeOdChildEleWithName(appInfoEle.get(0), reqField);
+ assertThrows(
+ MalformedXmlException.class,
+ () -> new AppInfoFactory().createFromOdElements(appInfoEle));
+ }
+ }
+
+ /** Tests missing optional fields passes. */
+ @Test
+ public void testMissingOptionalFields() throws Exception {
+ for (String optField : OPTIONAL_FIELD_NAMES) {
+ var ele =
+ TestUtils.getElementsFromResource(
+ Paths.get(APP_INFO_HR_PATH, ALL_FIELDS_VALID_FILE_NAME));
+ ele.get(0).removeAttribute(optField);
+ AppInfo appInfo = new AppInfoFactory().createFromHrElements(ele);
+ appInfo.toOdDomElements(TestUtils.document());
+ }
+
+ for (String optField : OPTIONAL_FIELD_NAMES_OD) {
+ var ele =
+ TestUtils.getElementsFromResource(
+ Paths.get(APP_INFO_OD_PATH, ALL_FIELDS_VALID_FILE_NAME));
+ TestUtils.removeOdChildEleWithName(ele.get(0), optField);
+ AppInfo appInfo = new AppInfoFactory().createFromOdElements(ele);
+ appInfo.toHrDomElements(TestUtils.document());
+ }
+ }
+
+ private void testHrToOdAppInfo(String fileName) throws Exception {
+ TestUtils.testHrToOd(
+ TestUtils.document(),
+ new AppInfoFactory(),
+ APP_INFO_HR_PATH,
+ APP_INFO_OD_PATH,
+ fileName);
+ }
+
+ private void testOdToHrAppInfo(String fileName) throws Exception {
+ TestUtils.testOdToHr(
+ TestUtils.document(),
+ new AppInfoFactory(),
+ APP_INFO_OD_PATH,
+ APP_INFO_HR_PATH,
+ fileName);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataLabelsTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataLabelsTest.java
new file mode 100644
index 000000000000..ff4374166dd3
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataLabelsTest.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2017 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.asllib.marshallable;
+
+import com.android.asllib.testutils.TestUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class DataLabelsTest {
+ private static final String DATA_LABELS_HR_PATH = "com/android/asllib/datalabels/hr";
+ private static final String DATA_LABELS_OD_PATH = "com/android/asllib/datalabels/od";
+
+ private static final String ACCESSED_VALID_BOOL_FILE_NAME =
+ "data-labels-accessed-valid-bool.xml";
+ private static final String ACCESSED_INVALID_BOOL_FILE_NAME =
+ "data-labels-accessed-invalid-bool.xml";
+ private static final String COLLECTED_VALID_BOOL_FILE_NAME =
+ "data-labels-collected-valid-bool.xml";
+ private static final String COLLECTED_EPHEMERAL_FILE_NAME =
+ "data-labels-collected-ephemeral.xml";
+ private static final String COLLECTED_EPHEMERAL_COLLISION_FILE_NAME =
+ "data-labels-collected-ephemeral-collision.xml";
+ private static final String COLLECTED_INVALID_BOOL_FILE_NAME =
+ "data-labels-collected-invalid-bool.xml";
+ private static final String SHARED_VALID_BOOL_FILE_NAME = "data-labels-shared-valid-bool.xml";
+ private static final String SHARED_INVALID_BOOL_FILE_NAME =
+ "data-labels-shared-invalid-bool.xml";
+
+ private static final String ACTIONS_IN_APP_FILE_NAME = "data-category-actions-in-app.xml";
+ private static final String APP_PERFORMANCE_FILE_NAME = "data-category-app-performance.xml";
+ private static final String AUDIO_FILE_NAME = "data-category-audio.xml";
+ private static final String CALENDAR_FILE_NAME = "data-category-calendar.xml";
+ private static final String CONTACTS_FILE_NAME = "data-category-contacts.xml";
+ private static final String EMAIL_TEXT_MESSAGE_FILE_NAME =
+ "data-category-email-text-message.xml";
+ private static final String FINANCIAL_FILE_NAME = "data-category-financial.xml";
+ private static final String HEALTH_FITNESS_FILE_NAME = "data-category-health-fitness.xml";
+ private static final String IDENTIFIERS_FILE_NAME = "data-category-identifiers.xml";
+ private static final String LOCATION_FILE_NAME = "data-category-location.xml";
+ private static final String PERSONAL_FILE_NAME = "data-category-personal.xml";
+ private static final String PERSONAL_PARTIAL_FILE_NAME = "data-category-personal-partial.xml";
+ private static final String PHOTO_VIDEO_FILE_NAME = "data-category-photo-video.xml";
+ private static final String SEARCH_AND_BROWSING_FILE_NAME =
+ "data-category-search-and-browsing.xml";
+ private static final String STORAGE_FILE_NAME = "data-category-storage.xml";
+ private static final String PERSONAL_MISSING_PURPOSE_FILE_NAME =
+ "data-category-personal-missing-purpose.xml";
+ private static final String PERSONAL_EMPTY_PURPOSE_FILE_NAME =
+ "data-category-personal-empty-purpose.xml";
+ private static final String UNRECOGNIZED_FILE_NAME = "data-category-unrecognized.xml";
+ private static final String UNRECOGNIZED_TYPE_FILE_NAME = "data-category-unrecognized-type.xml";
+
+ @Before
+ public void setUp() throws Exception {
+ System.out.println("set up.");
+ }
+
+ /** Test for data labels collected valid bool. */
+ @Test
+ public void testDataLabelsCollectedValidBool() throws Exception {
+ System.out.println("starting testDataLabelsCollectedValidBool.");
+ testHrToOdDataLabels(COLLECTED_VALID_BOOL_FILE_NAME);
+ testOdToHrDataLabels(COLLECTED_VALID_BOOL_FILE_NAME);
+ }
+
+ /** Test for data labels collected ephemeral. */
+ @Test
+ public void testDataLabelsCollectedEphemeral() throws Exception {
+ System.out.println("starting testDataLabelsCollectedEphemeral.");
+ testHrToOdDataLabels(COLLECTED_EPHEMERAL_FILE_NAME);
+ testOdToHrDataLabels(COLLECTED_EPHEMERAL_FILE_NAME);
+ }
+
+ /** Test for data labels ephemeral collision. */
+ @Test
+ public void testDataLabelsCollectedEphemeralCollision() throws Exception {
+ System.out.println("starting testDataLabelsCollectedEphemeralCollision.");
+ hrToOdExpectException(COLLECTED_EPHEMERAL_COLLISION_FILE_NAME);
+ }
+
+ /** Test for data labels collected invalid bool. */
+ @Test
+ public void testDataLabelsCollectedInvalidBool() throws Exception {
+ System.out.println("starting testDataLabelsCollectedInvalidBool.");
+ hrToOdExpectException(COLLECTED_INVALID_BOOL_FILE_NAME);
+ odToHrExpectException(COLLECTED_INVALID_BOOL_FILE_NAME);
+ }
+
+ /** Test for data labels shared valid bool. */
+ @Test
+ public void testDataLabelsSharedValidBool() throws Exception {
+ System.out.println("starting testDataLabelsSharedValidBool.");
+ testHrToOdDataLabels(SHARED_VALID_BOOL_FILE_NAME);
+ testOdToHrDataLabels(SHARED_VALID_BOOL_FILE_NAME);
+ }
+
+ /** Test for data labels shared invalid bool. */
+ @Test
+ public void testDataLabelsSharedInvalidBool() throws Exception {
+ System.out.println("starting testDataLabelsSharedInvalidBool.");
+ hrToOdExpectException(SHARED_INVALID_BOOL_FILE_NAME);
+ }
+
+ /* Data categories bidirectional tests... */
+
+ /** Test for data labels actions in app. */
+ @Test
+ public void testDataLabelsActionsInApp() throws Exception {
+ System.out.println("starting testDataLabelsActionsInApp.");
+ testHrToOdDataLabels(ACTIONS_IN_APP_FILE_NAME);
+ testOdToHrDataLabels(ACTIONS_IN_APP_FILE_NAME);
+ }
+
+ /** Test for data labels app performance. */
+ @Test
+ public void testDataLabelsAppPerformance() throws Exception {
+ System.out.println("starting testDataLabelsAppPerformance.");
+ testHrToOdDataLabels(APP_PERFORMANCE_FILE_NAME);
+ testOdToHrDataLabels(APP_PERFORMANCE_FILE_NAME);
+ }
+
+ /** Test for data labels audio. */
+ @Test
+ public void testDataLabelsAudio() throws Exception {
+ System.out.println("starting testDataLabelsAudio.");
+ testHrToOdDataLabels(AUDIO_FILE_NAME);
+ testOdToHrDataLabels(AUDIO_FILE_NAME);
+ }
+
+ /** Test for data labels calendar. */
+ @Test
+ public void testDataLabelsCalendar() throws Exception {
+ System.out.println("starting testDataLabelsCalendar.");
+ testHrToOdDataLabels(CALENDAR_FILE_NAME);
+ testOdToHrDataLabels(CALENDAR_FILE_NAME);
+ }
+
+ /** Test for data labels contacts. */
+ @Test
+ public void testDataLabelsContacts() throws Exception {
+ System.out.println("starting testDataLabelsContacts.");
+ testHrToOdDataLabels(CONTACTS_FILE_NAME);
+ testOdToHrDataLabels(CONTACTS_FILE_NAME);
+ }
+
+ /** Test for data labels email text message. */
+ @Test
+ public void testDataLabelsEmailTextMessage() throws Exception {
+ System.out.println("starting testDataLabelsEmailTextMessage.");
+ testHrToOdDataLabels(EMAIL_TEXT_MESSAGE_FILE_NAME);
+ testOdToHrDataLabels(EMAIL_TEXT_MESSAGE_FILE_NAME);
+ }
+
+ /** Test for data labels financial. */
+ @Test
+ public void testDataLabelsFinancial() throws Exception {
+ System.out.println("starting testDataLabelsFinancial.");
+ testHrToOdDataLabels(FINANCIAL_FILE_NAME);
+ testOdToHrDataLabels(FINANCIAL_FILE_NAME);
+ }
+
+ /** Test for data labels health fitness. */
+ @Test
+ public void testDataLabelsHealthFitness() throws Exception {
+ System.out.println("starting testDataLabelsHealthFitness.");
+ testHrToOdDataLabels(HEALTH_FITNESS_FILE_NAME);
+ testOdToHrDataLabels(HEALTH_FITNESS_FILE_NAME);
+ }
+
+ /** Test for data labels identifiers. */
+ @Test
+ public void testDataLabelsIdentifiers() throws Exception {
+ System.out.println("starting testDataLabelsIdentifiers.");
+ testHrToOdDataLabels(IDENTIFIERS_FILE_NAME);
+ testOdToHrDataLabels(IDENTIFIERS_FILE_NAME);
+ }
+
+ /** Test for data labels location. */
+ @Test
+ public void testDataLabelsLocation() throws Exception {
+ System.out.println("starting testDataLabelsLocation.");
+ testHrToOdDataLabels(LOCATION_FILE_NAME);
+ testOdToHrDataLabels(LOCATION_FILE_NAME);
+ }
+
+ /** Test for data labels personal. */
+ @Test
+ public void testDataLabelsPersonal() throws Exception {
+ System.out.println("starting testDataLabelsPersonal.");
+ testHrToOdDataLabels(PERSONAL_FILE_NAME);
+ testOdToHrDataLabels(PERSONAL_FILE_NAME);
+ }
+
+ /** Test for data labels personal partial. */
+ @Test
+ public void testDataLabelsPersonalPartial() throws Exception {
+ System.out.println("starting testDataLabelsPersonalPartial.");
+ testHrToOdDataLabels(PERSONAL_PARTIAL_FILE_NAME);
+ testOdToHrDataLabels(PERSONAL_PARTIAL_FILE_NAME);
+ }
+
+ /** Test for data labels photo video. */
+ @Test
+ public void testDataLabelsPhotoVideo() throws Exception {
+ System.out.println("starting testDataLabelsPhotoVideo.");
+ testHrToOdDataLabels(PHOTO_VIDEO_FILE_NAME);
+ testOdToHrDataLabels(PHOTO_VIDEO_FILE_NAME);
+ }
+
+ /** Test for data labels search and browsing. */
+ @Test
+ public void testDataLabelsSearchAndBrowsing() throws Exception {
+ System.out.println("starting testDataLabelsSearchAndBrowsing.");
+ testHrToOdDataLabels(SEARCH_AND_BROWSING_FILE_NAME);
+ testOdToHrDataLabels(SEARCH_AND_BROWSING_FILE_NAME);
+ }
+
+ /** Test for data labels storage. */
+ @Test
+ public void testDataLabelsStorage() throws Exception {
+ System.out.println("starting testDataLabelsStorage.");
+ testHrToOdDataLabels(STORAGE_FILE_NAME);
+ testOdToHrDataLabels(STORAGE_FILE_NAME);
+ }
+
+ /** Test for data labels hr unrecognized data category. */
+ @Test
+ public void testDataLabelsHrUnrecognizedDataCategory() throws Exception {
+ System.out.println("starting testDataLabelsHrUnrecognizedDataCategory.");
+ hrToOdExpectException(UNRECOGNIZED_FILE_NAME);
+ }
+
+ /** Test for data labels hr unrecognized data type. */
+ @Test
+ public void testDataLabelsHrUnrecognizedDataType() throws Exception {
+ System.out.println("starting testDataLabelsHrUnrecognizedDataType.");
+ hrToOdExpectException(UNRECOGNIZED_TYPE_FILE_NAME);
+ }
+
+ /** Test for data labels hr missing purpose. */
+ @Test
+ public void testDataLabelsHrMissingPurpose() throws Exception {
+ System.out.println("starting testDataLabelsHrMissingPurpose.");
+ hrToOdExpectException(PERSONAL_MISSING_PURPOSE_FILE_NAME);
+ }
+
+ /** Test for data labels hr empty purpose. */
+ @Test
+ public void testDataLabelsHrEmptyPurpose() throws Exception {
+ System.out.println("starting testDataLabelsHrEmptyPurpose.");
+ hrToOdExpectException(PERSONAL_EMPTY_PURPOSE_FILE_NAME);
+ }
+
+ /** Test for data labels od unrecognized data category. */
+ @Test
+ public void testDataLabelsOdUnrecognizedDataCategory() throws Exception {
+ System.out.println("starting testDataLabelsOdUnrecognizedDataCategory.");
+ odToHrExpectException(UNRECOGNIZED_FILE_NAME);
+ }
+
+ /** Test for data labels od unrecognized data type. */
+ @Test
+ public void testDataLabelsOdUnrecognizedDataType() throws Exception {
+ System.out.println("starting testDataLabelsOdUnrecognizedDataCategory.");
+ odToHrExpectException(UNRECOGNIZED_TYPE_FILE_NAME);
+ }
+
+ /** Test for data labels od missing purpose. */
+ @Test
+ public void testDataLabelsOdMissingPurpose() throws Exception {
+ System.out.println("starting testDataLabelsOdMissingPurpose.");
+ odToHrExpectException(PERSONAL_MISSING_PURPOSE_FILE_NAME);
+ }
+
+ /** Test for data labels od empty purpose. */
+ @Test
+ public void testDataLabelsOdEmptyPurpose() throws Exception {
+ System.out.println("starting testDataLabelsOdEmptyPurpose.");
+ odToHrExpectException(PERSONAL_EMPTY_PURPOSE_FILE_NAME);
+ }
+
+ private void hrToOdExpectException(String fileName) {
+ TestUtils.hrToOdExpectException(new DataLabelsFactory(), DATA_LABELS_HR_PATH, fileName);
+ }
+
+ private void odToHrExpectException(String fileName) {
+ TestUtils.odToHrExpectException(new DataLabelsFactory(), DATA_LABELS_OD_PATH, fileName);
+ }
+
+ private void testHrToOdDataLabels(String fileName) throws Exception {
+ TestUtils.testHrToOd(
+ TestUtils.document(),
+ new DataLabelsFactory(),
+ DATA_LABELS_HR_PATH,
+ DATA_LABELS_OD_PATH,
+ fileName);
+ }
+
+ private void testOdToHrDataLabels(String fileName) throws Exception {
+ TestUtils.testOdToHr(
+ TestUtils.document(),
+ new DataLabelsFactory(),
+ DATA_LABELS_OD_PATH,
+ DATA_LABELS_HR_PATH,
+ fileName);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataTypeEqualityTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataTypeEqualityTest.java
new file mode 100644
index 000000000000..da7f2879720e
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataTypeEqualityTest.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2017 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.asllib.marshallable;
+
+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 org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@RunWith(JUnit4.class)
+public class DataTypeEqualityTest {
+
+ public static final List<String> OPTIONAL_FIELD_NAMES =
+ List.of("isDataDeletable", "isDataEncrypted");
+ public static final List<String> OPTIONAL_FIELD_NAMES_OD =
+ List.of("is_data_deletable", "is_data_encrypted");
+
+ /** Logic for setting up tests (empty if not yet needed). */
+ public static void main(String[] params) throws Exception {}
+
+ @Before
+ public void setUp() throws Exception {
+ System.out.println("set up.");
+ }
+
+ /** Test for equality different order. */
+ @Test
+ public void testEqualityDifferentOrder() throws Exception {
+ System.out.println("starting testEqualityDifferentOrder.");
+ DataType dataType1 =
+ new DataType(
+ "datatype1",
+ Arrays.asList(
+ DataType.Purpose.ADVERTISING, DataType.Purpose.PERSONALIZATION),
+ true,
+ false,
+ null);
+ DataType dataType2 =
+ new DataType(
+ "datatype1",
+ Arrays.asList(
+ DataType.Purpose.PERSONALIZATION, DataType.Purpose.ADVERTISING),
+ true,
+ false,
+ null);
+ assertEquals(dataType1, dataType2);
+ assertEquals(dataType2, dataType1);
+ }
+
+ /** Test for contains different order. */
+ @Test
+ public void testContainsDifferentOrder() throws Exception {
+ System.out.println("starting testContainsDifferentOrder.");
+ DataType dataType1 =
+ new DataType(
+ "datatype1",
+ Arrays.asList(
+ DataType.Purpose.ADVERTISING, DataType.Purpose.PERSONALIZATION),
+ true,
+ false,
+ null);
+ DataType dataType2 =
+ new DataType(
+ "datatype1",
+ Arrays.asList(
+ DataType.Purpose.PERSONALIZATION, DataType.Purpose.ADVERTISING),
+ true,
+ false,
+ null);
+ Set<DataType> set = new HashSet<>();
+ set.add(dataType1);
+ assertTrue(set.contains(dataType2));
+ }
+
+ /** Test for inequality. */
+ @Test
+ public void testInequality() throws Exception {
+ System.out.println("starting testInequality.");
+ DataType dataType1 =
+ new DataType(
+ "datatype1",
+ Arrays.asList(
+ DataType.Purpose.ADVERTISING, DataType.Purpose.PERSONALIZATION),
+ true,
+ false,
+ null);
+ DataType dataType2 =
+ new DataType(
+ "datatype1",
+ Arrays.asList(DataType.Purpose.PERSONALIZATION),
+ true,
+ false,
+ null);
+ assertNotEquals(dataType1, dataType2);
+ assertNotEquals(dataType2, dataType1);
+ }
+
+ /** Test for inequality bool. */
+ @Test
+ public void testInequalityBool() throws Exception {
+ System.out.println("starting testInequalityBool.");
+ DataType dataType1 =
+ new DataType(
+ "datatype1",
+ Arrays.asList(
+ DataType.Purpose.ADVERTISING, DataType.Purpose.PERSONALIZATION),
+ true,
+ false,
+ null);
+ DataType dataType2 =
+ new DataType(
+ "datatype1",
+ Arrays.asList(
+ DataType.Purpose.ADVERTISING, DataType.Purpose.PERSONALIZATION),
+ true,
+ false,
+ true);
+ assertNotEquals(dataType1, dataType2);
+ assertNotEquals(dataType2, dataType1);
+ }
+
+ /** Test for does not contain. */
+ @Test
+ public void testDoesNotContain() throws Exception {
+ System.out.println("starting testDoesNotContain.");
+ System.out.println("starting testContainsDifferentOrder.");
+ DataType dataType1 =
+ new DataType(
+ "datatype1",
+ Arrays.asList(
+ DataType.Purpose.ADVERTISING, DataType.Purpose.PERSONALIZATION),
+ true,
+ false,
+ null);
+ DataType dataType2 =
+ new DataType(
+ "datatype1",
+ Arrays.asList(DataType.Purpose.PERSONALIZATION),
+ true,
+ false,
+ null);
+ Set<DataType> set = new HashSet<>();
+ set.add(dataType1);
+ assertFalse(set.contains(dataType2));
+ }
+}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DeveloperInfoTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DeveloperInfoTest.java
new file mode 100644
index 000000000000..72e8d654b542
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DeveloperInfoTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2017 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.asllib.marshallable;
+
+import static org.junit.Assert.assertThrows;
+
+import com.android.asllib.testutils.TestUtils;
+import com.android.asllib.util.MalformedXmlException;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.nio.file.Paths;
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public class DeveloperInfoTest {
+ private static final String DEVELOPER_INFO_HR_PATH = "com/android/asllib/developerinfo/hr";
+ private static final String DEVELOPER_INFO_OD_PATH = "com/android/asllib/developerinfo/od";
+ public static final List<String> REQUIRED_FIELD_NAMES =
+ List.of("address", "countryRegion", "email", "name", "relationship");
+ public static final List<String> REQUIRED_FIELD_NAMES_OD =
+ List.of("address", "country_region", "email", "name", "relationship");
+ public static final List<String> OPTIONAL_FIELD_NAMES = List.of("website", "registryId");
+ public static final List<String> OPTIONAL_FIELD_NAMES_OD =
+ List.of("website", "app_developer_registry_id");
+
+ private static final String ALL_FIELDS_VALID_FILE_NAME = "all-fields-valid.xml";
+
+ /** Logic for setting up tests (empty if not yet needed). */
+ public static void main(String[] params) throws Exception {}
+
+ @Before
+ public void setUp() throws Exception {
+ System.out.println("set up.");
+ }
+
+ /** Test for all fields valid. */
+ @Test
+ public void testAllFieldsValid() throws Exception {
+ System.out.println("starting testAllFieldsValid.");
+ testHrToOdDeveloperInfo(ALL_FIELDS_VALID_FILE_NAME);
+ testOdToHrDeveloperInfo(ALL_FIELDS_VALID_FILE_NAME);
+ }
+
+ /** Tests missing required fields fails. */
+ @Test
+ public void testMissingRequiredFields() throws Exception {
+ System.out.println("Starting testMissingRequiredFields");
+ for (String reqField : REQUIRED_FIELD_NAMES) {
+ System.out.println("testing missing required field: " + reqField);
+ var developerInfoEle =
+ TestUtils.getElementsFromResource(
+ Paths.get(DEVELOPER_INFO_HR_PATH, ALL_FIELDS_VALID_FILE_NAME));
+ developerInfoEle.get(0).removeAttribute(reqField);
+
+ assertThrows(
+ MalformedXmlException.class,
+ () -> new DeveloperInfoFactory().createFromHrElements(developerInfoEle));
+ }
+
+ for (String reqField : REQUIRED_FIELD_NAMES_OD) {
+ System.out.println("testing missing required field od: " + reqField);
+ var developerInfoEle =
+ TestUtils.getElementsFromResource(
+ Paths.get(DEVELOPER_INFO_OD_PATH, ALL_FIELDS_VALID_FILE_NAME));
+ TestUtils.removeOdChildEleWithName(developerInfoEle.get(0), reqField);
+
+ assertThrows(
+ MalformedXmlException.class,
+ () -> new DeveloperInfoFactory().createFromOdElements(developerInfoEle));
+ }
+ }
+
+ /** Tests missing optional fields passes. */
+ @Test
+ public void testMissingOptionalFields() throws Exception {
+ for (String optField : OPTIONAL_FIELD_NAMES) {
+ var developerInfoEle =
+ TestUtils.getElementsFromResource(
+ Paths.get(DEVELOPER_INFO_HR_PATH, ALL_FIELDS_VALID_FILE_NAME));
+ developerInfoEle.get(0).removeAttribute(optField);
+ DeveloperInfo developerInfo =
+ new DeveloperInfoFactory().createFromHrElements(developerInfoEle);
+ developerInfo.toOdDomElements(TestUtils.document());
+ }
+
+ for (String optField : OPTIONAL_FIELD_NAMES_OD) {
+ var developerInfoEle =
+ TestUtils.getElementsFromResource(
+ Paths.get(DEVELOPER_INFO_OD_PATH, ALL_FIELDS_VALID_FILE_NAME));
+ TestUtils.removeOdChildEleWithName(developerInfoEle.get(0), optField);
+ DeveloperInfo developerInfo =
+ new DeveloperInfoFactory().createFromOdElements(developerInfoEle);
+ developerInfo.toHrDomElements(TestUtils.document());
+ }
+ }
+
+ private void testHrToOdDeveloperInfo(String fileName) throws Exception {
+ TestUtils.testHrToOd(
+ TestUtils.document(),
+ new DeveloperInfoFactory(),
+ DEVELOPER_INFO_HR_PATH,
+ DEVELOPER_INFO_OD_PATH,
+ fileName);
+ }
+
+ private void testOdToHrDeveloperInfo(String fileName) throws Exception {
+ TestUtils.testOdToHr(
+ TestUtils.document(),
+ new DeveloperInfoFactory(),
+ DEVELOPER_INFO_OD_PATH,
+ DEVELOPER_INFO_HR_PATH,
+ fileName);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SafetyLabelsTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SafetyLabelsTest.java
new file mode 100644
index 000000000000..bba6b548beaf
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SafetyLabelsTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2017 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.asllib.marshallable;
+
+import com.android.asllib.testutils.TestUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class SafetyLabelsTest {
+ private static final String SAFETY_LABELS_HR_PATH = "com/android/asllib/safetylabels/hr";
+ private static final String SAFETY_LABELS_OD_PATH = "com/android/asllib/safetylabels/od";
+
+ private static final String MISSING_VERSION_FILE_NAME = "missing-version.xml";
+ private static final String VALID_EMPTY_FILE_NAME = "valid-empty.xml";
+ private static final String WITH_DATA_LABELS_FILE_NAME = "with-data-labels.xml";
+ private static final String WITH_SECURITY_LABELS_FILE_NAME = "with-security-labels.xml";
+ private static final String WITH_THIRD_PARTY_VERIFICATION_FILE_NAME =
+ "with-third-party-verification.xml";
+
+ @Before
+ public void setUp() throws Exception {
+ System.out.println("set up.");
+ }
+
+ /** Test for safety labels missing version. */
+ @Test
+ public void testSafetyLabelsMissingVersion() throws Exception {
+ System.out.println("starting testSafetyLabelsMissingVersion.");
+ hrToOdExpectException(MISSING_VERSION_FILE_NAME);
+ odToHrExpectException(MISSING_VERSION_FILE_NAME);
+ }
+
+ /** Test for safety labels valid empty. */
+ @Test
+ public void testSafetyLabelsValidEmptyFile() throws Exception {
+ System.out.println("starting testSafetyLabelsValidEmptyFile.");
+ testHrToOdSafetyLabels(VALID_EMPTY_FILE_NAME);
+ testOdToHrSafetyLabels(VALID_EMPTY_FILE_NAME);
+ }
+
+ /** Test for safety labels with data labels. */
+ @Test
+ public void testSafetyLabelsWithDataLabels() throws Exception {
+ System.out.println("starting testSafetyLabelsWithDataLabels.");
+ testHrToOdSafetyLabels(WITH_DATA_LABELS_FILE_NAME);
+ testOdToHrSafetyLabels(WITH_DATA_LABELS_FILE_NAME);
+ }
+
+ /** Test for safety labels with security labels. */
+ @Test
+ public void testSafetyLabelsWithSecurityLabels() throws Exception {
+ System.out.println("starting testSafetyLabelsWithSecurityLabels.");
+ testHrToOdSafetyLabels(WITH_SECURITY_LABELS_FILE_NAME);
+ testOdToHrSafetyLabels(WITH_SECURITY_LABELS_FILE_NAME);
+ }
+
+ /** Test for safety labels with third party verification. */
+ @Test
+ public void testSafetyLabelsWithThirdPartyVerification() throws Exception {
+ System.out.println("starting testSafetyLabelsWithThirdPartyVerification.");
+ testHrToOdSafetyLabels(WITH_THIRD_PARTY_VERIFICATION_FILE_NAME);
+ testOdToHrSafetyLabels(WITH_THIRD_PARTY_VERIFICATION_FILE_NAME);
+ }
+
+ private void hrToOdExpectException(String fileName) {
+ TestUtils.hrToOdExpectException(new SafetyLabelsFactory(), SAFETY_LABELS_HR_PATH, fileName);
+ }
+
+ private void odToHrExpectException(String fileName) {
+ TestUtils.odToHrExpectException(new SafetyLabelsFactory(), SAFETY_LABELS_OD_PATH, fileName);
+ }
+
+ private void testHrToOdSafetyLabels(String fileName) throws Exception {
+ TestUtils.testHrToOd(
+ TestUtils.document(),
+ new SafetyLabelsFactory(),
+ SAFETY_LABELS_HR_PATH,
+ SAFETY_LABELS_OD_PATH,
+ fileName);
+ }
+
+ private void testOdToHrSafetyLabels(String fileName) throws Exception {
+ TestUtils.testOdToHr(
+ TestUtils.document(),
+ new SafetyLabelsFactory(),
+ SAFETY_LABELS_OD_PATH,
+ SAFETY_LABELS_HR_PATH,
+ fileName);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SecurityLabelsTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SecurityLabelsTest.java
new file mode 100644
index 000000000000..a940bc63c685
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SecurityLabelsTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.asllib.marshallable;
+
+
+import com.android.asllib.testutils.TestUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.nio.file.Paths;
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public class SecurityLabelsTest {
+ private static final String SECURITY_LABELS_HR_PATH = "com/android/asllib/securitylabels/hr";
+ private static final String SECURITY_LABELS_OD_PATH = "com/android/asllib/securitylabels/od";
+
+ public static final List<String> OPTIONAL_FIELD_NAMES =
+ List.of("isDataDeletable", "isDataEncrypted");
+ public static final List<String> OPTIONAL_FIELD_NAMES_OD =
+ List.of("is_data_deletable", "is_data_encrypted");
+
+ private static final String ALL_FIELDS_VALID_FILE_NAME = "all-fields-valid.xml";
+
+ /** Logic for setting up tests (empty if not yet needed). */
+ public static void main(String[] params) throws Exception {}
+
+ @Before
+ public void setUp() throws Exception {
+ System.out.println("set up.");
+ }
+
+ /** Test for all fields valid. */
+ @Test
+ public void testAllFieldsValid() throws Exception {
+ System.out.println("starting testAllFieldsValid.");
+ testHrToOdSecurityLabels(ALL_FIELDS_VALID_FILE_NAME);
+ testOdToHrSecurityLabels(ALL_FIELDS_VALID_FILE_NAME);
+ }
+
+ /** Tests missing optional fields passes. */
+ @Test
+ public void testMissingOptionalFields() throws Exception {
+ for (String optField : OPTIONAL_FIELD_NAMES) {
+ var ele =
+ TestUtils.getElementsFromResource(
+ Paths.get(SECURITY_LABELS_HR_PATH, ALL_FIELDS_VALID_FILE_NAME));
+ ele.get(0).removeAttribute(optField);
+ SecurityLabels securityLabels = new SecurityLabelsFactory().createFromHrElements(ele);
+ securityLabels.toOdDomElements(TestUtils.document());
+ }
+ for (String optField : OPTIONAL_FIELD_NAMES_OD) {
+ var ele =
+ TestUtils.getElementsFromResource(
+ Paths.get(SECURITY_LABELS_OD_PATH, ALL_FIELDS_VALID_FILE_NAME));
+ TestUtils.removeOdChildEleWithName(ele.get(0), optField);
+ SecurityLabels securityLabels = new SecurityLabelsFactory().createFromOdElements(ele);
+ securityLabels.toHrDomElements(TestUtils.document());
+ }
+ }
+
+ private void testHrToOdSecurityLabels(String fileName) throws Exception {
+ TestUtils.testHrToOd(
+ TestUtils.document(),
+ new SecurityLabelsFactory(),
+ SECURITY_LABELS_HR_PATH,
+ SECURITY_LABELS_OD_PATH,
+ fileName);
+ }
+
+ private void testOdToHrSecurityLabels(String fileName) throws Exception {
+ TestUtils.testOdToHr(
+ TestUtils.document(),
+ new SecurityLabelsFactory(),
+ SECURITY_LABELS_OD_PATH,
+ SECURITY_LABELS_HR_PATH,
+ fileName);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SystemAppSafetyLabelTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SystemAppSafetyLabelTest.java
new file mode 100644
index 000000000000..87d3e4499f5c
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SystemAppSafetyLabelTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 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.asllib.marshallable;
+
+import com.android.asllib.testutils.TestUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class SystemAppSafetyLabelTest {
+ private static final String SYSTEM_APP_SAFETY_LABEL_HR_PATH =
+ "com/android/asllib/systemappsafetylabel/hr";
+ private static final String SYSTEM_APP_SAFETY_LABEL_OD_PATH =
+ "com/android/asllib/systemappsafetylabel/od";
+
+ private static final String VALID_FILE_NAME = "valid.xml";
+ private static final String MISSING_BOOL_FILE_NAME = "missing-bool.xml";
+
+ /** Logic for setting up tests (empty if not yet needed). */
+ public static void main(String[] params) throws Exception {}
+
+ @Before
+ public void setUp() throws Exception {
+ System.out.println("set up.");
+ }
+
+ /** Test for valid. */
+ @Test
+ public void testValid() throws Exception {
+ System.out.println("starting testValid.");
+ testHrToOdSystemAppSafetyLabel(VALID_FILE_NAME);
+ testOdToHrSystemAppSafetyLabel(VALID_FILE_NAME);
+ }
+
+ /** Tests missing bool. */
+ @Test
+ public void testMissingBool() throws Exception {
+ System.out.println("starting testMissingBool.");
+ hrToOdExpectException(MISSING_BOOL_FILE_NAME);
+ odToHrExpectException(MISSING_BOOL_FILE_NAME);
+ }
+
+ private void hrToOdExpectException(String fileName) {
+ TestUtils.hrToOdExpectException(
+ new SystemAppSafetyLabelFactory(), SYSTEM_APP_SAFETY_LABEL_HR_PATH, fileName);
+ }
+
+ private void odToHrExpectException(String fileName) {
+ TestUtils.odToHrExpectException(
+ new SystemAppSafetyLabelFactory(), SYSTEM_APP_SAFETY_LABEL_OD_PATH, fileName);
+ }
+
+ private void testHrToOdSystemAppSafetyLabel(String fileName) throws Exception {
+ TestUtils.testHrToOd(
+ TestUtils.document(),
+ new SystemAppSafetyLabelFactory(),
+ SYSTEM_APP_SAFETY_LABEL_HR_PATH,
+ SYSTEM_APP_SAFETY_LABEL_OD_PATH,
+ fileName);
+ }
+
+ private void testOdToHrSystemAppSafetyLabel(String fileName) throws Exception {
+ TestUtils.testOdToHr(
+ TestUtils.document(),
+ new SystemAppSafetyLabelFactory(),
+ SYSTEM_APP_SAFETY_LABEL_OD_PATH,
+ SYSTEM_APP_SAFETY_LABEL_HR_PATH,
+ fileName);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/ThirdPartyVerificationTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/ThirdPartyVerificationTest.java
new file mode 100644
index 000000000000..ec86d0f863af
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/ThirdPartyVerificationTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 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.asllib.marshallable;
+
+import com.android.asllib.testutils.TestUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ThirdPartyVerificationTest {
+ private static final String THIRD_PARTY_VERIFICATION_HR_PATH =
+ "com/android/asllib/thirdpartyverification/hr";
+ private static final String THIRD_PARTY_VERIFICATION_OD_PATH =
+ "com/android/asllib/thirdpartyverification/od";
+
+ private static final String VALID_FILE_NAME = "valid.xml";
+ private static final String MISSING_URL_FILE_NAME = "missing-url.xml";
+
+ /** Logic for setting up tests (empty if not yet needed). */
+ public static void main(String[] params) throws Exception {}
+
+ @Before
+ public void setUp() throws Exception {
+ System.out.println("set up.");
+ }
+
+ /** Test for valid. */
+ @Test
+ public void testValid() throws Exception {
+ System.out.println("starting testValid.");
+ testHrToOdThirdPartyVerification(VALID_FILE_NAME);
+ testOdToHrThirdPartyVerification(VALID_FILE_NAME);
+ }
+
+ /** Tests missing url. */
+ @Test
+ public void testMissingUrl() throws Exception {
+ System.out.println("starting testMissingUrl.");
+ hrToOdExpectException(MISSING_URL_FILE_NAME);
+ odToHrExpectException(MISSING_URL_FILE_NAME);
+ }
+
+ private void hrToOdExpectException(String fileName) {
+ TestUtils.hrToOdExpectException(
+ new ThirdPartyVerificationFactory(), THIRD_PARTY_VERIFICATION_HR_PATH, fileName);
+ }
+
+ private void odToHrExpectException(String fileName) {
+ TestUtils.odToHrExpectException(
+ new ThirdPartyVerificationFactory(), THIRD_PARTY_VERIFICATION_OD_PATH, fileName);
+ }
+
+ private void testHrToOdThirdPartyVerification(String fileName) throws Exception {
+ TestUtils.testHrToOd(
+ TestUtils.document(),
+ new ThirdPartyVerificationFactory(),
+ THIRD_PARTY_VERIFICATION_HR_PATH,
+ THIRD_PARTY_VERIFICATION_OD_PATH,
+ fileName);
+ }
+
+ private void testOdToHrThirdPartyVerification(String fileName) throws Exception {
+ TestUtils.testOdToHr(
+ TestUtils.document(),
+ new ThirdPartyVerificationFactory(),
+ THIRD_PARTY_VERIFICATION_OD_PATH,
+ THIRD_PARTY_VERIFICATION_HR_PATH,
+ fileName);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/TransparencyInfoTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/TransparencyInfoTest.java
new file mode 100644
index 000000000000..f49424061427
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/TransparencyInfoTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2017 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.asllib.marshallable;
+
+import com.android.asllib.testutils.TestUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class TransparencyInfoTest {
+ private static final String TRANSPARENCY_INFO_HR_PATH =
+ "com/android/asllib/transparencyinfo/hr";
+ private static final String TRANSPARENCY_INFO_OD_PATH =
+ "com/android/asllib/transparencyinfo/od";
+
+ private static final String VALID_EMPTY_FILE_NAME = "valid-empty.xml";
+ private static final String WITH_DEVELOPER_INFO_FILE_NAME = "with-developer-info.xml";
+ private static final String WITH_APP_INFO_FILE_NAME = "with-app-info.xml";
+
+ @Before
+ public void setUp() throws Exception {
+ System.out.println("set up.");
+ }
+
+ /** Test for transparency info valid empty. */
+ @Test
+ public void testTransparencyInfoValidEmptyFile() throws Exception {
+ System.out.println("starting testTransparencyInfoValidEmptyFile.");
+ testHrToOdTransparencyInfo(VALID_EMPTY_FILE_NAME);
+ testOdToHrTransparencyInfo(VALID_EMPTY_FILE_NAME);
+ }
+
+ /** Test for transparency info with developer info. */
+ @Test
+ public void testTransparencyInfoWithDeveloperInfo() throws Exception {
+ System.out.println("starting testTransparencyInfoWithDeveloperInfo.");
+ testHrToOdTransparencyInfo(WITH_DEVELOPER_INFO_FILE_NAME);
+ testOdToHrTransparencyInfo(WITH_DEVELOPER_INFO_FILE_NAME);
+ }
+
+ /** Test for transparency info with app info. */
+ @Test
+ public void testTransparencyInfoWithAppInfo() throws Exception {
+ System.out.println("starting testTransparencyInfoWithAppInfo.");
+ testHrToOdTransparencyInfo(WITH_APP_INFO_FILE_NAME);
+ testOdToHrTransparencyInfo(WITH_APP_INFO_FILE_NAME);
+ }
+
+ private void testHrToOdTransparencyInfo(String fileName) throws Exception {
+ TestUtils.testHrToOd(
+ TestUtils.document(),
+ new TransparencyInfoFactory(),
+ TRANSPARENCY_INFO_HR_PATH,
+ TRANSPARENCY_INFO_OD_PATH,
+ fileName);
+ }
+
+ private void testOdToHrTransparencyInfo(String fileName) throws Exception {
+ TestUtils.testOdToHr(
+ TestUtils.document(),
+ new TransparencyInfoFactory(),
+ TRANSPARENCY_INFO_OD_PATH,
+ TRANSPARENCY_INFO_HR_PATH,
+ fileName);
+ }
+}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/testutils/TestUtils.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/testutils/TestUtils.java
new file mode 100644
index 000000000000..ea90993e0785
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/testutils/TestUtils.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2017 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.asllib.testutils;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import com.android.asllib.marshallable.AslMarshallable;
+import com.android.asllib.marshallable.AslMarshallableFactory;
+import com.android.asllib.util.MalformedXmlException;
+import com.android.asllib.util.XmlUtils;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Optional;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+public class TestUtils {
+ public static final String HOLDER_TAG_NAME = "holder_of_flattened_for_testing";
+
+ /** Reads a Resource file into a String. */
+ public static String readStrFromResource(Path filePath) throws IOException {
+ InputStream hrStream =
+ TestUtils.class.getClassLoader().getResourceAsStream(filePath.toString());
+ return new String(hrStream.readAllBytes(), StandardCharsets.UTF_8);
+ }
+
+ /** Gets List of Element from a path to an existing Resource. */
+ public static List<Element> getElementsFromResource(Path filePath)
+ throws ParserConfigurationException, IOException, SAXException {
+ String str = readStrFromResource(filePath);
+ InputStream stream = new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8));
+
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(true);
+ Document document = factory.newDocumentBuilder().parse(stream);
+ Element root = document.getDocumentElement();
+ if (root.getTagName().equals(HOLDER_TAG_NAME)) {
+ String tagName =
+ XmlUtils.asElementList(root.getChildNodes()).stream()
+ .findFirst()
+ .get()
+ .getTagName();
+ return XmlUtils.getChildrenByTagName(root, tagName);
+ } else {
+ return List.of(root);
+ }
+ }
+
+ /** Reads a Document into a String. */
+ public static String docToStr(Document doc, boolean omitXmlDeclaration)
+ throws TransformerException {
+ TransformerFactory transformerFactory = TransformerFactory.newInstance();
+ Transformer transformer = transformerFactory.newTransformer();
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+ transformer.setOutputProperty(
+ OutputKeys.OMIT_XML_DECLARATION, omitXmlDeclaration ? "yes" : "no");
+
+ ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+ StreamResult streamResult = new StreamResult(outStream); // out
+ DOMSource domSource = new DOMSource(doc);
+ transformer.transform(domSource, streamResult);
+
+ return outStream.toString(StandardCharsets.UTF_8);
+ }
+
+ /** Removes on-device style child with the corresponding name */
+ public static void removeOdChildEleWithName(Element ele, String childNameName) {
+ Optional<Element> childEle =
+ XmlUtils.asElementList(ele.getChildNodes()).stream()
+ .filter(e -> e.getAttribute(XmlUtils.OD_ATTR_NAME).equals(childNameName))
+ .findFirst();
+ if (childEle.isEmpty()) {
+ throw new IllegalStateException(
+ String.format("%s was not found in %s", childNameName, ele.getTagName()));
+ }
+ ele.removeChild(childEle.get());
+ }
+
+ /**
+ * Gets formatted XML for slightly more robust comparison checking than naive string comparison.
+ */
+ public static String getFormattedXml(String xmlStr, boolean omitXmlDeclaration)
+ throws ParserConfigurationException, IOException, SAXException, TransformerException {
+ InputStream stream = new ByteArrayInputStream(xmlStr.getBytes(StandardCharsets.UTF_8));
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(true);
+ Document document = factory.newDocumentBuilder().parse(stream);
+ stripEmptyElements(document);
+ return docToStr(document, omitXmlDeclaration);
+ }
+
+ /** Helper for getting a new Document */
+ public static Document document() throws ParserConfigurationException {
+ return DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
+ }
+
+ /** Helper for testing human-readable to on-device conversion expecting exception */
+ public static <T extends AslMarshallable> void hrToOdExpectException(
+ AslMarshallableFactory<T> factory, String hrFolderPath, String fileName) {
+ assertThrows(
+ MalformedXmlException.class,
+ () -> {
+ factory.createFromHrElements(
+ TestUtils.getElementsFromResource(Paths.get(hrFolderPath, fileName)));
+ });
+ }
+
+ /** Helper for testing on-device to human-readable conversion expecting exception */
+ public static <T extends AslMarshallable> void odToHrExpectException(
+ AslMarshallableFactory<T> factory, String odFolderPath, String fileName) {
+ assertThrows(
+ MalformedXmlException.class,
+ () -> {
+ factory.createFromOdElements(
+ TestUtils.getElementsFromResource(Paths.get(odFolderPath, fileName)));
+ });
+ }
+
+ /** Helper for testing human-readable to on-device conversion */
+ public static <T extends AslMarshallable> void testHrToOd(
+ Document doc,
+ AslMarshallableFactory<T> factory,
+ String hrFolderPath,
+ String odFolderPath,
+ String fileName)
+ throws Exception {
+ testFormatToFormat(doc, factory, hrFolderPath, odFolderPath, fileName, true);
+ }
+
+ /** Helper for testing on-device to human-readable conversion */
+ public static <T extends AslMarshallable> void testOdToHr(
+ Document doc,
+ AslMarshallableFactory<T> factory,
+ String odFolderPath,
+ String hrFolderPath,
+ String fileName)
+ throws Exception {
+ testFormatToFormat(doc, factory, odFolderPath, hrFolderPath, fileName, false);
+ }
+
+ /** Helper for testing format to format conversion */
+ private static <T extends AslMarshallable> void testFormatToFormat(
+ Document doc,
+ AslMarshallableFactory<T> factory,
+ String inFolderPath,
+ String outFolderPath,
+ String fileName,
+ boolean hrToOd)
+ throws Exception {
+ AslMarshallable marshallable =
+ hrToOd
+ ? factory.createFromHrElements(
+ TestUtils.getElementsFromResource(
+ Paths.get(inFolderPath, fileName)))
+ : factory.createFromOdElements(
+ TestUtils.getElementsFromResource(
+ Paths.get(inFolderPath, fileName)));
+
+ List<Element> elements =
+ hrToOd ? marshallable.toOdDomElements(doc) : marshallable.toHrDomElements(doc);
+ if (elements.isEmpty()) {
+ throw new IllegalStateException("elements was empty.");
+ } else if (elements.size() == 1) {
+ doc.appendChild(elements.get(0));
+ } else {
+ Element root = doc.createElement(TestUtils.HOLDER_TAG_NAME);
+ for (var child : elements) {
+ root.appendChild(child);
+ }
+ doc.appendChild(root);
+ }
+ String converted = TestUtils.getFormattedXml(TestUtils.docToStr(doc, true), true);
+ System.out.println("Converted: " + converted);
+ String expectedOutContents =
+ TestUtils.getFormattedXml(
+ TestUtils.readStrFromResource(Paths.get(outFolderPath, fileName)), true);
+ System.out.println("Expected: " + expectedOutContents);
+ assertEquals(expectedOutContents, converted);
+ }
+
+ private static void stripEmptyElements(Node node) {
+ NodeList children = node.getChildNodes();
+ for (int i = 0; i < children.getLength(); ++i) {
+ Node child = children.item(i);
+ if (child.getNodeType() == Node.TEXT_NODE) {
+ if (child.getTextContent().trim().length() == 0) {
+ child.getParentNode().removeChild(child);
+ i--;
+ }
+ }
+ stripEmptyElements(child);
+ }
+ }
+}
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/missing-version.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/missing-version.xml
new file mode 100644
index 000000000000..ec0cd702fd43
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/missing-version.xml
@@ -0,0 +1,3 @@
+<app-metadata-bundles>
+
+</app-metadata-bundles> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/valid-empty.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/valid-empty.xml
new file mode 100644
index 000000000000..19bfd826f770
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/valid-empty.xml
@@ -0,0 +1 @@
+<app-metadata-bundles version="123456"></app-metadata-bundles> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/with-safety-labels.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/with-safety-labels.xml
new file mode 100644
index 000000000000..53794a1d1c80
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/with-safety-labels.xml
@@ -0,0 +1,4 @@
+<app-metadata-bundles version="123456">
+ <safety-labels version="12345">
+ </safety-labels>
+</app-metadata-bundles> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/with-system-app-safety-label.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/with-system-app-safety-label.xml
new file mode 100644
index 000000000000..afb048632bc6
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/with-system-app-safety-label.xml
@@ -0,0 +1,4 @@
+<app-metadata-bundles version="123456">
+<system-app-safety-label declaration="true">
+</system-app-safety-label>
+</app-metadata-bundles> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/with-transparency-info.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/with-transparency-info.xml
new file mode 100644
index 000000000000..00bcfa80e9b1
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/with-transparency-info.xml
@@ -0,0 +1,4 @@
+<app-metadata-bundles version="123456">
+<transparency-info>
+</transparency-info>
+</app-metadata-bundles> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/missing-version.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/missing-version.xml
new file mode 100644
index 000000000000..1aa3aa94ca6d
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/missing-version.xml
@@ -0,0 +1,2 @@
+<bundle>
+</bundle> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/valid-empty.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/valid-empty.xml
new file mode 100644
index 000000000000..37bdfad4065f
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/valid-empty.xml
@@ -0,0 +1,3 @@
+<bundle>
+ <long name="version" value="123456"/>
+</bundle> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/with-safety-labels.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/with-safety-labels.xml
new file mode 100644
index 000000000000..74644ed0413c
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/with-safety-labels.xml
@@ -0,0 +1,6 @@
+<bundle>
+ <long name="version" value="123456"/>
+ <pbundle_as_map name="safety_labels">
+ <long name="version" value="12345"/>
+ </pbundle_as_map>
+</bundle>
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/with-system-app-safety-label.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/with-system-app-safety-label.xml
new file mode 100644
index 000000000000..e8640c4f0934
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/with-system-app-safety-label.xml
@@ -0,0 +1,6 @@
+<bundle>
+ <long name="version" value="123456"/>
+ <pbundle_as_map name="system_app_safety_label">
+ <boolean name="declaration" value="true"/>
+ </pbundle_as_map>
+</bundle> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/with-transparency-info.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/with-transparency-info.xml
new file mode 100644
index 000000000000..63c5094333cc
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/with-transparency-info.xml
@@ -0,0 +1,4 @@
+<bundle>
+ <long name="version" value="123456"/>
+ <pbundle_as_map name="transparency_info"/>
+</bundle> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/appinfo/hr/all-fields-valid.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/appinfo/hr/all-fields-valid.xml
new file mode 100644
index 000000000000..883170a2d36f
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/appinfo/hr/all-fields-valid.xml
@@ -0,0 +1,14 @@
+<app-info
+ title="beervision"
+ description="a beer app"
+ containsAds="true"
+ obeyAps="false"
+ adsFingerprinting="false"
+ securityFingerprinting="false"
+ privacyPolicy="www.example.com"
+ securityEndpoints="url1|url2|url3"
+ firstPartyEndpoints="url1"
+ serviceProviderEndpoints="url55|url56"
+ category="Food and drink"
+ email="max@maxloh.com"
+ website="www.example.com" /> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/appinfo/od/all-fields-valid.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/appinfo/od/all-fields-valid.xml
new file mode 100644
index 000000000000..6e976a3278de
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/appinfo/od/all-fields-valid.xml
@@ -0,0 +1,25 @@
+
+<pbundle_as_map name="app_info">
+ <string name="title" value="beervision"/>
+ <string name="description" value="a beer app"/>
+ <boolean name="contains_ads" value="true"/>
+ <boolean name="obey_aps" value="false"/>
+ <boolean name="ads_fingerprinting" value="false"/>
+ <boolean name="security_fingerprinting" value="false"/>
+ <string name="privacy_policy" value="www.example.com"/>
+ <string-array name="security_endpoint" num="3">
+ <item value="url1"/>
+ <item value="url2"/>
+ <item value="url3"/>
+ </string-array>
+ <string-array name="first_party_endpoint" num="1">
+ <item value="url1"/>
+ </string-array>
+ <string-array name="service_provider_endpoint" num="2">
+ <item value="url55"/>
+ <item value="url56"/>
+ </string-array>
+ <string name="category" value="Food and drink"/>
+ <string name="email" value="max@maxloh.com"/>
+ <string name="website" value="www.example.com"/>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-actions-in-app.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-actions-in-app.xml
new file mode 100644
index 000000000000..520e525679b8
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-actions-in-app.xml
@@ -0,0 +1,17 @@
+<holder_of_flattened_for_testing>
+ <data-shared dataCategory="actions_in_app"
+ dataType="user_interaction"
+ purposes="analytics" />
+ <data-shared dataCategory="actions_in_app"
+ dataType="in_app_search_history"
+ purposes="analytics" />
+ <data-shared dataCategory="actions_in_app"
+ dataType="installed_apps"
+ purposes="analytics" />
+ <data-shared dataCategory="actions_in_app"
+ dataType="user_generated_content"
+ purposes="analytics" />
+ <data-shared dataCategory="actions_in_app"
+ dataType="other"
+ purposes="analytics" />
+</holder_of_flattened_for_testing> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-app-performance.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-app-performance.xml
new file mode 100644
index 000000000000..0d08e5b5ae4d
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-app-performance.xml
@@ -0,0 +1,11 @@
+<holder_of_flattened_for_testing>
+ <data-shared dataCategory="app_performance"
+ dataType="crash_logs"
+ purposes="analytics" />
+ <data-shared dataCategory="app_performance"
+ dataType="performance_diagnostics"
+ purposes="analytics" />
+ <data-shared dataCategory="app_performance"
+ dataType="other"
+ purposes="analytics" />
+</holder_of_flattened_for_testing> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-audio.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-audio.xml
new file mode 100644
index 000000000000..b1cf3b44fd4a
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-audio.xml
@@ -0,0 +1,11 @@
+<holder_of_flattened_for_testing>
+ <data-shared dataCategory="audio"
+ dataType="sound_recordings"
+ purposes="analytics" />
+ <data-shared dataCategory="audio"
+ dataType="music_files"
+ purposes="analytics" />
+ <data-shared dataCategory="audio"
+ dataType="other"
+ purposes="analytics" />
+</holder_of_flattened_for_testing> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-calendar.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-calendar.xml
new file mode 100644
index 000000000000..a723070c43b7
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-calendar.xml
@@ -0,0 +1,5 @@
+<holder_of_flattened_for_testing>
+ <data-shared dataCategory="calendar"
+ dataType="calendar"
+ purposes="analytics" />
+</holder_of_flattened_for_testing> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-contacts.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-contacts.xml
new file mode 100644
index 000000000000..2fe28ffe4940
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-contacts.xml
@@ -0,0 +1,5 @@
+<holder_of_flattened_for_testing>
+ <data-shared dataCategory="contacts"
+ dataType="contacts"
+ purposes="analytics" />
+</holder_of_flattened_for_testing> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-email-text-message.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-email-text-message.xml
new file mode 100644
index 000000000000..49a326fc43e5
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-email-text-message.xml
@@ -0,0 +1,11 @@
+<holder_of_flattened_for_testing>
+ <data-shared dataCategory="email_text_message"
+ dataType="emails"
+ purposes="analytics" />
+ <data-shared dataCategory="email_text_message"
+ dataType="text_messages"
+ purposes="analytics" />
+ <data-shared dataCategory="email_text_message"
+ dataType="other"
+ purposes="analytics" />
+</holder_of_flattened_for_testing> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-financial.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-financial.xml
new file mode 100644
index 000000000000..f5de3707ef8a
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-financial.xml
@@ -0,0 +1,14 @@
+<holder_of_flattened_for_testing>
+ <data-shared dataCategory="financial"
+ dataType="card_bank_account"
+ purposes="analytics" />
+ <data-shared dataCategory="financial"
+ dataType="purchase_history"
+ purposes="analytics" />
+ <data-shared dataCategory="financial"
+ dataType="credit_score"
+ purposes="analytics" />
+ <data-shared dataCategory="financial"
+ dataType="other"
+ purposes="analytics" />
+</holder_of_flattened_for_testing> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-health-fitness.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-health-fitness.xml
new file mode 100644
index 000000000000..9891f8170bd7
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-health-fitness.xml
@@ -0,0 +1,8 @@
+<holder_of_flattened_for_testing>
+ <data-shared dataCategory="health_fitness"
+ dataType="health"
+ purposes="analytics" />
+ <data-shared dataCategory="health_fitness"
+ dataType="fitness"
+ purposes="analytics" />
+</holder_of_flattened_for_testing> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-identifiers.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-identifiers.xml
new file mode 100644
index 000000000000..3e74da1ad527
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-identifiers.xml
@@ -0,0 +1,5 @@
+<holder_of_flattened_for_testing>
+ <data-shared dataCategory="identifiers"
+ dataType="other"
+ purposes="analytics" />
+</holder_of_flattened_for_testing> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-location.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-location.xml
new file mode 100644
index 000000000000..4762f16d64db
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-location.xml
@@ -0,0 +1,8 @@
+<holder_of_flattened_for_testing>
+ <data-shared dataCategory="location"
+ dataType="approx_location"
+ purposes="analytics" />
+ <data-shared dataCategory="location"
+ dataType="precise_location"
+ purposes="analytics" />
+</holder_of_flattened_for_testing> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-personal-empty-purpose.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-personal-empty-purpose.xml
new file mode 100644
index 000000000000..964e178e4dbd
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-personal-empty-purpose.xml
@@ -0,0 +1,5 @@
+<holder_of_flattened_for_testing>
+ <data-shared dataCategory="personal"
+ dataType="email_address"
+ purposes="" />
+</holder_of_flattened_for_testing> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-personal-missing-purpose.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-personal-missing-purpose.xml
new file mode 100644
index 000000000000..3ce1288fb2c3
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-personal-missing-purpose.xml
@@ -0,0 +1,4 @@
+<holder_of_flattened_for_testing>
+ <data-shared dataCategory="personal"
+ dataType="email_address" />
+</holder_of_flattened_for_testing> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-personal-partial.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-personal-partial.xml
new file mode 100644
index 000000000000..68baae30ef4f
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-personal-partial.xml
@@ -0,0 +1,8 @@
+<holder_of_flattened_for_testing>
+ <data-shared dataCategory="personal"
+ dataType="name"
+ purposes="analytics|developer_communications" />
+ <data-shared dataCategory="personal"
+ dataType="email_address"
+ purposes="analytics" />
+</holder_of_flattened_for_testing> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-personal-unrecognized-type.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-personal-unrecognized-type.xml
new file mode 100644
index 000000000000..921a90a3fde7
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-personal-unrecognized-type.xml
@@ -0,0 +1,5 @@
+<holder_of_flattened_for_testing>
+ <data-shared dataCategory="personal"
+ dataType="unrecognized"
+ purposes="analytics" />
+</holder_of_flattened_for_testing> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-personal.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-personal.xml
new file mode 100644
index 000000000000..4533773ec9c1
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-personal.xml
@@ -0,0 +1,31 @@
+<holder_of_flattened_for_testing>
+ <data-shared dataCategory="personal"
+ dataType="name"
+ ephemeral="true"
+ isCollectionOptional="true"
+ purposes="analytics|developer_communications" />
+ <data-shared dataCategory="personal"
+ dataType="email_address"
+ purposes="analytics" />
+ <data-shared dataCategory="personal"
+ dataType="physical_address"
+ purposes="analytics" />
+ <data-shared dataCategory="personal"
+ dataType="phone_number"
+ purposes="analytics" />
+ <data-shared dataCategory="personal"
+ dataType="race_ethnicity"
+ purposes="analytics" />
+ <data-shared dataCategory="personal"
+ dataType="political_or_religious_beliefs"
+ purposes="analytics" />
+ <data-shared dataCategory="personal"
+ dataType="sexual_orientation_or_gender_identity"
+ purposes="analytics" />
+ <data-shared dataCategory="personal"
+ dataType="personal_identifiers"
+ purposes="analytics" />
+ <data-shared dataCategory="personal"
+ dataType="other"
+ purposes="analytics" />
+</holder_of_flattened_for_testing> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-photo-video.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-photo-video.xml
new file mode 100644
index 000000000000..234fb265ae55
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-photo-video.xml
@@ -0,0 +1,8 @@
+<holder_of_flattened_for_testing>
+ <data-shared dataCategory="photo_video"
+ dataType="photos"
+ purposes="analytics" />
+ <data-shared dataCategory="photo_video"
+ dataType="videos"
+ purposes="analytics" />
+</holder_of_flattened_for_testing> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-search-and-browsing.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-search-and-browsing.xml
new file mode 100644
index 000000000000..db851633aec5
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-search-and-browsing.xml
@@ -0,0 +1,5 @@
+<holder_of_flattened_for_testing>
+ <data-shared dataCategory="search_and_browsing"
+ dataType="web_browsing_history"
+ purposes="analytics" />
+</holder_of_flattened_for_testing> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-storage.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-storage.xml
new file mode 100644
index 000000000000..9aad02de8877
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-storage.xml
@@ -0,0 +1,5 @@
+<holder_of_flattened_for_testing>
+ <data-shared dataCategory="storage"
+ dataType="files_docs"
+ purposes="analytics" />
+</holder_of_flattened_for_testing> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-unrecognized.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-unrecognized.xml
new file mode 100644
index 000000000000..64b9ea72e05b
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/hr/data-category-unrecognized.xml
@@ -0,0 +1,5 @@
+<holder_of_flattened_for_testing>
+ <data-shared dataCategory="unrecognized"
+ dataType="email_address"
+ purposes="analytics" />
+</holder_of_flattened_for_testing> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-actions-in-app.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-actions-in-app.xml
new file mode 100644
index 000000000000..5b99900b5a8a
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-actions-in-app.xml
@@ -0,0 +1,27 @@
+<pbundle_as_map name="actions_in_app">
+ <pbundle_as_map name="user_interaction">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="in_app_search_history">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="installed_apps">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="user_generated_content">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="other">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-app-performance.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-app-performance.xml
new file mode 100644
index 000000000000..0fe102202be5
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-app-performance.xml
@@ -0,0 +1,17 @@
+<pbundle_as_map name="app_performance">
+ <pbundle_as_map name="crash_logs">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="performance_diagnostics">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="other">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-audio.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-audio.xml
new file mode 100644
index 000000000000..51f1dfd6d823
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-audio.xml
@@ -0,0 +1,17 @@
+<pbundle_as_map name="audio">
+ <pbundle_as_map name="sound_recordings">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="music_files">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="other">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-calendar.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-calendar.xml
new file mode 100644
index 000000000000..326da47a1cb9
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-calendar.xml
@@ -0,0 +1,7 @@
+<pbundle_as_map name="calendar">
+ <pbundle_as_map name="calendar">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-contacts.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-contacts.xml
new file mode 100644
index 000000000000..5d4387d2f906
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-contacts.xml
@@ -0,0 +1,7 @@
+<pbundle_as_map name="contacts">
+ <pbundle_as_map name="contacts">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-email-text-message.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-email-text-message.xml
new file mode 100644
index 000000000000..5ac98f56ace6
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-email-text-message.xml
@@ -0,0 +1,17 @@
+<pbundle_as_map name="email_text_message">
+ <pbundle_as_map name="emails">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="text_messages">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="other">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-financial.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-financial.xml
new file mode 100644
index 000000000000..a66f1a44dcb3
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-financial.xml
@@ -0,0 +1,22 @@
+<pbundle_as_map name="financial">
+ <pbundle_as_map name="card_bank_account">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="purchase_history">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="credit_score">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="other">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-health-fitness.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-health-fitness.xml
new file mode 100644
index 000000000000..8e697b47364c
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-health-fitness.xml
@@ -0,0 +1,12 @@
+<pbundle_as_map name="health_fitness">
+ <pbundle_as_map name="health">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="fitness">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-identifiers.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-identifiers.xml
new file mode 100644
index 000000000000..34b4016e1364
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-identifiers.xml
@@ -0,0 +1,7 @@
+<pbundle_as_map name="identifiers">
+ <pbundle_as_map name="other">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-location.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-location.xml
new file mode 100644
index 000000000000..db2e6965ff2a
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-location.xml
@@ -0,0 +1,12 @@
+<pbundle_as_map name="location">
+ <pbundle_as_map name="approx_location">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="precise_location">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-personal-partial.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-personal-partial.xml
new file mode 100644
index 000000000000..839922a8bec6
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-personal-partial.xml
@@ -0,0 +1,13 @@
+<pbundle_as_map name="personal">
+ <pbundle_as_map name="name">
+ <int-array name="purposes" num="2">
+ <item value="2" />
+ <item value="3" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="email_address">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-personal.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-personal.xml
new file mode 100644
index 000000000000..43650b6a77fe
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-personal.xml
@@ -0,0 +1,50 @@
+<pbundle_as_map name="personal">
+ <pbundle_as_map name="name">
+ <int-array name="purposes" num="2">
+ <item value="2" />
+ <item value="3" />
+ </int-array>
+ <boolean name="is_collection_optional" value="true" />
+ <boolean name="ephemeral" value="true" />
+ </pbundle_as_map>
+ <pbundle_as_map name="email_address">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="physical_address">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="phone_number">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="race_ethnicity">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="political_or_religious_beliefs">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="sexual_orientation_or_gender_identity">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="personal_identifiers">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="other">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-photo-video.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-photo-video.xml
new file mode 100644
index 000000000000..2a3178005df3
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-photo-video.xml
@@ -0,0 +1,12 @@
+<pbundle_as_map name="photo_video">
+ <pbundle_as_map name="photos">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="videos">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-search-and-browsing.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-search-and-browsing.xml
new file mode 100644
index 000000000000..9e654efcc5ab
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-search-and-browsing.xml
@@ -0,0 +1,7 @@
+<pbundle_as_map name="search_and_browsing">
+ <pbundle_as_map name="web_browsing_history">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-storage.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-storage.xml
new file mode 100644
index 000000000000..9abc37fdb2b9
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datacategory/od/data-category-storage.xml
@@ -0,0 +1,7 @@
+<pbundle_as_map name="storage">
+ <pbundle_as_map name="files_docs">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-actions-in-app.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-actions-in-app.xml
new file mode 100644
index 000000000000..680e01a97af3
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-actions-in-app.xml
@@ -0,0 +1,12 @@
+<data-labels>
+ <data-shared dataType="actions_in_app_data_type_user_interaction"
+ purposes="analytics" />
+ <data-shared dataType="actions_in_app_data_type_in_app_search_history"
+ purposes="analytics" />
+ <data-shared dataType="actions_in_app_data_type_installed_apps"
+ purposes="analytics" />
+ <data-shared dataType="actions_in_app_data_type_user_generated_content"
+ purposes="analytics" />
+ <data-shared dataType="actions_in_app_data_type_other"
+ purposes="analytics" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-app-performance.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-app-performance.xml
new file mode 100644
index 000000000000..db114bfe15db
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-app-performance.xml
@@ -0,0 +1,8 @@
+<data-labels>
+ <data-shared dataType="app_performance_data_type_crash_logs"
+ purposes="analytics" />
+ <data-shared dataType="app_performance_data_type_performance_diagnostics"
+ purposes="analytics" />
+ <data-shared dataType="app_performance_data_type_other"
+ purposes="analytics" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-audio.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-audio.xml
new file mode 100644
index 000000000000..cf273f4dada0
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-audio.xml
@@ -0,0 +1,8 @@
+<data-labels>
+ <data-shared dataType="audio_data_type_sound_recordings"
+ purposes="analytics" />
+ <data-shared dataType="audio_data_type_music_files"
+ purposes="analytics" />
+ <data-shared dataType="audio_data_type_other"
+ purposes="analytics" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-calendar.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-calendar.xml
new file mode 100644
index 000000000000..16f9d9b699e4
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-calendar.xml
@@ -0,0 +1,4 @@
+<data-labels>
+ <data-shared dataType="calendar_data_type_calendar"
+ purposes="analytics" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-contacts.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-contacts.xml
new file mode 100644
index 000000000000..6d7a4e8c976d
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-contacts.xml
@@ -0,0 +1,4 @@
+<data-labels>
+ <data-shared dataType="contacts_data_type_contacts"
+ purposes="analytics" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-email-text-message.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-email-text-message.xml
new file mode 100644
index 000000000000..7a9e978afd10
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-email-text-message.xml
@@ -0,0 +1,8 @@
+<data-labels>
+ <data-shared dataType="email_text_message_data_type_emails"
+ purposes="analytics" />
+ <data-shared dataType="email_text_message_data_type_text_messages"
+ purposes="analytics" />
+ <data-shared dataType="email_text_message_data_type_other"
+ purposes="analytics" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-financial.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-financial.xml
new file mode 100644
index 000000000000..24385b64fe6c
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-financial.xml
@@ -0,0 +1,10 @@
+<data-labels>
+ <data-shared dataType="financial_data_type_card_bank_account"
+ purposes="analytics" />
+ <data-shared dataType="financial_data_type_purchase_history"
+ purposes="analytics" />
+ <data-shared dataType="financial_data_type_credit_score"
+ purposes="analytics" />
+ <data-shared dataType="financial_data_type_other"
+ purposes="analytics" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-health-fitness.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-health-fitness.xml
new file mode 100644
index 000000000000..faf30b059eef
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-health-fitness.xml
@@ -0,0 +1,6 @@
+<data-labels>
+ <data-shared dataType="health_fitness_data_type_health"
+ purposes="analytics" />
+ <data-shared dataType="health_fitness_data_type_fitness"
+ purposes="analytics" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-identifiers.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-identifiers.xml
new file mode 100644
index 000000000000..510190660bef
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-identifiers.xml
@@ -0,0 +1,4 @@
+<data-labels>
+ <data-shared dataType="identifiers_data_type_other"
+ purposes="analytics" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-location.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-location.xml
new file mode 100644
index 000000000000..72cda7e40deb
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-location.xml
@@ -0,0 +1,6 @@
+<data-labels>
+ <data-shared dataType="location_data_type_approx_location"
+ purposes="analytics" />
+ <data-shared dataType="location_data_type_precise_location"
+ purposes="analytics" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-empty-purpose.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-empty-purpose.xml
new file mode 100644
index 000000000000..25586814c966
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-empty-purpose.xml
@@ -0,0 +1,4 @@
+<data-labels>
+ <data-shared dataType="personal_data_type_email_address"
+ purposes="" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-missing-purpose.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-missing-purpose.xml
new file mode 100644
index 000000000000..c5a54755387b
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-missing-purpose.xml
@@ -0,0 +1,3 @@
+<data-labels>
+ <data-shared dataType="personal_data_type_email_address" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-partial.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-partial.xml
new file mode 100644
index 000000000000..6ccf336f8556
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-partial.xml
@@ -0,0 +1,6 @@
+<data-labels>
+ <data-shared dataType="personal_data_type_name"
+ purposes="analytics|developer_communications" />
+ <data-shared dataType="personal_data_type_email_address"
+ purposes="analytics" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-unrecognized-type.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-unrecognized-type.xml
new file mode 100644
index 000000000000..bd88adafea60
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-unrecognized-type.xml
@@ -0,0 +1,4 @@
+<data-labels>
+ <data-shared dataType="personal_data_type_unrecognized"
+ purposes="analytics" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal.xml
new file mode 100644
index 000000000000..742ed86ed4e5
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal.xml
@@ -0,0 +1,21 @@
+<data-labels>
+ <data-shared dataType="personal_data_type_name"
+ isSharingOptional="true"
+ purposes="analytics|developer_communications" />
+ <data-shared dataType="personal_data_type_email_address"
+ purposes="analytics" />
+ <data-shared dataType="personal_data_type_physical_address"
+ purposes="analytics" />
+ <data-shared dataType="personal_data_type_phone_number"
+ purposes="analytics" />
+ <data-shared dataType="personal_data_type_race_ethnicity"
+ purposes="analytics" />
+ <data-shared dataType="personal_data_type_political_or_religious_beliefs"
+ purposes="analytics" />
+ <data-shared dataType="personal_data_type_sexual_orientation_or_gender_identity"
+ purposes="analytics" />
+ <data-shared dataType="personal_data_type_personal_identifiers"
+ purposes="analytics" />
+ <data-shared dataType="personal_data_type_other"
+ purposes="analytics" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-photo-video.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-photo-video.xml
new file mode 100644
index 000000000000..d416063b87da
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-photo-video.xml
@@ -0,0 +1,6 @@
+<data-labels>
+ <data-shared dataType="photo_video_data_type_photos"
+ purposes="analytics" />
+ <data-shared dataType="photo_video_data_type_videos"
+ purposes="analytics" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-search-and-browsing.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-search-and-browsing.xml
new file mode 100644
index 000000000000..3d932d6d01bf
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-search-and-browsing.xml
@@ -0,0 +1,4 @@
+<data-labels>
+ <data-shared dataType="search_and_browsing_data_type_web_browsing_history"
+ purposes="analytics" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-storage.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-storage.xml
new file mode 100644
index 000000000000..704cb1c4674e
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-storage.xml
@@ -0,0 +1,4 @@
+<data-labels>
+ <data-shared dataType="storage_data_type_files_docs"
+ purposes="analytics" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized-type.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized-type.xml
new file mode 100644
index 000000000000..bd88adafea60
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized-type.xml
@@ -0,0 +1,4 @@
+<data-labels>
+ <data-shared dataType="personal_data_type_unrecognized"
+ purposes="analytics" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized.xml
new file mode 100644
index 000000000000..a578d7343f28
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized.xml
@@ -0,0 +1,4 @@
+<data-labels>
+ <data-shared dataType="unrecognized_data_type_email_address"
+ purposes="analytics" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-collected-shared.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-collected-shared.xml
new file mode 100644
index 000000000000..c0bd6529eac0
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-collected-shared.xml
@@ -0,0 +1,8 @@
+<data-labels>
+ <data-accessed dataType="location_data_type_approx_location"
+ purposes="app_functionality" />
+ <data-collected dataType="location_data_type_precise_location"
+ purposes="app_functionality" />
+ <data-shared dataType="personal_data_type_name"
+ purposes="app_functionality" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-invalid-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-invalid-bool.xml
new file mode 100644
index 000000000000..d09fc3b48f51
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-invalid-bool.xml
@@ -0,0 +1,5 @@
+<data-labels>
+ <data-accessed dataType="location_data_type_approx_location"
+ isSharingOptional="false"
+ purposes="app_functionality" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-valid-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-valid-bool.xml
new file mode 100644
index 000000000000..6e7f81257116
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-valid-bool.xml
@@ -0,0 +1,4 @@
+<data-labels>
+ <data-accessed dataType="location_data_type_approx_location"
+ purposes="app_functionality" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-ephemeral-collision.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-ephemeral-collision.xml
new file mode 100644
index 000000000000..ee362feb2ca5
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-ephemeral-collision.xml
@@ -0,0 +1,14 @@
+<data-labels>
+ <data-collected dataType="photo_video_data_type_photos"
+ isCollectionOptional="true"
+ purposes="app_functionality" />
+ <data-collected-ephemeral dataType="location_data_type_approx_location"
+ isCollectionOptional="false"
+ purposes="app_functionality" />
+ <data-collected dataType="location_data_type_approx_location"
+ isCollectionOptional="true"
+ purposes="app_functionality" />
+ <data-collected-ephemeral dataType="contacts_data_type_contacts"
+ isCollectionOptional="true"
+ purposes="app_functionality" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-ephemeral.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-ephemeral.xml
new file mode 100644
index 000000000000..79c900027666
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-ephemeral.xml
@@ -0,0 +1,14 @@
+<data-labels>
+ <data-collected dataType="photo_video_data_type_photos"
+ isCollectionOptional="true"
+ purposes="app_functionality" />
+ <data-collected dataType="location_data_type_precise_location"
+ isCollectionOptional="true"
+ purposes="app_functionality" />
+ <data-collected-ephemeral dataType="location_data_type_approx_location"
+ isCollectionOptional="false"
+ purposes="app_functionality" />
+ <data-collected-ephemeral dataType="contacts_data_type_contacts"
+ isCollectionOptional="true"
+ purposes="app_functionality" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-invalid-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-invalid-bool.xml
new file mode 100644
index 000000000000..801fadaadcdd
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-invalid-bool.xml
@@ -0,0 +1,5 @@
+<data-labels>
+ <data-collected dataType="location_data_type_approx_location"
+ isSharingOptional="false"
+ purposes="app_functionality" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-valid-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-valid-bool.xml
new file mode 100644
index 000000000000..1ada12db0af8
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-valid-bool.xml
@@ -0,0 +1,5 @@
+<data-labels>
+ <data-collected dataType="location_data_type_approx_location"
+ isCollectionOptional="false"
+ purposes="app_functionality" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-shared-invalid-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-shared-invalid-bool.xml
new file mode 100644
index 000000000000..b327d88b2b8a
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-shared-invalid-bool.xml
@@ -0,0 +1,5 @@
+<data-labels>
+ <data-shared dataType="location_data_type_approx_location"
+ isCollectionOptional="false"
+ purposes="app_functionality" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-shared-valid-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-shared-valid-bool.xml
new file mode 100644
index 000000000000..34bd0de2bf51
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-shared-valid-bool.xml
@@ -0,0 +1,5 @@
+<data-labels>
+ <data-shared dataType="location_data_type_approx_location"
+ isSharingOptional="false"
+ purposes="app_functionality" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-actions-in-app.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-actions-in-app.xml
new file mode 100644
index 000000000000..c5fef58cb25c
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-actions-in-app.xml
@@ -0,0 +1,31 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="actions_in_app">
+ <pbundle_as_map name="user_interaction">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="in_app_search_history">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="installed_apps">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="user_generated_content">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="other">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-app-performance.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-app-performance.xml
new file mode 100644
index 000000000000..4570145a0436
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-app-performance.xml
@@ -0,0 +1,21 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="app_performance">
+ <pbundle_as_map name="crash_logs">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="performance_diagnostics">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="other">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-audio.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-audio.xml
new file mode 100644
index 000000000000..120f626549f7
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-audio.xml
@@ -0,0 +1,21 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="audio">
+ <pbundle_as_map name="sound_recordings">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="music_files">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="other">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-calendar.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-calendar.xml
new file mode 100644
index 000000000000..59eb93812690
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-calendar.xml
@@ -0,0 +1,11 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="calendar">
+ <pbundle_as_map name="calendar">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-contacts.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-contacts.xml
new file mode 100644
index 000000000000..f952bfb4df79
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-contacts.xml
@@ -0,0 +1,11 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="contacts">
+ <pbundle_as_map name="contacts">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-email-text-message.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-email-text-message.xml
new file mode 100644
index 000000000000..bcaa716559d4
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-email-text-message.xml
@@ -0,0 +1,21 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="email_text_message">
+ <pbundle_as_map name="emails">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="text_messages">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="other">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-financial.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-financial.xml
new file mode 100644
index 000000000000..a7bc82e38dac
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-financial.xml
@@ -0,0 +1,26 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="financial">
+ <pbundle_as_map name="card_bank_account">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="purchase_history">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="credit_score">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="other">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-health-fitness.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-health-fitness.xml
new file mode 100644
index 000000000000..f3ab2bd90733
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-health-fitness.xml
@@ -0,0 +1,16 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="health_fitness">
+ <pbundle_as_map name="health">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="fitness">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-identifiers.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-identifiers.xml
new file mode 100644
index 000000000000..05c07e54c5a8
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-identifiers.xml
@@ -0,0 +1,11 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="identifiers">
+ <pbundle_as_map name="other">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-location.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-location.xml
new file mode 100644
index 000000000000..931d1adc0218
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-location.xml
@@ -0,0 +1,16 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="location">
+ <pbundle_as_map name="approx_location">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="precise_location">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-empty-purpose.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-empty-purpose.xml
new file mode 100644
index 000000000000..83f4a67cd54d
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-empty-purpose.xml
@@ -0,0 +1,11 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="personal">
+ <pbundle_as_map name="email_address">
+
+ <int-array name="purposes" num="2">
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-missing-purpose.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-missing-purpose.xml
new file mode 100644
index 000000000000..532f5deee655
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-missing-purpose.xml
@@ -0,0 +1,8 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="personal">
+ <pbundle_as_map name="email_address">
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-partial.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-partial.xml
new file mode 100644
index 000000000000..974ea6922fce
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-partial.xml
@@ -0,0 +1,17 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="personal">
+ <pbundle_as_map name="name">
+ <int-array name="purposes" num="2">
+ <item value="2" />
+ <item value="3" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="email_address">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal.xml
new file mode 100644
index 000000000000..62c26ab20c68
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal.xml
@@ -0,0 +1,53 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="personal">
+ <pbundle_as_map name="name">
+ <int-array name="purposes" num="2">
+ <item value="2" />
+ <item value="3" />
+ </int-array>
+ <boolean name="is_sharing_optional" value="true" />
+ </pbundle_as_map>
+ <pbundle_as_map name="email_address">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="physical_address">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="phone_number">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="race_ethnicity">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="political_or_religious_beliefs">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="sexual_orientation_or_gender_identity">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="personal_identifiers">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="other">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-photo-video.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-photo-video.xml
new file mode 100644
index 000000000000..a752b5152c25
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-photo-video.xml
@@ -0,0 +1,16 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="photo_video">
+ <pbundle_as_map name="photos">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="videos">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-search-and-browsing.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-search-and-browsing.xml
new file mode 100644
index 000000000000..99bc1881b4f1
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-search-and-browsing.xml
@@ -0,0 +1,11 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="search_and_browsing">
+ <pbundle_as_map name="web_browsing_history">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-storage.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-storage.xml
new file mode 100644
index 000000000000..a4d2a6249119
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-storage.xml
@@ -0,0 +1,11 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="storage">
+ <pbundle_as_map name="files_docs">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-unrecognized-type.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-unrecognized-type.xml
new file mode 100644
index 000000000000..16195df53b3a
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-unrecognized-type.xml
@@ -0,0 +1,11 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="personal">
+ <pbundle_as_map name="unrecognized">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-unrecognized.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-unrecognized.xml
new file mode 100644
index 000000000000..511940eafd4a
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-unrecognized.xml
@@ -0,0 +1,11 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="unrecognized">
+ <pbundle_as_map name="email_address">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-accessed-collected-shared.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-accessed-collected-shared.xml
new file mode 100644
index 000000000000..f86dbc1a63b2
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-accessed-collected-shared.xml
@@ -0,0 +1,29 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_accessed">
+ <pbundle_as_map name="location">
+ <pbundle_as_map name="approx_location">
+ <int-array name="purposes" num="1">
+ <item value="1"/>
+ </int-array>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ <pbundle_as_map name="data_collected">
+ <pbundle_as_map name="location">
+ <pbundle_as_map name="precise_location">
+ <int-array name="purposes" num="1">
+ <item value="1"/>
+ </int-array>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="personal">
+ <pbundle_as_map name="name">
+ <int-array name="purposes" num="1">
+ <item value="1"/>
+ </int-array>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-accessed-valid-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-accessed-valid-bool.xml
new file mode 100644
index 000000000000..df000aa2a882
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-accessed-valid-bool.xml
@@ -0,0 +1,11 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_accessed">
+ <pbundle_as_map name="location">
+ <pbundle_as_map name="approx_location">
+ <int-array name="purposes" num="1">
+ <item value="1"/>
+ </int-array>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-collected-ephemeral.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-collected-ephemeral.xml
new file mode 100644
index 000000000000..c671c4be0f5a
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-collected-ephemeral.xml
@@ -0,0 +1,38 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_collected">
+ <pbundle_as_map name="photo_video">
+ <pbundle_as_map name="photos">
+ <int-array name="purposes" num="1">
+ <item value="1"/>
+ </int-array>
+ <boolean name="is_collection_optional" value="true"/>
+ <boolean name="ephemeral" value="false"/>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ <pbundle_as_map name="location">
+ <pbundle_as_map name="precise_location">
+ <int-array name="purposes" num="1">
+ <item value="1"/>
+ </int-array>
+ <boolean name="is_collection_optional" value="true"/>
+ <boolean name="ephemeral" value="false"/>
+ </pbundle_as_map>
+ <pbundle_as_map name="approx_location">
+ <int-array name="purposes" num="1">
+ <item value="1"/>
+ </int-array>
+ <boolean name="is_collection_optional" value="false"/>
+ <boolean name="ephemeral" value="true"/>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ <pbundle_as_map name="contacts">
+ <pbundle_as_map name="contacts">
+ <int-array name="purposes" num="1">
+ <item value="1"/>
+ </int-array>
+ <boolean name="is_collection_optional" value="true"/>
+ <boolean name="ephemeral" value="true"/>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-collected-invalid-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-collected-invalid-bool.xml
new file mode 100644
index 000000000000..54cc8e7049b2
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-collected-invalid-bool.xml
@@ -0,0 +1,13 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_collected">
+ <pbundle_as_map name="location">
+ <pbundle_as_map name="approx_location">
+ <int-array name="purposes" num="1">
+ <item value="1"/>
+ </int-array>
+ <boolean name="is_sharing_optional" value="false"/>
+ <boolean name="ephemeral" value="false"/>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-collected-valid-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-collected-valid-bool.xml
new file mode 100644
index 000000000000..252c7282da20
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-collected-valid-bool.xml
@@ -0,0 +1,13 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_collected">
+ <pbundle_as_map name="location">
+ <pbundle_as_map name="approx_location">
+ <int-array name="purposes" num="1">
+ <item value="1"/>
+ </int-array>
+ <boolean name="is_collection_optional" value="false"/>
+ <boolean name="ephemeral" value="false"/>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-shared-valid-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-shared-valid-bool.xml
new file mode 100644
index 000000000000..0edd8fa264c7
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-shared-valid-bool.xml
@@ -0,0 +1,12 @@
+<pbundle_as_map name="data_labels">
+<pbundle_as_map name="data_shared">
+ <pbundle_as_map name="location">
+ <pbundle_as_map name="approx_location">
+ <int-array name="purposes" num="1">
+ <item value="1"/>
+ </int-array>
+ <boolean name="is_sharing_optional" value="false"/>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/developerinfo/hr/all-fields-valid.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/developerinfo/hr/all-fields-valid.xml
new file mode 100644
index 000000000000..908d8ea2f53e
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/developerinfo/hr/all-fields-valid.xml
@@ -0,0 +1,8 @@
+<developer-info
+ name="max"
+ email="max@example.com"
+ address="111 blah lane"
+ countryRegion="US"
+ relationship="aosp"
+ website="example.com"
+ registryId="registry_id" /> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/developerinfo/od/all-fields-valid.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/developerinfo/od/all-fields-valid.xml
new file mode 100644
index 000000000000..784ec6156c1d
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/developerinfo/od/all-fields-valid.xml
@@ -0,0 +1,9 @@
+<pbundle_as_map name="developer_info">
+ <string name="name" value="max"/>
+ <string name="email" value="max@example.com"/>
+ <string name="address" value="111 blah lane"/>
+ <string name="country_region" value="US"/>
+ <long name="relationship" value="5"/>
+ <string name="website" value="example.com"/>
+ <string name="app_developer_registry_id" value="registry_id"/>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/missing-version.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/missing-version.xml
new file mode 100644
index 000000000000..762f3bdf7875
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/missing-version.xml
@@ -0,0 +1,2 @@
+<safety-labels>
+</safety-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/valid-empty.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/valid-empty.xml
new file mode 100644
index 000000000000..7decfd4865b1
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/valid-empty.xml
@@ -0,0 +1 @@
+<safety-labels version="12345"></safety-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-data-labels.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-data-labels.xml
new file mode 100644
index 000000000000..84456daedbd5
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-data-labels.xml
@@ -0,0 +1,7 @@
+<safety-labels version="12345">
+ <data-labels>
+ <data-shared dataType="location_data_type_approx_location"
+ isSharingOptional="false"
+ purposes="app_functionality" />
+ </data-labels>
+</safety-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-security-labels.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-security-labels.xml
new file mode 100644
index 000000000000..940e48a68ce8
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-security-labels.xml
@@ -0,0 +1,6 @@
+<safety-labels version="12345">
+ <security-labels
+ isDataDeletable="true"
+ isDataEncrypted="false"
+ />
+</safety-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-third-party-verification.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-third-party-verification.xml
new file mode 100644
index 000000000000..bfbc5ae70974
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-third-party-verification.xml
@@ -0,0 +1,4 @@
+<safety-labels version="12345">
+<third-party-verification url="www.example.com">
+ </third-party-verification>
+</safety-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/missing-version.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/missing-version.xml
new file mode 100644
index 000000000000..3fbe3599cd82
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/missing-version.xml
@@ -0,0 +1,2 @@
+<pbundle_as_map name="safety_labels">
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/valid-empty.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/valid-empty.xml
new file mode 100644
index 000000000000..4f03d88a3bd2
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/valid-empty.xml
@@ -0,0 +1,3 @@
+<pbundle_as_map name="safety_labels">
+ <long name="version" value="12345"/>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-data-labels.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-data-labels.xml
new file mode 100644
index 000000000000..fa2a3f841e09
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-data-labels.xml
@@ -0,0 +1,15 @@
+<pbundle_as_map name="safety_labels">
+ <long name="version" value="12345"/>
+ <pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="location">
+ <pbundle_as_map name="approx_location">
+ <int-array name="purposes" num="1">
+ <item value="1"/>
+ </int-array>
+ <boolean name="is_sharing_optional" value="false"/>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-security-labels.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-security-labels.xml
new file mode 100644
index 000000000000..b39c562b30d0
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-security-labels.xml
@@ -0,0 +1,7 @@
+<pbundle_as_map name="safety_labels">
+ <long name="version" value="12345"/>
+ <pbundle_as_map name="security_labels">
+ <boolean name="is_data_deletable" value="true" />
+ <boolean name="is_data_encrypted" value="false" />
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-third-party-verification.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-third-party-verification.xml
new file mode 100644
index 000000000000..10653ff5027b
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-third-party-verification.xml
@@ -0,0 +1,6 @@
+<pbundle_as_map name="safety_labels">
+ <long name="version" value="12345"/>
+ <pbundle_as_map name="third_party_verification">
+ <string name="url" value="www.example.com"/>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/securitylabels/hr/all-fields-valid.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/securitylabels/hr/all-fields-valid.xml
new file mode 100644
index 000000000000..e2fb592cba07
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/securitylabels/hr/all-fields-valid.xml
@@ -0,0 +1,4 @@
+<security-labels
+ isDataDeletable="true"
+ isDataEncrypted="false">
+</security-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/securitylabels/od/all-fields-valid.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/securitylabels/od/all-fields-valid.xml
new file mode 100644
index 000000000000..7b2f656ec832
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/securitylabels/od/all-fields-valid.xml
@@ -0,0 +1,4 @@
+<pbundle_as_map name="security_labels">
+ <boolean name="is_data_deletable" value="true" />
+ <boolean name="is_data_encrypted" value="false" />
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/hr/missing-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/hr/missing-bool.xml
new file mode 100644
index 000000000000..ff26c05abdb0
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/hr/missing-bool.xml
@@ -0,0 +1 @@
+<system-app-safety-label></system-app-safety-label> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/hr/valid.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/hr/valid.xml
new file mode 100644
index 000000000000..f01d7d299d4a
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/hr/valid.xml
@@ -0,0 +1 @@
+<system-app-safety-label declaration="true"></system-app-safety-label> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/missing-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/missing-bool.xml
new file mode 100644
index 000000000000..33b796552463
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/missing-bool.xml
@@ -0,0 +1,2 @@
+<pbundle_as_map name="system_app_safety_label">
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/valid.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/valid.xml
new file mode 100644
index 000000000000..fad631b37e38
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/valid.xml
@@ -0,0 +1,3 @@
+<pbundle_as_map name="system_app_safety_label">
+ <boolean name="declaration" value="true"/>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/thirdpartyverification/hr/missing-url.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/thirdpartyverification/hr/missing-url.xml
new file mode 100644
index 000000000000..6738ac2f2446
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/thirdpartyverification/hr/missing-url.xml
@@ -0,0 +1 @@
+<third-party-verification></third-party-verification> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/thirdpartyverification/hr/valid.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/thirdpartyverification/hr/valid.xml
new file mode 100644
index 000000000000..2a664f22b916
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/thirdpartyverification/hr/valid.xml
@@ -0,0 +1 @@
+<third-party-verification url="www.example.com"></third-party-verification> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/thirdpartyverification/od/missing-url.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/thirdpartyverification/od/missing-url.xml
new file mode 100644
index 000000000000..0b5a46f904e4
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/thirdpartyverification/od/missing-url.xml
@@ -0,0 +1,2 @@
+<pbundle_as_map name="third_party_verification">
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/thirdpartyverification/od/valid.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/thirdpartyverification/od/valid.xml
new file mode 100644
index 000000000000..dbeb592d7e88
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/thirdpartyverification/od/valid.xml
@@ -0,0 +1,3 @@
+<pbundle_as_map name="third_party_verification">
+ <string name="url" value="www.example.com"/>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/hr/valid-empty.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/hr/valid-empty.xml
new file mode 100644
index 000000000000..254a37fb99e8
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/hr/valid-empty.xml
@@ -0,0 +1,4 @@
+
+<transparency-info>
+
+</transparency-info> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/hr/with-app-info.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/hr/with-app-info.xml
new file mode 100644
index 000000000000..a7c48fc68cf1
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/hr/with-app-info.xml
@@ -0,0 +1,4 @@
+
+<transparency-info>
+ <app-info title="beervision" description="a beer app" containsAds="true" obeyAps="false" adsFingerprinting="false" securityFingerprinting="false" privacyPolicy="www.example.com" securityEndpoints="url1|url2|url3" firstPartyEndpoints="url1" serviceProviderEndpoints="url55|url56" category="Food and drink" email="max@maxloh.com" />
+</transparency-info> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/hr/with-developer-info.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/hr/with-developer-info.xml
new file mode 100644
index 000000000000..d16caaea320f
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/hr/with-developer-info.xml
@@ -0,0 +1,11 @@
+
+<transparency-info>
+ <developer-info
+ name="max"
+ email="max@example.com"
+ address="111 blah lane"
+ countryRegion="US"
+ relationship="aosp"
+ website="example.com"
+ registryId="registry_id" />
+</transparency-info> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/valid-empty.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/valid-empty.xml
new file mode 100644
index 000000000000..af574cf92b3a
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/valid-empty.xml
@@ -0,0 +1 @@
+<pbundle_as_map name="transparency_info"/> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/with-app-info.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/with-app-info.xml
new file mode 100644
index 000000000000..b813641f74f8
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/with-app-info.xml
@@ -0,0 +1,26 @@
+
+<pbundle_as_map name="transparency_info">
+ <pbundle_as_map name="app_info">
+ <string name="title" value="beervision"/>
+ <string name="description" value="a beer app"/>
+ <boolean name="contains_ads" value="true"/>
+ <boolean name="obey_aps" value="false"/>
+ <boolean name="ads_fingerprinting" value="false"/>
+ <boolean name="security_fingerprinting" value="false"/>
+ <string name="privacy_policy" value="www.example.com"/>
+ <string-array name="security_endpoint" num="3">
+ <item value="url1"/>
+ <item value="url2"/>
+ <item value="url3"/>
+ </string-array>
+ <string-array name="first_party_endpoint" num="1">
+ <item value="url1"/>
+ </string-array>
+ <string-array name="service_provider_endpoint" num="2">
+ <item value="url55"/>
+ <item value="url56"/>
+ </string-array>
+ <string name="category" value="Food and drink"/>
+ <string name="email" value="max@maxloh.com"/>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/with-developer-info.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/with-developer-info.xml
new file mode 100644
index 000000000000..d7a4e1a959b7
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/with-developer-info.xml
@@ -0,0 +1,12 @@
+
+<pbundle_as_map name="transparency_info">
+ <pbundle_as_map name="developer_info">
+ <string name="name" value="max"/>
+ <string name="email" value="max@example.com"/>
+ <string name="address" value="111 blah lane"/>
+ <string name="country_region" value="US"/>
+ <long name="relationship" value="5"/>
+ <string name="website" value="example.com"/>
+ <string name="app_developer_registry_id" value="registry_id"/>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/contacts/hr.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/contacts/hr.xml
new file mode 100644
index 000000000000..b2ff4495a6d2
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/contacts/hr.xml
@@ -0,0 +1,11 @@
+<app-metadata-bundles version="123">
+ <safety-labels version="12345">
+ <data-labels>
+ <data-shared dataCategory="contacts"
+ dataType="contacts"
+ isSharingOptional="false"
+ ephemeral="true"
+ purposes="analytics" />
+ </data-labels>
+ </safety-labels>
+</app-metadata-bundles> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/contacts/od.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/contacts/od.xml
new file mode 100644
index 000000000000..81277bf456a4
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/contacts/od.xml
@@ -0,0 +1,19 @@
+<bundle>
+ <long name="version" value="123"/>
+ <pbundle_as_map name="safety_labels">
+ <long name="version" value="12345"/>
+ <pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="contacts">
+ <pbundle_as_map name="contacts">
+ <int-array name="purposes" num="1">
+ <item value="2"/>
+ </int-array>
+ <boolean name="is_sharing_optional" value="false"/>
+ <boolean name="ephemeral" value="true"/>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ </pbundle_as_map>
+</bundle> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/hr.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/hr.xml
new file mode 100644
index 000000000000..41b32b5ed7b0
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/hr.xml
@@ -0,0 +1,33 @@
+<app-metadata-bundles version="123">
+ <safety-labels version="12345">
+ <data-labels>
+ <data-shared
+ dataType="location_data_type_approx_location"
+ isSharingOptional="false"
+ purposes="app_functionality" />
+ <data-shared
+ dataType="location_data_type_precise_location"
+ isSharingOptional="true"
+ purposes="app_functionality|analytics" />
+ </data-labels>
+ <security-labels
+ isDataDeletable="true"
+ isDataEncrypted="false"
+ />
+ <third-party-verification url="www.example.com">
+ </third-party-verification>
+ </safety-labels>
+ <system-app-safety-label declaration="true">
+ </system-app-safety-label>
+ <transparency-info>
+ <developer-info
+ name="max"
+ email="max@example.com"
+ address="111 blah lane"
+ countryRegion="US"
+ relationship="aosp"
+ website="example.com"
+ registryId="registry_id" />
+ <app-info title="beervision" description="a beer app" containsAds="true" obeyAps="false" adsFingerprinting="false" securityFingerprinting="false" privacyPolicy="www.example.com" securityEndpoints="url1|url2|url3" firstPartyEndpoints="url1" serviceProviderEndpoints="url55|url56" category="Food and drink" email="max@maxloh.com" />
+ </transparency-info>
+</app-metadata-bundles> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/od.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/od.xml
new file mode 100644
index 000000000000..c11ac4359ecd
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/od.xml
@@ -0,0 +1,69 @@
+<bundle>
+ <long name="version" value="123"/>
+ <pbundle_as_map name="safety_labels">
+ <long name="version" value="12345"/>
+ <pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="location">
+ <pbundle_as_map name="approx_location">
+ <int-array name="purposes" num="1">
+ <item value="1"/>
+ </int-array>
+ <boolean name="is_sharing_optional" value="false"/>
+ </pbundle_as_map>
+ <pbundle_as_map name="precise_location">
+ <int-array name="purposes" num="2">
+ <item value="1"/>
+ <item value="2"/>
+ </int-array>
+ <boolean name="is_sharing_optional" value="true"/>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ <pbundle_as_map name="security_labels">
+ <boolean name="is_data_deletable" value="true"/>
+ <boolean name="is_data_encrypted" value="false"/>
+ </pbundle_as_map>
+ <pbundle_as_map name="third_party_verification">
+ <string name="url" value="www.example.com"/>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ <pbundle_as_map name="system_app_safety_label">
+ <boolean name="declaration" value="true"/>
+ </pbundle_as_map>
+ <pbundle_as_map name="transparency_info">
+ <pbundle_as_map name="developer_info">
+ <string name="name" value="max"/>
+ <string name="email" value="max@example.com"/>
+ <string name="address" value="111 blah lane"/>
+ <string name="country_region" value="US"/>
+ <long name="relationship" value="5"/>
+ <string name="website" value="example.com"/>
+ <string name="app_developer_registry_id" value="registry_id"/>
+ </pbundle_as_map>
+ <pbundle_as_map name="app_info">
+ <string name="title" value="beervision"/>
+ <string name="description" value="a beer app"/>
+ <boolean name="contains_ads" value="true"/>
+ <boolean name="obey_aps" value="false"/>
+ <boolean name="ads_fingerprinting" value="false"/>
+ <boolean name="security_fingerprinting" value="false"/>
+ <string name="privacy_policy" value="www.example.com"/>
+ <string-array name="security_endpoint" num="3">
+ <item value="url1"/>
+ <item value="url2"/>
+ <item value="url3"/>
+ </string-array>
+ <string-array name="first_party_endpoint" num="1">
+ <item value="url1"/>
+ </string-array>
+ <string-array name="service_provider_endpoint" num="2">
+ <item value="url55"/>
+ <item value="url56"/>
+ </string-array>
+ <string name="category" value="Food and drink"/>
+ <string name="email" value="max@maxloh.com"/>
+ </pbundle_as_map>
+ </pbundle_as_map>
+</bundle> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/location/hr.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/location/hr.xml
new file mode 100644
index 000000000000..ac844b3b2767
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/location/hr.xml
@@ -0,0 +1,16 @@
+<app-metadata-bundles version="123">
+ <safety-labels version="12345">
+ <data-labels>
+ <data-shared dataCategory="location"
+ dataType="precise_location"
+ isSharingOptional="true"
+ ephemeral="true"
+ purposes="app_functionality|analytics" />
+ <data-shared dataCategory="location"
+ dataType="approx_location"
+ isSharingOptional="false"
+ ephemeral="false"
+ purposes="app_functionality" />
+ </data-labels>
+ </safety-labels>
+</app-metadata-bundles> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/location/od.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/location/od.xml
new file mode 100644
index 000000000000..d0a3bfa7e64f
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/location/od.xml
@@ -0,0 +1,27 @@
+<bundle>
+ <long name="version" value="123"/>
+ <pbundle_as_map name="safety_labels">
+ <long name="version" value="12345"/>
+ <pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="location">
+ <pbundle_as_map name="precise_location">
+ <int-array name="purposes" num="2">
+ <item value="1"/>
+ <item value="2"/>
+ </int-array>
+ <boolean name="is_sharing_optional" value="true"/>
+ <boolean name="ephemeral" value="true"/>
+ </pbundle_as_map>
+ <pbundle_as_map name="approx_location">
+ <int-array name="purposes" num="1">
+ <item value="1"/>
+ </int-array>
+ <boolean name="is_sharing_optional" value="false"/>
+ <boolean name="ephemeral" value="false"/>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ </pbundle_as_map>
+</bundle> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/test.xml b/tools/app_metadata_bundles/src/test/resources/test.xml
new file mode 100644
index 000000000000..202cc1e1c526
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/test.xml
@@ -0,0 +1,16 @@
+<app-metadata-bundles>
+ <safety-labels version="12345">
+ <data-labels>
+ <data-shared dataCategory="location"
+ dataType="precise_location"
+ isSharingOptional="true"
+ ephemeral="true"
+ purposes="app_functionality|analytics" />
+ <data-shared dataCategory="location"
+ dataType="approx_location"
+ isSharingOptional="false"
+ ephemeral="false"
+ purposes="app_functionality" />
+ </data-labels>
+ </safety-labels>
+</app-metadata-bundles> \ No newline at end of file
diff --git a/tools/codegen/src/com/android/codegen/Main.kt b/tools/codegen/src/com/android/codegen/Main.kt
index bcc6230fd53f..bcc6230fd53f 100755..100644
--- a/tools/codegen/src/com/android/codegen/Main.kt
+++ b/tools/codegen/src/com/android/codegen/Main.kt
diff --git a/tools/hoststubgen/TEST_MAPPING b/tools/hoststubgen/TEST_MAPPING
index b5d5b5fb6d92..856e6eefba15 100644
--- a/tools/hoststubgen/TEST_MAPPING
+++ b/tools/hoststubgen/TEST_MAPPING
@@ -1,14 +1,7 @@
{
- "presubmit": [
- // TODO(b/326897452): Reenable after JDK 21 switch.
- // { "name": "tiny-framework-dump-test" },
- { "name": "hoststubgentest" },
- { "name": "hoststubgen-invoke-test" }
- ],
- "ravenwood-presubmit": [
+ "imports": [
{
- "name": "CtsUtilTestCasesRavenwood",
- "host": true
+ "path": "frameworks/base/ravenwood"
}
]
}
diff --git a/tools/hoststubgen/hoststubgen/Android.bp b/tools/hoststubgen/hoststubgen/Android.bp
index 30333da5e86c..682adbc86d06 100644
--- a/tools/hoststubgen/hoststubgen/Android.bp
+++ b/tools/hoststubgen/hoststubgen/Android.bp
@@ -82,13 +82,30 @@ java_library {
jarjar_rules: "jarjar-rules.txt",
}
+// For sharing the code with other tools
+java_library_host {
+ name: "hoststubgen-lib",
+ defaults: ["ravenwood-internal-only-visibility-java"],
+ srcs: ["src/**/*.kt"],
+ static_libs: [
+ "hoststubgen-helper-runtime",
+ ],
+ libs: [
+ "junit",
+ "ow2-asm",
+ "ow2-asm-analysis",
+ "ow2-asm-commons",
+ "ow2-asm-tree",
+ "ow2-asm-util",
+ ],
+}
+
// Host-side stub generator tool.
java_binary_host {
name: "hoststubgen",
- main_class: "com.android.hoststubgen.Main",
- srcs: ["src/**/*.kt"],
+ main_class: "com.android.hoststubgen.HostStubGenMain",
static_libs: [
- "hoststubgen-helper-runtime",
+ "hoststubgen-lib",
"junit",
"ow2-asm",
"ow2-asm-analysis",
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
index 1089f82b6472..5dde265c79fb 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
@@ -16,6 +16,7 @@
package com.android.hoststubgen
import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.dumper.ApiDumper
import com.android.hoststubgen.filters.AnnotationBasedFilter
import com.android.hoststubgen.filters.ClassWidePolicyPropagatingFilter
import com.android.hoststubgen.filters.ConstantFilter
@@ -32,7 +33,8 @@ import com.android.hoststubgen.visitors.PackageRedirectRemapper
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.ClassWriter
-import org.objectweb.asm.tree.ClassNode
+import org.objectweb.asm.commons.ClassRemapper
+import org.objectweb.asm.commons.Remapper
import org.objectweb.asm.util.CheckClassAdapter
import java.io.BufferedInputStream
import java.io.FileOutputStream
@@ -52,7 +54,7 @@ class HostStubGen(val options: HostStubGenOptions) {
val stats = HostStubGenStats()
// Load all classes.
- val allClasses = loadClassStructures(options.inJar.get)
+ val allClasses = ClassNodes.loadClassStructures(options.inJar.get)
// Dump the classes, if specified.
options.inputJarDumpFile.ifSet {
@@ -70,7 +72,7 @@ class HostStubGen(val options: HostStubGenOptions) {
}
// Build the filters.
- val filter = buildFilter(errors, allClasses, options)
+ val (filter, policyFileRemapper) = buildFilter(errors, allClasses, options)
// Transform the jar.
convert(
@@ -82,62 +84,24 @@ class HostStubGen(val options: HostStubGenOptions) {
allClasses,
errors,
stats,
+ policyFileRemapper,
+ options.numShards.get,
+ options.shard.get,
)
// Dump statistics, if specified.
options.statsFile.ifSet {
- PrintWriter(it).use { pw -> stats.dump(pw) }
+ PrintWriter(it).use { pw -> stats.dumpOverview(pw) }
log.i("Dump file created at $it")
}
- }
-
- /**
- * Load all the classes, without code.
- */
- private fun loadClassStructures(inJar: String): ClassNodes {
- log.i("Reading class structure from $inJar ...")
- val start = System.currentTimeMillis()
-
- val allClasses = ClassNodes()
-
- log.withIndent {
- ZipFile(inJar).use { inZip ->
- val inEntries = inZip.entries()
-
- while (inEntries.hasMoreElements()) {
- val entry = inEntries.nextElement()
-
- BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
- if (entry.name.endsWith(".class")) {
- val cr = ClassReader(bis)
- val cn = ClassNode()
- cr.accept(cn, ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG
- or ClassReader.SKIP_FRAMES)
- if (!allClasses.addClass(cn)) {
- log.w("Duplicate class found: ${cn.name}")
- }
- } else if (entry.name.endsWith(".dex")) {
- // Seems like it's an ART jar file. We can't process it.
- // It's a fatal error.
- throw InvalidJarFileException(
- "$inJar is not a desktop jar file. It contains a *.dex file.")
- } else {
- // Unknown file type. Skip.
- while (bis.available() > 0) {
- bis.skip((1024 * 1024).toLong())
- }
- }
- }
- }
+ options.apiListFile.ifSet {
+ PrintWriter(it).use { pw ->
+ // TODO, when dumping a jar that's not framework-minus-apex.jar, we need to feed
+ // framework-minus-apex.jar so that we can dump inherited methods from it.
+ ApiDumper(pw, allClasses, null, filter).dump()
}
+ log.i("API list file created at $it")
}
- if (allClasses.size == 0) {
- log.w("$inJar contains no *.class files.")
- }
-
- val end = System.currentTimeMillis()
- log.i("Done reading class structure in %.1f second(s).", (end - start) / 1000.0)
- return allClasses
}
/**
@@ -148,7 +112,7 @@ class HostStubGen(val options: HostStubGenOptions) {
errors: HostStubGenErrors,
allClasses: ClassNodes,
options: HostStubGenOptions,
- ): OutputFilter {
+ ): Pair<OutputFilter, Remapper?> {
// We build a "chain" of multiple filters here.
//
// The filters are build in from "inside", meaning the first filter created here is
@@ -201,10 +165,14 @@ class HostStubGen(val options: HostStubGenOptions) {
filter,
)
+ var policyFileRemapper: Remapper? = null
+
// Next, "text based" filter, which allows to override polices without touching
// the target code.
options.policyOverrideFile.ifSet {
- filter = createFilterFromTextPolicyFile(it, allClasses, filter)
+ val (f, p) = createFilterFromTextPolicyFile(it, allClasses, filter)
+ filter = f
+ policyFileRemapper = p
}
// If `--intersect-stub-jar` is provided, load from these jar files too.
@@ -219,7 +187,7 @@ class HostStubGen(val options: HostStubGenOptions) {
// Apply the implicit filter.
filter = ImplicitOutputFilter(errors, allClasses, filter)
- return filter
+ return Pair(filter, policyFileRemapper)
}
/**
@@ -229,7 +197,7 @@ class HostStubGen(val options: HostStubGenOptions) {
val intersectingJars = mutableMapOf<String, ClassNodes>()
filenames.forEach { filename ->
- intersectingJars[filename] = loadClassStructures(filename)
+ intersectingJars[filename] = ClassNodes.loadClassStructures(filename)
}
return intersectingJars
}
@@ -246,6 +214,9 @@ class HostStubGen(val options: HostStubGenOptions) {
classes: ClassNodes,
errors: HostStubGenErrors,
stats: HostStubGenStats,
+ remapper: Remapper?,
+ numShards: Int,
+ shard: Int,
) {
log.i("Converting %s into [stub: %s, impl: %s] ...", inJar, outStubJar, outImplJar)
log.i("ASM CheckClassAdapter is %s", if (enableChecker) "enabled" else "disabled")
@@ -254,17 +225,32 @@ class HostStubGen(val options: HostStubGenOptions) {
val packageRedirector = PackageRedirectRemapper(options.packageRedirects)
+ var itemIndex = 0
+ var numItemsProcessed = 0
+ var numItems = -1 // == Unknown
+
log.withIndent {
// Open the input jar file and process each entry.
ZipFile(inJar).use { inZip ->
+
+ numItems = inZip.size()
+ val shardStart = numItems * shard / numShards
+ val shardNextStart = numItems * (shard + 1) / numShards
+
maybeWithZipOutputStream(outStubJar) { stubOutStream ->
maybeWithZipOutputStream(outImplJar) { implOutStream ->
val inEntries = inZip.entries()
while (inEntries.hasMoreElements()) {
val entry = inEntries.nextElement()
+ val inShard = (shardStart <= itemIndex) && (itemIndex < shardNextStart)
+ itemIndex++
+ if (!inShard) {
+ continue
+ }
convertSingleEntry(inZip, entry, stubOutStream, implOutStream,
- filter, packageRedirector, enableChecker, classes, errors,
- stats)
+ filter, packageRedirector, remapper,
+ enableChecker, classes, errors, stats)
+ numItemsProcessed++
}
log.i("Converted all entries.")
}
@@ -274,7 +260,8 @@ class HostStubGen(val options: HostStubGenOptions) {
}
}
val end = System.currentTimeMillis()
- log.i("Done transforming the jar in %.1f second(s).", (end - start) / 1000.0)
+ log.i("Done transforming the jar in %.1f second(s). %d / %d item(s) processed.",
+ (end - start) / 1000.0, numItemsProcessed, numItems)
}
private fun <T> maybeWithZipOutputStream(filename: String?, block: (ZipOutputStream?) -> T): T {
@@ -294,6 +281,7 @@ class HostStubGen(val options: HostStubGenOptions) {
implOutStream: ZipOutputStream?,
filter: OutputFilter,
packageRedirector: PackageRedirectRemapper,
+ remapper: Remapper?,
enableChecker: Boolean,
classes: ClassNodes,
errors: HostStubGenErrors,
@@ -311,7 +299,7 @@ class HostStubGen(val options: HostStubGenOptions) {
// If it's a class, convert it.
if (name.endsWith(".class")) {
processSingleClass(inZip, entry, stubOutStream, implOutStream, filter,
- packageRedirector, enableChecker, classes, errors, stats)
+ packageRedirector, remapper, enableChecker, classes, errors, stats)
return
}
@@ -362,6 +350,7 @@ class HostStubGen(val options: HostStubGenOptions) {
implOutStream: ZipOutputStream?,
filter: OutputFilter,
packageRedirector: PackageRedirectRemapper,
+ remapper: Remapper?,
enableChecker: Boolean,
classes: ClassNodes,
errors: HostStubGenErrors,
@@ -373,16 +362,24 @@ class HostStubGen(val options: HostStubGenOptions) {
log.d("Removing class: %s %s", classInternalName, classPolicy)
return
}
+ // If we're applying a remapper, we need to rename the file too.
+ var newName = entry.name
+ remapper?.mapType(classInternalName)?.let { remappedName ->
+ if (remappedName != classInternalName) {
+ log.d("Renaming class file: %s -> %s", classInternalName, remappedName)
+ newName = remappedName + ".class"
+ }
+ }
// Generate stub first.
if (stubOutStream != null && classPolicy.policy.needsInStub) {
log.v("Creating stub class: %s Policy: %s", classInternalName, classPolicy)
log.withIndent {
BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
- val newEntry = ZipEntry(entry.name)
+ val newEntry = ZipEntry(newName)
stubOutStream.putNextEntry(newEntry)
convertClass(classInternalName, /*forImpl=*/false, bis,
- stubOutStream, filter, packageRedirector, enableChecker, classes,
- errors, null)
+ stubOutStream, filter, packageRedirector, remapper,
+ enableChecker, classes, errors, null)
stubOutStream.closeEntry()
}
}
@@ -391,11 +388,11 @@ class HostStubGen(val options: HostStubGenOptions) {
log.v("Creating impl class: %s Policy: %s", classInternalName, classPolicy)
log.withIndent {
BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
- val newEntry = ZipEntry(entry.name)
+ val newEntry = ZipEntry(newName)
implOutStream.putNextEntry(newEntry)
convertClass(classInternalName, /*forImpl=*/true, bis,
- implOutStream, filter, packageRedirector, enableChecker, classes,
- errors, stats)
+ implOutStream, filter, packageRedirector, remapper,
+ enableChecker, classes, errors, stats)
implOutStream.closeEntry()
}
}
@@ -412,6 +409,7 @@ class HostStubGen(val options: HostStubGenOptions) {
out: OutputStream,
filter: OutputFilter,
packageRedirector: PackageRedirectRemapper,
+ remapper: Remapper?,
enableChecker: Boolean,
classes: ClassNodes,
errors: HostStubGenErrors,
@@ -428,6 +426,12 @@ class HostStubGen(val options: HostStubGenOptions) {
if (enableChecker) {
outVisitor = CheckClassAdapter(outVisitor)
}
+
+ // Remapping should happen at the end.
+ remapper?.let {
+ outVisitor = ClassRemapper(outVisitor, remapper)
+ }
+
val visitorOptions = BaseAdapter.Options(
enablePreTrace = options.enablePreTrace.get,
enablePostTrace = options.enablePostTrace.get,
@@ -436,7 +440,7 @@ class HostStubGen(val options: HostStubGenOptions) {
stats = stats,
)
outVisitor = BaseAdapter.getVisitor(classInternalName, classes, outVisitor, filter,
- packageRedirector, forImpl, visitorOptions)
+ packageRedirector, remapper, forImpl, visitorOptions)
cr.accept(outVisitor, ClassReader.EXPAND_FRAMES)
val data = cw.toByteArray()
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt
index 4882c00d2b3c..45e7e301c0d1 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt
@@ -13,18 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@file:JvmName("Main")
+@file:JvmName("HostStubGenMain")
package com.android.hoststubgen
import java.io.PrintWriter
-const val COMMAND_NAME = "HostStubGen"
-
/**
* Entry point.
*/
fun main(args: Array<String>) {
+ executableName = "HostStubGen"
+
var success = false
var clanupOnError = false
@@ -33,7 +33,7 @@ fun main(args: Array<String>) {
val options = HostStubGenOptions.parseArgs(args)
clanupOnError = options.cleanUpOnError.get
- log.v("HostStubGen started")
+ log.v("$executableName started")
log.v("Options: $options")
// Run.
@@ -41,7 +41,7 @@ fun main(args: Array<String>) {
success = true
} catch (e: Throwable) {
- log.e("$COMMAND_NAME: Error: ${e.message}")
+ log.e("$executableName: Error: ${e.message}")
if (e !is UserErrorException) {
e.printStackTrace(PrintWriter(log.getWriter(LogLevel.Error)))
}
@@ -49,7 +49,7 @@ fun main(args: Array<String>) {
TODO("Remove output jars here")
}
} finally {
- log.i("$COMMAND_NAME finished")
+ log.i("$executableName finished")
log.flush()
}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
index 9f5d524517d0..2f833a873133 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
@@ -110,6 +110,11 @@ class HostStubGenOptions(
var enableNonStubMethodCallDetection: SetOnce<Boolean> = SetOnce(false),
var statsFile: SetOnce<String?> = SetOnce(null),
+
+ var apiListFile: SetOnce<String?> = SetOnce(null),
+
+ var numShards: SetOnce<Int> = SetOnce(1),
+ var shard: SetOnce<Int> = SetOnce(0),
) {
companion object {
@@ -160,6 +165,13 @@ class HostStubGenOptions(
fun SetOnce<String?>.setNextStringArg(): String = nextArg().also { this.set(it) }
fun MutableSet<String>.addUniqueAnnotationArg(): String =
nextArg().also { this += ensureUniqueAnnotation(it) }
+ fun SetOnce<Int>.setNextIntArg(): String = nextArg().also {
+ try {
+ this.set(it.toInt())
+ } catch (e: NumberFormatException) {
+ throw ArgumentsException("Invalid integer for $arg: $it")
+ }
+ }
try {
when (arg) {
@@ -255,6 +267,10 @@ class HostStubGenOptions(
"--debug-log" -> setLogFile(LogLevel.Debug, nextArg())
"--stats-file" -> ret.statsFile.setNextStringArg()
+ "--supported-api-list-file" -> ret.apiListFile.setNextStringArg()
+
+ "--num-shards" -> ret.numShards.setNextIntArg()
+ "--shard-index" -> ret.shard.setNextIntArg()
else -> throw ArgumentsException("Unknown option: $arg")
}
@@ -268,7 +284,7 @@ class HostStubGenOptions(
}
if (!ret.outStubJar.isSet && !ret.outImplJar.isSet) {
log.w("Neither --out-stub-jar nor --out-impl-jar is set." +
- " $COMMAND_NAME will not generate jar files.")
+ " $executableName will not generate jar files.")
}
if (ret.enableNonStubMethodCallDetection.get) {
@@ -392,6 +408,9 @@ class HostStubGenOptions(
enablePostTrace=$enablePostTrace,
enableNonStubMethodCallDetection=$enableNonStubMethodCallDetection,
statsFile=$statsFile,
+ apiListFile=$apiListFile,
+ numShards=$numShards,
+ shard=$shard,
}
""".trimIndent()
}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt
index 50518e1ccd9c..9045db210495 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt
@@ -15,7 +15,8 @@
*/
package com.android.hoststubgen
-import com.android.hoststubgen.asm.toHumanReadableClassName
+import com.android.hoststubgen.asm.getOuterClassNameFromFullClassName
+import com.android.hoststubgen.asm.getPackageNameFromFullClassName
import com.android.hoststubgen.filters.FilterPolicyWithReason
import org.objectweb.asm.Opcodes
import java.io.PrintWriter
@@ -29,8 +30,25 @@ open class HostStubGenStats {
private val stats = mutableMapOf<String, Stats>()
- fun onVisitPolicyForMethod(fullClassName: String, methodName: String, descriptor: String,
- policy: FilterPolicyWithReason, access: Int) {
+ data class Api(
+ val fullClassName: String,
+ val methodName: String,
+ val methodDesc: String,
+ )
+
+ private val apis = mutableListOf<Api>()
+
+ fun onVisitPolicyForMethod(
+ fullClassName: String,
+ methodName: String,
+ descriptor: String,
+ policy: FilterPolicyWithReason,
+ access: Int
+ ) {
+ if (policy.policy.isSupported) {
+ apis.add(Api(fullClassName, methodName, descriptor))
+ }
+
// Ignore methods that aren't public
if ((access and Opcodes.ACC_PUBLIC) == 0) return
// Ignore methods that are abstract
@@ -38,8 +56,8 @@ open class HostStubGenStats {
// Ignore methods where policy isn't relevant
if (policy.isIgnoredForStats) return
- val packageName = resolvePackageName(fullClassName)
- val className = resolveClassName(fullClassName)
+ val packageName = getPackageNameFromFullClassName(fullClassName)
+ val className = getOuterClassNameFromFullClassName(fullClassName)
// Ignore methods for certain generated code
if (className.endsWith("Proto")
@@ -60,30 +78,15 @@ open class HostStubGenStats {
classStats.total += 1
}
- fun dump(pw: PrintWriter) {
+ fun dumpOverview(pw: PrintWriter) {
pw.printf("PackageName,ClassName,SupportedMethods,TotalMethods\n")
- stats.forEach { (packageName, packageStats) ->
+ stats.toSortedMap().forEach { (packageName, packageStats) ->
if (packageStats.supported > 0) {
- packageStats.children.forEach { (className, classStats) ->
+ packageStats.children.toSortedMap().forEach { (className, classStats) ->
pw.printf("%s,%s,%d,%d\n", packageName, className,
classStats.supported, classStats.total)
}
}
}
}
-
- private fun resolvePackageName(fullClassName: String): String {
- val start = fullClassName.lastIndexOf('/')
- return fullClassName.substring(0, start).toHumanReadableClassName()
- }
-
- private fun resolveClassName(fullClassName: String): String {
- val start = fullClassName.lastIndexOf('/')
- val end = fullClassName.indexOf('$')
- if (end == -1) {
- return fullClassName.substring(start + 1)
- } else {
- return fullClassName.substring(start + 1, end)
- }
- }
}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt
index 937e56c2cbb5..10179eefcb95 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt
@@ -16,6 +16,11 @@
package com.android.hoststubgen
/**
+ * Name of this executable. Set it in the main method.
+ */
+var executableName = "[command name not set]"
+
+/**
* A regex that maches whitespate.
*/
val whitespaceRegex = """\s+""".toRegex()
@@ -84,3 +89,10 @@ class ParseException : Exception, UserErrorException {
}
}
}
+
+/**
+ * Escape a string for a CSV field.
+ */
+fun csvEscape(value: String): String {
+ return "\"" + value.replace("\"", "\"\"") + "\""
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
index 0579c2bb0525..3f2b13aed5c0 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
@@ -34,6 +34,9 @@ val CLASS_INITIALIZER_NAME = "<clinit>"
/** Descriptor of the class initializer method. */
val CLASS_INITIALIZER_DESC = "()V"
+/** Name of constructors. */
+val CTOR_NAME = "<init>"
+
/**
* Find any of [anyAnnotations] from the list of visible / invisible annotations.
*/
@@ -73,17 +76,45 @@ fun findAnnotationValueAsString(an: AnnotationNode, propertyName: String): Strin
return null
}
-private val removeLastElement = """[./][^./]*$""".toRegex()
+val periodOrSlash = charArrayOf('.', '/')
-fun getPackageNameFromClassName(className: String): String {
- return className.replace(removeLastElement, "")
+fun getPackageNameFromFullClassName(fullClassName: String): String {
+ val pos = fullClassName.lastIndexOfAny(periodOrSlash)
+ if (pos == -1) {
+ return ""
+ } else {
+ return fullClassName.substring(0, pos)
+ }
+}
+
+fun getClassNameFromFullClassName(fullClassName: String): String {
+ val pos = fullClassName.lastIndexOfAny(periodOrSlash)
+ if (pos == -1) {
+ return fullClassName
+ } else {
+ return fullClassName.substring(pos + 1)
+ }
}
-fun resolveClassName(className: String, packageName: String): String {
+fun getOuterClassNameFromFullClassName(fullClassName: String): String {
+ val start = fullClassName.lastIndexOfAny(periodOrSlash)
+ val end = fullClassName.indexOf('$')
+ if (end == -1) {
+ return fullClassName.substring(start + 1)
+ } else {
+ return fullClassName.substring(start + 1, end)
+ }
+}
+
+/**
+ * If [className] is a fully qualified name, just return it.
+ * Otherwise, prepend [defaultPackageName].
+ */
+fun resolveClassNameWithDefaultPackage(className: String, defaultPackageName: String): String {
if (className.contains('.') || className.contains('/')) {
return className
}
- return "$packageName.$className"
+ return "$defaultPackageName.$className"
}
fun String.toJvmClassName(): String {
@@ -135,10 +166,10 @@ fun writeByteCodeToPushArguments(
// Note, long and double will consume two local variable spaces, so the extra `i++`.
when (type) {
Type.VOID_TYPE -> throw HostStubGenInternalException("VOID_TYPE not expected")
- Type.BOOLEAN_TYPE, Type.INT_TYPE, Type.SHORT_TYPE, Type.CHAR_TYPE
+ Type.BOOLEAN_TYPE, Type.CHAR_TYPE, Type.BYTE_TYPE, Type.SHORT_TYPE, Type.INT_TYPE
-> writer.visitVarInsn(Opcodes.ILOAD, i)
- Type.LONG_TYPE -> writer.visitVarInsn(Opcodes.LLOAD, i++)
Type.FLOAT_TYPE -> writer.visitVarInsn(Opcodes.FLOAD, i)
+ Type.LONG_TYPE -> writer.visitVarInsn(Opcodes.LLOAD, i++)
Type.DOUBLE_TYPE -> writer.visitVarInsn(Opcodes.DLOAD, i++)
else -> writer.visitVarInsn(Opcodes.ALOAD, i)
}
@@ -154,10 +185,10 @@ fun writeByteCodeToReturn(methodDescriptor: String, writer: MethodVisitor) {
// See https://en.wikipedia.org/wiki/List_of_Java_bytecode_instructions
when (type) {
Type.VOID_TYPE -> writer.visitInsn(Opcodes.RETURN)
- Type.BOOLEAN_TYPE, Type.INT_TYPE, Type.SHORT_TYPE, Type.CHAR_TYPE
+ Type.BOOLEAN_TYPE, Type.CHAR_TYPE, Type.BYTE_TYPE, Type.SHORT_TYPE, Type.INT_TYPE
-> writer.visitInsn(Opcodes.IRETURN)
- Type.LONG_TYPE -> writer.visitInsn(Opcodes.LRETURN)
Type.FLOAT_TYPE -> writer.visitInsn(Opcodes.FRETURN)
+ Type.LONG_TYPE -> writer.visitInsn(Opcodes.LRETURN)
Type.DOUBLE_TYPE -> writer.visitInsn(Opcodes.DRETURN)
else -> writer.visitInsn(Opcodes.ARETURN)
}
@@ -259,6 +290,16 @@ fun FieldNode.getVisibility(): Visibility {
return Visibility.fromAccess(this.access)
}
+/** Return the [access] flags without the visibility */
+fun clearVisibility(access: Int): Int {
+ return access and (Opcodes.ACC_PUBLIC or Opcodes.ACC_PROTECTED or Opcodes.ACC_PRIVATE).inv()
+}
+
+/** Return the visibility part of the [access] flags */
+fun getVisibility(access: Int): Int {
+ return access and (Opcodes.ACC_PUBLIC or Opcodes.ACC_PROTECTED or Opcodes.ACC_PRIVATE)
+}
+
/*
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
index bc34ef0dc8a7..92906a75b93a 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
@@ -16,13 +16,18 @@
package com.android.hoststubgen.asm
import com.android.hoststubgen.ClassParseException
+import com.android.hoststubgen.InvalidJarFileException
+import com.android.hoststubgen.log
+import org.objectweb.asm.ClassReader
import org.objectweb.asm.tree.AnnotationNode
import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.FieldNode
import org.objectweb.asm.tree.MethodNode
import org.objectweb.asm.tree.TypeAnnotationNode
+import java.io.BufferedInputStream
import java.io.PrintWriter
import java.util.Arrays
+import java.util.zip.ZipFile
/**
* Stores all classes loaded from a jar file, in a form of [ClassNode]
@@ -62,8 +67,8 @@ class ClassNodes {
/** Find a field, which may not exist. */
fun findField(
- className: String,
- fieldName: String,
+ className: String,
+ fieldName: String,
): FieldNode? {
return findClass(className)?.fields?.firstOrNull { it.name == fieldName }?.let { fn ->
return fn
@@ -72,14 +77,14 @@ class ClassNodes {
/** Find a method, which may not exist. */
fun findMethod(
- className: String,
- methodName: String,
- descriptor: String,
+ className: String,
+ methodName: String,
+ descriptor: String,
): MethodNode? {
return findClass(className)?.methods
- ?.firstOrNull { it.name == methodName && it.desc == descriptor }?.let { mn ->
- return mn
- }
+ ?.firstOrNull { it.name == methodName && it.desc == descriptor }?.let { mn ->
+ return mn
+ }
}
/** @return true if a class has a class initializer. */
@@ -106,26 +111,33 @@ class ClassNodes {
private fun dumpClass(pw: PrintWriter, cn: ClassNode) {
pw.printf("Class: %s [access: %x]\n", cn.name, cn.access)
- dumpAnnotations(pw, " ",
- cn.visibleTypeAnnotations, cn.invisibleTypeAnnotations,
- cn.visibleAnnotations, cn.invisibleAnnotations,
- )
+ dumpAnnotations(
+ pw, " ",
+ cn.visibleTypeAnnotations, cn.invisibleTypeAnnotations,
+ cn.visibleAnnotations, cn.invisibleAnnotations,
+ )
for (f in cn.fields ?: emptyList()) {
- pw.printf(" Field: %s [sig: %s] [desc: %s] [access: %x]\n",
- f.name, f.signature, f.desc, f.access)
- dumpAnnotations(pw, " ",
- f.visibleTypeAnnotations, f.invisibleTypeAnnotations,
- f.visibleAnnotations, f.invisibleAnnotations,
- )
+ pw.printf(
+ " Field: %s [sig: %s] [desc: %s] [access: %x]\n",
+ f.name, f.signature, f.desc, f.access
+ )
+ dumpAnnotations(
+ pw, " ",
+ f.visibleTypeAnnotations, f.invisibleTypeAnnotations,
+ f.visibleAnnotations, f.invisibleAnnotations,
+ )
}
for (m in cn.methods ?: emptyList()) {
- pw.printf(" Method: %s [sig: %s] [desc: %s] [access: %x]\n",
- m.name, m.signature, m.desc, m.access)
- dumpAnnotations(pw, " ",
- m.visibleTypeAnnotations, m.invisibleTypeAnnotations,
- m.visibleAnnotations, m.invisibleAnnotations,
- )
+ pw.printf(
+ " Method: %s [sig: %s] [desc: %s] [access: %x]\n",
+ m.name, m.signature, m.desc, m.access
+ )
+ dumpAnnotations(
+ pw, " ",
+ m.visibleTypeAnnotations, m.invisibleTypeAnnotations,
+ m.visibleAnnotations, m.invisibleAnnotations,
+ )
}
}
@@ -136,7 +148,7 @@ class ClassNodes {
invisibleTypeAnnotations: List<TypeAnnotationNode>?,
visibleAnnotations: List<AnnotationNode>?,
invisibleAnnotations: List<AnnotationNode>?,
- ) {
+ ) {
for (an in visibleTypeAnnotations ?: emptyList()) {
pw.printf("%sTypeAnnotation(vis): %s\n", prefix, an.desc)
}
@@ -166,4 +178,55 @@ class ClassNodes {
}
}
}
+
+ companion object {
+ /**
+ * Load all the classes, without code.
+ */
+ fun loadClassStructures(inJar: String): ClassNodes {
+ log.i("Reading class structure from $inJar ...")
+ val start = System.currentTimeMillis()
+
+ val allClasses = ClassNodes()
+
+ log.withIndent {
+ ZipFile(inJar).use { inZip ->
+ val inEntries = inZip.entries()
+
+ while (inEntries.hasMoreElements()) {
+ val entry = inEntries.nextElement()
+
+ BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
+ if (entry.name.endsWith(".class")) {
+ val cr = ClassReader(bis)
+ val cn = ClassNode()
+ cr.accept(cn, ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG
+ or ClassReader.SKIP_FRAMES)
+ if (!allClasses.addClass(cn)) {
+ log.w("Duplicate class found: ${cn.name}")
+ }
+ } else if (entry.name.endsWith(".dex")) {
+ // Seems like it's an ART jar file. We can't process it.
+ // It's a fatal error.
+ throw InvalidJarFileException(
+ "$inJar is not a desktop jar file. It contains a *.dex file.")
+ } else {
+ // Unknown file type. Skip.
+ while (bis.available() > 0) {
+ bis.skip((1024 * 1024).toLong())
+ }
+ }
+ }
+ }
+ }
+ }
+ if (allClasses.size == 0) {
+ log.w("$inJar contains no *.class files.")
+ }
+
+ val end = System.currentTimeMillis()
+ log.i("Done reading class structure in %.1f second(s).", (end - start) / 1000.0)
+ return allClasses
+ }
+ }
} \ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt
new file mode 100644
index 000000000000..aaefee4f71e8
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.dumper
+
+import com.android.hoststubgen.asm.CLASS_INITIALIZER_NAME
+import com.android.hoststubgen.asm.CTOR_NAME
+import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.getClassNameFromFullClassName
+import com.android.hoststubgen.asm.getPackageNameFromFullClassName
+import com.android.hoststubgen.asm.toHumanReadableClassName
+import com.android.hoststubgen.csvEscape
+import com.android.hoststubgen.filters.FilterPolicy
+import com.android.hoststubgen.filters.FilterPolicyWithReason
+import com.android.hoststubgen.filters.OutputFilter
+import com.android.hoststubgen.log
+import org.objectweb.asm.Type
+import org.objectweb.asm.tree.ClassNode
+import java.io.PrintWriter
+
+/**
+ * Dump all the API methods in [classes], with inherited methods, with their policies.
+ */
+class ApiDumper(
+ val pw: PrintWriter,
+ val classes: ClassNodes,
+ val frameworkClasses: ClassNodes?,
+ val filter: OutputFilter,
+) {
+ private data class MethodKey(
+ val name: String,
+ val descriptor: String,
+ )
+
+ val javaStandardApiPolicy = FilterPolicy.Stub.withReason("Java standard API")
+
+ private val shownMethods = mutableSetOf<MethodKey>()
+
+ /**
+ * Do the dump.
+ */
+ fun dump() {
+ pw.printf("PackageName,ClassName,FromSubclass,DeclareClass,MethodName,MethodDesc" +
+ ",Supported,Policy,Reason\n")
+
+ classes.forEach { classNode ->
+ shownMethods.clear()
+ dump(classNode, classNode)
+ }
+ }
+
+ private fun dumpMethod(
+ classPackage: String,
+ className: String,
+ isSuperClass: Boolean,
+ methodClassName: String,
+ methodName: String,
+ methodDesc: String,
+ policy: FilterPolicyWithReason,
+ ) {
+ pw.printf(
+ "%s,%s,%d,%s,%s,%s,%d,%s,%s\n",
+ csvEscape(classPackage),
+ csvEscape(className),
+ if (isSuperClass) { 1 } else { 0 },
+ csvEscape(methodClassName),
+ csvEscape(methodName),
+ csvEscape(methodDesc),
+ if (policy.policy.isSupported) { 1 } else { 0 },
+ policy.policy,
+ csvEscape(policy.reason),
+ )
+ }
+
+ private fun isDuplicate(methodName: String, methodDesc: String): Boolean {
+ val methodKey = MethodKey(methodName, methodDesc)
+
+ if (shownMethods.contains(methodKey)) {
+ return true
+ }
+ shownMethods.add(methodKey)
+ return false
+ }
+
+ private fun dump(
+ dumpClass: ClassNode,
+ methodClass: ClassNode,
+ ) {
+ val pkg = getPackageNameFromFullClassName(dumpClass.name).toHumanReadableClassName()
+ val cls = getClassNameFromFullClassName(dumpClass.name).toHumanReadableClassName()
+
+ val isSuperClass = dumpClass != methodClass
+
+ methodClass.methods?.sortedWith(compareBy({ it.name }, { it.desc }))?.forEach { method ->
+
+ // Don't print ctor's from super classes.
+ if (isSuperClass) {
+ if (CTOR_NAME == method.name || CLASS_INITIALIZER_NAME == method.name) {
+ return@forEach
+ }
+ }
+ // If we already printed the method from a subclass, don't print it.
+ if (isDuplicate(method.name, method.desc)) {
+ return@forEach
+ }
+
+ val policy = filter.getPolicyForMethod(methodClass.name, method.name, method.desc)
+
+ // Let's skip "Remove" APIs. Ideally we want to print it, just to make the CSV
+ // complete, we still need to hide methods substituted (== @RavenwoodReplace) methods
+ // and for now we don't have an easy way to detect it.
+ if (policy.policy == FilterPolicy.Remove) {
+ return@forEach
+ }
+
+ val renameTo = filter.getRenameTo(methodClass.name, method.name, method.desc)
+
+ dumpMethod(pkg, cls, isSuperClass, methodClass.name.toHumanReadableClassName(),
+ renameTo ?: method.name, method.desc, policy)
+ }
+
+ // Dump super class methods.
+ dumpSuper(dumpClass, methodClass.superName)
+
+ // Dump interface methods (which may have default methods).
+ methodClass.interfaces?.sorted()?.forEach { interfaceName ->
+ dumpSuper(dumpClass, interfaceName)
+ }
+ }
+
+ /**
+ * Dump a given super class / interface.
+ */
+ private fun dumpSuper(
+ dumpClass: ClassNode,
+ methodClassName: String,
+ ) {
+ classes.findClass(methodClassName)?.let { methodClass ->
+ dump(dumpClass, methodClass)
+ return
+ }
+ frameworkClasses?.findClass(methodClassName)?.let { methodClass ->
+ dump(dumpClass, methodClass)
+ return
+ }
+ if (methodClassName.startsWith("java/") ||
+ methodClassName.startsWith("javax/")
+ ) {
+ dumpStandardClass(dumpClass, methodClassName)
+ return
+ }
+ log.w("Super class or interface $methodClassName (used by ${dumpClass.name}) not found.")
+ }
+
+ /**
+ * Dump methods from Java standard classes.
+ */
+ private fun dumpStandardClass(
+ dumpClass: ClassNode,
+ methodClassName: String,
+ ) {
+ val pkg = getPackageNameFromFullClassName(dumpClass.name).toHumanReadableClassName()
+ val cls = getClassNameFromFullClassName(dumpClass.name).toHumanReadableClassName()
+
+ val methodClassName = methodClassName.toHumanReadableClassName()
+
+ try {
+ val clazz = Class.forName(methodClassName)
+
+ // Method.getMethods() returns only public methods, but with inherited ones.
+ // Method.getDeclaredMethods() returns private methods too, but no inherited methods.
+ //
+ // Since we're only interested in public ones, just use getMethods().
+ clazz.methods.forEach { method ->
+ val methodName = method.name
+ val methodDesc = Type.getMethodDescriptor(method)
+
+ // If we already printed the method from a subclass, don't print it.
+ if (isDuplicate(methodName, methodDesc)) {
+ return@forEach
+ }
+
+ dumpMethod(pkg, cls, true, methodClassName,
+ methodName, methodDesc, javaStandardApiPolicy)
+ }
+ } catch (e: ClassNotFoundException) {
+ log.w("JVM type $methodClassName (used by ${dumpClass.name}) not found.")
+ }
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
index 16785d1d9598..6b360b79c327 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
@@ -25,6 +25,7 @@ class AndroidHeuristicsFilter(
val aidlPolicy: FilterPolicyWithReason?,
val featureFlagsPolicy: FilterPolicyWithReason?,
val syspropsPolicy: FilterPolicyWithReason?,
+ val rFilePolicy: FilterPolicyWithReason?,
fallback: OutputFilter
) : DelegatingFilter(fallback) {
override fun getPolicyForClass(className: String): FilterPolicyWithReason {
@@ -37,6 +38,9 @@ class AndroidHeuristicsFilter(
if (syspropsPolicy != null && classes.isSyspropsClass(className)) {
return syspropsPolicy
}
+ if (rFilePolicy != null && classes.isRClass(className)) {
+ return rFilePolicy
+ }
return super.getPolicyForClass(className)
}
}
@@ -74,3 +78,10 @@ private fun ClassNodes.isSyspropsClass(className: String): Boolean {
return className.startsWith("android/sysprop/")
&& className.endsWith("Properties")
}
+
+/**
+ * @return if a given class "seems like" an R class or its nested classes.
+ */
+private fun ClassNodes.isRClass(className: String): Boolean {
+ return className.endsWith("/R") || className.contains("/R$")
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
index 78b13fd36f06..5a26fc69d473 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
@@ -19,14 +19,14 @@ import com.android.hoststubgen.HostStubGenErrors
import com.android.hoststubgen.HostStubGenInternalException
import com.android.hoststubgen.asm.CLASS_INITIALIZER_DESC
import com.android.hoststubgen.asm.CLASS_INITIALIZER_NAME
-import com.android.hoststubgen.asm.isAnonymousInnerClass
-import com.android.hoststubgen.log
import com.android.hoststubgen.asm.ClassNodes
import com.android.hoststubgen.asm.isAnnotation
+import com.android.hoststubgen.asm.isAnonymousInnerClass
import com.android.hoststubgen.asm.isAutoGeneratedEnumMember
import com.android.hoststubgen.asm.isEnum
import com.android.hoststubgen.asm.isSynthetic
import com.android.hoststubgen.asm.isVisibilityPrivateOrPackagePrivate
+import com.android.hoststubgen.log
import org.objectweb.asm.tree.ClassNode
/**
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
index 5659a35170c1..2e144f5513bc 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
@@ -15,11 +15,11 @@
*/
package com.android.hoststubgen.filters
-import com.android.hoststubgen.UnknownApiException
import com.android.hoststubgen.addNonNullElement
import com.android.hoststubgen.asm.ClassNodes
import com.android.hoststubgen.asm.toHumanReadableClassName
import com.android.hoststubgen.asm.toHumanReadableMethodName
+import com.android.hoststubgen.log
// TODO: Validate all input names.
@@ -48,30 +48,30 @@ class InMemoryOutputFilter(
return mPolicies[getClassKey(className)] ?: super.getPolicyForClass(className)
}
- private fun ensureClassExists(className: String) {
+ private fun checkClass(className: String) {
if (classes.findClass(className) == null) {
- throw UnknownApiException("Unknown class $className")
+ log.w("Unknown class $className")
}
}
- private fun ensureFieldExists(className: String, fieldName: String) {
+ private fun checkField(className: String, fieldName: String) {
if (classes.findField(className, fieldName) == null) {
- throw UnknownApiException("Unknown field $className.$fieldName")
+ log.w("Unknown field $className.$fieldName")
}
}
- private fun ensureMethodExists(
+ private fun checkMethod(
className: String,
methodName: String,
descriptor: String
) {
if (classes.findMethod(className, methodName, descriptor) == null) {
- throw UnknownApiException("Unknown method $className.$methodName$descriptor")
+ log.w("Unknown method $className.$methodName$descriptor")
}
}
fun setPolicyForClass(className: String, policy: FilterPolicyWithReason) {
- ensureClassExists(className)
+ checkClass(className)
mPolicies[getClassKey(className)] = policy
}
@@ -81,7 +81,7 @@ class InMemoryOutputFilter(
}
fun setPolicyForField(className: String, fieldName: String, policy: FilterPolicyWithReason) {
- ensureFieldExists(className, fieldName)
+ checkField(className, fieldName)
mPolicies[getFieldKey(className, fieldName)] = policy
}
@@ -100,7 +100,7 @@ class InMemoryOutputFilter(
descriptor: String,
policy: FilterPolicyWithReason,
) {
- ensureMethodExists(className, methodName, descriptor)
+ checkMethod(className, methodName, descriptor)
mPolicies[getMethodKey(className, methodName, descriptor)] = policy
}
@@ -110,8 +110,8 @@ class InMemoryOutputFilter(
}
fun setRenameTo(className: String, methodName: String, descriptor: String, toName: String) {
- ensureMethodExists(className, methodName, descriptor)
- ensureMethodExists(className, toName, descriptor)
+ checkMethod(className, methodName, descriptor)
+ checkMethod(className, toName, descriptor)
mRenames[getMethodKey(className, methodName, descriptor)] = toName
}
@@ -121,7 +121,7 @@ class InMemoryOutputFilter(
}
fun setNativeSubstitutionClass(from: String, to: String) {
- ensureClassExists(from)
+ checkClass(from)
// Native substitute classes may be provided from other jars, so we can't do this check.
// ensureClassExists(to)
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt
new file mode 100644
index 000000000000..c67e6714d4c2
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.filters
+
+import com.android.hoststubgen.asm.toHumanReadableClassName
+import com.android.hoststubgen.utils.Trie
+
+/**
+ * Filter to apply a policy to classes inside a package, either directly or indirectly.
+ */
+class PackageFilter(
+ fallback: OutputFilter
+) : DelegatingFilter(fallback) {
+
+ private val mPackagePolicies = PackagePolicyTrie()
+
+ // We want to pick the most specific filter for a package name.
+ // Since any package with a matching prefix is a valid match, we can use a prefix tree
+ // to help us find the nearest matching filter.
+ private class PackagePolicyTrie : Trie<String, String, FilterPolicyWithReason>() {
+ // Split package name into individual component
+ override fun splitToComponents(key: String): Iterator<String> {
+ return key.split('.').iterator()
+ }
+ }
+
+ private fun getPackageKey(packageName: String): String {
+ return packageName.toHumanReadableClassName()
+ }
+
+ private fun getPackageKeyFromClass(className: String): String {
+ val clazz = className.toHumanReadableClassName()
+ val idx = clazz.lastIndexOf('.')
+ return if (idx >= 0) clazz.substring(0, idx) else ""
+ }
+
+ /**
+ * Add a policy to all classes inside a package, either directly or indirectly.
+ */
+ fun addPolicy(packageName: String, policy: FilterPolicyWithReason) {
+ mPackagePolicies[getPackageKey(packageName)] = policy
+ }
+
+ override fun getPolicyForClass(className: String): FilterPolicyWithReason {
+ return mPackagePolicies[getPackageKeyFromClass(className)]
+ ?: super.getPolicyForClass(className)
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
index 75b5fc8f77ea..18280034c2f4 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
@@ -17,27 +17,34 @@ package com.android.hoststubgen.filters
import com.android.hoststubgen.ParseException
import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.toHumanReadableClassName
import com.android.hoststubgen.log
import com.android.hoststubgen.normalizeTextLine
import com.android.hoststubgen.whitespaceRegex
import org.objectweb.asm.Opcodes
+import org.objectweb.asm.commons.Remapper
import org.objectweb.asm.tree.ClassNode
import java.io.BufferedReader
import java.io.FileReader
import java.io.PrintWriter
import java.util.Objects
+import java.util.regex.Pattern
/**
* Print a class node as a "keep" policy.
*/
fun printAsTextPolicy(pw: PrintWriter, cn: ClassNode) {
- pw.printf("class %s\t%s\n", cn.name, "keep")
+ pw.printf("class %s %s\n", cn.name.toHumanReadableClassName(), "keep")
- for (f in cn.fields ?: emptyList()) {
- pw.printf(" field %s\t%s\n", f.name, "keep")
+ cn.fields?.let {
+ for (f in it.sortedWith(compareBy({ it.name }))) {
+ pw.printf(" field %s %s\n", f.name, "keep")
+ }
}
- for (m in cn.methods ?: emptyList()) {
- pw.printf(" method %s\t%s\t%s\n", m.name, m.desc, "keep")
+ cn.methods?.let {
+ for (m in it.sortedWith(compareBy({ it.name }, { it.desc }))) {
+ pw.printf(" method %s %s %s\n", m.name, m.desc, "keep")
+ }
}
}
@@ -55,27 +62,27 @@ fun createFilterFromTextPolicyFile(
filename: String,
classes: ClassNodes,
fallback: OutputFilter,
- ): OutputFilter {
+ ): Pair<OutputFilter, Remapper?> {
log.i("Loading offloaded annotations from $filename ...")
log.withIndent {
val subclassFilter = SubclassFilter(classes, fallback)
- val imf = InMemoryOutputFilter(classes, subclassFilter)
+ val packageFilter = PackageFilter(subclassFilter)
+ val imf = InMemoryOutputFilter(classes, packageFilter)
var lineNo = 0
var aidlPolicy: FilterPolicyWithReason? = null
var featureFlagsPolicy: FilterPolicyWithReason? = null
var syspropsPolicy: FilterPolicyWithReason? = null
+ var rFilePolicy: FilterPolicyWithReason? = null
+ val typeRenameSpec = mutableListOf<TextFilePolicyRemapper.TypeRenameSpec>()
try {
BufferedReader(FileReader(filename)).use { reader ->
var className = ""
while (true) {
- var line = reader.readLine()
- if (line == null) {
- break
- }
+ var line = reader.readLine() ?: break
lineNo++
line = normalizeTextLine(line)
@@ -89,6 +96,31 @@ fun createFilterFromTextPolicyFile(
val fields = line.split(whitespaceRegex).toTypedArray()
when (fields[0].lowercase()) {
+ "p", "package" -> {
+ if (fields.size < 3) {
+ throw ParseException("Package ('p') expects 2 fields.")
+ }
+ val name = fields[1]
+ val rawPolicy = fields[2]
+ if (resolveExtendingClass(name) != null) {
+ throw ParseException("Package can't be a super class type")
+ }
+ if (resolveSpecialClass(name) != SpecialClass.NotSpecial) {
+ throw ParseException("Package can't be a special class type")
+ }
+ if (rawPolicy.startsWith("!")) {
+ throw ParseException("Package can't have a substitution")
+ }
+ if (rawPolicy.startsWith("~")) {
+ throw ParseException("Package can't have a class load hook")
+ }
+ val policy = parsePolicy(rawPolicy)
+ if (!policy.isUsableWithClasses) {
+ throw ParseException("Package can't have policy '$policy'")
+ }
+ packageFilter.addPolicy(name, policy.withReason(FILTER_REASON))
+ }
+
"c", "class" -> {
if (fields.size < 3) {
throw ParseException("Class ('c') expects 2 fields.")
@@ -162,6 +194,14 @@ fun createFilterFromTextPolicyFile(
syspropsPolicy = policy.withReason(
"$FILTER_REASON (special-class sysprops)")
}
+ SpecialClass.RFile -> {
+ if (rFilePolicy != null) {
+ throw ParseException(
+ "Policy for R file already defined")
+ }
+ rFilePolicy = policy.withReason(
+ "$FILTER_REASON (special-class R file)")
+ }
}
}
}
@@ -214,6 +254,22 @@ fun createFilterFromTextPolicyFile(
imf.setRenameTo(className, fromName, signature, name)
}
}
+ "r", "rename" -> {
+ if (fields.size < 3) {
+ throw ParseException("Rename ('r') expects 2 fields.")
+ }
+ // Add ".*" to make it a prefix match.
+ val pattern = Pattern.compile(fields[1] + ".*")
+
+ // Removing the leading /'s from the prefix. This allows
+ // using a single '/' as an empty suffix, which is useful to have a
+ // "negative" rename rule to avoid subsequent raname's from getting
+ // applied. (Which is needed for services.jar)
+ val prefix = fields[2].trimStart('/')
+
+ typeRenameSpec += TextFilePolicyRemapper.TypeRenameSpec(
+ pattern, prefix)
+ }
else -> {
throw ParseException("Unknown directive \"${fields[0]}\"")
@@ -225,13 +281,16 @@ fun createFilterFromTextPolicyFile(
throw e.withSourceInfo(filename, lineNo)
}
- var ret: OutputFilter = imf
- if (aidlPolicy != null || featureFlagsPolicy != null || syspropsPolicy != null) {
- log.d("AndroidHeuristicsFilter enabled")
- ret = AndroidHeuristicsFilter(
- classes, aidlPolicy, featureFlagsPolicy, syspropsPolicy, imf)
+ var remapper: TextFilePolicyRemapper? = null
+ if (typeRenameSpec.isNotEmpty()) {
+ remapper = TextFilePolicyRemapper(typeRenameSpec)
}
- return ret
+
+ // Wrap the in-memory-filter with AHF.
+ return Pair(
+ AndroidHeuristicsFilter(
+ classes, aidlPolicy, featureFlagsPolicy, syspropsPolicy, rFilePolicy, imf),
+ remapper)
}
}
@@ -240,6 +299,7 @@ private enum class SpecialClass {
Aidl,
FeatureFlags,
Sysprops,
+ RFile,
}
private fun resolveSpecialClass(className: String): SpecialClass {
@@ -250,6 +310,7 @@ private fun resolveSpecialClass(className: String): SpecialClass {
":aidl" -> return SpecialClass.Aidl
":feature_flags" -> return SpecialClass.FeatureFlags
":sysprops" -> return SpecialClass.Sysprops
+ ":r" -> return SpecialClass.RFile
}
throw ParseException("Invalid special class name \"$className\"")
}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapper.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapper.kt
new file mode 100644
index 000000000000..2d94bb4758ba
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapper.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.filters
+
+import com.android.hoststubgen.log
+import org.objectweb.asm.commons.Remapper
+import java.util.regex.Pattern
+
+/**
+ * A [Remapper] that provides a simple "jarjar" functionality.
+ */
+class TextFilePolicyRemapper(
+ val typeRenameSpecs: List<TypeRenameSpec>
+) : Remapper() {
+ /**
+ * When a package name matches [typeInternalNamePattern], we prepend [typeInternalNamePrefix]
+ * to it.
+ */
+ data class TypeRenameSpec(
+ val typeInternalNamePattern: Pattern,
+ val typeInternalNamePrefix: String,
+ )
+
+ private val cache = mutableMapOf<String, String>()
+
+ override fun mapType(typeInternalName: String): String {
+// if (typeInternalName == null) {
+// return null // do we need it??
+// }
+ cache[typeInternalName]?.let {
+ return it
+ }
+
+ var mapped: String = typeInternalName
+ typeRenameSpecs.forEach {
+ if (it.typeInternalNamePattern.matcher(typeInternalName).matches()) {
+ mapped = it.typeInternalNamePrefix + typeInternalName
+ log.d("Renaming type $typeInternalName to $mapped")
+ }
+ }
+ cache[typeInternalName] = mapped
+ return mapped
+ }
+
+ // TODO Do we need to implement mapPackage(), etc too?
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt
new file mode 100644
index 000000000000..1b3d79cddb8e
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.utils
+
+abstract class Trie<Key, Component, Value> {
+
+ private val root = TrieNode<Component, Value>()
+
+ abstract fun splitToComponents(key: Key): Iterator<Component>
+
+ operator fun set(key: Key, value: Value) {
+ val node = root.getExactNode(splitToComponents(key))
+ node.value = value
+ }
+
+ operator fun get(key: Key): Value? {
+ return root.getNearestValue(null, splitToComponents(key))
+ }
+
+ private class TrieNode<Component, Value> {
+ private val children = mutableMapOf<Component, TrieNode<Component, Value>>()
+ var value: Value? = null
+
+ fun getExactNode(components: Iterator<Component>): TrieNode<Component, Value> {
+ val n = components.next()
+ val child = children.getOrPut(n) { TrieNode() }
+ return if (components.hasNext()) {
+ child.getExactNode(components)
+ } else {
+ child
+ }
+ }
+
+ fun getNearestValue(current: Value?, components: Iterator<Component>): Value? {
+ val n = components.next()
+ val child = children[n] ?: return current
+ val newValue = child.value ?: current
+ return if (components.hasNext()) {
+ child.getNearestValue(newValue, components)
+ } else {
+ newValue
+ }
+ }
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
index 45e140c8e3ff..bad0449f1efd 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
@@ -20,8 +20,8 @@ import com.android.hoststubgen.HostStubGenStats
import com.android.hoststubgen.LogLevel
import com.android.hoststubgen.asm.ClassNodes
import com.android.hoststubgen.asm.UnifiedVisitor
-import com.android.hoststubgen.asm.getPackageNameFromClassName
-import com.android.hoststubgen.asm.resolveClassName
+import com.android.hoststubgen.asm.getPackageNameFromFullClassName
+import com.android.hoststubgen.asm.resolveClassNameWithDefaultPackage
import com.android.hoststubgen.asm.toJvmClassName
import com.android.hoststubgen.filters.FilterPolicy
import com.android.hoststubgen.filters.FilterPolicyWithReason
@@ -34,6 +34,7 @@ import org.objectweb.asm.FieldVisitor
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes
import org.objectweb.asm.commons.ClassRemapper
+import org.objectweb.asm.commons.Remapper
import org.objectweb.asm.util.TraceClassVisitor
import java.io.PrintWriter
@@ -89,7 +90,7 @@ abstract class BaseAdapter (
) {
super.visit(version, access, name, signature, superName, interfaces)
currentClassName = name
- currentPackageName = getPackageNameFromClassName(name)
+ currentPackageName = getPackageNameFromFullClassName(name)
classPolicy = filter.getPolicyForClass(currentClassName)
log.d("[%s] visit: %s (package: %s)", this.javaClass.simpleName, name, currentPackageName)
@@ -98,7 +99,8 @@ abstract class BaseAdapter (
log.indent()
filter.getNativeSubstitutionClass(currentClassName)?.let { className ->
- val fullClassName = resolveClassName(className, currentPackageName).toJvmClassName()
+ val fullClassName = resolveClassNameWithDefaultPackage(className, currentPackageName)
+ .toJvmClassName()
log.d(" NativeSubstitutionClass: $fullClassName")
if (classes.findClass(fullClassName) == null) {
log.w("Native substitution class $fullClassName not found. Class must be " +
@@ -194,6 +196,8 @@ abstract class BaseAdapter (
return null
}
+ var newAccess = access
+
// Maybe rename the method.
val newName: String
val renameTo = filter.getRenameTo(currentClassName, name, descriptor)
@@ -204,8 +208,9 @@ abstract class BaseAdapter (
// (the one with the @substitute/replace annotation).
// `name` is the name of the method we're currently visiting, so it's usually a
// "...$ravewnwood" name.
- if (!checkSubstitutionMethodCompatibility(
- classes, currentClassName, newName, name, descriptor, options.errors)) {
+ newAccess = checkSubstitutionMethodCompatibility(
+ classes, currentClassName, newName, name, descriptor, options.errors)
+ if (newAccess == NOT_COMPATIBLE) {
return null
}
@@ -220,7 +225,7 @@ abstract class BaseAdapter (
// But note, we only use it when calling the super's method,
// but not for visitMethodInner(), because when subclass wants to change access,
// it can do so inside visitMethodInner().
- val newAccess = updateAccessFlags(access, name, descriptor)
+ newAccess = updateAccessFlags(newAccess, name, descriptor)
val ret = visitMethodInner(access, newName, descriptor, signature, exceptions, policy,
renameTo != null,
@@ -255,13 +260,14 @@ abstract class BaseAdapter (
companion object {
fun getVisitor(
- classInternalName: String,
- classes: ClassNodes,
- nextVisitor: ClassVisitor,
- filter: OutputFilter,
- packageRedirector: PackageRedirectRemapper,
- forImpl: Boolean,
- options: Options,
+ classInternalName: String,
+ classes: ClassNodes,
+ nextVisitor: ClassVisitor,
+ filter: OutputFilter,
+ packageRedirector: PackageRedirectRemapper,
+ remapper: Remapper?,
+ forImpl: Boolean,
+ options: Options,
): ClassVisitor {
var next = nextVisitor
@@ -302,4 +308,4 @@ abstract class BaseAdapter (
return ret
}
}
-} \ No newline at end of file
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/Helper.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/Helper.kt
index 9d66c32e76ee..dc4f26bdda34 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/Helper.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/Helper.kt
@@ -17,12 +17,19 @@ package com.android.hoststubgen.visitors
import com.android.hoststubgen.HostStubGenErrors
import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.clearVisibility
import com.android.hoststubgen.asm.getVisibility
import com.android.hoststubgen.asm.isStatic
+const val NOT_COMPATIBLE: Int = -1
+
/**
* Make sure substitution from and to methods have matching definition.
- * (static-ness, visibility.)
+ * (static-ness, etc)
+ *
+ * If the methods are compatible, return the "merged" [access] of the new method.
+ *
+ * If they are not compatible, returns [NOT_COMPATIBLE]
*/
fun checkSubstitutionMethodCompatibility(
classes: ClassNodes,
@@ -31,33 +38,31 @@ fun checkSubstitutionMethodCompatibility(
toMethodName: String, // the one with either a "_host" or "$ravenwood" prefix. (typically)
descriptor: String,
errors: HostStubGenErrors,
-): Boolean {
+): Int {
val from = classes.findMethod(className, fromMethodName, descriptor)
if (from == null) {
errors.onErrorFound(
- "Substitution-from method not found: $className.$fromMethodName$descriptor")
- return false
+ "Substitution-from method not found: $className.$fromMethodName$descriptor"
+ )
+ return NOT_COMPATIBLE
}
val to = classes.findMethod(className, toMethodName, descriptor)
if (to == null) {
// This shouldn't happen, because the visitor visited this method...
errors.onErrorFound(
- "Substitution-to method not found: $className.$toMethodName$descriptor")
- return false
+ "Substitution-to method not found: $className.$toMethodName$descriptor"
+ )
+ return NOT_COMPATIBLE
}
if (from.isStatic() != to.isStatic()) {
errors.onErrorFound(
"Substitution method must have matching static-ness: " +
- "$className.$fromMethodName$descriptor")
- return false
- }
- if (from.getVisibility().ordinal > to.getVisibility().ordinal) {
- errors.onErrorFound(
- "Substitution method cannot have smaller visibility than original: " +
- "$className.$fromMethodName$descriptor")
- return false
+ "$className.$fromMethodName$descriptor"
+ )
+ return NOT_COMPATIBLE
}
- return true
+ // Return the substitution's access flag but with the original method's visibility.
+ return clearVisibility (to.access) or getVisibility(from.access)
}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt
index b3790e12a111..e90ecd7ef678 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt
@@ -20,6 +20,18 @@ import org.objectweb.asm.commons.Remapper
/**
* A [Remapper] for `--package-redirect`
+ *
+ * This is a feature to update all calls to specific packages to another package, which allows
+ * implementing a class in a different package, when the original package isn't allowed to modify.
+ *
+ * For example, using this, we can implement `dalvik.*` APIs in a separate package, and update
+ * all calls to the `dalvik` package to a different package.
+ *
+ * For this purpose, we don't expect the "renamed-from" classes to be in the target jar,
+ * so we don't apply the remapper to them. (The exclusion happens at the callsite of this class)
+ *
+ * TODO: Currently it's not used. Maybe remove, or just unify with the other remapper feature
+ * with TextFileFilterPolicyParser.kt.
*/
class PackageRedirectRemapper(
packageRedirects: List<Pair<String, String>>,
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
index f70a17d9b7cd..c127e677f84d 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -322,6 +322,78 @@ NestMembers:
InnerClasses:
public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class
+ Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R$Nested
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/R$Nested
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 3
+ public static int[] ARRAY;
+ descriptor: [I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+
+ public com.android.hoststubgen.test.tinyframework.R$Nested();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/R$Nested;
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: iconst_1
+ x: newarray int
+ x: dup
+ x: iconst_0
+ x: iconst_1
+ x: iastore
+ x: putstatic #x // Field ARRAY:[I
+ x: return
+ LineNumberTable:
+}
+SourceFile: "R.java"
+NestHost: class com/android/hoststubgen/test/tinyframework/R
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+## Class: com/android/hoststubgen/test/tinyframework/R.class
+ Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/R
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 3
+ public com.android.hoststubgen.test.tinyframework.R();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/R;
+}
+SourceFile: "R.java"
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/R$Nested
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
Compiled from "TinyFrameworkCallerCheck.java"
class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
@@ -572,9 +644,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota
suffix="_host"
)
- public static int nativeAddThree_host(int);
+ private static int nativeAddThree_host(int);
descriptor: (I)I
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
x: iload_0
@@ -1833,7 +1905,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 10, attributes: 2
+ interfaces: 0, fields: 1, methods: 11, attributes: 2
int value;
descriptor: I
flags: (0x0000)
@@ -1938,6 +2010,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
x: athrow
LineNumberTable:
+
+ public static native byte nativeBytePlus(byte, byte);
+ descriptor: (BB)B
+ flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
}
SourceFile: "TinyFrameworkNative.java"
RuntimeInvisibleAnnotations:
@@ -1955,7 +2031,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 4, attributes: 2
+ interfaces: 0, fields: 0, methods: 5, attributes: 2
public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -2013,6 +2089,22 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
Start Length Slot Name Signature
0 7 0 source Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
0 7 1 arg I
+
+ public static byte nativeBytePlus(byte, byte);
+ descriptor: (BB)B
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: iload_0
+ x: iload_1
+ x: iadd
+ x: i2b
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 arg1 B
+ 0 5 1 arg2 B
}
SourceFile: "TinyFrameworkNative_host.java"
RuntimeInvisibleAnnotations:
@@ -2614,6 +2706,187 @@ SourceFile: "TinyFrameworkPackageRedirect.java"
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.class
+ Compiled from "TinyFrameworkRenamedClassCaller.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 2, attributes: 2
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller;
+
+ public static int foo(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ x: dup
+ x: iload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed."<init>":(I)V
+ x: invokevirtual #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getValue:()I
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 12 0 value I
+}
+SourceFile: "TinyFrameworkRenamedClassCaller.java"
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.class
+ Compiled from "TinyFrameworkToBeRenamed.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 2
+ private final int mValue;
+ descriptor: I
+ flags: (0x0012) ACC_PRIVATE, ACC_FINAL
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iload_1
+ x: putfield #x // Field mValue:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ 0 10 1 value I
+
+ public int getValue();
+ descriptor: ()I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: getfield #x // Field mValue:I
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+}
+SourceFile: "TinyFrameworkToBeRenamed.java"
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class
+ Compiled from "A.java"
+public class com.android.hoststubgen.test.tinyframework.packagetest.A
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/A
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.packagetest.A();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/packagetest/A;
+}
+SourceFile: "A.java"
+## Class: com/android/hoststubgen/test/tinyframework/packagetest/B.class
+ Compiled from "B.java"
+public class com.android.hoststubgen.test.tinyframework.packagetest.B
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/B
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.packagetest.B();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/packagetest/B;
+}
+SourceFile: "B.java"
+## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/A.class
+ Compiled from "A.java"
+public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/sub/A
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.packagetest.sub.A();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/packagetest/sub/A;
+}
+SourceFile: "A.java"
+## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/B.class
+ Compiled from "B.java"
+public class com.android.hoststubgen.test.tinyframework.packagetest.sub.B
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/sub/B
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.packagetest.sub.B();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/packagetest/sub/B;
+}
+SourceFile: "B.java"
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
Compiled from "C1.java"
public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
index 37de857b9780..17ba48c67d98 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
@@ -122,6 +122,100 @@ RuntimeVisibleAnnotations:
NestMembers:
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
+## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class
+ Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R$Nested
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/R$Nested
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 4
+ public static int[] ARRAY;
+ descriptor: [I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public com.android.hoststubgen.test.tinyframework.R$Nested();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestHost: class com/android/hoststubgen/test/tinyframework/R
+## Class: com/android/hoststubgen/test/tinyframework/R.class
+ Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/R
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 4
+ public com.android.hoststubgen.test.tinyframework.R();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/R$Nested
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
Compiled from "TinyFrameworkCallerCheck.java"
class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
@@ -1554,7 +1648,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 9, attributes: 3
+ interfaces: 0, fields: 1, methods: 10, attributes: 3
int value;
descriptor: I
flags: (0x0000)
@@ -1686,6 +1780,15 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public static native byte nativeBytePlus(byte, byte);
+ descriptor: (BB)B
+ flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
}
SourceFile: "TinyFrameworkNative.java"
RuntimeVisibleAnnotations:
@@ -2074,6 +2177,88 @@ RuntimeVisibleAnnotations:
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.class
+ Compiled from "TinyFrameworkRenamedClassCaller.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 2, attributes: 3
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public static int foo(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+SourceFile: "TinyFrameworkRenamedClassCaller.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class
+ Compiled from "A.java"
+public class com.android.hoststubgen.test.tinyframework.packagetest.A
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/A
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "A.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/A.class
+ Compiled from "A.java"
+public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/sub/A
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "A.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
Compiled from "C1.java"
public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
@@ -2284,3 +2469,62 @@ RuntimeVisibleAnnotations:
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.class
+ Compiled from "TinyFrameworkToBeRenamed.java"
+public class rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 3
+ private final int mValue;
+ descriptor: I
+ flags: (0x0012) ACC_PRIVATE, ACC_FINAL
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public int getValue();
+ descriptor: ()I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+SourceFile: "TinyFrameworkToBeRenamed.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
index c9c607c58c68..0f5f7e747a2e 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
@@ -348,6 +348,108 @@ RuntimeVisibleAnnotations:
NestMembers:
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
+## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class
+ Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R$Nested
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/R$Nested
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 4
+ public static int[] ARRAY;
+ descriptor: [I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public com.android.hoststubgen.test.tinyframework.R$Nested();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/R$Nested;
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: iconst_1
+ x: newarray int
+ x: dup
+ x: iconst_0
+ x: iconst_1
+ x: iastore
+ x: putstatic #x // Field ARRAY:[I
+ x: return
+ LineNumberTable:
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestHost: class com/android/hoststubgen/test/tinyframework/R
+## Class: com/android/hoststubgen/test/tinyframework/R.class
+ Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/R
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 4
+ public com.android.hoststubgen.test.tinyframework.R();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/R;
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/R$Nested
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
Compiled from "TinyFrameworkCallerCheck.java"
class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
@@ -2236,7 +2338,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 10, attributes: 3
+ interfaces: 0, fields: 1, methods: 11, attributes: 3
int value;
descriptor: I
flags: (0x0000)
@@ -2435,6 +2537,23 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public static byte nativeBytePlus(byte, byte);
+ descriptor: (BB)B
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: iload_0
+ x: iload_1
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeBytePlus:(BB)B
+ x: ireturn
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
}
SourceFile: "TinyFrameworkNative.java"
RuntimeVisibleAnnotations:
@@ -2457,7 +2576,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 4, attributes: 3
+ interfaces: 0, fields: 0, methods: 5, attributes: 3
public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -2551,6 +2670,31 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public static byte nativeBytePlus(byte, byte);
+ descriptor: (BB)B
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String nativeBytePlus
+ x: ldc #x // String (BB)B
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iload_0
+ x: iload_1
+ x: iadd
+ x: i2b
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 15 5 0 arg1 B
+ 15 5 1 arg2 B
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
}
SourceFile: "TinyFrameworkNative_host.java"
RuntimeVisibleAnnotations:
@@ -3396,6 +3540,95 @@ RuntimeVisibleAnnotations:
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.class
+ Compiled from "TinyFrameworkRenamedClassCaller.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 2, attributes: 3
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller;
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public static int foo(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ x: dup
+ x: iload_0
+ x: invokespecial #x // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed."<init>":(I)V
+ x: invokevirtual #x // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getValue:()I
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 12 0 value I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+SourceFile: "TinyFrameworkRenamedClassCaller.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class
+ Compiled from "A.java"
+public class com.android.hoststubgen.test.tinyframework.packagetest.A
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/A
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "A.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/A.class
+ Compiled from "A.java"
+public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/sub/A
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "A.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
Compiled from "C1.java"
public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
@@ -3786,3 +4019,70 @@ RuntimeVisibleAnnotations:
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.class
+ Compiled from "TinyFrameworkToBeRenamed.java"
+public class rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 3
+ private final int mValue;
+ descriptor: I
+ flags: (0x0012) ACC_PRIVATE, ACC_FINAL
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iload_1
+ x: putfield #x // Field mValue:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ 0 10 1 value I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public int getValue();
+ descriptor: ()I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: getfield #x // Field mValue:I
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+SourceFile: "TinyFrameworkToBeRenamed.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
index 37de857b9780..17ba48c67d98 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
@@ -122,6 +122,100 @@ RuntimeVisibleAnnotations:
NestMembers:
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
+## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class
+ Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R$Nested
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/R$Nested
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 4
+ public static int[] ARRAY;
+ descriptor: [I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public com.android.hoststubgen.test.tinyframework.R$Nested();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestHost: class com/android/hoststubgen/test/tinyframework/R
+## Class: com/android/hoststubgen/test/tinyframework/R.class
+ Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/R
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 4
+ public com.android.hoststubgen.test.tinyframework.R();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/R$Nested
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
Compiled from "TinyFrameworkCallerCheck.java"
class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
@@ -1554,7 +1648,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 9, attributes: 3
+ interfaces: 0, fields: 1, methods: 10, attributes: 3
int value;
descriptor: I
flags: (0x0000)
@@ -1686,6 +1780,15 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public static native byte nativeBytePlus(byte, byte);
+ descriptor: (BB)B
+ flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
}
SourceFile: "TinyFrameworkNative.java"
RuntimeVisibleAnnotations:
@@ -2074,6 +2177,88 @@ RuntimeVisibleAnnotations:
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.class
+ Compiled from "TinyFrameworkRenamedClassCaller.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 2, attributes: 3
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public static int foo(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+SourceFile: "TinyFrameworkRenamedClassCaller.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class
+ Compiled from "A.java"
+public class com.android.hoststubgen.test.tinyframework.packagetest.A
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/A
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "A.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/A.class
+ Compiled from "A.java"
+public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/sub/A
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "A.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
Compiled from "C1.java"
public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
@@ -2284,3 +2469,62 @@ RuntimeVisibleAnnotations:
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.class
+ Compiled from "TinyFrameworkToBeRenamed.java"
+public class rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 3
+ private final int mValue;
+ descriptor: I
+ flags: (0x0012) ACC_PRIVATE, ACC_FINAL
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public int getValue();
+ descriptor: ()I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+SourceFile: "TinyFrameworkToBeRenamed.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
index a57907d9398b..3beea643823a 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
@@ -481,6 +481,136 @@ RuntimeVisibleAnnotations:
NestMembers:
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
+## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class
+ Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R$Nested
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/R$Nested
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 4
+ public static int[] ARRAY;
+ descriptor: [I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public com.android.hoststubgen.test.tinyframework.R$Nested();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/R$Nested
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/R$Nested;
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/R$Nested
+ x: ldc #x // String <clinit>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/R$Nested
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: iconst_1
+ x: newarray int
+ x: dup
+ x: iconst_0
+ x: iconst_1
+ x: iastore
+ x: putstatic #x // Field ARRAY:[I
+ x: return
+ LineNumberTable:
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestHost: class com/android/hoststubgen/test/tinyframework/R
+## Class: com/android/hoststubgen/test/tinyframework/R.class
+ Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/R
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 2, attributes: 4
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/R
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.R();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/R
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/R;
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/R$Nested
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
Compiled from "TinyFrameworkCallerCheck.java"
class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
@@ -2743,7 +2873,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 11, attributes: 3
+ interfaces: 0, fields: 1, methods: 12, attributes: 3
int value;
descriptor: I
flags: (0x0000)
@@ -3002,6 +3132,28 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public static byte nativeBytePlus(byte, byte);
+ descriptor: (BB)B
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+ x: ldc #x // String nativeBytePlus
+ x: ldc #x // String (BB)B
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iload_0
+ x: iload_1
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeBytePlus:(BB)B
+ x: ireturn
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
}
SourceFile: "TinyFrameworkNative.java"
RuntimeVisibleAnnotations:
@@ -3024,7 +3176,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 5, attributes: 3
+ interfaces: 0, fields: 0, methods: 6, attributes: 3
private static {};
descriptor: ()V
flags: (0x000a) ACC_PRIVATE, ACC_STATIC
@@ -3148,6 +3300,36 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public static byte nativeBytePlus(byte, byte);
+ descriptor: (BB)B
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String nativeBytePlus
+ x: ldc #x // String (BB)B
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String nativeBytePlus
+ x: ldc #x // String (BB)B
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iload_0
+ x: iload_1
+ x: iadd
+ x: i2b
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 5 0 arg1 B
+ 26 5 1 arg2 B
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
}
SourceFile: "TinyFrameworkNative_host.java"
RuntimeVisibleAnnotations:
@@ -4226,6 +4408,133 @@ RuntimeVisibleAnnotations:
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.class
+ Compiled from "TinyFrameworkRenamedClassCaller.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 3, attributes: 3
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller;
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public static int foo(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
+ x: ldc #x // String foo
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: new #x // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ x: dup
+ x: iload_0
+ x: invokespecial #x // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed."<init>":(I)V
+ x: invokevirtual #x // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getValue:()I
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 12 0 value I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+SourceFile: "TinyFrameworkRenamedClassCaller.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class
+ Compiled from "A.java"
+public class com.android.hoststubgen.test.tinyframework.packagetest.A
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/A
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/packagetest/A
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "A.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/A.class
+ Compiled from "A.java"
+public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/sub/A
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/packagetest/sub/A
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "A.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
Compiled from "C1.java"
public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
@@ -4809,3 +5118,90 @@ RuntimeVisibleAnnotations:
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.class
+ Compiled from "TinyFrameworkToBeRenamed.java"
+public class rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 3, attributes: 3
+ private final int mValue;
+ descriptor: I
+ flags: (0x0012) ACC_PRIVATE, ACC_FINAL
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ x: ldc #x // String <init>
+ x: ldc #x // String (I)V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iload_1
+ x: putfield #x // Field mValue:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ 11 10 1 value I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public int getValue();
+ descriptor: ()I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ x: ldc #x // String getValue
+ x: ldc #x // String ()I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: getfield #x // Field mValue:I
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+SourceFile: "TinyFrameworkToBeRenamed.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
index d30208452a40..75c9721020bd 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
@@ -19,6 +19,9 @@ class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy ~com
# Heuristics rule: Stub all the AIDL classes.
class :aidl stubclass
+# Heuristics rule: Stub all the R classes.
+class :r stubclass
+
# Default is "remove", so let's put all the base classes / interfaces in the stub first.
class com.android.hoststubgen.test.tinyframework.subclasstest.C1 stub
class com.android.hoststubgen.test.tinyframework.subclasstest.C2 stub
@@ -37,3 +40,17 @@ class *com.android.hoststubgen.test.tinyframework.subclasstest.CA remove
class *com.android.hoststubgen.test.tinyframework.subclasstest.I1 keep
class *com.android.hoststubgen.test.tinyframework.subclasstest.IA remove
+
+# Test package directive
+package com.android.hoststubgen.test.tinyframework.packagetest stub
+class com.android.hoststubgen.test.tinyframework.packagetest.B remove
+class com.android.hoststubgen.test.tinyframework.packagetest.sub.B remove
+# The following rules are the same as above
+# class com.android.hoststubgen.test.tinyframework.packagetest.A stub
+# class com.android.hoststubgen.test.tinyframework.packagetest.sub.A stub
+
+
+# "rename" takes a type internal name, so '/'s is used as a separator.
+# The leading / in the prefix is not needed (it'll be stripped), but it's added to make
+# sure the stripping works.
+rename ^.*/TinyFrameworkToBeRenamed$ /rename_prefix/ \ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
index 1dec6ab092cb..cee29dcd1d59 100755
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
@@ -46,7 +46,6 @@ def check_one_file(filename):
class TestWithGoldenOutput(unittest.TestCase):
# Test to check the generated jar files to the golden output.
- @unittest.skip("Disabled until JDK 21 is merged and the golden files updated")
def test_compare_to_golden(self):
files = os.listdir(GOLDEN_DIR)
files.sort()
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java
new file mode 100644
index 000000000000..b1bedf4b6853
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework;
+
+public class R {
+ public static class Nested {
+ public static int[] ARRAY = new int[] {1};
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.java
index ab387e0938c3..6d8a48a37fea 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.java
@@ -73,7 +73,8 @@ public class TinyFrameworkClassAnnotations {
@HostSideTestSubstitute(suffix = "_host")
public static native int nativeAddThree(int value);
- public static int nativeAddThree_host(int value) {
+ // This method is private, but at runtime, it'll inherit the visibility of the original method
+ private static int nativeAddThree_host(int value) {
return value + 3;
}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java
index 5a5e22db59e5..09ee183a2dcc 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java
@@ -52,4 +52,6 @@ public class TinyFrameworkNative {
public static void nativeStillNotSupported_should_be_like_this() {
throw new RuntimeException();
}
+
+ public static native byte nativeBytePlus(byte arg1, byte arg2);
}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java
index 749ebaa378e3..b23c21602967 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java
@@ -34,4 +34,8 @@ public class TinyFrameworkNative_host {
public static int nativeNonStaticAddToValue(TinyFrameworkNative source, int arg) {
return source.value + arg;
}
+
+ public static byte nativeBytePlus(byte arg1, byte arg2) {
+ return (byte) (arg1 + arg2);
+ }
}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java
new file mode 100644
index 000000000000..31a164af03f5
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java
@@ -0,0 +1,28 @@
+/*
+ * 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.hoststubgen.test.tinyframework;
+
+import android.hosttest.annotation.HostSideTestWholeClassStub;
+
+@HostSideTestWholeClassStub
+public class TinyFrameworkRenamedClassCaller {
+ /** Calls the class that'll be renamed. */
+ public static int foo(int value) {
+ // When TinyFrameworkToBeRenamed gets renamed, this callsite should be updated too,
+ // so this code should work as-is.
+ return new TinyFrameworkToBeRenamed(value).getValue();
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java
new file mode 100644
index 000000000000..1430bcb0276b
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java
@@ -0,0 +1,34 @@
+/*
+ * 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.hoststubgen.test.tinyframework;
+
+import android.hosttest.annotation.HostSideTestWholeClassStub;
+
+/**
+ * This class will be renamed by the "rename" directive in the policy file.
+ */
+@HostSideTestWholeClassStub
+public class TinyFrameworkToBeRenamed {
+ private final int mValue;
+
+ public TinyFrameworkToBeRenamed(int value) {
+ mValue = value;
+ }
+
+ public int getValue() {
+ return mValue;
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java
new file mode 100644
index 000000000000..6a52e4401b45
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.packagetest;
+
+public class A {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java
new file mode 100644
index 000000000000..1374a288f7aa
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.packagetest;
+
+public class B {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java
new file mode 100644
index 000000000000..361a7fd04842
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.packagetest.sub;
+
+public class A {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java
new file mode 100644
index 000000000000..716595a44243
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.packagetest.sub;
+
+public class B {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
index ba17c75132f2..bf0f6545d1f5 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
@@ -19,6 +19,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
+import com.android.hoststubgen.test.tinyframework.R.Nested;
import com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.SubClass;
import org.junit.Rule;
@@ -154,13 +155,22 @@ public class TinyFrameworkClassTest {
}
@Test
+ public void testNativeSubstitutionLong() {
+ assertThat(TinyFrameworkNative.nativeLongPlus(1L, 2L)).isEqualTo(3L);
+ }
+
+ @Test
+ public void testNativeSubstitutionByte() {
+ assertThat(TinyFrameworkNative.nativeBytePlus((byte) 3, (byte) 4)).isEqualTo(7);
+ }
+
+ @Test
public void testNativeSubstitutionClass_nonStatic() {
TinyFrameworkNative instance = new TinyFrameworkNative();
instance.setValue(5);
assertThat(instance.nativeNonStaticAddToValue(3)).isEqualTo(8);
}
-
@Test
public void testSubstituteNativeWithThrow() throws Exception {
// We can't use TinyFrameworkNative.nativeStillNotSupported() directly in this class,
@@ -319,4 +329,14 @@ public class TinyFrameworkClassTest {
assertThat(IPretendingAidl.Stub.addOne(1)).isEqualTo(2);
assertThat(IPretendingAidl.Stub.Proxy.addTwo(1)).isEqualTo(3);
}
+
+ @Test
+ public void testRFileHeuristics() {
+ assertThat(Nested.ARRAY.length).isEqualTo(1);
+ }
+
+ @Test
+ public void testTypeRename() {
+ assertThat(TinyFrameworkRenamedClassCaller.foo(1)).isEqualTo(1);
+ }
}
diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt b/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt
new file mode 100644
index 000000000000..081d03909926
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.utils
+
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Test
+
+class TrieTest {
+
+ private class TestTrie : Trie<String, Char, Int>() {
+ override fun splitToComponents(key: String): Iterator<Char> {
+ return key.toCharArray().iterator()
+ }
+ }
+
+ @Test
+ fun testPrefixTree() {
+ val trie = TestTrie()
+ trie["ab"] = 1
+ trie["abc"] = 2
+ trie["ab123"] = 3
+ assertNull(trie["a"])
+ assertNull(trie["x"])
+ assertNull(trie["a1"])
+ assertEquals(1, trie["ab"])
+ assertEquals(2, trie["abc"])
+ assertEquals(2, trie["abcd"])
+ assertEquals(1, trie["ab1"])
+ assertEquals(1, trie["ab12"])
+ assertEquals(3, trie["ab123"])
+ assertEquals(1, trie["ab@"])
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/visitors/HelperTest.kt b/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/visitors/HelperTest.kt
index 0ea90ed2fbf0..75e2536a98fa 100644
--- a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/visitors/HelperTest.kt
+++ b/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/visitors/HelperTest.kt
@@ -71,7 +71,7 @@ class HelperTest {
addClass(cn)
}
- fun check(from: MethodNode?, to: MethodNode?, expected: Boolean) {
+ fun check(from: MethodNode?, to: MethodNode?, expected: Int) {
assertThat(checkSubstitutionMethodCompatibility(
classes,
cn.name,
@@ -82,21 +82,21 @@ class HelperTest {
)).isEqualTo(expected)
}
- check(staticPublic, staticPublic, true)
- check(staticPrivate, staticPrivate, true)
- check(nonStaticPublic, nonStaticPublic, true)
- check(nonStaticPProtected, nonStaticPProtected, true)
+ check(staticPublic, staticPublic, Opcodes.ACC_PUBLIC or Opcodes.ACC_STATIC)
+ check(staticPrivate, staticPrivate, Opcodes.ACC_PRIVATE or Opcodes.ACC_STATIC)
+ check(nonStaticPublic, nonStaticPublic, Opcodes.ACC_PUBLIC)
+ check(nonStaticPProtected, nonStaticPProtected, 0)
- check(staticPublic, null, false)
- check(null, staticPublic, false)
+ check(staticPublic, null, NOT_COMPATIBLE)
+ check(null, staticPublic, NOT_COMPATIBLE)
- check(staticPublic, nonStaticPublic, false)
- check(nonStaticPublic, staticPublic, false)
+ check(staticPublic, nonStaticPublic, NOT_COMPATIBLE)
+ check(nonStaticPublic, staticPublic, NOT_COMPATIBLE)
- check(staticPublic, staticPrivate, false)
- check(staticPrivate, staticPublic, true)
+ check(staticPublic, staticPrivate, Opcodes.ACC_PUBLIC or Opcodes.ACC_STATIC)
+ check(staticPrivate, staticPublic, Opcodes.ACC_PRIVATE or Opcodes.ACC_STATIC)
- check(nonStaticPublic, nonStaticPProtected, false)
- check(nonStaticPProtected, nonStaticPublic, true)
+ check(nonStaticPublic, nonStaticPProtected, Opcodes.ACC_PUBLIC)
+ check(nonStaticPProtected, nonStaticPublic, 0)
}
} \ No newline at end of file
diff --git a/tools/hoststubgen/scripts/dump-jar b/tools/hoststubgen/scripts/dump-jar
index 992665ed58ee..fe546fe9cc92 100755
--- a/tools/hoststubgen/scripts/dump-jar
+++ b/tools/hoststubgen/scripts/dump-jar
@@ -97,6 +97,7 @@ filter_output() {
# - Remove the constant pool
# - Remove the line number table
# - Some other transient lines
+ # - Sometimes the javap shows mysterious warnings, so remove them too.
#
# `/PATTERN-1/,/PATTERN-1/{//!d}` is a trick to delete lines between two patterns, without
# the start and the end lines.
@@ -106,7 +107,8 @@ filter_output() {
-e '/^ *line *[0-9][0-9]*: *[0-9][0-9]*$/d' \
-e '/SHA-256 checksum/d' \
-e '/Last modified/d' \
- -e '/^Classfile jar/d'
+ -e '/^Classfile jar/d' \
+ -e '/\[warning\]/d'
else
cat # Print as-is.
fi
diff --git a/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt b/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt
index 4c1fa6ec40b3..4995eebdd79e 100644
--- a/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt
+++ b/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt
@@ -30,7 +30,7 @@ import javax.lang.model.SourceVersion
import javax.lang.model.element.AnnotationValue
import javax.lang.model.element.TypeElement
import javax.tools.Diagnostic.Kind
-import javax.tools.StandardLocation.CLASS_OUTPUT
+import javax.tools.StandardLocation.SOURCE_OUTPUT
import kotlin.collections.set
/**
@@ -126,7 +126,7 @@ class IntDefProcessor : AbstractProcessor() {
@Throws(IOException::class)
private fun outputToFile(annotationTypeToIntDefMapping: Map<String, IntDefMapping>) {
val resource = processingEnv.filer.createResource(
- CLASS_OUTPUT, "com.android.winscope", outputName)
+ SOURCE_OUTPUT, "com.android.winscope", outputName)
val writer = resource.openWriter()
serializeTo(annotationTypeToIntDefMapping, writer)
writer.close()
diff --git a/tools/processors/intdef_mappings/test/android/processor/IntDefProcessorTest.kt b/tools/processors/intdef_mappings/test/android/processor/IntDefProcessorTest.kt
index c0c159c98aac..d87b6df901b2 100644
--- a/tools/processors/intdef_mappings/test/android/processor/IntDefProcessorTest.kt
+++ b/tools/processors/intdef_mappings/test/android/processor/IntDefProcessorTest.kt
@@ -24,7 +24,7 @@ import junit.framework.Assert.assertEquals
import org.junit.Test
import java.io.StringWriter
import javax.tools.JavaFileObject
-import javax.tools.StandardLocation.CLASS_OUTPUT
+import javax.tools.StandardLocation.SOURCE_OUTPUT
/**
* Tests for [IntDefProcessor]
@@ -112,7 +112,7 @@ class IntDefProcessorTest {
.compile(filesToCompile.toMutableList())
assertThat(compilation).succeeded()
- assertThat(compilation).generatedFile(CLASS_OUTPUT, "com.android.winscope",
+ assertThat(compilation).generatedFile(SOURCE_OUTPUT, "com.android.winscope",
"intDefMapping.json").contentsAsUtf8String().isEqualTo(expectedFile)
}
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
index 59db66b95250..fd7d2b35bb6f 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
@@ -27,9 +27,16 @@ import com.github.javaparser.ast.CompilationUnit
import com.github.javaparser.ast.Modifier
import com.github.javaparser.ast.NodeList
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration
+import com.github.javaparser.ast.expr.ArrayAccessExpr
+import com.github.javaparser.ast.expr.ArrayCreationExpr
+import com.github.javaparser.ast.expr.ArrayInitializerExpr
import com.github.javaparser.ast.expr.AssignExpr
+import com.github.javaparser.ast.expr.BooleanLiteralExpr
+import com.github.javaparser.ast.expr.Expression
import com.github.javaparser.ast.expr.FieldAccessExpr
+import com.github.javaparser.ast.expr.IntegerLiteralExpr
import com.github.javaparser.ast.expr.MethodCallExpr
+import com.github.javaparser.ast.expr.MethodReferenceExpr
import com.github.javaparser.ast.expr.NameExpr
import com.github.javaparser.ast.expr.NullLiteralExpr
import com.github.javaparser.ast.expr.ObjectCreationExpr
@@ -49,8 +56,7 @@ import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.jar.JarOutputStream
import java.util.zip.ZipEntry
-import kotlin.math.abs
-import kotlin.random.Random
+import kotlin.math.absoluteValue
import kotlin.system.exitProcess
object ProtoLogTool {
@@ -82,7 +88,11 @@ object ProtoLogTool {
}
private fun processClasses(command: CommandOptions) {
- val generationHash = abs(Random.nextInt())
+ // A deterministic hash based on the group jar path and the source files we are processing.
+ // The hash is required to make sure different ProtoLogImpls don't conflict.
+ val generationHash = (command.javaSourceArgs.toTypedArray() + command.protoLogGroupsJarArg)
+ .contentHashCode().absoluteValue
+
// Need to generate a new impl class to inject static constants into the class.
val generatedProtoLogImplClass =
"com.android.internal.protolog.ProtoLogImpl_$generationHash"
@@ -169,6 +179,8 @@ object ProtoLogTool {
val classNameNode = classDeclaration.findFirst(SimpleName::class.java).get()
classNameNode.setId(protoLogImplGenName)
+ injectCacheClass(classDeclaration, groups, protoLogGroupsClassName)
+
injectConstants(classDeclaration,
viewerConfigFilePath, legacyViewerConfigFilePath, legacyOutputFilePath, groups,
protoLogGroupsClassName)
@@ -217,6 +229,12 @@ object ProtoLogTool {
field.variables.first().setInitializer(
MethodCallExpr().setName("createLogGroupsMap"))
}
+ ProtoLogToolInjected.Value.CACHE_UPDATER.name -> {
+ field.setFinal(true)
+ field.variables.first().setInitializer(MethodReferenceExpr()
+ .setScope(NameExpr("Cache"))
+ .setIdentifier("update"))
+ }
else -> error("Unhandled ProtoLogToolInjected value: $valueName.")
}
}
@@ -263,6 +281,61 @@ object ProtoLogTool {
}
}
+ private fun injectCacheClass(
+ classDeclaration: ClassOrInterfaceDeclaration,
+ groups: Map<String, LogGroup>,
+ protoLogGroupsClassName: String,
+ ) {
+ val cacheClass = ClassOrInterfaceDeclaration()
+ .setName("Cache")
+ .setPublic(true)
+ .setStatic(true)
+ for (group in groups) {
+ val nodeList = NodeList<Expression>()
+ val defaultVal = BooleanLiteralExpr(group.value.textEnabled || group.value.enabled)
+ repeat(LogLevel.entries.size) { nodeList.add(defaultVal) }
+ cacheClass.addFieldWithInitializer(
+ "boolean[]",
+ "${group.key}_enabled",
+ ArrayCreationExpr().setElementType("boolean[]").setInitializer(
+ ArrayInitializerExpr().setValues(nodeList)
+ ),
+ Modifier.Keyword.PUBLIC,
+ Modifier.Keyword.STATIC
+ )
+ }
+
+ val updateBlockStmt = BlockStmt()
+ for (group in groups) {
+ for (level in LogLevel.entries) {
+ updateBlockStmt.addStatement(
+ AssignExpr()
+ .setTarget(
+ ArrayAccessExpr()
+ .setName(NameExpr("${group.key}_enabled"))
+ .setIndex(IntegerLiteralExpr(level.ordinal))
+ ).setValue(
+ MethodCallExpr()
+ .setName("isEnabled")
+ .setArguments(NodeList(
+ FieldAccessExpr()
+ .setScope(NameExpr(protoLogGroupsClassName))
+ .setName(group.value.name),
+ FieldAccessExpr()
+ .setScope(NameExpr("LogLevel"))
+ .setName(level.toString()),
+ ))
+ )
+ )
+ }
+ }
+
+ cacheClass.addMethod("update").setPrivate(true).setStatic(true)
+ .setBody(updateBlockStmt)
+
+ classDeclaration.addMember(cacheClass)
+ }
+
private fun tryParse(code: String, fileName: String): CompilationUnit {
try {
return StaticJavaParser.parse(code)
diff --git a/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt b/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
index 2b7164191dd0..6a8a0717b2f1 100644
--- a/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
@@ -22,6 +22,7 @@ import com.github.javaparser.StaticJavaParser
import com.github.javaparser.ast.CompilationUnit
import com.github.javaparser.ast.NodeList
import com.github.javaparser.ast.body.VariableDeclarator
+import com.github.javaparser.ast.expr.ArrayAccessExpr
import com.github.javaparser.ast.expr.CastExpr
import com.github.javaparser.ast.expr.Expression
import com.github.javaparser.ast.expr.FieldAccessExpr
@@ -35,6 +36,8 @@ import com.github.javaparser.ast.expr.TypeExpr
import com.github.javaparser.ast.expr.VariableDeclarationExpr
import com.github.javaparser.ast.stmt.BlockStmt
import com.github.javaparser.ast.stmt.ExpressionStmt
+import com.github.javaparser.ast.stmt.IfStmt
+import com.github.javaparser.ast.stmt.Statement
import com.github.javaparser.ast.type.ArrayType
import com.github.javaparser.ast.type.ClassOrInterfaceType
import com.github.javaparser.ast.type.PrimitiveType
@@ -74,6 +77,8 @@ class SourceTransformer(
private val protoLogImplClassNode =
StaticJavaParser.parseExpression<FieldAccessExpr>(protoLogImplClassName)
+ private val protoLogImplCacheClassNode =
+ StaticJavaParser.parseExpression<FieldAccessExpr>("$protoLogImplClassName.Cache")
private var processedCode: MutableList<String> = mutableListOf()
private var offsets: IntArray = IntArray(0)
/** The path of the file being processed, relative to $ANDROID_BUILD_TOP */
@@ -121,8 +126,9 @@ class SourceTransformer(
group: LogGroup,
level: LogLevel,
messageString: String
- ): BlockStmt {
+ ): Statement {
val hash = CodeUtils.hash(packagePath, messageString, level, group)
+
val newCall = call.clone()
if (!group.textEnabled) {
// Remove message string if text logging is not enabled by default.
@@ -166,11 +172,15 @@ class SourceTransformer(
}
blockStmt.addStatement(ExpressionStmt(newCall))
- return blockStmt
+ val isLogEnabled = ArrayAccessExpr()
+ .setName(NameExpr("$protoLogImplCacheClassNode.${group.name}_enabled"))
+ .setIndex(IntegerLiteralExpr(level.ordinal))
+
+ return IfStmt(isLogEnabled, blockStmt, null)
}
private fun injectProcessedCallStatementInCode(
- processedCallStatement: BlockStmt,
+ processedCallStatement: Statement,
parentStmt: ExpressionStmt
) {
// Inline the new statement.
diff --git a/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt b/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt
index de0b5bae118e..82aa93da613b 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt
@@ -76,7 +76,7 @@ class SourceTransformerTest {
class Test {
void test() {
- { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, -1473209266730422156L, 9, "test %d %f", protoLogParam0, protoLogParam1); }
+ if (org.example.ProtoLogImpl.Cache.TEST_GROUP_enabled[3]) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, -1473209266730422156L, 9, "test %d %f", protoLogParam0, protoLogParam1); }
}
}
""".trimIndent()
@@ -86,7 +86,7 @@ class SourceTransformerTest {
class Test {
void test() {
- { long protoLogParam0 = 100; double protoLogParam1 = 0.1; String protoLogParam2 = String.valueOf("test"); org.example.ProtoLogImpl.w(TEST_GROUP, -4447034859795564700L, 9, "test %d %f " + "abc %s\n test", protoLogParam0, protoLogParam1, protoLogParam2);
+ if (org.example.ProtoLogImpl.Cache.TEST_GROUP_enabled[3]) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; String protoLogParam2 = String.valueOf("test"); org.example.ProtoLogImpl.w(TEST_GROUP, -4447034859795564700L, 9, "test %d %f " + "abc %s\n test", protoLogParam0, protoLogParam1, protoLogParam2);
}
}
@@ -98,8 +98,8 @@ class SourceTransformerTest {
class Test {
void test() {
- { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, -1473209266730422156L, 9, "test %d %f", protoLogParam0, protoLogParam1); } /* ProtoLog.w(TEST_GROUP, "test %d %f", 100, 0.1); */ { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, -1473209266730422156L, 9, "test %d %f", protoLogParam0, protoLogParam1); }
- { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, -1473209266730422156L, 9, "test %d %f", protoLogParam0, protoLogParam1); }
+ if (org.example.ProtoLogImpl.Cache.TEST_GROUP_enabled[3]) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, -1473209266730422156L, 9, "test %d %f", protoLogParam0, protoLogParam1); } /* ProtoLog.w(TEST_GROUP, "test %d %f", 100, 0.1); */ if (org.example.ProtoLogImpl.Cache.TEST_GROUP_enabled[3]) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, -1473209266730422156L, 9, "test %d %f", protoLogParam0, protoLogParam1); }
+ if (org.example.ProtoLogImpl.Cache.TEST_GROUP_enabled[3]) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, -1473209266730422156L, 9, "test %d %f", protoLogParam0, protoLogParam1); }
}
}
""".trimIndent()
@@ -109,7 +109,7 @@ class SourceTransformerTest {
class Test {
void test() {
- { org.example.ProtoLogImpl.w(TEST_GROUP, 3218600869538902408L, 0, "test", (Object[]) null); }
+ if (org.example.ProtoLogImpl.Cache.TEST_GROUP_enabled[3]) { org.example.ProtoLogImpl.w(TEST_GROUP, 3218600869538902408L, 0, "test", (Object[]) null); }
}
}
""".trimIndent()
@@ -119,7 +119,7 @@ class SourceTransformerTest {
class Test {
void test() {
- { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, -1473209266730422156L, 9, null, protoLogParam0, protoLogParam1); }
+ if (org.example.ProtoLogImpl.Cache.TEST_GROUP_enabled[3]) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, -1473209266730422156L, 9, null, protoLogParam0, protoLogParam1); }
}
}
""".trimIndent()
@@ -129,7 +129,7 @@ class SourceTransformerTest {
class Test {
void test() {
- { long protoLogParam0 = 100; double protoLogParam1 = 0.1; String protoLogParam2 = String.valueOf("test"); org.example.ProtoLogImpl.w(TEST_GROUP, -4447034859795564700L, 9, null, protoLogParam0, protoLogParam1, protoLogParam2);
+ if (org.example.ProtoLogImpl.Cache.TEST_GROUP_enabled[3]) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; String protoLogParam2 = String.valueOf("test"); org.example.ProtoLogImpl.w(TEST_GROUP, -4447034859795564700L, 9, null, protoLogParam0, protoLogParam1, protoLogParam2);
}
}
diff --git a/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt b/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt
index 0b619488c49c..9c443324defb 100644
--- a/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt
+++ b/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt
@@ -23,13 +23,21 @@ import java.io.IOException
import java.util.zip.ZipFile
fun main(args: Array<String>) {
- if (args.size != 2) {
+ if (args.size < 2 || args.size > 3) {
usage()
}
val zipFileName = args[0]
val aidlFileName = args[1]
+ var stable = false
+ if (args.size == 3) {
+ if (args[2] != "--guarantee_stable") {
+ usage()
+ }
+ stable = true
+ }
+
val zipFile: ZipFile
try {
@@ -55,6 +63,9 @@ fun main(args: Array<String>) {
val outFile = File(aidlFileName)
val outWriter = outFile.bufferedWriter()
for (parcelable in parcelables) {
+ if (stable) {
+ outWriter.write("@JavaOnlyStableParcelable ")
+ }
outWriter.write("parcelable ")
outWriter.write(parcelable.replace('/', '.').replace('$', '.'))
outWriter.write(";\n")
diff --git a/tools/systemfeatures/Android.bp b/tools/systemfeatures/Android.bp
index 2cebfe9790d0..aca25eb8f603 100644
--- a/tools/systemfeatures/Android.bp
+++ b/tools/systemfeatures/Android.bp
@@ -30,9 +30,9 @@ java_binary_host {
genrule {
name: "systemfeatures-gen-tests-srcs",
cmd: "$(location systemfeatures-gen-tool) com.android.systemfeatures.RwNoFeatures --readonly=false > $(location RwNoFeatures.java) && " +
- "$(location systemfeatures-gen-tool) com.android.systemfeatures.RoNoFeatures --readonly=true > $(location RoNoFeatures.java) && " +
+ "$(location systemfeatures-gen-tool) com.android.systemfeatures.RoNoFeatures --readonly=true --feature-apis=WATCH > $(location RoNoFeatures.java) && " +
"$(location systemfeatures-gen-tool) com.android.systemfeatures.RwFeatures --readonly=false --feature=WATCH:1 --feature=WIFI:0 --feature=VULKAN:-1 --feature=AUTO: > $(location RwFeatures.java) && " +
- "$(location systemfeatures-gen-tool) com.android.systemfeatures.RoFeatures --readonly=true --feature=WATCH:1 --feature=WIFI:0 --feature=VULKAN:-1 --feature=AUTO: > $(location RoFeatures.java)",
+ "$(location systemfeatures-gen-tool) com.android.systemfeatures.RoFeatures --readonly=true --feature=WATCH:1 --feature=WIFI:0 --feature=VULKAN:-1 --feature=AUTO: --feature-apis=WATCH,PC > $(location RoFeatures.java)",
out: [
"RwNoFeatures.java",
"RoNoFeatures.java",
diff --git a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
index 9bfda451067f..e537ffcb56bd 100644
--- a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
+++ b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
@@ -32,6 +32,7 @@ import javax.lang.model.element.Modifier
* <pre>
* <cmd> com.foo.RoSystemFeatures --readonly=true \
* --feature=WATCH:0 --feature=AUTOMOTIVE: --feature=VULKAN:9348
+ * --feature-apis=WATCH,PC,LEANBACK
* </pre>
*
* This generates a class that has the following signature:
@@ -45,12 +46,15 @@ import javax.lang.model.element.Modifier
* public static boolean hasFeatureAutomotive(Context context);
* @AssumeTrueForR8
* public static boolean hasFeatureVulkan(Context context);
+ * public static boolean hasFeaturePc(Context context);
+ * public static boolean hasFeatureLeanback(Context context);
* public static Boolean maybeHasFeature(String feature, int version);
* }
* </pre>
*/
object SystemFeaturesGenerator {
private const val FEATURE_ARG = "--feature="
+ private const val FEATURE_APIS_ARG = "--feature-apis="
private const val READONLY_ARG = "--readonly="
private val PACKAGEMANAGER_CLASS = ClassName.get("android.content.pm", "PackageManager")
private val CONTEXT_CLASS = ClassName.get("android.content", "Context")
@@ -64,6 +68,15 @@ object SystemFeaturesGenerator {
println(" Options:")
println(" --readonly=true|false Whether to encode features as build-time constants")
println(" --feature=\$NAME:\$VER A feature+version pair (blank version == disabled)")
+ println(" This will always generate associated query APIs,")
+ println(" adding to or replacing those from `--feature-apis=`.")
+ println(" --feature-apis=\$NAME_1,\$NAME_2")
+ println(" A comma-separated set of features for which to always")
+ println(" generate named query APIs. If a feature in this set is")
+ println(" not explicitly defined via `--feature=`, then a simple")
+ println(" runtime passthrough API will be generated, regardless")
+ println(" of the `--readonly` flag. This allows decoupling the")
+ println(" API surface from variations in device feature sets.")
}
/** Main entrypoint for build-time system feature codegen. */
@@ -76,18 +89,42 @@ object SystemFeaturesGenerator {
var readonly = false
var outputClassName: ClassName? = null
- val features = mutableListOf<FeatureInfo>()
+ val featureArgs = mutableListOf<FeatureArg>()
+ // We could just as easily hardcode this list, as the static API surface should change
+ // somewhat infrequently, but this decouples the codegen from the framework completely.
+ val featureApiArgs = mutableSetOf<String>()
for (arg in args) {
when {
arg.startsWith(READONLY_ARG) ->
readonly = arg.substring(READONLY_ARG.length).toBoolean()
arg.startsWith(FEATURE_ARG) -> {
- features.add(parseFeatureArg(arg))
+ featureArgs.add(parseFeatureArg(arg))
+ }
+ arg.startsWith(FEATURE_APIS_ARG) -> {
+ featureApiArgs.addAll(
+ arg.substring(FEATURE_APIS_ARG.length).split(",").map {
+ parseFeatureName(it)
+ }
+ )
}
else -> outputClassName = ClassName.bestGuess(arg)
}
}
+ // First load in all of the feature APIs we want to generate. Explicit feature definitions
+ // will then override this set with the appropriate readonly and version value.
+ val features = mutableMapOf<String, FeatureInfo>()
+ featureApiArgs.associateByTo(
+ features,
+ { it },
+ { FeatureInfo(it, version = null, readonly = false) },
+ )
+ featureArgs.associateByTo(
+ features,
+ { it.name },
+ { FeatureInfo(it.name, it.version, readonly) },
+ )
+
outputClassName
?: run {
println("Output class name must be provided.")
@@ -100,8 +137,8 @@ object SystemFeaturesGenerator {
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addJavadoc("@hide")
- addFeatureMethodsToClass(classBuilder, readonly, features)
- addMaybeFeatureMethodToClass(classBuilder, readonly, features)
+ addFeatureMethodsToClass(classBuilder, features.values)
+ addMaybeFeatureMethodToClass(classBuilder, features.values)
// TODO(b/203143243): Add validation of build vs runtime values to ensure consistency.
JavaFile.builder(outputClassName.packageName(), classBuilder.build())
@@ -115,13 +152,16 @@ object SystemFeaturesGenerator {
* * "--feature=WATCH:7" -> Feature enabled w/ version 7
* * "--feature=WATCH:" -> Feature disabled
*/
- private fun parseFeatureArg(arg: String): FeatureInfo {
+ private fun parseFeatureArg(arg: String): FeatureArg {
val featureArgs = arg.substring(FEATURE_ARG.length).split(":")
- val name = featureArgs[0].let { if (!it.startsWith("FEATURE_")) "FEATURE_$it" else it }
+ val name = parseFeatureName(featureArgs[0])
val version = featureArgs.getOrNull(1)?.toIntOrNull()
- return FeatureInfo(name, version)
+ return FeatureArg(name, version)
}
+ private fun parseFeatureName(name: String): String =
+ if (name.startsWith("FEATURE_")) name else "FEATURE_$name"
+
/*
* Adds per-feature query methods to the class with the form:
* {@code public static boolean hasFeatureX(Context context)},
@@ -129,8 +169,7 @@ object SystemFeaturesGenerator {
*/
private fun addFeatureMethodsToClass(
builder: TypeSpec.Builder,
- readonly: Boolean,
- features: List<FeatureInfo>
+ features: Collection<FeatureInfo>,
) {
for (feature in features) {
// Turn "FEATURE_FOO" into "hasFeatureFoo".
@@ -142,7 +181,7 @@ object SystemFeaturesGenerator {
.returns(Boolean::class.java)
.addParameter(CONTEXT_CLASS, "context")
- if (readonly) {
+ if (feature.readonly) {
val featureEnabled = compareValues(feature.version, 0) >= 0
methodBuilder.addAnnotation(
if (featureEnabled) ASSUME_TRUE_CLASS else ASSUME_FALSE_CLASS
@@ -158,19 +197,17 @@ object SystemFeaturesGenerator {
builder.addMethod(methodBuilder.build())
}
- if (!readonly) {
- builder.addMethod(
- MethodSpec.methodBuilder("hasFeatureFallback")
- .addModifiers(Modifier.PRIVATE, Modifier.STATIC)
- .returns(Boolean::class.java)
- .addParameter(CONTEXT_CLASS, "context")
- .addParameter(String::class.java, "featureName")
- .addStatement(
- "return context.getPackageManager().hasSystemFeature(featureName, 0)"
- )
- .build()
- )
- }
+ // This is a trivial method, even if unused based on readonly-codegen, it does little harm
+ // to always include it.
+ builder.addMethod(
+ MethodSpec.methodBuilder("hasFeatureFallback")
+ .addModifiers(Modifier.PRIVATE, Modifier.STATIC)
+ .returns(Boolean::class.java)
+ .addParameter(CONTEXT_CLASS, "context")
+ .addParameter(String::class.java, "featureName")
+ .addStatement("return context.getPackageManager().hasSystemFeature(featureName, 0)")
+ .build()
+ )
}
/*
@@ -185,8 +222,7 @@ object SystemFeaturesGenerator {
*/
private fun addMaybeFeatureMethodToClass(
builder: TypeSpec.Builder,
- readonly: Boolean,
- features: List<FeatureInfo>
+ features: Collection<FeatureInfo>,
) {
val methodBuilder =
MethodSpec.methodBuilder("maybeHasFeature")
@@ -196,16 +232,27 @@ object SystemFeaturesGenerator {
.addParameter(String::class.java, "featureName")
.addParameter(Int::class.java, "version")
- if (readonly) {
- methodBuilder.beginControlFlow("switch (featureName)")
- for (feature in features) {
- methodBuilder.addCode("case \$T.\$N: ", PACKAGEMANAGER_CLASS, feature.name)
- if (feature.version != null) {
- methodBuilder.addStatement("return \$L >= version", feature.version)
- } else {
- methodBuilder.addStatement("return false")
- }
+ var hasSwitchBlock = false
+ for (feature in features) {
+ // We only return non-null results for queries against readonly-defined features.
+ if (!feature.readonly) {
+ continue
+ }
+ if (!hasSwitchBlock) {
+ // As an optimization, only create the switch block if needed. Even an empty
+ // switch-on-string block can induce a hash, which we can avoid if readonly
+ // support is completely disabled.
+ hasSwitchBlock = true
+ methodBuilder.beginControlFlow("switch (featureName)")
}
+ methodBuilder.addCode("case \$T.\$N: ", PACKAGEMANAGER_CLASS, feature.name)
+ if (feature.version != null) {
+ methodBuilder.addStatement("return \$L >= version", feature.version)
+ } else {
+ methodBuilder.addStatement("return false")
+ }
+ }
+ if (hasSwitchBlock) {
methodBuilder.addCode("default: ")
methodBuilder.addStatement("break")
methodBuilder.endControlFlow()
@@ -214,5 +261,7 @@ object SystemFeaturesGenerator {
builder.addMethod(methodBuilder.build())
}
- private data class FeatureInfo(val name: String, val version: Int?)
+ private data class FeatureArg(val name: String, val version: Int?)
+
+ private data class FeatureInfo(val name: String, val version: Int?, val readonly: Boolean)
}
diff --git a/tools/systemfeatures/tests/PackageManager.java b/tools/systemfeatures/tests/PackageManager.java
index 645d500bc762..db670482065a 100644
--- a/tools/systemfeatures/tests/PackageManager.java
+++ b/tools/systemfeatures/tests/PackageManager.java
@@ -19,6 +19,7 @@ package android.content.pm;
/** Stub for testing */
public class PackageManager {
public static final String FEATURE_AUTO = "automotive";
+ public static final String FEATURE_PC = "pc";
public static final String FEATURE_VULKAN = "vulkan";
public static final String FEATURE_WATCH = "watch";
public static final String FEATURE_WIFI = "wifi";
diff --git a/tools/systemfeatures/tests/SystemFeaturesGeneratorTest.java b/tools/systemfeatures/tests/SystemFeaturesGeneratorTest.java
index 547d2cbd26f9..6dfd244a807b 100644
--- a/tools/systemfeatures/tests/SystemFeaturesGeneratorTest.java
+++ b/tools/systemfeatures/tests/SystemFeaturesGeneratorTest.java
@@ -68,6 +68,13 @@ public class SystemFeaturesGeneratorTest {
assertThat(RoNoFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 0)).isNull();
assertThat(RoNoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isNull();
assertThat(RoNoFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull();
+
+ // Also ensure we fall back to the PackageManager for feature APIs without an accompanying
+ // versioned feature definition.
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH, 0)).thenReturn(true);
+ assertThat(RwFeatures.hasFeatureWatch(mContext)).isTrue();
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH, 0)).thenReturn(false);
+ assertThat(RwFeatures.hasFeatureWatch(mContext)).isFalse();
}
@Test
@@ -127,6 +134,16 @@ public class SystemFeaturesGeneratorTest {
assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isFalse();
assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 100)).isFalse();
+ // For feature APIs without an associated feature definition, conditional queries should
+ // report null, and explicit queries should report runtime-defined versions.
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_PC, 0)).thenReturn(true);
+ assertThat(RoFeatures.hasFeaturePc(mContext)).isTrue();
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_PC, 0)).thenReturn(false);
+ assertThat(RoFeatures.hasFeaturePc(mContext)).isFalse();
+ assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_PC, -1)).isNull();
+ assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_PC, 0)).isNull();
+ assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_PC, 100)).isNull();
+
// For undefined types, conditional queries should report null (unknown).
assertThat(RoFeatures.maybeHasFeature("com.arbitrary.feature", -1)).isNull();
assertThat(RoFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull();