[automerger skipped] Merge "DO NOT MERGE Add an OEM configurable limit for zen rules" into qt-dev am: 81f051eff4 am: 14dec12d60 -s ours am: 112d4908aa -s ours am: 9b1a9dc79f -s ours
am skip reason: subject contains skip directive
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17045726
Change-Id: Ib9be9ab77f7a88b0e679383e04fee65ecdaad581
diff --git a/.gitignore b/.gitignore
index c47cc8b..5018436 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,6 @@
.idea
*.iml
*.sw*
-gen/
\ No newline at end of file
+gen/
+.vscode/
+*.code-workspace
diff --git a/.prebuilt_info/OWNERS b/.prebuilt_info/OWNERS
new file mode 100644
index 0000000..eb8b89b
--- /dev/null
+++ b/.prebuilt_info/OWNERS
@@ -0,0 +1 @@
+per-file prebuilt_info_packages_CtsShim_*.asciipb = file:/packages/CtsShim/OWNERS
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
index 29bcfe0..0ccd951 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "6508977"
+ build_id: "7552332"
target: "CtsShim"
source_file: "aosp_arm64/CtsShimPriv.apk"
}
@@ -8,5 +8,8 @@
version: ""
version_group: ""
git_project: "platform/frameworks/base"
- git_branch: "rvc-dev"
+ git_branch: "sc-dev"
+ transform: TRANSFORM_NONE
+ transform_options {
+ }
}
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
index be172e6..7e85c8f 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "6508977"
+ build_id: "7552332"
target: "CtsShim"
source_file: "aosp_arm64/CtsShim.apk"
}
@@ -8,5 +8,8 @@
version: ""
version_group: ""
git_project: "platform/frameworks/base"
- git_branch: "rvc-dev"
+ git_branch: "sc-dev"
+ transform: TRANSFORM_NONE
+ transform_options {
+ }
}
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
index 13eca13..20c2785 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "6508977"
+ build_id: "7552332"
target: "CtsShim"
source_file: "aosp_x86_64/CtsShimPriv.apk"
}
@@ -8,5 +8,8 @@
version: ""
version_group: ""
git_project: "platform/frameworks/base"
- git_branch: "rvc-dev"
+ git_branch: "sc-dev"
+ transform: TRANSFORM_NONE
+ transform_options {
+ }
}
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
index 2e863fe..13e3ae5 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "6508977"
+ build_id: "7552332"
target: "CtsShim"
source_file: "aosp_x86_64/CtsShim.apk"
}
@@ -8,5 +8,8 @@
version: ""
version_group: ""
git_project: "platform/frameworks/base"
- git_branch: "rvc-dev"
+ git_branch: "sc-dev"
+ transform: TRANSFORM_NONE
+ transform_options {
+ }
}
diff --git a/Android.bp b/Android.bp
index 95e3041..3f7581c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -25,205 +25,53 @@
//
// READ ME: ########################################################
-filegroup {
- name: "framework-core-sources",
- srcs: [
- "core/java/**/*.java",
- "core/java/**/*.aidl",
- ],
- path: "core/java",
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
}
-// These are subset of framework-core-sources that are needed by the
-// android.test.mock library. Ideally, the library should use public APIs only,
-// but unfortunately its API signature has some references to these private APIs.
-filegroup {
- name: "framework-core-sources-for-test-mock",
- srcs: [
- "core/java/android/app/IApplicationThread.aidl",
- "core/java/android/app/IServiceConnection.aidl",
- "core/java/android/content/IContentProvider.java",
- "core/java/android/content/pm/IPackageDataObserver.aidl",
- "core/java/android/content/pm/InstantAppInfo.java",
- "core/java/android/content/pm/KeySet.java",
- "core/java/android/content/pm/PackageManager.java",
- "core/java/android/content/pm/VerifierDeviceIdentity.java",
- "core/java/android/content/res/Resources.java",
- "core/java/android/os/storage/VolumeInfo.java",
- "core/java/android/view/DisplayAdjustments.java",
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'fileGroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ "SPDX-license-identifier-BSD",
+ "SPDX-license-identifier-CC-BY",
+ "SPDX-license-identifier-CPL-1.0",
+ "SPDX-license-identifier-GPL",
+ "SPDX-license-identifier-GPL-2.0",
+ "SPDX-license-identifier-MIT",
+ "SPDX-license-identifier-Unicode-DFS",
+ "SPDX-license-identifier-W3C",
+ "legacy_unencumbered",
],
- path: "core/java",
- visibility: ["//frameworks/base/test-mock"],
-}
-
-filegroup {
- name: "framework-drm-sources",
- srcs: [
- "drm/java/**/*.java",
+ license_text: [
+ "NOTICE",
],
- path: "drm/java",
-}
-
-filegroup {
- name: "framework-graphics-sources",
- srcs: [
- "graphics/java/**/*.java",
- "graphics/java/**/*.aidl",
- ],
- path: "graphics/java",
-}
-
-filegroup {
- name: "framework-identity-sources",
- srcs: [
- "identity/java/**/*.java",
- ],
- path: "identity/java",
-}
-
-filegroup {
- name: "framework-keystore-sources",
- srcs: [
- "keystore/java/**/*.java",
- "keystore/java/**/*.aidl",
- ],
- path: "keystore/java",
-}
-
-filegroup {
- name: "framework-location-sources",
- srcs: [
- "location/java/**/*.java",
- "location/java/**/*.aidl",
- ],
- path: "location/java",
-}
-
-filegroup {
- name: "framework-lowpan-sources",
- srcs: [
- "lowpan/java/**/*.java",
- "lowpan/java/**/*.aidl",
- ],
- path: "lowpan/java",
-}
-
-filegroup {
- name: "framework-media-sources",
- srcs: [
- "media/java/**/*.java",
- "media/java/**/*.aidl",
- ],
- path: "media/java",
-}
-
-filegroup {
- name: "framework-mca-effect-sources",
- srcs: [
- "media/mca/effect/java/**/*.java",
- ],
- path: "media/mca/effect/java",
-}
-
-filegroup {
- name: "framework-mca-filterfw-sources",
- srcs: [
- "media/mca/filterfw/java/**/*.java",
- ],
- path: "media/mca/filterfw/java",
-}
-
-filegroup {
- name: "framework-mca-filterpacks-sources",
- srcs: [
- "media/mca/filterpacks/java/**/*.java",
- ],
- path: "media/mca/filterpacks/java",
-}
-
-filegroup {
- name: "framework-mime-sources",
- srcs: [
- "mime/java/**/*.java",
- ],
- path: "mime/java",
-}
-
-filegroup {
- name: "framework-opengl-sources",
- srcs: [
- "opengl/java/**/*.java",
- ],
- path: "opengl/java",
-}
-
-filegroup {
- name: "framework-rs-sources",
- srcs: [
- "rs/java/**/*.java",
- ],
- path: "rs/java",
-}
-
-filegroup {
- name: "framework-sax-sources",
- srcs: [
- "sax/java/**/*.java",
- ],
- path: "sax/java",
-}
-
-filegroup {
- name: "framework-telecomm-sources",
- srcs: [
- "telecomm/java/**/*.java",
- "telecomm/java/**/*.aidl",
- ],
- path: "telecomm/java",
-}
-
-filegroup {
- name: "framework-telephony-sources",
- srcs: [
- "telephony/java/**/*.java",
- "telephony/java/**/*.aidl",
- ],
- path: "telephony/java",
-}
-
-genrule {
- name: "statslog-telephony-common-java-gen",
- tools: ["stats-log-api-gen"],
- cmd: "$(location stats-log-api-gen) --java $(out) --module telephony_common"
- + " --javaPackage com.android.internal.telephony --javaClass TelephonyCommonStatsLog",
- out: ["com/android/internal/telephony/TelephonyCommonStatsLog.java"],
-}
-
-filegroup {
- name: "framework-telephony-common-sources",
- srcs: [
- "telephony/common/**/*.java",
- ":statslog-telephony-common-java-gen",
- ],
-}
-
-filegroup {
- name: "framework-mms-sources",
- srcs: [
- "mms/java/**/*.java",
- "mms/java/**/*.aidl",
- ],
- path: "mms/java",
}
filegroup {
name: "framework-non-updatable-sources",
srcs: [
// Java/AIDL sources under frameworks/base
+ ":framework-annotations",
":framework-blobstore-sources",
":framework-core-sources",
":framework-drm-sources",
- ":framework-graphics-sources",
+ ":framework-graphics-nonupdatable-sources",
":framework-jobscheduler-sources", // jobscheduler is not a module for R
":framework-keystore-sources",
":framework-identity-sources",
@@ -240,6 +88,7 @@
":framework-telecomm-sources",
":framework-telephony-common-sources",
":framework-telephony-sources",
+ ":framework-vcn-util-sources",
":framework-wifi-annotations",
":framework-wifi-non-updatable-sources",
":PacProcessor-aidl-sources",
@@ -250,24 +99,36 @@
":platform-compat-native-aidl",
// AIDL sources from external directories
+ ":android.hardware.security.keymint-V1-java-source",
+ ":android.hardware.security.secureclock-V1-java-source",
+ ":android.security.apc-java-source",
+ ":android.security.authorization-java-source",
+ ":android.security.legacykeystore-java-source",
+ ":android.security.maintenance-java-source",
+ ":android.security.metrics-java-source",
+ ":android.system.keystore2-V1-java-source",
":credstore_aidl",
":dumpstate_aidl",
":framework_native_aidl",
":gatekeeper_aidl",
":gsiservice_aidl",
+ ":idmap2_aidl",
":guiconstants_aidl",
+ ":idmap2_core_aidl",
":incidentcompanion_aidl",
+ ":inputconstants_aidl",
":installd_aidl",
- ":keystore_aidl",
":libaudioclient_aidl",
":libbinder_aidl",
":libbluetooth-binder-aidl",
":libcamera_client_aidl",
":libcamera_client_framework_aidl",
+ ":packagemanager_aidl",
":libupdate_engine_aidl",
":resourcemanager_aidl",
":storaged_aidl",
":vold_aidl",
+ ":deviceproductinfoconstants_aidl",
// For the generated R.java and Manifest.java
":framework-res{.aapt.srcjar}",
@@ -275,43 +136,56 @@
// etc.
":framework-javastream-protos",
":statslog-framework-java-gen", // FrameworkStatsLog.java
+ ":audio_policy_configuration_V7_0",
],
}
-filegroup {
- name: "framework-updatable-sources",
- srcs: [
- ":framework-mediaprovider-sources",
- ":framework-permission-sources",
- ":framework-sdkextensions-sources",
- ":framework-statsd-sources",
- ":framework-tethering-srcs",
- ":framework-wifi-updatable-sources",
- ":updatable-media-srcs",
- ]
-}
-
java_library {
name: "framework-updatable-stubs-module_libs_api",
static_libs: [
+ "android.net.ipsec.ike.stubs.module_lib",
+ "framework-appsearch.stubs.module_lib",
+ "framework-connectivity.stubs.module_lib",
+ "framework-graphics.stubs.module_lib",
"framework-media.stubs.module_lib",
"framework-mediaprovider.stubs.module_lib",
"framework-permission.stubs.module_lib",
+ "framework-permission-s.stubs.module_lib",
+ "framework-scheduling.stubs.module_lib",
"framework-sdkextensions.stubs.module_lib",
"framework-statsd.stubs.module_lib",
"framework-tethering.stubs.module_lib",
"framework-wifi.stubs.module_lib",
],
sdk_version: "module_current",
- visibility: [":__pkg__"],
+ visibility: ["//visibility:private"],
}
-filegroup {
- name: "framework-all-sources",
- srcs: [
- ":framework-mime-sources",
- ":framework-non-updatable-sources",
- ":framework-updatable-sources",
+java_library {
+ name: "framework-all",
+ installable: false,
+ static_libs: [
+ "android.net.ipsec.ike.impl",
+ "framework-minus-apex",
+ "framework-appsearch.impl",
+ "framework-connectivity.impl",
+ "framework-graphics.impl",
+ "framework-mediaprovider.impl",
+ "framework-permission.impl",
+ "framework-permission-s.impl",
+ "framework-scheduling.impl",
+ "framework-sdkextensions.impl",
+ "framework-statsd.impl",
+ "framework-tethering.impl",
+ "framework-wifi.impl",
+ "updatable-media",
+ ],
+ apex_available: ["//apex_available:platform"],
+ sdk_version: "core_platform",
+ visibility: [
+ // DO NOT ADD ANY MORE ENTRIES TO THIS LIST
+ "//external/robolectric-shadows:__subpackages__",
+ "//frameworks/layoutlib:__subpackages__",
],
}
@@ -351,15 +225,16 @@
name: "framework-internal-utils",
static_libs: [
"apex_aidl_interface-java",
- "suspend_control_aidl_interface-java",
"framework-protos",
- "game-driver-protos",
+ "updatable-driver-protos",
+ "ota_metadata_proto_java",
"android.hidl.base-V1.0-java",
"android.hardware.cas-V1.0-java",
"android.hardware.cas-V1.1-java",
"android.hardware.cas-V1.2-java",
"android.hardware.contexthub-V1.0-java",
"android.hardware.contexthub-V1.1-java",
+ "android.hardware.contexthub-V1.2-java",
"android.hardware.gnss-V1.0-java",
"android.hardware.gnss-V2.1-java",
"android.hardware.health-V1.0-java-constants",
@@ -369,140 +244,135 @@
"android.hardware.radio-V1.3-java",
"android.hardware.radio-V1.4-java",
"android.hardware.radio-V1.5-java",
+ "android.hardware.radio-V1.6-java",
"android.hardware.thermal-V1.0-java-constants",
"android.hardware.thermal-V1.0-java",
"android.hardware.thermal-V1.1-java",
"android.hardware.thermal-V2.0-java",
"android.hardware.tv.input-V1.0-java-constants",
"android.hardware.tv.tuner-V1.0-java-constants",
+ "android.hardware.tv.tuner-V1.1-java-constants",
"android.hardware.usb-V1.0-java-constants",
"android.hardware.usb-V1.1-java-constants",
"android.hardware.usb-V1.2-java-constants",
"android.hardware.usb.gadget-V1.0-java",
+ "android.hardware.usb.gadget-V1.1-java",
+ "android.hardware.usb.gadget-V1.2-java",
"android.hardware.vibrator-V1.0-java",
"android.hardware.vibrator-V1.1-java",
"android.hardware.vibrator-V1.2-java",
"android.hardware.vibrator-V1.3-java",
+ "android.hardware.vibrator-V2-java",
+ "android.system.suspend.control.internal-java",
"devicepolicyprotosnano",
"com.android.sysprop.apex",
"com.android.sysprop.init",
+ "com.android.sysprop.localization",
"PlatformProperties",
],
sdk_version: "core_platform",
installable: false,
}
-java_defaults {
- name: "framework-defaults",
- defaults: ["framework-aidl-export-defaults"],
- installable: true,
+// NOTE: This filegroup is exposed for vendor libraries to depend on and is referenced in
+// documentation. Do not remove without consulting the treble/hidl teams.
+filegroup {
+ name: "framework-jarjar-rules",
+ srcs: ["framework-jarjar-rules.txt"],
+ visibility: ["//visibility:public"],
+}
+java_defaults {
+ name: "framework-minus-apex-defaults",
+ defaults: ["framework-aidl-export-defaults"],
+ srcs: [
+ ":framework-non-updatable-sources",
+ "core/java/**/*.logtags",
+ ],
aidl: {
generate_get_transaction_name: true,
+ local_include_dirs: [
+ "media/aidl",
+ ],
+ include_dirs: [
+ "frameworks/av/aidl",
+ "frameworks/native/libs/permission/aidl",
+ "packages/modules/Connectivity/framework/aidl-export",
+ ],
},
-
- srcs: ["core/java/**/*.logtags"],
-
- exclude_srcs: [
- // See comment on framework-atb-backward-compatibility module below
- "core/java/android/content/pm/AndroidTestBaseUpdater.java",
- ],
-
- sdk_version: "core_platform",
- libs: [
- "app-compat-annotations",
- "ext",
- "unsupportedappusage",
- ],
-
- jarjar_rules: ":framework-jarjar-rules",
-
- static_libs: [
- "framework-internal-utils",
- ],
-
dxflags: [
"--core-library",
"--multi-dex",
],
-
+ jarjar_rules: ":framework-jarjar-rules",
+ javac_shard_size: 150,
plugins: [
"view-inspector-annotation-processor",
"staledataclass-annotation-processor",
+ "error_prone_android_framework",
],
-
required: [
+ "framework-platform-compat-config",
// TODO: remove gps_debug and protolog.conf.json when the build system propagates "required" properly.
"gps_debug.conf",
+ "icu4j-platform-compat-config",
"protolog.conf.json.gz",
+ "services-platform-compat-config",
+ "documents-ui-compat-config",
+ "calendar-provider-compat-config",
],
-}
-
-filegroup {
- name: "framework-jarjar-rules",
- srcs: ["framework-jarjar-rules.txt"],
-}
-
-filegroup {
- name: "libincident_aidl",
- srcs: [
- "core/java/android/os/IIncidentDumpCallback.aidl",
- "core/java/android/os/IIncidentManager.aidl",
- "core/java/android/os/IIncidentReportStatusListener.aidl",
+ libs: [
+ "app-compat-annotations",
+ "ext",
+ "framework-updatable-stubs-module_libs_api",
+ "unsupportedappusage",
],
- path: "core/java",
-}
-
-filegroup {
- name: "libvibrator_aidl",
- srcs: [
- "core/java/android/os/IExternalVibrationController.aidl",
- "core/java/android/os/IExternalVibratorService.aidl",
+ sdk_version: "core_platform",
+ static_libs: [
+ "bouncycastle-repackaged-unbundled",
+ "framework-internal-utils",
+ // If MimeMap ever becomes its own APEX, then this dependency would need to be removed
+ // in favor of an API stubs dependency in java_library "framework" below.
+ "mimemap",
+ "av-types-aidl-java",
+ "tv_tuner_resource_manager_aidl_interface-java",
+ "soundtrigger_middleware-aidl-java",
+ "modules-utils-preconditions",
+ "modules-utils-os",
+ "framework-permission-aidl-java",
],
- path: "core/java",
-}
-
-filegroup {
- name: "libpowermanager_aidl",
- srcs: [
- "core/java/android/os/Temperature.aidl",
- "core/java/android/os/CoolingDevice.aidl",
- "core/java/android/os/IThermalEventListener.aidl",
- "core/java/android/os/IThermalStatusListener.aidl",
- "core/java/android/os/IThermalService.aidl",
- ],
- path: "core/java",
}
java_library {
name: "framework-minus-apex",
- defaults: ["framework-defaults"],
- srcs: [":framework-non-updatable-sources"],
+ defaults: ["framework-minus-apex-defaults"],
installable: true,
- javac_shard_size: 150,
- required: [
- "framework-platform-compat-config",
- "libcore-platform-compat-config",
- "services-platform-compat-config",
- "documents-ui-compat-config",
- ],
- libs: ["framework-updatable-stubs-module_libs_api"],
- static_libs: [
- // If MimeMap ever becomes its own APEX, then this dependency would need to be removed
- // in favor of an API stubs dependency in java_library "framework" below.
- "mimemap",
- ],
// For backwards compatibility.
stem: "framework",
apex_available: ["//apex_available:platform"],
visibility: [
"//frameworks/base",
// TODO(b/147128803) remove the below lines
+ "//frameworks/base/apex/appsearch/framework",
"//frameworks/base/apex/blobstore/framework",
"//frameworks/base/apex/jobscheduler/framework",
"//frameworks/base/packages/Tethering/tests/unit",
+ "//packages/modules/Connectivity/Tethering/tests/unit",
],
+ errorprone: {
+ javacflags: [
+ "-Xep:AndroidFrameworkBinderIdentity:ERROR",
+ "-Xep:AndroidFrameworkCompatChange:ERROR",
+ "-Xep:AndroidFrameworkUid:ERROR",
+ ],
+ },
+}
+
+java_library {
+ name: "framework-minus-apex-intdefs",
+ defaults: ["framework-minus-apex-defaults"],
+ plugins: ["intdef-annotation-processor"],
}
// This "framework" module is NOT installed to the device. It's
@@ -521,167 +391,28 @@
static_libs: [
"app-compat-annotations",
"framework-minus-apex",
+ "framework-appsearch.impl", // TODO(b/146218515): should be removed
"framework-updatable-stubs-module_libs_api",
],
sdk_version: "core_platform",
apex_available: ["//apex_available:platform"],
}
-java_library {
- name: "framework-all",
- defaults: ["framework-defaults"],
- srcs: [":framework-all-sources"],
- installable: false,
- static_libs: [
- "exoplayer2-extractor",
- "android.hardware.wifi-V1.0-java-constants",
- // Additional dependencies needed to build the ike API classes.
- "ike-internals",
- ],
- apex_available: ["//apex_available:platform"],
- visibility: [
- // DO NOT ADD ANY MORE ENTRIES TO THIS LIST
- "//external/robolectric-shadows:__subpackages__",
- "//frameworks/base",
- "//frameworks/layoutlib:__subpackages__",
- ],
-}
-
platform_compat_config {
name: "framework-platform-compat-config",
src: ":framework-minus-apex",
}
-// A temporary build target that is conditionally included on the bootclasspath if
-// android.test.base library has been removed and which provides support for
-// maintaining backwards compatibility for APKs that target pre-P and depend on
-// android.test.base classes. This is used iff REMOVE_ATB_FROM_BCP=true is
-// specified on the build command line.
-java_library {
- name: "framework-atb-backward-compatibility",
- installable: true,
- libs: ["app-compat-annotations"],
- srcs: [
- "core/java/android/content/pm/AndroidTestBaseUpdater.java",
- ],
-}
-
-genrule {
- name: "statslog-framework-java-gen",
- tools: ["stats-log-api-gen"],
- cmd: "$(location stats-log-api-gen) --java $(out) --module framework" +
- " --javaPackage com.android.internal.util --javaClass FrameworkStatsLog --worksource",
- out: ["com/android/internal/util/FrameworkStatsLog.java"],
-}
-
-java_library {
- name: "uieventloggerlib",
- srcs: [
- "core/java/com/android/internal/logging/UiEvent.java",
- "core/java/com/android/internal/logging/UiEventLogger.java",
- "core/java/com/android/internal/logging/UiEventLoggerImpl.java",
- "core/java/com/android/internal/logging/InstanceId.java",
- "core/java/com/android/internal/logging/InstanceIdSequence.java",
- ":statslog-framework-java-gen",
- ],
-}
-
-gensrcs {
- name: "framework-javastream-protos",
- depfile: true,
-
- tools: [
- "aprotoc",
- "protoc-gen-javastream",
- "soong_zip",
- ],
-
- cmd: "mkdir -p $(genDir)/$(in) " +
- "&& $(location aprotoc) " +
- " --plugin=$(location protoc-gen-javastream) " +
- " --dependency_out=$(depfile) " +
- " --javastream_out=$(genDir)/$(in) " +
- " -Iexternal/protobuf/src " +
- " -I . " +
- " $(in) " +
- "&& $(location soong_zip) -jar -o $(out) -C $(genDir)/$(in) -D $(genDir)/$(in)",
-
- srcs: [
- ":ipconnectivity-proto-src",
- "core/proto/**/*.proto",
- "libs/incident/**/*.proto",
- ],
- output_extension: "srcjar",
-}
-
-gensrcs {
- name: "framework-cppstream-protos",
- depfile: true,
-
- tools: [
- "aprotoc",
- "protoc-gen-cppstream",
- ],
-
- cmd: "mkdir -p $(genDir) " +
- "&& $(location aprotoc) " +
- " --plugin=$(location protoc-gen-cppstream) " +
- " --dependency_out=$(depfile) " +
- " --cppstream_out=$(genDir) " +
- " -Iexternal/protobuf/src " +
- " -I . " +
- " $(in)",
-
- srcs: [
- ":ipconnectivity-proto-src",
- "core/proto/**/*.proto",
- "libs/incident/**/*.proto",
- ],
-
- output_extension: "proto.h",
-}
-
-filegroup {
- name: "framework-annotations",
- srcs: [
- "core/java/android/annotation/CallbackExecutor.java",
- "core/java/android/annotation/CheckResult.java",
- "core/java/android/annotation/CurrentTimeMillisLong.java",
- "core/java/android/annotation/Hide.java",
- "core/java/android/annotation/IntDef.java",
- "core/java/android/annotation/IntRange.java",
- "core/java/android/annotation/LongDef.java",
- "core/java/android/annotation/NonNull.java",
- "core/java/android/annotation/Nullable.java",
- "core/java/android/annotation/RequiresPermission.java",
- "core/java/android/annotation/SdkConstant.java",
- "core/java/android/annotation/StringDef.java",
- "core/java/android/annotation/SystemApi.java",
- "core/java/android/annotation/SystemService.java",
- "core/java/android/annotation/TestApi.java",
- "core/java/android/annotation/WorkerThread.java",
- "core/java/com/android/internal/annotations/GuardedBy.java",
- "core/java/com/android/internal/annotations/VisibleForTesting.java",
- "core/java/com/android/internal/annotations/Immutable.java",
- ],
-}
-
-java_library {
- name: "framework-annotations-lib",
- srcs: [ ":framework-annotations" ],
- sdk_version: "core_current",
-}
-
filegroup {
name: "framework-ike-shared-srcs",
visibility: ["//packages/modules/IPsec"],
srcs: [
- "core/java/android/annotation/StringDef.java",
"core/java/android/net/annotations/PolicyDirection.java",
"core/java/com/android/internal/util/HexDump.java",
"core/java/com/android/internal/util/IState.java",
"core/java/com/android/internal/util/State.java",
"core/java/com/android/internal/util/StateMachine.java",
+ "services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java",
"telephony/java/android/telephony/Annotation.java",
],
}
@@ -691,45 +422,22 @@
srcs: [
// TODO: remove these annotations as soon as we can use andoid.support.annotations.*
":framework-annotations",
+ ":modules-utils-preconditions-srcs",
"core/java/android/net/DhcpResults.java",
+ "core/java/android/util/IndentingPrintWriter.java",
"core/java/android/util/LocalLog.java",
"core/java/com/android/internal/util/HexDump.java",
"core/java/com/android/internal/util/IndentingPrintWriter.java",
"core/java/com/android/internal/util/IState.java",
"core/java/com/android/internal/util/MessageUtils.java",
- "core/java/com/android/internal/util/Preconditions.java",
"core/java/com/android/internal/util/RingBufferIndices.java",
"core/java/com/android/internal/util/State.java",
"core/java/com/android/internal/util/StateMachine.java",
- "core/java/com/android/internal/util/TrafficStatsConstants.java",
"core/java/com/android/internal/util/WakeupMessage.java",
"core/java/com/android/internal/util/TokenBucket.java",
],
}
-filegroup {
- name: "framework-services-net-module-wifi-shared-srcs",
- srcs: [
- "core/java/android/net/DhcpResults.java",
- "core/java/android/net/util/IpUtils.java",
- "core/java/android/util/LocalLog.java",
- ],
-}
-
-// keep these files in sync with the package/Tethering/jarjar-rules.txt for the tethering module.
-filegroup {
- name: "framework-tethering-shared-srcs",
- srcs: [
- "core/java/android/util/LocalLog.java",
- "core/java/com/android/internal/util/IndentingPrintWriter.java",
- "core/java/com/android/internal/util/IState.java",
- "core/java/com/android/internal/util/MessageUtils.java",
- "core/java/com/android/internal/util/State.java",
- "core/java/com/android/internal/util/StateMachine.java",
- "core/java/com/android/internal/util/TrafficStatsConstants.java",
- ],
-}
-
// Build ext.jar
// ============================================================
java_library {
@@ -744,525 +452,138 @@
dxflags: ["--core-library"],
}
-// ==== java proto host library ==============================
-java_library_host {
- name: "platformprotos",
- srcs: [
- ":ipconnectivity-proto-src",
- "cmds/am/proto/instrumentation_data.proto",
- "cmds/statsd/src/**/*.proto",
- "core/proto/**/*.proto",
- "libs/incident/proto/**/*.proto",
- ],
- proto: {
- include_dirs: ["external/protobuf/src"],
- type: "full",
- },
- errorprone: {
- javacflags: ["-Xep:MissingOverride:OFF"], // b/72714520
- },
-}
-
-// ==== java proto device library (for test only) ==============================
-java_library {
- name: "platformprotosnano",
- proto: {
- type: "nano",
- output_params: ["store_unknown_fields=true"],
- include_dirs: ["external/protobuf/src"],
- },
- exclude_srcs: [
- "core/proto/android/privacy.proto",
- "core/proto/android/section.proto",
- ],
- sdk_version: "9",
- srcs: [
- ":ipconnectivity-proto-src",
- "core/proto/**/*.proto",
- "libs/incident/proto/android/os/**/*.proto",
- ],
-}
-
-// ==== java proto device library (for test only) ==============================
-java_library {
- name: "platformprotoslite",
- proto: {
- type: "lite",
- include_dirs: ["external/protobuf/src"],
- },
-
- srcs: [
- ":ipconnectivity-proto-src",
- "core/proto/**/*.proto",
- "libs/incident/proto/android/os/**/*.proto",
- ],
- exclude_srcs: [
- "core/proto/android/privacy.proto",
- "core/proto/android/section.proto",
- ],
- sdk_version: "core_current",
- // Protos have lots of MissingOverride and similar.
- errorprone: {
- javacflags: ["-XepDisableAllChecks"],
- },
-}
-
-// ==== c++ proto device library ==============================
-cc_defaults {
- name: "libplatformprotos-defaults",
-
- proto: {
- export_proto_headers: true,
- include_dirs: ["external/protobuf/src"],
- },
-
- cflags: [
- "-Wall",
- "-Werror",
- "-Wno-unused-parameter",
- ],
-
- srcs: [
- ":ipconnectivity-proto-src",
- "core/proto/**/*.proto",
- ],
-}
-
-cc_library {
- name: "libplatformprotos",
- defaults: ["libplatformprotos-defaults"],
- host_supported: true,
-
- target: {
- host: {
- proto: {
- type: "full",
- },
- },
- android: {
- proto: {
- type: "lite",
- },
- shared_libs: [
- "libprotobuf-cpp-lite",
- ],
- shared: {
- enabled: false,
- },
- },
- },
-}
-
-// This library is meant for vendor code that needs to output protobuf. It links
-// against the static version of libprotobuf-cpp-lite, for which we can not guarantee
-// binary compatibility.
-cc_library {
- name: "libplatformprotos-static",
- defaults: ["libplatformprotos-defaults"],
- host_supported: false,
-
- // This is okay because this library is only built as a static library. The C++
- // API is not guaranteed. The proto API is guaranteed to be stable via Metrics Council,
- // but is not authorized to be used outside of debugging.
- vendor_available: true,
-
- target: {
- android: {
- proto: {
- type: "lite",
- },
- static_libs: [
- "libprotobuf-cpp-lite",
- ],
- shared: {
- enabled: false,
- },
- },
- },
-}
-
-
-// This is the full proto version of libplatformprotos. It may only
-// be used by test code that is not shipped on the device.
-cc_library {
- name: "libplatformprotos-test",
- defaults: ["libplatformprotos-defaults"],
- host_supported: false,
-
- target: {
- android: {
- proto: {
- type: "full",
- },
- shared: {
- enabled: false,
- },
- },
- },
-}
-
-filegroup {
- name: "incremental_aidl",
- srcs: [
- "core/java/android/os/incremental/IIncrementalServiceConnector.aidl",
- "core/java/android/os/incremental/IncrementalFileSystemControlParcel.aidl",
- ],
- path: "core/java",
-}
-
-filegroup {
- name: "dataloader_aidl",
- srcs: [
- "core/java/android/content/pm/DataLoaderParamsParcel.aidl",
- "core/java/android/content/pm/DataLoaderType.aidl",
- "core/java/android/content/pm/FileSystemControlParcel.aidl",
- "core/java/android/content/pm/IDataLoader.aidl",
- "core/java/android/content/pm/IDataLoaderManager.aidl",
- "core/java/android/content/pm/InstallationFileParcel.aidl",
- "core/java/android/content/pm/InstallationFileLocation.aidl",
- "core/java/android/content/pm/IDataLoaderStatusListener.aidl",
- "core/java/android/content/pm/IPackageInstallerSessionFileSystemConnector.aidl",
- ],
- path: "core/java",
-}
-
-filegroup {
- name: "incremental_manager_aidl",
- srcs: [
- "core/java/android/os/incremental/IIncrementalService.aidl",
- "core/java/android/os/incremental/IncrementalNewFileParams.aidl",
- "core/java/android/os/incremental/IStorageHealthListener.aidl",
- "core/java/android/os/incremental/StorageHealthCheckParams.aidl",
- ],
- path: "core/java",
-}
-
-aidl_interface {
- name: "libincremental_aidl",
- unstable: true,
- srcs: [
- ":incremental_aidl",
- ],
- backend: {
- java: {
- sdk_version: "28",
- },
- cpp: {
- enabled: true,
- },
- ndk: {
- enabled: true,
- },
- },
-}
-
-aidl_interface {
- name: "libdataloader_aidl",
- unstable: true,
- srcs: [
- ":dataloader_aidl",
- ],
- imports: [
- "libincremental_aidl",
- ],
- backend: {
- java: {
- sdk_version: "28",
- },
- cpp: {
- enabled: true,
- },
- ndk: {
- enabled: false,
- },
- },
-}
-
-aidl_interface {
- name: "libincremental_manager_aidl",
- unstable: true,
- srcs: [
- ":incremental_manager_aidl",
- ],
- imports: [
- "libincremental_aidl",
- "libdataloader_aidl",
- ],
- backend: {
- java: {
- sdk_version: "28",
- },
- cpp: {
- enabled: true,
- },
- ndk: {
- enabled: false,
- },
- },
-}
-
-// TODO(b/77285514): remove this once the last few hidl interfaces have been
-// updated to use hwbinder.stubs.
-java_library {
- name: "hwbinder",
- sdk_version: "core_platform",
-
- srcs: [
- "core/java/android/os/HidlSupport.java",
- "core/java/android/annotation/IntDef.java",
- "core/java/android/annotation/IntRange.java",
- "core/java/android/annotation/NonNull.java",
- "core/java/android/annotation/Nullable.java",
- "core/java/android/annotation/SystemApi.java",
- "core/java/android/annotation/TestApi.java",
- "core/java/android/os/HidlMemory.java",
- "core/java/android/os/HwBinder.java",
- "core/java/android/os/HwBlob.java",
- "core/java/android/os/HwParcel.java",
- "core/java/android/os/IHwBinder.java",
- "core/java/android/os/IHwInterface.java",
- "core/java/android/os/DeadObjectException.java",
- "core/java/android/os/DeadSystemException.java",
- "core/java/android/os/NativeHandle.java",
- "core/java/android/os/RemoteException.java",
- "core/java/android/util/AndroidException.java",
- ],
- libs: [ "unsupportedappusage" ],
-
- dxflags: ["--core-library"],
- installable: false,
-}
-
-python_defaults {
- name: "base_default",
- version: {
- py2: {
- enabled: true,
- embedded_launcher: true,
- },
- py3: {
- enabled: false,
- embedded_launcher: false,
- },
- },
-}
-
-python_binary_host {
- name: "fontchain_linter",
- defaults: ["base_default"],
- main: "tools/fonts/fontchain_linter.py",
- srcs: [
- "tools/fonts/fontchain_linter.py",
- ],
- libs: [
- "fontTools",
- ],
-}
-
-filegroup {
- name: "framework-media-annotation-srcs",
- srcs: [
- ":framework-annotations",
- "core/java/android/annotation/CallbackExecutor.java",
- "core/java/android/annotation/CallSuper.java",
- "core/java/android/annotation/DrawableRes.java",
- "core/java/android/annotation/LongDef.java",
- "core/java/android/annotation/StringDef.java",
- ],
-}
-
-filegroup {
- name: "framework-mediaprovider-annotation-sources",
- srcs: [
- ":framework-annotations",
- "core/java/android/annotation/BytesLong.java",
- "core/java/android/annotation/CurrentTimeSecondsLong.java",
- "core/java/android/annotation/DurationMillisLong.java",
- ],
-}
-
-// Creates an index of AIDL methods; used for adding UnsupportedAppUsage
-// annotations to private apis
-aidl_mapping {
- name: "framework-aidl-mappings",
- srcs: [":framework-all-sources"],
- output: "framework-aidl-mappings.txt",
-}
-
-// Avoid including Parcelable classes as we don't want to have two copies of
-// Parcelable cross the libraries. This is used by telephony-common (frameworks/opt/telephony)
-// and TeleService app (packages/services/Telephony).
-filegroup {
- name: "framework-telephony-common-shared-srcs",
- srcs: [
- "core/java/android/os/BasicShellCommandHandler.java",
- "core/java/android/os/RegistrantList.java",
- "core/java/android/os/Registrant.java",
- "core/java/android/util/LocalLog.java",
- "core/java/android/util/TimeUtils.java",
- "core/java/com/android/internal/os/SomeArgs.java",
- "core/java/com/android/internal/util/AsyncChannel.java",
- "core/java/com/android/internal/util/AsyncService.java",
- "core/java/com/android/internal/util/BitwiseInputStream.java",
- "core/java/com/android/internal/util/FastXmlSerializer.java",
- "core/java/com/android/internal/util/HexDump.java",
- "core/java/com/android/internal/util/IState.java",
- "core/java/com/android/internal/util/IndentingPrintWriter.java",
- "core/java/com/android/internal/util/Preconditions.java",
- "core/java/com/android/internal/util/State.java",
- "core/java/com/android/internal/util/StateMachine.java",
- "core/java/com/android/internal/util/UserIcons.java",
- ],
-}
-
-// Avoid including Parcelable classes as we don't want to have two copies of
-// Parcelable cross the process. This is used by framework-telephony (frameworks/base/telephony).
-filegroup {
- name: "framework-telephony-shared-srcs",
- srcs: [
- "core/java/android/util/RecurrenceRule.java",
- "core/java/com/android/internal/os/SomeArgs.java",
- "core/java/com/android/internal/util/BitwiseInputStream.java",
- "core/java/com/android/internal/util/BitwiseOutputStream.java",
- "core/java/com/android/internal/util/FunctionalUtils.java",
- "core/java/com/android/internal/util/HexDump.java",
- "core/java/com/android/internal/util/IndentingPrintWriter.java",
- "core/java/com/android/internal/util/Preconditions.java",
- ],
-}
-
-// Avoid including Parcelable classes as we don't want to have two copies of
-// Parcelable cross the process.
-filegroup {
- name: "framework-cellbroadcast-shared-srcs",
- srcs: [
- "core/java/android/os/HandlerExecutor.java",
- "core/java/android/util/LocalLog.java",
- "core/java/com/android/internal/util/IState.java",
- "core/java/com/android/internal/util/Preconditions.java",
- "core/java/com/android/internal/util/State.java",
- "core/java/com/android/internal/util/StateMachine.java",
- ],
-}
-
-filegroup {
- name: "framework-ims-common-shared-srcs",
- srcs: [
- "core/java/android/os/RegistrantList.java",
- "core/java/android/os/Registrant.java",
- "core/java/com/android/internal/os/SomeArgs.java",
- "core/java/com/android/internal/util/Preconditions.java",
- ],
-}
-
// utility classes statically linked into framework-wifi and dynamically linked
// into wifi-service
java_library {
name: "framework-wifi-util-lib",
sdk_version: "module_current",
+ min_sdk_version: "30",
srcs: [
- "core/java/android/content/pm/BaseParceledListSlice.java",
- "core/java/android/content/pm/ParceledListSlice.java",
+ ":modules-utils-preconditions-srcs",
"core/java/android/os/HandlerExecutor.java",
"core/java/com/android/internal/util/AsyncChannel.java",
"core/java/com/android/internal/util/AsyncService.java",
"core/java/com/android/internal/util/Protocol.java",
- "core/java/com/android/internal/util/Preconditions.java",
"telephony/java/android/telephony/Annotation.java",
":net-utils-framework-wifi-common-srcs",
],
libs: [
"framework-annotations-lib",
+ "framework-connectivity.stubs.module_lib",
"unsupportedappusage",
],
visibility: [
"//frameworks/base/wifi",
"//frameworks/base/services/net",
+ "//packages/modules/Wifi/framework",
],
}
-filegroup {
- name: "framework-wifi-util-lib-aidls",
- srcs: ["core/java/android/content/pm/ParceledListSlice.aidl"],
- path: "core/java",
-}
-
-// utility classes statically linked into wifi-service
-filegroup {
- name: "framework-wifi-service-shared-srcs",
- srcs: [
- "core/java/android/net/InterfaceConfiguration.java",
- "core/java/android/os/BasicShellCommandHandler.java",
- "core/java/android/util/BackupUtils.java",
- "core/java/android/util/Rational.java",
- "core/java/com/android/internal/util/FastXmlSerializer.java",
- "core/java/com/android/internal/util/HexDump.java",
- "core/java/com/android/internal/util/IState.java",
- "core/java/com/android/internal/util/MessageUtils.java",
- "core/java/com/android/internal/util/State.java",
- "core/java/com/android/internal/util/StateMachine.java",
- "core/java/com/android/internal/util/WakeupMessage.java",
- ],
- visibility: ["//frameworks/opt/net/wifi/service"],
-}
-
// TODO(b/145644363): move this to under StubLibraries.bp or ApiDocs.bp
metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.xml) " +
- "--ignore-classes-on-classpath " +
"--hide-package com.android.server " +
+ "--hide-package android.audio.policy.configuration.V7_0 " +
"--error UnhiddenSystemApi " +
"--hide RequiresPermission " +
"--hide CallbackInterface " +
"--hide MissingPermission --hide BroadcastBehavior " +
"--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " +
"--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo " +
+ "--error NoSettingsProvider " +
"--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.* " +
"--api-lint-ignore-prefix android.icu. " +
"--api-lint-ignore-prefix java. " +
"--api-lint-ignore-prefix junit. " +
"--api-lint-ignore-prefix org. "
+packages_to_document = [
+ "android",
+ "dalvik",
+ "java",
+ "javax",
+ "junit",
+ "org.apache.http",
+ "org.json",
+ "org.w3c.dom",
+ "org.xml.sax",
+ "org.xmlpull",
+]
+
+filegroup {
+ name: "android-non-updatable-stub-sources",
+ srcs: [
+ ":framework-mime-sources", // mimemap builds separately but has no separate droidstubs.
+ ":framework-non-updatable-sources",
+ ":opt-telephony-srcs",
+ ":opt-net-voip-srcs",
+ "core/java/**/*.logtags",
+ "**/package.html",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+// These defaults are used for both the jar stubs and the doc stubs.
+stubs_defaults {
+ name: "android-non-updatable-stubs-defaults",
+ srcs: [":android-non-updatable-stub-sources"],
+ sdk_version: "none",
+ system_modules: "none",
+ java_version: "1.8",
+ arg_files: ["core/res/AndroidManifest.xml"],
+ // TODO(b/147699819): remove below aidl includes.
+ aidl: {
+ local_include_dirs: [
+ "apex/media/aidl/stable",
+ "media/aidl",
+ "telephony/java",
+ ],
+ include_dirs: [
+ "frameworks/av/aidl",
+ "frameworks/native/libs/permission/aidl",
+ "packages/modules/Connectivity/framework/aidl-export",
+ ],
+ },
+ // These are libs from framework-internal-utils that are required (i.e. being referenced)
+ // from framework-non-updatable-sources. Add more here when there's a need.
+ // DO NOT add the entire framework-internal-utils. It might cause unnecessary circular
+ // dependencies gets bigger.
+ libs: [
+ "android.hardware.cas-V1.2-java",
+ "android.hardware.health-V1.0-java-constants",
+ "android.hardware.radio-V1.5-java",
+ "android.hardware.radio-V1.6-java",
+ "android.hardware.thermal-V1.0-java-constants",
+ "android.hardware.thermal-V2.0-java",
+ "android.hardware.tv.input-V1.0-java-constants",
+ "android.hardware.tv.tuner-V1.0-java-constants",
+ "android.hardware.tv.tuner-V1.1-java-constants",
+ "android.hardware.usb-V1.0-java-constants",
+ "android.hardware.usb-V1.1-java-constants",
+ "android.hardware.usb.gadget-V1.0-java",
+ "android.hardware.vibrator-V1.3-java",
+ "framework-protos",
+ "art.module.public.api",
+ // There are a few classes from modules used by the core that
+ // need to be resolved by metalava. We use a prebuilt stub of the
+ // full sdk to ensure we can resolve them. If a new class gets added,
+ // the prebuilts/sdk/current needs to be updated.
+ "sdk_system_current_android",
+ // NOTE: The below can be removed once the prebuilt stub contains IKE.
+ "sdk_system_current_android.net.ipsec.ike",
+ ],
+ filter_packages: packages_to_document,
+ high_mem: true, // Lots of sources => high memory use, see b/170701554
+ installable: false,
+ annotations_enabled: true,
+ previous_api: ":android.api.public.latest",
+ merge_annotations_dirs: ["metalava-manual"],
+ defaults_visibility: ["//visibility:private"],
+ visibility: ["//frameworks/base/api"],
+}
+
build = [
"StubLibraries.bp",
"ApiDocs.bp",
+ "ProtoLibraries.bp",
+ "TestProtoLibraries.bp",
]
-
-java_library {
- name: "framework-telephony",
- srcs: [
- //":framework-telephony-sources",
- //":framework-telephony-shared-srcs",
- ],
- // TODO: change to framework-system-stub to build against system APIs.
- libs: [
- "framework-minus-apex",
- "unsupportedappusage",
- ],
- static_libs: [
- "libphonenumber-platform",
- "app-compat-annotations",
- ],
- sdk_version: "core_platform",
- aidl: {
- export_include_dirs: ["telephony/java"],
- include_dirs: [
- "frameworks/native/aidl/binder",
- "frameworks/native/aidl/gui",
- ]
- },
- jarjar_rules: ":framework-telephony-jarjar-rules",
- dxflags: [
- "--core-library",
- "--multi-dex",
- ],
- // This is to break the dependency from boot jars.
- dex_preopt: {
- enabled: false,
- },
- installable: true,
-}
-
-filegroup {
- name: "framework-telephony-jarjar-rules",
- srcs: ["telephony/framework-telephony-jarjar-rules.txt"],
-}
diff --git a/Android.mk b/Android.mk
index d3627e1..46529eb 100644
--- a/Android.mk
+++ b/Android.mk
@@ -32,10 +32,6 @@
# ============================================================
include $(CLEAR_VARS)
-# This is used by ide.mk as the list of source files that are
-# always included.
-INTERNAL_SDK_SOURCE_DIRS := $(addprefix $(LOCAL_PATH)/,$(dirs_to_document))
-
# sdk.atree needs to copy the whole dir: $(OUT_DOCS)/offline-sdk to the final zip.
# So keep offline-sdk-timestamp target here, and unzip offline-sdk-docs.zip to
# $(OUT_DOCS)/offline-sdk.
@@ -60,7 +56,7 @@
$(SDK_METADATA): $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/framework-doc-stubs-metadata.zip
rm -rf $(SDK_METADATA_DIR)
mkdir -p $(SDK_METADATA_DIR)
- unzip -qo $< -d $(SDK_METADATA_DIR)
+ unzip -DDqo $< -d $(SDK_METADATA_DIR)
.PHONY: framework-doc-stubs
framework-doc-stubs: $(SDK_METADATA)
diff --git a/ApiDocs.bp b/ApiDocs.bp
index a81342a..aae4a71 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -56,24 +56,51 @@
]
stubs_defaults {
- name: "framework-doc-stubs-default",
+ name: "android-non-updatable-doc-stubs-defaults",
+ defaults: ["android-non-updatable-stubs-defaults"],
srcs: [
- ":framework-mime-sources",
- ":framework-non-updatable-sources",
- ":framework-updatable-sources",
- "core/java/**/*.logtags",
+ // No longer part of the stubs, but are included in the docs.
"test-base/src/**/*.java",
- ":opt-telephony-srcs",
- ":opt-net-voip-srcs",
- ":art-module-public-api-stubs-source",
- ":conscrypt.module.public.api{.public.stubs.source}",
- ":android_icu4j_public_api_files",
"test-mock/src/**/*.java",
"test-runner/src/**/*.java",
],
libs: framework_docs_only_libs,
create_doc_stubs: true,
+ write_sdk_values: true,
+}
+
+stubs_defaults {
+ name: "framework-doc-stubs-default",
+ srcs: [
+ ":android-non-updatable-stub-sources",
+
+ // Module sources
+ ":art.module.public.api{.public.stubs.source}",
+ ":conscrypt.module.public.api{.public.stubs.source}",
+ ":framework-appsearch-sources",
+ ":framework-connectivity-sources",
+ ":framework-graphics-srcs",
+ ":framework-mediaprovider-sources",
+ ":framework-permission-sources",
+ ":framework-permission-s-sources",
+ ":framework-scheduling-sources",
+ ":framework-sdkextensions-sources",
+ ":framework-statsd-sources",
+ ":framework-tethering-srcs",
+ ":framework-wifi-updatable-sources",
+ ":i18n.module.public.api{.public.stubs.source}",
+ ":ike-srcs",
+ ":updatable-media-srcs",
+
+ // No longer part of the stubs, but are included in the docs.
+ ":android-test-base-sources",
+ ":android-test-mock-sources",
+ ":android-test-runner-sources",
+ ],
+ libs: framework_docs_only_libs,
+ create_doc_stubs: true,
annotations_enabled: true,
+ filter_packages: packages_to_document,
api_levels_annotations_enabled: true,
api_levels_annotations_dirs: [
"sdk-dir",
@@ -83,6 +110,27 @@
merge_annotations_dirs: [
"metalava-manual",
],
+ // TODO(b/169090544): remove below aidl includes.
+ aidl: {
+ local_include_dirs: ["media/aidl"],
+ include_dirs: [
+ "frameworks/av/aidl",
+ "frameworks/native/libs/permission/aidl",
+ ],
+ },
+}
+
+droidstubs {
+ name: "android-non-updatable-doc-stubs",
+ defaults: ["android-non-updatable-doc-stubs-defaults"],
+ args: metalava_framework_docs_args,
+}
+
+droidstubs {
+ name: "android-non-updatable-doc-stubs-system",
+ defaults: ["android-non-updatable-doc-stubs-defaults"],
+ args: metalava_framework_docs_args +
+ " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ",
}
droidstubs {
@@ -101,7 +149,8 @@
arg_files: [
"core/res/AndroidManifest.xml",
],
- args: metalava_framework_docs_args + " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ",
+ args: metalava_framework_docs_args +
+ " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ",
write_sdk_values: true,
}
@@ -131,7 +180,7 @@
],
knowntags: [
"docs/knowntags.txt",
- ":known-oj-tags",
+ ":art.module.public.api{.doctags}",
],
custom_template: "droiddoc-templates-sdk",
resourcesdir: "docs/html/reference/images/",
@@ -150,12 +199,18 @@
":current-support-api",
":current-androidx-api",
],
- create_stubs: false,
+ // TODO(b/169090544): remove below aidl includes.
+ aidl: {
+ local_include_dirs: ["media/aidl"],
+ include_dirs: [
+ "frameworks/av/aidl",
+ "frameworks/native/libs/permission/aidl",
+ ],
+ },
}
doc_defaults {
name: "framework-dokka-docs-default",
- create_stubs: false,
}
droiddoc {
@@ -320,7 +375,7 @@
":framework-doc-stubs",
],
args: "-noJdkLink -links https://kotlinlang.org/api/latest/jvm/stdlib/^external/dokka/package-list " +
- "-noStdlibLink",
+ "-noStdlibLink",
proofread_file: "ds-dokka-proofread.txt",
dokka_enabled: true,
}
@@ -340,7 +395,7 @@
targets: ["docs"],
},
cmd: "$(location zip2zip) -i $(location :ds-docs-kt{.docs.zip}) -o $(genDir)/ds-docs-kt-moved.zip **/*:en/reference/kotlin && " +
- "$(location merge_zips) $(out) $(location :ds-docs-java{.docs.zip}) $(genDir)/ds-docs-kt-moved.zip",
+ "$(location merge_zips) $(out) $(location :ds-docs-java{.docs.zip}) $(genDir)/ds-docs-kt-moved.zip",
}
java_genrule {
@@ -357,11 +412,11 @@
dist: {
targets: ["docs"],
},
- cmd: "unzip $(location :ds-docs-java{.docs.zip}) -d $(genDir) && " +
- "unzip $(location :ds-docs-kt{.docs.zip}) -d $(genDir)/en/reference/kotlin && " +
- "SWITCHER=$$(cd $$(dirname $(location switcher4)) && pwd)/$$(basename $(location switcher4)) && " +
- "(cd $(genDir)/en/reference && $$SWITCHER --work platform) && " +
- "$(location soong_zip) -o $(out) -C $(genDir) -D $(genDir)",
+ cmd: "unzip -q $(location :ds-docs-java{.docs.zip}) -d $(genDir) && " +
+ "unzip -q $(location :ds-docs-kt{.docs.zip}) -d $(genDir)/en/reference/kotlin && " +
+ "SWITCHER=$$(cd $$(dirname $(location switcher4)) && pwd)/$$(basename $(location switcher4)) && " +
+ "(cd $(genDir)/en/reference && $$SWITCHER --work platform) > /dev/null && " +
+ "$(location soong_zip) -o $(out) -C $(genDir) -D $(genDir)",
}
droiddoc {
@@ -373,7 +428,6 @@
hdf: [
"android.whichdoc online",
],
- proofread_file: "ds-static-docs-proofrerad.txt",
args: framework_docs_only_args +
" -staticonly " +
" -toroot / " +
@@ -390,7 +444,6 @@
hdf: [
"android.whichdoc online",
],
- proofread_file: "ds-ref-navtree-docs-proofrerad.txt",
args: framework_docs_only_args +
" -toroot / " +
" -atLinksNavtree " +
@@ -437,4 +490,3 @@
" -referenceonly " +
" -title \"Android SDK - Including hidden APIs.\"",
}
-
diff --git a/BATTERY_STATS_OWNERS b/BATTERY_STATS_OWNERS
new file mode 100644
index 0000000..7728975
--- /dev/null
+++ b/BATTERY_STATS_OWNERS
@@ -0,0 +1,4 @@
+# OWNERS of BatteryStats related files
+bookatz@google.com
+dplotnikov@google.com
+mwachens@google.com
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..95577d8
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,4 @@
+third_party {
+ # would be NOTICE save for libs/usb/tests/accessorytest/f_accessory.h
+ license_type: RESTRICTED
+}
diff --git a/MULTIUSER_OWNERS b/MULTIUSER_OWNERS
new file mode 100644
index 0000000..fbc611a
--- /dev/null
+++ b/MULTIUSER_OWNERS
@@ -0,0 +1,4 @@
+# OWNERS of Multiuser related files
+bookatz@google.com
+omakoto@google.com
+yamasani@google.com
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..4970dd1
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,33 @@
+# This top-level list should remain narrowly defined as team leads; individual
+# teams are strongly encouraged to define narrower OWNERS files at deeper
+# levels within the source tree; see OWNERS.md for more details
+akulian@google.com
+dsandler@android.com
+dsandler@google.com
+hackbod@android.com
+hackbod@google.com
+jjaggi@google.com
+jsharkey@android.com
+jsharkey@google.com
+lorenzo@google.com
+michaelwr@google.com
+nandana@google.com
+narayan@google.com
+ogunwale@google.com
+roosa@google.com
+svetoslavganov@android.com
+svetoslavganov@google.com
+yamasani@google.com
+
+# API changes are already covered by API-Review+1 (http://mdb/android-api-council)
+# via https://android.git.corp.google.com/All-Projects/+/refs/meta/config/rules.pl.
+per-file */api/*current.txt = *
+
+# Support bulk translation updates
+per-file */res*/values*/*.xml = byi@google.com, delphij@google.com
+
+per-file Android.bp = file:platform/build/soong:/OWNERS
+per-file Android.mk = file:platform/build/soong:/OWNERS
+per-file ApiDocs.bp = file:platform/build/soong:/OWNERS
+per-file StubLibraries.bp = file:platform/build/soong:/OWNERS
+per-file ProtoLibraries.bp = file:platform/build/soong:/OWNERS
diff --git a/OWNERS.md b/OWNERS.md
new file mode 100644
index 0000000..601b5c64
--- /dev/null
+++ b/OWNERS.md
@@ -0,0 +1,68 @@
+# Background
+
+As general background, `OWNERS` files expedite code reviews by helping code
+authors quickly find relevant reviewers, and they also ensure that stakeholders
+are involved in code changes in their areas.
+
+The structure of `frameworks/base/` is unique among Android repositories, and
+it's evolved into a complex interleaved structure over the years. Because of
+this structure, the best place to authoritatively define `OWNERS` can vary
+wildly, but here are some common patterns:
+
+* `core/java/` contains source that is included in the base classpath, and as
+such it's where most APIs are defined:
+ * `core/java/android/app/`
+ * `core/java/android/content/`
+* `services/core/` contains most system services, and these directories
+typically have more granularity than `core/java/`, since they can be refactored
+without API changes:
+ * `services/core/java/com/android/server/net/`
+ * `services/core/java/com/android/server/wm/`
+* `services/` contains several system services that have been isolated from the
+main `services/core/` project:
+ * `services/appwidget/`
+ * `services/midi/`
+* `apex/` contains Mainline modules:
+ * `apex/jobscheduler/`
+ * `apex/permission/`
+* Finally, some teams may have dedicated top-level directories:
+ * `media/`
+ * `wifi/`
+
+# Design
+
+Area maintainers are strongly encouraged to list people in a single
+authoritative `OWNERS` file in **exactly one** location. Then, other paths
+should reference that single authoritative `OWNERS` file using an include
+directive. This approach ensures that updates are applied consistently across
+the tree, reducing maintenance burden.
+
+# Examples
+
+The exact syntax of `OWNERS` files can be difficult to get correct, so here are
+some common examples:
+
+```
+# Complete include of top-level owners from this repo
+include /ZYGOTE_OWNERS
+# Partial include of top-level owners from this repo
+per-file ZygoteFile.java = file:/ZYGOTE_OWNERS
+```
+```
+# Complete include of subdirectory owners from this repo
+include /services/core/java/com/android/server/net/OWNERS
+# Partial include of subdirectory owners from this repo
+per-file NetworkFile.java = file:/services/core/java/com/android/server/net/OWNERS
+```
+```
+# Complete include of top-level owners from another repo
+include platform/libcore:/OWNERS
+# Partial include of top-level owners from another repo
+per-file LibcoreFile.java = file:platform/libcore:/OWNERS
+```
+```
+# Complete include of subdirectory owners from another repo
+include platform/frameworks/av:/camera/OWNERS
+# Partial include of subdirectory owners from another repo
+per-file CameraFile.java = file:platform/frameworks/av:/camera/OWNERS
+```
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 2fd2e33..65b2511 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,22 +1,28 @@
[Builtin Hooks]
clang_format = true
+bpfmt = true
[Builtin Hooks Options]
# Only turn on clang-format check for the following subfolders.
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
cmds/hid/
cmds/input/
+ cmds/uinput/
core/jni/
+ libs/hwui/
libs/input/
+ native/
services/core/jni/
services/incremental/
-
+ tests/
+ tools/
+bpfmt = -d
[Hook Scripts]
checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
strings_lint_hook = ${REPO_ROOT}/frameworks/base/tools/stringslint/stringslint_sha.sh ${PREUPLOAD_COMMIT}
-hidden_api_txt_checksorted_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
+hidden_api_txt_checksorted_hook = ${REPO_ROOT}/tools/platform-compat/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
hidden_api_txt_exclude_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/exclude.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
diff --git a/ProtoLibraries.bp b/ProtoLibraries.bp
new file mode 100644
index 0000000..7e3cc27
--- /dev/null
+++ b/ProtoLibraries.bp
@@ -0,0 +1,253 @@
+// Copyright (C) 2021 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.
+
+gensrcs {
+ name: "framework-javastream-protos",
+ depfile: true,
+
+ tools: [
+ "aprotoc",
+ "protoc-gen-javastream",
+ "soong_zip",
+ ],
+
+ cmd: "mkdir -p $(genDir)/$(in) " +
+ "&& $(location aprotoc) " +
+ " --plugin=$(location protoc-gen-javastream) " +
+ " --dependency_out=$(depfile) " +
+ " --javastream_out=$(genDir)/$(in) " +
+ " -Iexternal/protobuf/src " +
+ " -I . " +
+ " $(in) " +
+ "&& $(location soong_zip) -jar -o $(out) -C $(genDir)/$(in) -D $(genDir)/$(in)",
+
+ srcs: [
+ ":ipconnectivity-proto-src",
+ ":libstats_atom_enum_protos",
+ ":libstats_atom_message_protos",
+ ":libtombstone_proto-src",
+ "core/proto/**/*.proto",
+ "libs/incident/**/*.proto",
+ ":service-permission-protos",
+ ],
+ output_extension: "srcjar",
+}
+
+gensrcs {
+ name: "framework-cppstream-protos",
+ depfile: true,
+
+ tools: [
+ "aprotoc",
+ "protoc-gen-cppstream",
+ ],
+
+ cmd: "mkdir -p $(genDir) " +
+ "&& $(location aprotoc) " +
+ " --plugin=$(location protoc-gen-cppstream) " +
+ " --dependency_out=$(depfile) " +
+ " --cppstream_out=$(genDir) " +
+ " -Iexternal/protobuf/src " +
+ " -I . " +
+ " $(in)",
+
+ srcs: [
+ ":ipconnectivity-proto-src",
+ ":libstats_atom_enum_protos",
+ ":libstats_atom_message_protos",
+ "core/proto/**/*.proto",
+ "libs/incident/**/*.proto",
+ ":service-permission-protos",
+ ],
+
+ output_extension: "proto.h",
+}
+
+// ==== java proto host library ==============================
+java_library_host {
+ name: "platformprotos",
+ srcs: [
+ ":ipconnectivity-proto-src",
+ ":libstats_atom_enum_protos",
+ ":libstats_atom_message_protos",
+ ":libstats_internal_protos",
+ ":statsd_internal_protos",
+ "cmds/am/proto/instrumentation_data.proto",
+ "cmds/statsd/src/**/*.proto",
+ "core/proto/**/*.proto",
+ "libs/incident/proto/**/*.proto",
+ ":service-permission-protos",
+ ],
+ proto: {
+ include_dirs: [
+ "external/protobuf/src",
+ "frameworks/proto_logging/stats",
+ ],
+ type: "full",
+ },
+ // Protos have lots of MissingOverride and similar.
+ errorprone: {
+ javacflags: ["-XepDisableAllChecks"],
+ },
+}
+
+// ==== java proto device library (for test only) ==============================
+java_library {
+ name: "platformprotosnano",
+ proto: {
+ type: "nano",
+ output_params: ["store_unknown_fields=true"],
+ include_dirs: ["external/protobuf/src"],
+ },
+ exclude_srcs: [
+ "core/proto/android/privacy.proto",
+ "core/proto/android/section.proto",
+ "core/proto/android/typedef.proto",
+ ],
+ sdk_version: "9",
+ srcs: [
+ ":ipconnectivity-proto-src",
+ ":libstats_atom_enum_protos",
+ ":libstats_atom_message_protos",
+ "core/proto/**/*.proto",
+ "libs/incident/proto/android/os/**/*.proto",
+ ":service-permission-protos",
+ ],
+}
+
+// ==== java proto device library (for test only) ==============================
+java_library {
+ name: "platformprotoslite",
+ proto: {
+ type: "lite",
+ include_dirs: ["external/protobuf/src"],
+ },
+
+ srcs: [
+ ":ipconnectivity-proto-src",
+ ":libstats_atom_enum_protos",
+ ":libstats_atom_message_protos",
+ "core/proto/**/*.proto",
+ "libs/incident/proto/android/os/**/*.proto",
+ ":service-permission-protos",
+ ],
+ exclude_srcs: [
+ "core/proto/android/privacy.proto",
+ "core/proto/android/section.proto",
+ "core/proto/android/typedef.proto",
+ ],
+ sdk_version: "core_current",
+ // Protos have lots of MissingOverride and similar.
+ errorprone: {
+ javacflags: ["-XepDisableAllChecks"],
+ },
+}
+
+// ==== c++ proto device library ==============================
+cc_defaults {
+ name: "libplatformprotos-defaults",
+
+ proto: {
+ export_proto_headers: true,
+ include_dirs: [
+ "external/protobuf/src",
+ ],
+ },
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ ],
+
+ srcs: [
+ ":ipconnectivity-proto-src",
+ ":libstats_atom_enum_protos",
+ ":libstats_atom_message_protos",
+ "core/proto/**/*.proto",
+ ":service-permission-protos",
+ ],
+}
+
+cc_library {
+ name: "libplatformprotos",
+ defaults: ["libplatformprotos-defaults"],
+ host_supported: true,
+
+ target: {
+ host: {
+ proto: {
+ type: "full",
+ },
+ },
+ android: {
+ proto: {
+ type: "lite",
+ },
+ shared_libs: [
+ "libprotobuf-cpp-lite",
+ ],
+ shared: {
+ enabled: false,
+ },
+ },
+ },
+}
+
+// This library is meant for vendor code that needs to output protobuf. It links
+// against the static version of libprotobuf-cpp-lite, for which we can not guarantee
+// binary compatibility.
+cc_library {
+ name: "libplatformprotos-static",
+ defaults: ["libplatformprotos-defaults"],
+ host_supported: false,
+
+ // This is okay because this library is only built as a static library. The C++
+ // API is not guaranteed. The proto API is guaranteed to be stable via Metrics Council,
+ // but is not authorized to be used outside of debugging.
+ vendor_available: true,
+
+ target: {
+ android: {
+ proto: {
+ type: "lite",
+ },
+ static_libs: [
+ "libprotobuf-cpp-lite",
+ ],
+ shared: {
+ enabled: false,
+ },
+ },
+ },
+}
+
+// This is the full proto version of libplatformprotos. It may only
+// be used by test code that is not shipped on the device.
+cc_library {
+ name: "libplatformprotos-test",
+ defaults: ["libplatformprotos-defaults"],
+ host_supported: false,
+
+ target: {
+ android: {
+ proto: {
+ type: "full",
+ },
+ shared: {
+ enabled: false,
+ },
+ },
+ },
+}
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 3eb482d..44c55c2 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -16,396 +16,421 @@
//
// raw source files --(metalava)--> stub source files --(javac)--> stub jar files
//
-// The metalava conversion is done by droidstub modules *-api-stubs-docs.
-// The javac compilation is done by java_library modules android_*_stubs_current.
+// - The metalava conversion is done by droidstub modules
+// - The javac compilation is done by java_library modules
+//
// The metalava conversion is also responsible for creating API signature files
-// and comparing them against the last API signature in api/*-current.txt files
-// and also against the latest frozen API signature in prebuilts/sdk/*/*/api/android.txt
-// files.
+// and comparing them against the checked in API signature, and also checking compatibility
+// with the latest frozen API signature.
/////////////////////////////////////////////////////////////////////
// Common metalava configs
/////////////////////////////////////////////////////////////////////
-packages_to_document = [
- "android",
- "dalvik",
- "java",
- "javax",
- "junit",
- "org.apache.http",
- "org.json",
- "org.w3c.dom",
- "org.xml.sax",
- "org.xmlpull",
-]
-
-stubs_defaults {
- name: "metalava-base-api-stubs-default",
- srcs: [
- ":framework-non-updatable-sources",
- "core/java/**/*.logtags",
- ":opt-telephony-srcs",
- ":opt-net-voip-srcs",
- ":art-module-public-api-stubs-source",
- ":android_icu4j_public_api_files",
- ],
- // TODO(b/147699819): remove below aidl includes.
- aidl: {
- local_include_dirs: ["telephony/java"],
- },
- libs: ["framework-internal-utils"],
- installable: false,
- annotations_enabled: true,
- previous_api: ":android.api.public.latest",
- merge_annotations_dirs: [
- "metalava-manual",
- ],
- api_levels_annotations_enabled: false,
- filter_packages: packages_to_document,
-}
-
-stubs_defaults {
- name: "metalava-full-api-stubs-default",
- defaults: ["metalava-base-api-stubs-default"],
- srcs: [
- ":conscrypt.module.public.api{.public.stubs.source}",
- ":framework-updatable-sources",
- ],
- sdk_version: "core_platform",
-}
-
stubs_defaults {
name: "metalava-non-updatable-api-stubs-default",
- defaults: ["metalava-base-api-stubs-default"],
- sdk_version: "core_platform",
- // There are a few classes from modules used as type arguments that
- // need to be resolved by metalava. For now, we can use a previously
- // finalized stub library to resolve them. If a new class gets added,
- // this may be need to be revisited to use a manually maintained stub
- // library with empty classes in order to resolve those references.
- libs: ["sdk_system_30_android"],
- aidl: {
- local_include_dirs: ["apex/media/aidl/stable"],
- },
+ defaults: ["android-non-updatable-stubs-defaults"],
+ api_levels_annotations_enabled: false,
+ defaults_visibility: ["//visibility:private"],
}
/////////////////////////////////////////////////////////////////////
-// *-api-stubs-docs modules providing source files for the stub libraries
+// These modules provide source files for the stub libraries
/////////////////////////////////////////////////////////////////////
-// api-stubs-docs, system-api-stubs-docs, and test-api-stubs-docs have APIs
-// from the non-updatable part of the platform as well as from the updatable
-// modules
-droidstubs {
- name: "api-stubs-docs",
- defaults: ["metalava-full-api-stubs-default"],
- removed_dex_api_filename: "removed-dex.txt",
- arg_files: [
- "core/res/AndroidManifest.xml",
- ],
- args: metalava_framework_docs_args,
- check_api: {
- current: {
- api_file: "api/current.txt",
- removed_api_file: "api/removed.txt",
- },
- last_released: {
- api_file: ":android.api.public.latest",
- removed_api_file: ":removed.api.public.latest",
- baseline_file: ":public-api-incompatibilities-with-last-released",
- },
- api_lint: {
- enabled: true,
- new_since: ":android.api.public.latest",
- baseline_file: "api/lint-baseline.txt",
- },
- },
- dist: {
- targets: ["sdk", "win_sdk"],
- dir: "apistubs/android/public/api",
- dest: "android.txt",
- },
- jdiff_enabled: true,
-}
-
droidstubs {
name: "api-stubs-docs-non-updatable",
defaults: ["metalava-non-updatable-api-stubs-default"],
- arg_files: ["core/res/AndroidManifest.xml"],
args: metalava_framework_docs_args,
check_api: {
current: {
- api_file: "non-updatable-api/current.txt",
- removed_api_file: "non-updatable-api/removed.txt",
- },
- api_lint: {
- enabled: true,
- new_since: ":android-non-updatable.api.public.latest",
- },
- },
-}
-
-priv_apps = " " +
- "--show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
- "\\) "
-
-module_libs = " " +
- " --show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" +
- "\\) "
-
-droidstubs {
- name: "system-api-stubs-docs",
- defaults: ["metalava-full-api-stubs-default"],
- removed_dex_api_filename: "system-removed-dex.txt",
- arg_files: [
- "core/res/AndroidManifest.xml",
- ],
- args: metalava_framework_docs_args + priv_apps,
- check_api: {
- current: {
- api_file: "api/system-current.txt",
- removed_api_file: "api/system-removed.txt",
+ api_file: "core/api/current.txt",
+ removed_api_file: "core/api/removed.txt",
},
last_released: {
- api_file: ":android.api.system.latest",
- removed_api_file: ":removed.api.system.latest",
- baseline_file: ":system-api-incompatibilities-with-last-released"
+ api_file: ":android-non-updatable.api.public.latest",
+ removed_api_file: ":android-non-updatable-removed.api.public.latest",
+ baseline_file: ":android-non-updatable-incompatibilities.api.public.latest",
},
api_lint: {
enabled: true,
- new_since: ":android.api.system.latest",
- baseline_file: "api/system-lint-baseline.txt",
+ new_since: ":android.api.public.latest",
},
},
- dist: {
- targets: ["sdk", "win_sdk"],
- dir: "apistubs/android/system/api",
- dest: "android.txt",
- },
- jdiff_enabled: true,
+ dists: [
+ {
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
+ dir: "apistubs/android/public/api",
+ dest: "android-non-updatable.txt",
+ tag: ".api.txt",
+ },
+ {
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
+ dir: "apistubs/android/public/api",
+ dest: "android-non-updatable-removed.txt",
+ tag: ".removed-api.txt",
+ },
+ ],
}
+priv_apps = " --show-annotation android.annotation.SystemApi\\(" +
+ "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
+ "\\)"
+
+priv_apps_in_stubs = " --show-for-stub-purposes-annotation android.annotation.SystemApi\\(" +
+ "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
+ "\\)"
+
+test = " --show-annotation android.annotation.TestApi"
+
+module_libs = " --show-annotation android.annotation.SystemApi\\(" +
+ "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" +
+ "\\)"
+
droidstubs {
name: "system-api-stubs-docs-non-updatable",
defaults: ["metalava-non-updatable-api-stubs-default"],
- arg_files: ["core/res/AndroidManifest.xml"],
args: metalava_framework_docs_args + priv_apps,
check_api: {
current: {
- api_file: "non-updatable-api/system-current.txt",
- removed_api_file: "non-updatable-api/system-removed.txt",
- },
- api_lint: {
- enabled: true,
- new_since: ":android-non-updatable.api.system.latest",
- baseline_file: "non-updatable-api/system-lint-baseline.txt",
- },
- },
-}
-
-droidstubs {
- name: "test-api-stubs-docs",
- defaults: ["metalava-full-api-stubs-default"],
- arg_files: [
- "core/res/AndroidManifest.xml",
- ],
- args: metalava_framework_docs_args + " --show-annotation android.annotation.TestApi",
- check_api: {
- current: {
- api_file: "api/test-current.txt",
- removed_api_file: "api/test-removed.txt",
- },
- api_lint: {
- enabled: true,
- baseline_file: "api/test-lint-baseline.txt",
- },
- },
- dist: {
- targets: ["sdk", "win_sdk"],
- dir: "apistubs/android/test/api",
- dest: "android.txt",
- },
-}
-
-/////////////////////////////////////////////////////////////////////
-// Following droidstubs modules are for extra APIs for modules.
-// The framework currently have two more API surfaces for modules:
-// @SystemApi(client=MODULE_APPS) and @SystemApi(client=MODULE_LIBRARIES)
-/////////////////////////////////////////////////////////////////////
-
-// TODO(b/146727827) remove the *-api module when we can teach metalava
-// about the relationship among the API surfaces. Currently, these modules are only to generate
-// the API signature files and ensure that the APIs evolve in a backwards compatible manner.
-// They however are NOT used for building the API stub.
-
-droidstubs {
- name: "module-lib-api",
- defaults: ["metalava-full-api-stubs-default"],
- arg_files: ["core/res/AndroidManifest.xml"],
- args: metalava_framework_docs_args + module_libs,
- check_api: {
- current: {
- api_file: "api/module-lib-current.txt",
- removed_api_file: "api/module-lib-removed.txt",
+ api_file: "core/api/system-current.txt",
+ removed_api_file: "core/api/system-removed.txt",
},
last_released: {
- api_file: ":android.api.module-lib.latest",
- removed_api_file: ":removed.api.module-lib.latest",
- baseline_file: ":module-lib-api-incompatibilities-with-last-released"
+ api_file: ":android-non-updatable.api.system.latest",
+ removed_api_file: ":android-non-updatable-removed.api.system.latest",
+ baseline_file: ":android-non-updatable-incompatibilities.api.system.latest",
},
api_lint: {
enabled: true,
- new_since: ":android.api.module-lib.latest",
- baseline_file: "api/module-lib-lint-baseline.txt",
+ new_since: ":android.api.system.latest",
+ baseline_file: "core/api/system-lint-baseline.txt",
},
},
- dist: {
- targets: ["sdk", "win_sdk"],
- dir: "apistubs/android/module-lib/api",
- dest: "android.txt",
+ dists: [
+ {
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
+ dir: "apistubs/android/system/api",
+ dest: "android-non-updatable.txt",
+ tag: ".api.txt",
+ },
+ {
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
+ dir: "apistubs/android/system/api",
+ dest: "android-non-updatable-removed.txt",
+ tag: ".removed-api.txt",
+ },
+ ],
+}
+
+droidstubs {
+ name: "test-api-stubs-docs-non-updatable",
+ defaults: ["metalava-non-updatable-api-stubs-default"],
+ args: metalava_framework_docs_args + test + priv_apps_in_stubs,
+ check_api: {
+ current: {
+ api_file: "core/api/test-current.txt",
+ removed_api_file: "core/api/test-removed.txt",
+ },
+ api_lint: {
+ enabled: true,
+ baseline_file: "core/api/test-lint-baseline.txt",
+ },
},
+ dists: [
+ {
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
+ dir: "apistubs/android/test/api",
+ dest: "android.txt",
+ tag: ".api.txt",
+ },
+ {
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
+ dir: "apistubs/android/test/api",
+ dest: "removed.txt",
+ tag: ".removed-api.txt",
+ },
+ {
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
+ dir: "apistubs/android/test/api",
+ dest: "android-non-updatable.txt",
+ tag: ".api.txt",
+ },
+ {
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
+ dir: "apistubs/android/test/api",
+ dest: "android-non-updatable-removed.txt",
+ tag: ".removed-api.txt",
+ },
+ ],
}
droidstubs {
name: "module-lib-api-stubs-docs-non-updatable",
defaults: ["metalava-non-updatable-api-stubs-default"],
- arg_files: ["core/res/AndroidManifest.xml"],
- args: metalava_framework_docs_args + module_libs,
+ args: metalava_framework_docs_args + priv_apps_in_stubs + module_libs,
check_api: {
current: {
- api_file: "non-updatable-api/module-lib-current.txt",
- removed_api_file: "non-updatable-api/module-lib-removed.txt",
+ api_file: "core/api/module-lib-current.txt",
+ removed_api_file: "core/api/module-lib-removed.txt",
+ },
+ last_released: {
+ api_file: ":android-non-updatable.api.module-lib.latest",
+ removed_api_file: ":android-non-updatable-removed.api.module-lib.latest",
},
api_lint: {
enabled: true,
- new_since: ":android-non-updatable.api.module-lib.latest",
+ new_since: ":android.api.module-lib.latest",
+ baseline_file: "core/api/module-lib-lint-baseline.txt",
},
},
-}
-
-// The following droidstub module generates source files for the API stub library for
-// modules. Note that it not only includes its own APIs but also other APIs that have
-// narrower scope (all @SystemApis, not just the ones with 'client=MODULE_LIBRARIES').
-
-droidstubs {
- name: "module-lib-api-stubs-docs",
- defaults: ["metalava-non-updatable-api-stubs-default"],
- arg_files: ["core/res/AndroidManifest.xml"],
- args: metalava_framework_docs_args + priv_apps + module_libs,
+ dists: [
+ {
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
+ dir: "apistubs/android/module-lib/api",
+ dest: "android-non-updatable.txt",
+ tag: ".api.txt",
+ },
+ {
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
+ dir: "apistubs/android/module-lib/api",
+ dest: "android-non-updatable-removed.txt",
+ tag: ".removed-api.txt",
+ },
+ ],
}
/////////////////////////////////////////////////////////////////////
// android_*_stubs_current modules are the stubs libraries compiled
-// from *-api-stubs-docs
+// from stub sources
/////////////////////////////////////////////////////////////////////
+modules_public_stubs = [
+ "android.net.ipsec.ike.stubs",
+ "art.module.public.api.stubs",
+ "conscrypt.module.public.api.stubs",
+ "framework-appsearch.stubs",
+ "framework-connectivity.stubs",
+ "framework-graphics.stubs",
+ "framework-media.stubs",
+ "framework-mediaprovider.stubs",
+ "framework-permission.stubs",
+ "framework-permission-s.stubs",
+ "framework-scheduling.stubs",
+ "framework-sdkextensions.stubs",
+ "framework-statsd.stubs",
+ "framework-tethering.stubs",
+ "framework-wifi.stubs",
+ "i18n.module.public.api.stubs",
+]
+
+modules_system_stubs = [
+ "android.net.ipsec.ike.stubs.system",
+ "art.module.public.api.stubs.system",
+ "conscrypt.module.public.api.stubs", // Only has public stubs
+ "framework-appsearch.stubs.system",
+ "framework-connectivity.stubs.system",
+ "framework-graphics.stubs.system",
+ "framework-media.stubs.system",
+ "framework-mediaprovider.stubs.system",
+ "framework-permission.stubs.system",
+ "framework-permission-s.stubs.system",
+ "framework-scheduling.stubs.system",
+ "framework-sdkextensions.stubs.system",
+ "framework-statsd.stubs.system",
+ "framework-tethering.stubs.system",
+ "framework-wifi.stubs.system",
+ "i18n.module.public.api.stubs", // Only has public stubs
+]
+
java_defaults {
- name: "android_defaults_stubs_current",
- libs: [ "stub-annotations" ],
- static_libs: [
- "framework-res-package-jar", // Export package of framework-res
- ],
- errorprone: {
- javacflags: [
- "-XepDisableAllChecks",
- ],
- },
- java_resources: [":notices-for-framework-stubs"],
+ name: "android-non-updatable_defaults_stubs_current",
+ libs: ["stub-annotations"],
+ static_libs: ["framework-res-package-jar"], // Export package of framework-res
sdk_version: "none",
system_modules: "none",
java_version: "1.8",
compile_dex: true,
+ dist: {
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
+ tag: ".jar",
+ dest: "android-non-updatable.jar",
+ },
+ defaults_visibility: ["//visibility:private"],
+ visibility: ["//visibility:private"],
}
-java_library_static {
- name: "android_monolith_stubs_current",
- srcs: [ ":api-stubs-docs" ],
- static_libs: [ "private-stub-annotations-jar" ],
- defaults: ["android_defaults_stubs_current"],
-}
-
-java_library_static {
- name: "android_merged_stubs_current",
- srcs: [ ":api-stubs-docs-non-updatable" ],
- static_libs: [
- "conscrypt.module.public.api.stubs",
- "framework-media.stubs",
- "framework-mediaprovider.stubs",
- "framework-permission.stubs",
- "framework-sdkextensions.stubs",
- "framework-statsd.stubs",
- "framework-tethering.stubs",
- "framework-wifi.stubs",
- "private-stub-annotations-jar",
- ],
- defaults: ["android_defaults_stubs_current"],
-}
-
-java_library_static {
- name: "android_stubs_current",
- static_libs: ["android_merged_stubs_current"],
- defaults: ["android_defaults_stubs_current"],
-}
-
-java_library_static {
- name: "android_system_monolith_stubs_current",
- srcs: [ ":system-api-stubs-docs" ],
- static_libs: [ "private-stub-annotations-jar" ],
- defaults: ["android_defaults_stubs_current"],
-}
-
-java_library_static {
- name: "android_system_merged_stubs_current",
- srcs: [ ":system-api-stubs-docs-non-updatable" ],
- static_libs: [
- "conscrypt.module.public.api.stubs",
- "framework-media.stubs.system",
- "framework-mediaprovider.stubs.system",
- "framework-permission.stubs.system",
- "framework-sdkextensions.stubs.system",
- "framework-statsd.stubs.system",
- "framework-tethering.stubs.system",
- "framework-wifi.stubs.system",
- "private-stub-annotations-jar",
- ],
- defaults: ["android_defaults_stubs_current"],
-}
-
-java_library_static {
- name: "android_system_stubs_current",
- static_libs: ["android_system_merged_stubs_current"],
- defaults: ["android_defaults_stubs_current"],
-}
-
-java_library_static {
- name: "android_test_stubs_current",
- srcs: [ ":test-api-stubs-docs" ],
- static_libs: [ "private-stub-annotations-jar" ],
- defaults: ["android_defaults_stubs_current"],
-}
-
-java_library_static {
- name: "android_module_lib_stubs_current",
- srcs: [ ":module-lib-api-stubs-docs" ],
- defaults: ["android_defaults_stubs_current"],
- libs: ["sdk_system_29_android"],
-}
-
-java_library_static {
- name: "android_non_updatable_stubs_current",
+java_library {
+ name: "android-non-updatable.stubs",
+ defaults: ["android-non-updatable_defaults_stubs_current"],
srcs: [":api-stubs-docs-non-updatable"],
- defaults: ["android_defaults_stubs_current"],
- libs: ["sdk_system_29_android"],
+ libs: modules_public_stubs,
+ dist: {
+ dir: "apistubs/android/public",
+ },
}
-java_library_static {
- name: "android_system_non_updatable_stubs_current",
+java_library {
+ name: "android-non-updatable.stubs.system",
+ defaults: ["android-non-updatable_defaults_stubs_current"],
srcs: [":system-api-stubs-docs-non-updatable"],
- defaults: ["android_defaults_stubs_current"],
- libs: ["sdk_system_29_android"],
+ libs: modules_system_stubs,
+ dist: {
+ dir: "apistubs/android/system",
+ },
+}
+
+java_library {
+ name: "android-non-updatable.stubs.module_lib",
+ defaults: ["android-non-updatable_defaults_stubs_current"],
+ srcs: [":module-lib-api-stubs-docs-non-updatable"],
+ libs: [
+ "sdk_system_current_android",
+ // NOTE: The below can be removed once the prebuilt stub contains IKE.
+ "sdk_system_current_android.net.ipsec.ike",
+ ],
+ dist: {
+ dir: "apistubs/android/module-lib",
+ },
+}
+
+java_library {
+ name: "android-non-updatable.stubs.test",
+ defaults: ["android-non-updatable_defaults_stubs_current"],
+ srcs: [":test-api-stubs-docs-non-updatable"],
+ libs: modules_system_stubs,
+ dist: {
+ dir: "apistubs/android/test",
+ },
+}
+
+java_defaults {
+ name: "android_stubs_dists_default",
+ dist: {
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
+ tag: ".jar",
+ dest: "android.jar",
+ },
+ defaults_visibility: ["//frameworks/base/services"],
+}
+
+java_library {
+ name: "android_stubs_current",
+ static_libs: modules_public_stubs + [
+ "android-non-updatable.stubs",
+ "private-stub-annotations-jar",
+ ],
+ defaults: ["android.jar_defaults"],
+}
+
+java_library {
+ name: "android_system_stubs_current",
+ static_libs: modules_system_stubs + [
+ "android-non-updatable.stubs.system",
+ "private-stub-annotations-jar",
+ ],
+ defaults: [
+ "android.jar_defaults",
+ "android_stubs_dists_default",
+ ],
+ dist: {
+ dir: "apistubs/android/system",
+ },
+ dists: [
+ {
+ // Legacy dist path
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
+ tag: ".jar",
+ dest: "android_system.jar",
+ },
+ ],
+}
+
+java_library {
+ name: "android_test_stubs_current",
+ // Modules do not have test APIs, but we want to include their SystemApis, like we include
+ // the SystemApi of framework-non-updatable-sources.
+ static_libs: modules_system_stubs + [
+ "android-non-updatable.stubs.test",
+ "private-stub-annotations-jar",
+ ],
+ defaults: [
+ "android.jar_defaults",
+ "android_stubs_dists_default",
+ ],
+ dist: {
+ dir: "apistubs/android/test",
+ },
+}
+
+java_library {
+ name: "android_module_lib_stubs_current",
+ defaults: [
+ "android.jar_defaults",
+ "android_stubs_dists_default",
+ ],
+ static_libs: [
+ "android-non-updatable.stubs.module_lib",
+ "art.module.public.api.stubs.module_lib",
+ ],
+ dist: {
+ dir: "apistubs/android/module-lib",
+ },
+}
+
+java_library {
+ name: "android_system_server_stubs_current",
+ defaults: ["android_stubs_dists_default"],
+ srcs: [":services-non-updatable-stubs"],
+ installable: false,
+ static_libs: [
+ "android_module_lib_stubs_current",
+ ],
+ sdk_version: "none",
+ system_modules: "none",
+ java_version: "1.8",
+ dist: {
+ dir: "apistubs/android/system-server",
+ },
}
/////////////////////////////////////////////////////////////////////
@@ -417,10 +442,6 @@
name: "hwbinder-stubs-docs",
srcs: [
"core/java/android/os/HidlSupport.java",
- "core/java/android/annotation/IntDef.java",
- "core/java/android/annotation/IntRange.java",
- "core/java/android/annotation/NonNull.java",
- "core/java/android/annotation/SystemApi.java",
"core/java/android/os/HidlMemory.java",
"core/java/android/os/HwBinder.java",
"core/java/android/os/HwBlob.java",
@@ -433,6 +454,7 @@
"core/java/android/os/RemoteException.java",
"core/java/android/util/AndroidException.java",
],
+ libs: ["framework-annotations-lib"],
installable: false,
sdk_version: "core_platform",
annotations_enabled: true,
@@ -441,38 +463,15 @@
"metalava-manual",
],
args: priv_apps,
+ visibility: ["//visibility:private"],
}
-java_library_static {
+java_library {
name: "hwbinder.stubs",
sdk_version: "core_current",
+ libs: ["framework-annotations-lib"],
srcs: [
":hwbinder-stubs-docs",
],
-}
-
-/////////////////////////////////////////////////////////////////////
-// api/*-current.txt files for use by modules in other directories
-// like the CTS test
-/////////////////////////////////////////////////////////////////////
-
-filegroup {
- name: "frameworks-base-api-current.txt",
- srcs: [
- "api/current.txt",
- ],
-}
-
-filegroup {
- name: "frameworks-base-api-system-current.txt",
- srcs: [
- "api/system-current.txt",
- ],
-}
-
-filegroup {
- name: "frameworks-base-api-system-removed.txt",
- srcs: [
- "api/system-removed.txt",
- ],
+ visibility: ["//visibility:public"],
}
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 2b12da2..c5c6012 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -1,4 +1,20 @@
{
+ "presubmit-large": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ],
"presubmit": [
{
"name": "FrameworksUiServicesTests",
@@ -39,7 +55,7 @@
]
},
{
- "name": "FrameworksServicesTests",
+ "name": "FrameworkPermissionTests",
"options": [
{
"include-annotation": "android.platform.test.annotations.Presubmit"
@@ -51,11 +67,82 @@
"exclude-annotation": "org.junit.Ignore"
}
]
+ },
+ {
+ "name": "FrameworksInProcessTests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
}
],
"postsubmit-managedprofile-stress": [
{
"name": "ManagedProfileLifecycleStressTest"
}
- ]
+ ],
+ "auto-postsubmit": [
+ // Test tag for automotive targets. These are only running in postsubmit so as to harden the
+ // automotive targets to avoid introducing additional test flake and build time. The plan for
+ // presubmit testing for auto is to augment the existing tests to cover auto use cases as well.
+ // Additionally, this tag is used in targeted test suites to limit resource usage on the test
+ // infra during the hardening phase.
+ // TODO: this tag to be removed once the above is no longer an issue.
+ {
+ "name": "FrameworksUiServicesTests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "ExtServicesUnitTests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "TestablesTests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
}
diff --git a/TestProtoLibraries.bp b/TestProtoLibraries.bp
new file mode 100644
index 0000000..8e269d0
--- /dev/null
+++ b/TestProtoLibraries.bp
@@ -0,0 +1,36 @@
+// Copyright 2021 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.
+
+java_library_host {
+ name: "platformtestprotos",
+ srcs: [
+ ":libstats_atom_enum_protos",
+ ":libstats_atom_message_protos",
+ ":libstats_internal_protos",
+ ":statsd_internal_protos",
+ ],
+ libs: [
+ "libprotobuf-java-full",
+ ],
+ proto: {
+ include_dirs: [
+ "external/protobuf/src",
+ "frameworks/proto_logging/stats",
+ ],
+ type: "full",
+ },
+ errorprone: {
+ javacflags: ["-Xep:MissingOverride:OFF"], // b/72714520
+ },
+}
diff --git a/ZYGOTE_OWNERS b/ZYGOTE_OWNERS
new file mode 100644
index 0000000..90a185b
--- /dev/null
+++ b/ZYGOTE_OWNERS
@@ -0,0 +1,5 @@
+calin@google.com
+chriswailes@google.com
+maco@google.com
+narayan@google.com
+ngeoffray@google.com
diff --git a/apct-tests/perftests/OWNERS b/apct-tests/perftests/OWNERS
new file mode 100644
index 0000000..7e7feaf
--- /dev/null
+++ b/apct-tests/perftests/OWNERS
@@ -0,0 +1,11 @@
+balejs@google.com
+carmenjackson@google.com
+cfijalkovich@google.com
+dualli@google.com
+edgararriaga@google.com
+jpakaravoor@google.com
+kevinjeon@google.com
+philipcuadra@google.com
+shombert@google.com
+timmurray@google.com
+wessam@google.com
diff --git a/apct-tests/perftests/autofill/Android.bp b/apct-tests/perftests/autofill/Android.bp
index 65c28fb..84145be 100644
--- a/apct-tests/perftests/autofill/Android.bp
+++ b/apct-tests/perftests/autofill/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // 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"],
+}
+
android_test {
name: "AutofillPerfTests",
srcs: ["src/**/*.java"],
@@ -20,7 +29,10 @@
"androidx.test.rules",
"androidx.annotation_annotation",
"apct-perftests-utils",
+ "compatibility-device-util-axt",
+ "collector-device-lib",
],
platform_apis: true,
test_suites: ["device-tests"],
+ data: [":perfetto_artifacts"],
}
diff --git a/apct-tests/perftests/autofill/AndroidManifest.xml b/apct-tests/perftests/autofill/AndroidManifest.xml
index 1e3532b..57595a2 100644
--- a/apct-tests/perftests/autofill/AndroidManifest.xml
+++ b/apct-tests/perftests/autofill/AndroidManifest.xml
@@ -18,7 +18,8 @@
<application>
<uses-library android:name="android.test.runner" />
- <activity android:name="android.perftests.utils.PerfTestActivity">
+ <activity android:name="android.perftests.utils.PerfTestActivity"
+ android:exported="true">
<intent-filter>
<action android:name="com.android.perftests.core.PERFTEST" />
</intent-filter>
@@ -27,7 +28,8 @@
<service
android:name="android.view.autofill.MyAutofillService"
android:label="PERF AutofillService"
- android:permission="android.permission.BIND_AUTOFILL_SERVICE" >
+ android:permission="android.permission.BIND_AUTOFILL_SERVICE"
+ android:exported="true">
<intent-filter>
<action android:name="android.service.autofill.AutofillService" />
</intent-filter>
diff --git a/apct-tests/perftests/autofill/AndroidTest.xml b/apct-tests/perftests/autofill/AndroidTest.xml
index 29f9f94..eee7bdc 100644
--- a/apct-tests/perftests/autofill/AndroidTest.xml
+++ b/apct-tests/perftests/autofill/AndroidTest.xml
@@ -21,8 +21,40 @@
<option name="test-file-name" value="AutofillPerfTests.apk" />
</target_preparer>
+ <!-- Needed for pushing the trace config file -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="push-file" key="trace_config_detailed.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" />
+ </target_preparer>
+
+ <!-- Needed for pulling the collected trace config on to the host -->
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="pull-pattern-keys" value="perfetto_file_path" />
+ </metrics_collector>
+
+ <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+ <option name="isolated-storage" value="false" />
+
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.perftests.autofill" />
<option name="hidden-api-checks" value="false"/>
+
+ <!-- Listener related args for collecting the traces and waiting for the device to stabilize. -->
+ <option name="device-listeners" value="android.device.collectors.ProcLoadListener,android.device.collectors.PerfettoListener" />
+ <!-- Guarantee that user defined RunListeners will be running before any of the default listeners defined in this runner. -->
+ <option name="instrumentation-arg" key="newRunListenerMode" value="true" />
+
+ <!-- ProcLoadListener related arguments -->
+ <!-- Wait for device last minute threshold to reach 3 with 2 minute timeout before starting the test run -->
+ <option name="instrumentation-arg" key="procload-collector:per_run" value="true" />
+ <option name="instrumentation-arg" key="proc-loadavg-threshold" value="3" />
+ <option name="instrumentation-arg" key="proc-loadavg-timeout" value="120000" />
+ <option name="instrumentation-arg" key="proc-loadavg-interval" value="10000" />
+
+ <!-- PerfettoListener related arguments -->
+ <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" />
+ <option name="instrumentation-arg" key="perfetto_config_file" value="trace_config.textproto" />
+
+ <option name="instrumentation-arg" key="newRunListenerMode" value="true" />
</test>
</configuration>
diff --git a/apct-tests/perftests/autofill/OWNERS b/apct-tests/perftests/autofill/OWNERS
new file mode 100644
index 0000000..c52751d
--- /dev/null
+++ b/apct-tests/perftests/autofill/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/service/autofill/OWNERS
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java b/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
index 48ce8ab..54e1860 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
@@ -11,46 +11,54 @@
* 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
+ * limitations under the License.
*/
package android.view.autofill;
+import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+
import static org.junit.Assert.assertTrue;
import android.os.Looper;
import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.PerfTestActivity;
-import android.perftests.utils.SettingsHelper;
import android.perftests.utils.SettingsStateKeeperRule;
import android.provider.Settings;
+import android.util.Log;
import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
-import org.junit.After;
+import org.junit.AfterClass;
import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Rule;
+import org.junit.rules.RuleChain;
/**
* Base class for all autofill tests.
*/
-@LargeTest
public abstract class AbstractAutofillPerfTestCase {
+ private static final String TAG = "AbstractAutofillPerfTestCase";
+
@ClassRule
public static final SettingsStateKeeperRule mServiceSettingsKeeper =
new SettingsStateKeeperRule(InstrumentationRegistry.getTargetContext(),
Settings.Secure.AUTOFILL_SERVICE);
- @Rule
- public ActivityTestRule<PerfTestActivity> mActivityRule =
+ protected final AutofillTestWatcher mTestWatcher = MyAutofillService.getTestWatcher();
+ protected ActivityTestRule<PerfTestActivity> mActivityRule =
new ActivityTestRule<>(PerfTestActivity.class);
+ protected PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
@Rule
- public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+ public final RuleChain mAllRules = RuleChain
+ .outerRule(mTestWatcher)
+ .around(mPerfStatusReporter)
+ .around(mActivityRule);
private final int mLayoutId;
@@ -58,6 +66,27 @@
mLayoutId = layoutId;
}
+ @BeforeClass
+ public static void disableDefaultAugmentedService() {
+ Log.v(TAG, "@BeforeClass: disableDefaultAugmentedService()");
+ setDefaultAugmentedAutofillServiceEnabled(false);
+ }
+
+ @AfterClass
+ public static void enableDefaultAugmentedService() {
+ Log.v(TAG, "@AfterClass: enableDefaultAugmentedService()");
+ setDefaultAugmentedAutofillServiceEnabled(true);
+ }
+
+ /**
+ * Enables / disables the default augmented autofill service.
+ */
+ private static void setDefaultAugmentedAutofillServiceEnabled(boolean enabled) {
+ Log.d(TAG, "setDefaultAugmentedAutofillServiceEnabled(): " + enabled);
+ runShellCommand("cmd autofill set default-augmented-service-enabled 0 %s",
+ Boolean.toString(enabled));
+ }
+
/**
* Prepares the activity so that by the time the test is run it has reference to its fields.
*/
@@ -74,41 +103,8 @@
});
}
- @Before
- public void enableService() {
- MyAutofillService.resetStaticState();
- MyAutofillService.setEnabled(true);
- }
-
- @After
- public void disableService() {
- // Must disable service so calls are ignored in case of errors during the test case;
- // otherwise, other tests will fail because these calls are made in the UI thread (as both
- // the service, the tests, and the app run in the same process).
- MyAutofillService.setEnabled(false);
- }
-
/**
* Initializes the {@link PerfTestActivity} after it was launched.
*/
protected abstract void onCreate(PerfTestActivity activity);
-
- /**
- * Uses the {@code settings} binary to set the autofill service.
- */
- protected void setService() {
- SettingsHelper.syncSet(InstrumentationRegistry.getTargetContext(),
- SettingsHelper.NAMESPACE_SECURE,
- Settings.Secure.AUTOFILL_SERVICE,
- MyAutofillService.COMPONENT_NAME);
- }
-
- /**
- * Uses the {@code settings} binary to reset the autofill service.
- */
- protected void resetService() {
- SettingsHelper.syncDelete(InstrumentationRegistry.getTargetContext(),
- SettingsHelper.NAMESPACE_SECURE,
- Settings.Secure.AUTOFILL_SERVICE);
- }
}
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/AutofillTestHelper.java b/apct-tests/perftests/autofill/src/android/view/autofill/AutofillTestHelper.java
new file mode 100644
index 0000000..0763729
--- /dev/null
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/AutofillTestHelper.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.autofill;
+
+import android.app.assist.AssistStructure;
+import android.app.assist.AssistStructure.ViewNode;
+import android.app.assist.AssistStructure.WindowNode;
+import android.service.autofill.FillContext;
+import android.util.Log;
+
+import java.util.List;
+
+/**
+ * Helper for common funcionalities.
+ */
+public class AutofillTestHelper {
+ private static final String TAG = "AutofillTestHelper";
+
+ /**
+ * Gets a node given its Android resource id, or {@code null} if not found.
+ */
+ public static ViewNode findNodeByResourceId(List<FillContext> contexts, String resourceId) {
+ for (FillContext context : contexts) {
+ ViewNode node = findNodeByResourceId(context.getStructure(), resourceId);
+ if (node != null) {
+ return node;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Gets a node if it matches the filter criteria for the given id.
+ */
+ private static ViewNode findNodeByResourceId(AssistStructure structure, String id) {
+ Log.v(TAG, "Parsing request for activity " + structure.getActivityComponent());
+ final int nodes = structure.getWindowNodeCount();
+ for (int i = 0; i < nodes; i++) {
+ final WindowNode windowNode = structure.getWindowNodeAt(i);
+ final ViewNode rootNode = windowNode.getRootViewNode();
+ final ViewNode node = findNodeByResourceId(rootNode, id);
+ if (node != null) {
+ return node;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Gets a node if it matches the filter criteria for the given id.
+ */
+ private static ViewNode findNodeByResourceId(ViewNode node, String id) {
+ if (id.equals(node.getIdEntry())) {
+ return node;
+ }
+ final int childrenSize = node.getChildCount();
+ if (childrenSize > 0) {
+ for (int i = 0; i < childrenSize; i++) {
+ final ViewNode found = findNodeByResourceId(node.getChildAt(i), id);
+ if (found != null) {
+ return found;
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/AutofillTestWatcher.java b/apct-tests/perftests/autofill/src/android/view/autofill/AutofillTestWatcher.java
new file mode 100644
index 0000000..f65067f
--- /dev/null
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/AutofillTestWatcher.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.autofill;
+
+import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+
+import android.perftests.utils.SettingsHelper;
+import android.provider.Settings;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.Timeout;
+
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Custom {@link TestWatcher} that does the setup and reset tasks for the tests.
+ */
+final class AutofillTestWatcher extends TestWatcher {
+
+ private static final String TAG = "AutofillTestWatcher";
+ private static final long GENERIC_TIMEOUT_MS = 10_000;
+
+ private static ServiceWatcher sServiceWatcher;
+
+ private String mOriginalLogLevel;
+
+ @Override
+ protected void starting(Description description) {
+ super.starting(description);
+ final String testName = description.getDisplayName();
+ Log.i(TAG, "Starting " + testName);
+
+ prepareDevice();
+ enableVerboseLog();
+ // Prepare the service before each test.
+ // Disable the current AutofillService.
+ resetAutofillService();
+ // Set MyAutofillService status enable, it can start to accept the calls.
+ enableMyAutofillService();
+ setServiceWatcher();
+ }
+
+ @Override
+ protected void finished(Description description) {
+ super.finished(description);
+ final String testName = description.getDisplayName();
+ Log.i(TAG, "Finished " + testName);
+ restoreLogLevel();
+ // Set MyAutofillService status disable, so the calls are ignored.
+ disableMyAutofillService();
+ clearServiceWatcher();
+ }
+
+ void waitServiceConnect() throws InterruptedException {
+ if (sServiceWatcher != null) {
+ Log.d(TAG, "waitServiceConnect()");
+ sServiceWatcher.waitOnConnected();
+ }
+ }
+
+ /**
+ * Uses the {@code settings} binary to set the autofill service.
+ */
+ void setAutofillService() {
+ String serviceName = MyAutofillService.COMPONENT_NAME;
+ SettingsHelper.syncSet(InstrumentationRegistry.getTargetContext(),
+ SettingsHelper.NAMESPACE_SECURE,
+ Settings.Secure.AUTOFILL_SERVICE,
+ serviceName);
+ // Waits until the service is actually enabled.
+ Timeout timeout = new Timeout("CONNECTION_TIMEOUT", GENERIC_TIMEOUT_MS, 2F,
+ GENERIC_TIMEOUT_MS);
+ try {
+ timeout.run("Enabling Autofill service", () -> {
+ return isAutofillServiceEnabled(serviceName) ? serviceName : null;
+ });
+ } catch (Exception e) {
+ throw new AssertionError("Enabling Autofill service failed.");
+ }
+ }
+
+ /**
+ * Uses the {@code settings} binary to reset the autofill service.
+ */
+ void resetAutofillService() {
+ SettingsHelper.syncDelete(InstrumentationRegistry.getTargetContext(),
+ SettingsHelper.NAMESPACE_SECURE,
+ Settings.Secure.AUTOFILL_SERVICE);
+ }
+
+ /**
+ * Checks whether the given service is set as the autofill service for the default user.
+ */
+ private boolean isAutofillServiceEnabled(String serviceName) {
+ String actualName = SettingsHelper.get(SettingsHelper.NAMESPACE_SECURE,
+ Settings.Secure.AUTOFILL_SERVICE);
+ return serviceName.equals(actualName);
+ }
+
+ private void prepareDevice() {
+ // Unlock screen.
+ runShellCommand("input keyevent KEYCODE_WAKEUP");
+
+ // Dismiss keyguard, in case it's set as "Swipe to unlock".
+ runShellCommand("wm dismiss-keyguard");
+
+ // Collapse notifications.
+ runShellCommand("cmd statusbar collapse");
+ }
+
+ private void enableMyAutofillService() {
+ MyAutofillService.resetStaticState();
+ MyAutofillService.setEnabled(true);
+ }
+
+ private void disableMyAutofillService() {
+ // Must disable service so calls are ignored in case of errors during the test case;
+ // otherwise, other tests will fail because these calls are made in the UI thread (as both
+ // the service, the tests, and the app run in the same process).
+ MyAutofillService.setEnabled(false);
+ }
+
+ private void enableVerboseLog() {
+ mOriginalLogLevel = runShellCommand("cmd autofill get log_level");
+ Log.d(TAG, "enableVerboseLog(), mOriginalLogLevel=" + mOriginalLogLevel);
+ if (!mOriginalLogLevel.equals("verbose")) {
+ runShellCommand("cmd autofill set log_level verbose");
+ }
+ }
+
+ private void restoreLogLevel() {
+ Log.d(TAG, "restoreLogLevel to " + mOriginalLogLevel);
+ if (!mOriginalLogLevel.equals("verbose")) {
+ runShellCommand("cmd autofill set log_level %s", mOriginalLogLevel);
+ }
+ }
+
+ private static void setServiceWatcher() {
+ if (sServiceWatcher == null) {
+ sServiceWatcher = new ServiceWatcher();
+ }
+ }
+
+ private static void clearServiceWatcher() {
+ if (sServiceWatcher != null) {
+ sServiceWatcher = null;
+ }
+ }
+
+ public static final class ServiceWatcher {
+ private final CountDownLatch mConnected = new CountDownLatch(1);
+
+ public static void onConnected() {
+ Log.i(TAG, "onConnected: sServiceWatcher=" + sServiceWatcher);
+
+ sServiceWatcher.mConnected.countDown();
+ }
+
+ @NonNull
+ public void waitOnConnected() throws InterruptedException {
+ await(mConnected, "not connected");
+ }
+
+ private void await(@NonNull CountDownLatch latch, @NonNull String fmt,
+ @Nullable Object... args)
+ throws InterruptedException {
+ final boolean called = latch.await(GENERIC_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ if (!called) {
+ throw new IllegalStateException(String.format(fmt, args)
+ + " in " + GENERIC_TIMEOUT_MS + "ms");
+ }
+ }
+ }
+}
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java b/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
index fb5ea80..a5d1e00 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
@@ -11,7 +11,7 @@
* 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
+ * limitations under the License.
*/
package android.view.autofill;
@@ -24,12 +24,18 @@
import android.view.View;
import android.widget.EditText;
+import androidx.test.filters.LargeTest;
+
import com.android.perftests.autofill.R;
import org.junit.Test;
+@LargeTest
public class LoginTest extends AbstractAutofillPerfTestCase {
+ public static final String ID_USERNAME = "username";
+ public static final String ID_PASSWORD = "password";
+
private EditText mUsername;
private EditText mPassword;
private AutofillManager mAfm;
@@ -51,15 +57,15 @@
*/
@Test
public void testFocus_noService() throws Throwable {
- resetService();
+ mTestWatcher.resetAutofillService();
- mActivityRule.runOnUiThread(() -> {
- BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- while (state.keepRunning()) {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mActivityRule.runOnUiThread(() -> {
mUsername.requestFocus();
mPassword.requestFocus();
- }
- });
+ });
+ }
}
/**
@@ -69,22 +75,23 @@
@Test
public void testFocus_serviceDoesNotAutofill() throws Throwable {
MyAutofillService.newCannedResponse().reply();
- setService();
+ mTestWatcher.setAutofillService();
// Must first focus in a field to trigger autofill and wait for service response
// outside the loop
mActivityRule.runOnUiThread(() -> mUsername.requestFocus());
+ mTestWatcher.waitServiceConnect();
MyAutofillService.getLastFillRequest();
// Then focus on password so loop start with focus away from username
mActivityRule.runOnUiThread(() -> mPassword.requestFocus());
- mActivityRule.runOnUiThread(() -> {
- BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- while (state.keepRunning()) {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mActivityRule.runOnUiThread(() -> {
mUsername.requestFocus();
mPassword.requestFocus();
- }
- });
+ });
+ }
}
/**
@@ -93,10 +100,10 @@
@Test
public void testFocus_autofillBothFields() throws Throwable {
MyAutofillService.newCannedResponse()
- .setUsername(mUsername.getAutofillId(), "user")
- .setPassword(mPassword.getAutofillId(), "pass")
+ .setUsername(ID_USERNAME, "user")
+ .setPassword(ID_PASSWORD, "pass")
.reply();
- setService();
+ mTestWatcher.setAutofillService();
// Callback is used to slow down the calls made to the autofill server so the
// app is not crashed due to binder exhaustion. But the time spent waiting for the callbacks
@@ -106,6 +113,7 @@
// Must first trigger autofill and wait for service response outside the loop
mActivityRule.runOnUiThread(() -> mUsername.requestFocus());
+ mTestWatcher.waitServiceConnect();
MyAutofillService.getLastFillRequest();
callback.expectEvent(mUsername, EVENT_INPUT_SHOWN);
@@ -146,10 +154,10 @@
public void testFocus_autofillUsernameOnly() throws Throwable {
// Must set ignored ids so focus on password does not trigger new requests
MyAutofillService.newCannedResponse()
- .setUsername(mUsername.getAutofillId(), "user")
- .setIgnored(mPassword.getAutofillId())
+ .setUsername(ID_USERNAME, "user")
+ .setIgnored(ID_PASSWORD)
.reply();
- setService();
+ mTestWatcher.setAutofillService();
// Callback is used to slow down the calls made to the autofill server so the
// app is not crashed due to binder exhaustion. But the time spent waiting for the callbacks
@@ -159,6 +167,7 @@
// Must first trigger autofill and wait for service response outside the loop
mActivityRule.runOnUiThread(() -> mUsername.requestFocus());
+ mTestWatcher.waitServiceConnect();
MyAutofillService.getLastFillRequest();
callback.expectEvent(mUsername, EVENT_INPUT_SHOWN);
@@ -193,7 +202,7 @@
*/
@Test
public void testChange_noService() throws Throwable {
- resetService();
+ mTestWatcher.resetAutofillService();
changeTest(false);
}
@@ -205,7 +214,7 @@
@Test
public void testChange_serviceDoesNotAutofill() throws Throwable {
MyAutofillService.newCannedResponse().reply();
- setService();
+ mTestWatcher.setAutofillService();
changeTest(true);
}
@@ -216,10 +225,10 @@
@Test
public void testChange_autofillBothFields() throws Throwable {
MyAutofillService.newCannedResponse()
- .setUsername(mUsername.getAutofillId(), "user")
- .setPassword(mPassword.getAutofillId(), "pass")
+ .setUsername(ID_USERNAME, "user")
+ .setPassword(ID_PASSWORD, "pass")
.reply();
- setService();
+ mTestWatcher.setAutofillService();
changeTest(true);
}
@@ -231,10 +240,10 @@
public void testChange_autofillUsernameOnly() throws Throwable {
// Must set ignored ids so focus on password does not trigger new requests
MyAutofillService.newCannedResponse()
- .setUsername(mUsername.getAutofillId(), "user")
- .setIgnored(mPassword.getAutofillId())
+ .setUsername(ID_USERNAME, "user")
+ .setIgnored(ID_PASSWORD)
.reply();
- setService();
+ mTestWatcher.setAutofillService();
changeTest(true);
}
@@ -244,27 +253,27 @@
// outside the loop
mActivityRule.runOnUiThread(() -> mUsername.requestFocus());
if (waitForService) {
+ mTestWatcher.waitServiceConnect();
MyAutofillService.getLastFillRequest();
}
- mActivityRule.runOnUiThread(() -> {
-
- BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- while (state.keepRunning()) {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mActivityRule.runOnUiThread(() -> {
mUsername.setText("");
mUsername.setText("a");
mPassword.setText("");
mPassword.setText("x");
- }
- });
+ });
+ }
}
@Test
public void testCallbacks() throws Throwable {
MyAutofillService.newCannedResponse()
- .setUsername(mUsername.getAutofillId(), "user")
- .setPassword(mPassword.getAutofillId(), "pass")
+ .setUsername(ID_USERNAME, "user")
+ .setPassword(ID_PASSWORD, "pass")
.reply();
- setService();
+ mTestWatcher.setAutofillService();
MyAutofillCallback callback = new MyAutofillCallback();
mAfm.registerCallback(callback);
@@ -272,6 +281,7 @@
// Must first focus in a field to trigger autofill and wait for service response
// outside the loop
mActivityRule.runOnUiThread(() -> mUsername.requestFocus());
+ mTestWatcher.waitServiceConnect();
MyAutofillService.getLastFillRequest();
callback.expectEvent(mUsername, EVENT_INPUT_SHOWN);
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/MyAutofillService.java b/apct-tests/perftests/autofill/src/android/view/autofill/MyAutofillService.java
index 7060233..5db6597 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/MyAutofillService.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/MyAutofillService.java
@@ -11,10 +11,11 @@
* 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
+ * limitations under the License.
*/
package android.view.autofill;
+import android.app.assist.AssistStructure.ViewNode;
import android.os.CancellationSignal;
import android.service.autofill.AutofillService;
import android.service.autofill.Dataset;
@@ -28,12 +29,9 @@
import android.widget.RemoteViews;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import com.android.perftests.autofill.R;
-import java.util.ArrayList;
-import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -44,7 +42,7 @@
public class MyAutofillService extends AutofillService {
private static final String TAG = "MyAutofillService";
- private static final int TIMEOUT_MS = 5000;
+ private static final int TIMEOUT_MS = 5_000;
private static final String PACKAGE_NAME = "com.android.perftests.autofill";
static final String COMPONENT_NAME = PACKAGE_NAME + "/android.view.autofill.MyAutofillService";
@@ -56,6 +54,14 @@
private static boolean sEnabled;
/**
+ * Returns the TestWatcher that was used for the testing.
+ */
+ @NonNull
+ public static AutofillTestWatcher getTestWatcher() {
+ return new AutofillTestWatcher();
+ }
+
+ /**
* Resets the static state associated with the service.
*/
static void resetStaticState() {
@@ -93,6 +99,11 @@
}
@Override
+ public void onConnected() {
+ AutofillTestWatcher.ServiceWatcher.onConnected();
+ }
+
+ @Override
public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal,
FillCallback callback) {
try {
@@ -120,20 +131,27 @@
boolean hasData = false;
if (response.mUsername != null) {
hasData = true;
- dataset.setValue(response.mUsername.first,
- AutofillValue.forText(response.mUsername.second));
+ AutofillId autofillId = getAutofillIdByResourceId(request, response.mUsername.first);
+ AutofillValue value = AutofillValue.forText(response.mUsername.second);
+ dataset.setValue(autofillId, value, newDatasetPresentation("dataset"));
}
if (response.mPassword != null) {
hasData = true;
- dataset.setValue(response.mPassword.first,
- AutofillValue.forText(response.mPassword.second));
+ AutofillId autofillId = getAutofillIdByResourceId(request, response.mPassword.first);
+ AutofillValue value = AutofillValue.forText(response.mPassword.second);
+ dataset.setValue(autofillId, value, newDatasetPresentation("dataset"));
}
if (hasData) {
FillResponse.Builder fillResponse = new FillResponse.Builder();
if (response.mIgnoredIds != null) {
- fillResponse.setIgnoredIds(response.mIgnoredIds);
+ int length = response.mIgnoredIds.length;
+ AutofillId[] requiredIds = new AutofillId[length];
+ for (int i = 0; i < length; i++) {
+ String resourceId = response.mIgnoredIds[i];
+ requiredIds[i] = getAutofillIdByResourceId(request, resourceId);
+ }
+ fillResponse.setIgnoredIds(requiredIds);
}
-
callback.onSuccess(fillResponse.addDataset(dataset.build()).build());
} else {
callback.onSuccess(null);
@@ -143,6 +161,16 @@
}
}
+ private AutofillId getAutofillIdByResourceId(FillRequest request, String resourceId)
+ throws Exception {
+ ViewNode node = AutofillTestHelper.findNodeByResourceId(request.getFillContexts(),
+ resourceId);
+ if (node == null) {
+ throw new AssertionError("No node with resource id " + resourceId);
+ }
+ return node.getAutofillId();
+ }
+
@Override
public void onSaveRequest(SaveRequest request, SaveCallback callback) {
// No current test should have triggered it...
@@ -151,9 +179,9 @@
}
static final class CannedResponse {
- private final Pair<AutofillId, String> mUsername;
- private final Pair<AutofillId, String> mPassword;
- private final AutofillId[] mIgnoredIds;
+ private final Pair<String, String> mUsername;
+ private final Pair<String, String> mPassword;
+ private final String[] mIgnoredIds;
private CannedResponse(@NonNull Builder builder) {
mUsername = builder.mUsername;
@@ -162,24 +190,24 @@
}
static class Builder {
- private Pair<AutofillId, String> mUsername;
- private Pair<AutofillId, String> mPassword;
- private AutofillId[] mIgnoredIds;
+ private Pair<String, String> mUsername;
+ private Pair<String, String> mPassword;
+ private String[] mIgnoredIds;
@NonNull
- Builder setUsername(@NonNull AutofillId id, @NonNull String value) {
+ Builder setUsername(@NonNull String id, @NonNull String value) {
mUsername = new Pair<>(id, value);
return this;
}
@NonNull
- Builder setPassword(@NonNull AutofillId id, @NonNull String value) {
+ Builder setPassword(@NonNull String id, @NonNull String value) {
mPassword = new Pair<>(id, value);
return this;
}
@NonNull
- Builder setIgnored(AutofillId... ids) {
+ Builder setIgnored(String... ids) {
mIgnoredIds = ids;
return this;
}
diff --git a/apct-tests/perftests/blobstore/Android.bp b/apct-tests/perftests/blobstore/Android.bp
index be5072c..25c4250 100644
--- a/apct-tests/perftests/blobstore/Android.bp
+++ b/apct-tests/perftests/blobstore/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // 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"],
+}
+
android_test {
name: "BlobStorePerfTests",
srcs: ["src/**/*.java"],
@@ -21,8 +30,9 @@
"androidx.annotation_annotation",
"apct-perftests-utils",
"ub-uiautomator",
+ "collector-device-lib-platform",
],
platform_apis: true,
test_suites: ["device-tests"],
certificate: "platform",
-}
\ No newline at end of file
+}
diff --git a/apct-tests/perftests/blobstore/AndroidTest.xml b/apct-tests/perftests/blobstore/AndroidTest.xml
index 19456c6..58761d0 100644
--- a/apct-tests/perftests/blobstore/AndroidTest.xml
+++ b/apct-tests/perftests/blobstore/AndroidTest.xml
@@ -24,5 +24,22 @@
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.perftests.blob" />
<option name="hidden-api-checks" value="false"/>
+
+ <!-- TODO: Add PerfettoListener to automatically capture perfetto traces for each test-->
+ <!-- Listener related args for collecting the traces and waiting for the device
+ to stabilize. -->
+ <option name="device-listeners"
+ value="android.device.collectors.ProcLoadListener" />
+ <!-- Guarantee that user defined RunListeners will be running before any of the default
+ listeners defined in this runner. -->
+ <option name="instrumentation-arg" key="newRunListenerMode" value="true" />
+
+ <!-- ProcLoadListener related arguments -->
+ <!-- Wait for device last minute threshold to reach 3 with 2 minute timeout before
+ starting the test run -->
+ <option name="instrumentation-arg" key="procload-collector:per_run" value="true" />
+ <option name="instrumentation-arg" key="proc-loadavg-threshold" value="3" />
+ <option name="instrumentation-arg" key="proc-loadavg-timeout" value="120000" />
+ <option name="instrumentation-arg" key="proc-loadavg-interval" value="10000" />
</test>
</configuration>
\ No newline at end of file
diff --git a/apct-tests/perftests/blobstore/OWNERS b/apct-tests/perftests/blobstore/OWNERS
new file mode 100644
index 0000000..65bb6b8
--- /dev/null
+++ b/apct-tests/perftests/blobstore/OWNERS
@@ -0,0 +1 @@
+include /apex/blobstore/OWNERS
diff --git a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
index 23f025b..5a04ba3 100644
--- a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
+++ b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
@@ -27,7 +27,7 @@
import androidx.test.filters.LargeTest;
import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.utils.blob.DummyBlobData;
+import com.android.utils.blob.FakeBlobData;
import org.junit.After;
import org.junit.Before;
@@ -96,7 +96,7 @@
mAtraceUtils.startTrace(ATRACE_CATEGORY_SYSTEM_SERVER);
try {
final List<Long> durations = new ArrayList<>();
- final DummyBlobData blobData = prepareDataBlob(fileSizeInMb);
+ final FakeBlobData blobData = prepareDataBlob(fileSizeInMb);
final TraceMarkParser parser = new TraceMarkParser(
line -> line.name.startsWith(ATRACE_COMPUTE_DIGEST_PREFIX));
while (mState.keepRunning(durations)) {
@@ -120,15 +120,15 @@
});
}
- private DummyBlobData prepareDataBlob(int fileSizeInMb) throws Exception {
- final DummyBlobData blobData = new DummyBlobData.Builder(mContext)
+ private FakeBlobData prepareDataBlob(int fileSizeInMb) throws Exception {
+ final FakeBlobData blobData = new FakeBlobData.Builder(mContext)
.setFileSize(fileSizeInMb * 1024 * 1024 /* bytes */)
.build();
blobData.prepare();
return blobData;
}
- private void commitBlob(DummyBlobData blobData) throws Exception {
+ private void commitBlob(FakeBlobData blobData) throws Exception {
final long sessionId = mBlobStoreManager.createSession(blobData.getBlobHandle());
try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
blobData.writeToSession(session);
diff --git a/apct-tests/perftests/contentcapture/Android.bp b/apct-tests/perftests/contentcapture/Android.bp
new file mode 100644
index 0000000..638403d
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/Android.bp
@@ -0,0 +1,38 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // 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"],
+}
+
+android_test {
+ name: "ContentCapturePerfTests",
+ srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
+ static_libs: [
+ "androidx.test.rules",
+ "androidx.annotation_annotation",
+ "apct-perftests-utils",
+ "collector-device-lib",
+ "compatibility-device-util-axt",
+ ],
+ platform_apis: true,
+ test_suites: ["device-tests"],
+ data: [":perfetto_artifacts"],
+}
diff --git a/apct-tests/perftests/contentcapture/AndroidManifest.xml b/apct-tests/perftests/contentcapture/AndroidManifest.xml
new file mode 100644
index 0000000..80957c7
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.perftests.contentcapture">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.view.contentcapture.CustomTestActivity"
+ android:exported="true">
+ </activity>
+
+ <service
+ android:name="android.view.contentcapture.MyContentCaptureService"
+ android:label="PERF ContentCaptureService"
+ android:permission="android.permission.BIND_CONTENT_CAPTURE_SERVICE"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.service.contentcapture.ContentCaptureService" />
+ </intent-filter>
+ </service>
+
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.perftests.contentcapture" />
+</manifest>
diff --git a/apct-tests/perftests/contentcapture/AndroidTest.xml b/apct-tests/perftests/contentcapture/AndroidTest.xml
new file mode 100644
index 0000000..d8e0a17
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/AndroidTest.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs ContentCapturePerfTests metric instrumentation.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-metric-instrumentation" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="ContentCapturePerfTests.apk" />
+ </target_preparer>
+
+ <!-- Needed for pushing the trace config file -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="push-file" key="trace_config_detailed.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" />
+ </target_preparer>
+
+ <!-- Needed for pulling the collected trace config on to the host -->
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="pull-pattern-keys" value="perfetto_file_path" />
+ </metrics_collector>
+
+ <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+ <option name="isolated-storage" value="false" />
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.perftests.contentcapture" />
+
+ <!-- Listener related args for collecting the traces and waiting for the device to stabilize. -->
+ <option name="device-listeners" value="android.device.collectors.ProcLoadListener,android.device.collectors.PerfettoListener" />
+ <!-- Guarantee that user defined RunListeners will be running before any of the default listeners defined in this runner. -->
+ <option name="instrumentation-arg" key="newRunListenerMode" value="true" />
+
+ <!-- ProcLoadListener related arguments -->
+ <!-- Wait for device last minute threshold to reach 3 with 2 minute timeout before starting the test run -->
+ <option name="instrumentation-arg" key="procload-collector:per_run" value="true" />
+ <option name="instrumentation-arg" key="proc-loadavg-threshold" value="3" />
+ <option name="instrumentation-arg" key="proc-loadavg-timeout" value="120000" />
+ <option name="instrumentation-arg" key="proc-loadavg-interval" value="10000" />
+
+ <!-- PerfettoListener related arguments -->
+ <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" />
+ <option name="instrumentation-arg" key="perfetto_config_file" value="trace_config.textproto" />
+
+ <option name="instrumentation-arg" key="newRunListenerMode" value="true" />
+ </test>
+</configuration>
diff --git a/apct-tests/perftests/contentcapture/OWNERS b/apct-tests/perftests/contentcapture/OWNERS
new file mode 100644
index 0000000..a28e00a
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/service/contentcapture/OWNERS
diff --git a/apct-tests/perftests/contentcapture/res/layout/test_container_activity.xml b/apct-tests/perftests/contentcapture/res/layout/test_container_activity.xml
new file mode 100644
index 0000000..ca1a11a
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/res/layout/test_container_activity.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/root_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:orientation="vertical" >
+</LinearLayout>
diff --git a/apct-tests/perftests/contentcapture/res/layout/test_login_activity.xml b/apct-tests/perftests/contentcapture/res/layout/test_login_activity.xml
new file mode 100644
index 0000000..9bab32c
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/res/layout/test_login_activity.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/root_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:orientation="vertical" >
+
+ <TextView
+ android:id="@+id/username_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Username" />
+
+ <EditText
+ android:id="@+id/username"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <TextView
+ android:id="@+id/password_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Password" />
+
+ <EditText
+ android:id="@+id/password"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textPassword" />
+
+</LinearLayout>
diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java
new file mode 100644
index 0000000..9b853fe
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.contentcapture;
+
+import static android.view.contentcapture.CustomTestActivity.INTENT_EXTRA_CUSTOM_VIEWS;
+import static android.view.contentcapture.CustomTestActivity.INTENT_EXTRA_LAYOUT_ID;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+
+import android.app.Application;
+import android.content.ContentCaptureOptions;
+import android.content.Context;
+import android.content.Intent;
+import android.os.BatteryManager;
+import android.os.UserHandle;
+import android.perftests.utils.PerfStatusReporter;
+import android.provider.Settings;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.compatibility.common.util.ActivitiesWatcher;
+import com.android.compatibility.common.util.ActivitiesWatcher.ActivityWatcher;
+import com.android.perftests.contentcapture.R;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.rules.TestRule;
+import org.junit.runners.model.Statement;
+
+/**
+ * Base class for all content capture tests.
+ */
+public abstract class AbstractContentCapturePerfTestCase {
+
+ private static final String TAG = AbstractContentCapturePerfTestCase.class.getSimpleName();
+ private static final long GENERIC_TIMEOUT_MS = 10_000;
+
+ private static int sOriginalStayOnWhilePluggedIn;
+ private static Context sContext = getInstrumentation().getTargetContext();
+
+ protected ActivitiesWatcher mActivitiesWatcher;
+
+ private MyContentCaptureService.ServiceWatcher mServiceWatcher;
+
+ @Rule
+ public ActivityTestRule<CustomTestActivity> mActivityRule =
+ new ActivityTestRule<>(CustomTestActivity.class, false, false);
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Rule
+ public TestRule mServiceDisablerRule = (base, description) -> {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ try {
+ base.evaluate();
+ } finally {
+ Log.v(TAG, "@mServiceDisablerRule: safelyDisableService()");
+ safelyDisableService();
+ }
+ }
+ };
+ };
+
+ private void safelyDisableService() {
+ try {
+ resetService();
+ MyContentCaptureService.resetStaticState();
+
+ if (mServiceWatcher != null) {
+ mServiceWatcher.waitOnDestroy();
+ }
+ } catch (Throwable t) {
+ Log.e(TAG, "error disabling service", t);
+ }
+ }
+
+ /**
+ * Sets the content capture service.
+ */
+ private static void setService(@NonNull String service) {
+ final int userId = getCurrentUserId();
+ Log.d(TAG, "Setting service for user " + userId + " to " + service);
+ // TODO(b/123540602): use @TestingAPI to get max duration constant
+ runShellCommand("cmd content_capture set temporary-service %d %s 119000", userId, service);
+ }
+
+ /**
+ * Resets the content capture service.
+ */
+ private static void resetService() {
+ final int userId = getCurrentUserId();
+ Log.d(TAG, "Resetting back user " + userId + " to default service");
+ runShellCommand("cmd content_capture set temporary-service %d", userId);
+ }
+
+ private static int getCurrentUserId() {
+ return UserHandle.myUserId();
+ }
+
+ @BeforeClass
+ public static void setStayAwake() {
+ Log.v(TAG, "@BeforeClass: setStayAwake()");
+ // Some test cases will restart the activity, and stay awake is necessary to ensure that
+ // the test will not screen off during the test.
+ // Keeping the activity screen on is not enough, screen off may occur between the activity
+ // finished and the next start
+ final int stayOnWhilePluggedIn = Settings.Global.getInt(sContext.getContentResolver(),
+ Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
+ sOriginalStayOnWhilePluggedIn = -1;
+ if (stayOnWhilePluggedIn != BatteryManager.BATTERY_PLUGGED_ANY) {
+ sOriginalStayOnWhilePluggedIn = stayOnWhilePluggedIn;
+ // Keep the device awake during testing.
+ setStayOnWhilePluggedIn(BatteryManager.BATTERY_PLUGGED_ANY);
+ }
+ }
+
+ @AfterClass
+ public static void resetStayAwake() {
+ Log.v(TAG, "@AfterClass: resetStayAwake()");
+ if (sOriginalStayOnWhilePluggedIn != -1) {
+ setStayOnWhilePluggedIn(sOriginalStayOnWhilePluggedIn);
+ }
+ }
+
+ private static void setStayOnWhilePluggedIn(int value) {
+ runShellCommand(String.format("settings put global %s %d",
+ Settings.Global.STAY_ON_WHILE_PLUGGED_IN, value));
+ }
+
+ @BeforeClass
+ public static void setAllowSelf() {
+ final ContentCaptureOptions options = new ContentCaptureOptions(null);
+ Log.v(TAG, "@BeforeClass: setAllowSelf(): options=" + options);
+ sContext.getApplicationContext().setContentCaptureOptions(options);
+ }
+
+ @AfterClass
+ public static void unsetAllowSelf() {
+ Log.v(TAG, "@AfterClass: unsetAllowSelf()");
+ clearOptions();
+ }
+
+ protected static void clearOptions() {
+ sContext.getApplicationContext().setContentCaptureOptions(null);
+ }
+
+ @BeforeClass
+ public static void disableDefaultService() {
+ Log.v(TAG, "@BeforeClass: disableDefaultService()");
+ setDefaultServiceEnabled(false);
+ }
+
+ @AfterClass
+ public static void enableDefaultService() {
+ Log.v(TAG, "@AfterClass: enableDefaultService()");
+ setDefaultServiceEnabled(true);
+ }
+
+ /**
+ * Enables / disables the default service.
+ */
+ private static void setDefaultServiceEnabled(boolean enabled) {
+ final int userId = getCurrentUserId();
+ Log.d(TAG, "setDefaultServiceEnabled(user=" + userId + ", enabled= " + enabled + ")");
+ runShellCommand("cmd content_capture set default-service-enabled %d %s", userId,
+ Boolean.toString(enabled));
+ }
+
+ @Before
+ public void prepareDevice() throws Exception {
+ Log.v(TAG, "@Before: prepareDevice()");
+
+ // Unlock screen.
+ runShellCommand("input keyevent KEYCODE_WAKEUP");
+
+ // Dismiss keyguard, in case it's set as "Swipe to unlock".
+ runShellCommand("wm dismiss-keyguard");
+
+ // Collapse notifications.
+ runShellCommand("cmd statusbar collapse");
+ }
+
+ @Before
+ public void registerLifecycleCallback() {
+ Log.v(TAG, "@Before: Registering lifecycle callback");
+ final Application app = (Application) sContext.getApplicationContext();
+ mActivitiesWatcher = new ActivitiesWatcher(GENERIC_TIMEOUT_MS);
+ app.registerActivityLifecycleCallbacks(mActivitiesWatcher);
+ }
+
+ @After
+ public void unregisterLifecycleCallback() {
+ Log.d(TAG, "@After: Unregistering lifecycle callback: " + mActivitiesWatcher);
+ if (mActivitiesWatcher != null) {
+ final Application app = (Application) sContext.getApplicationContext();
+ app.unregisterActivityLifecycleCallbacks(mActivitiesWatcher);
+ }
+ }
+
+ /**
+ * Sets {@link MyContentCaptureService} as the service for the current user and waits until
+ * its created, then add the perf test package into allow list.
+ */
+ public MyContentCaptureService enableService() throws InterruptedException {
+ if (mServiceWatcher != null) {
+ throw new IllegalStateException("There Can Be Only One!");
+ }
+
+ mServiceWatcher = MyContentCaptureService.setServiceWatcher();
+ setService(MyContentCaptureService.SERVICE_NAME);
+ mServiceWatcher.setAllowSelf();
+ return mServiceWatcher.waitOnCreate();
+ }
+
+ @NonNull
+ protected ActivityWatcher startWatcher() {
+ return mActivitiesWatcher.watch(CustomTestActivity.class);
+ }
+
+ /**
+ * Launch test activity with default login layout
+ */
+ protected CustomTestActivity launchActivity() {
+ return launchActivity(R.layout.test_login_activity, 0);
+ }
+
+ /**
+ * Launch test activity with give layout and parameter
+ */
+ protected CustomTestActivity launchActivity(int layoutId, int numViews) {
+ final Intent intent = new Intent(sContext, CustomTestActivity.class);
+ intent.putExtra(INTENT_EXTRA_LAYOUT_ID, layoutId);
+ intent.putExtra(INTENT_EXTRA_CUSTOM_VIEWS, numViews);
+ return mActivityRule.launchActivity(intent);
+ }
+
+ protected void finishActivity() {
+ try {
+ mActivityRule.finishActivity();
+ } catch (IllegalStateException e) {
+ // no op
+ }
+ }
+}
diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/CustomTestActivity.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/CustomTestActivity.java
new file mode 100644
index 0000000..e509837
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/CustomTestActivity.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.contentcapture;
+
+import android.app.Activity;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.perftests.contentcapture.R;
+
+/**
+ * A simple activity used for testing, e.g. performance of activity switching, or as a base
+ * container of testing view.
+ */
+public class CustomTestActivity extends Activity {
+ public static final String INTENT_EXTRA_LAYOUT_ID = "layout_id";
+ public static final String INTENT_EXTRA_CUSTOM_VIEWS = "custom_view_number";
+ public static final int MAX_VIEWS = 500;
+ private static final int CUSTOM_CONTAINER_LAYOUT_ID = R.layout.test_container_activity;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (getIntent().hasExtra(INTENT_EXTRA_LAYOUT_ID)) {
+ final int layoutId = getIntent().getIntExtra(INTENT_EXTRA_LAYOUT_ID,
+ /* defaultValue= */0);
+ setContentView(layoutId);
+ if (layoutId == CUSTOM_CONTAINER_LAYOUT_ID) {
+ createCustomViews(findViewById(R.id.root_view),
+ getIntent().getIntExtra(INTENT_EXTRA_CUSTOM_VIEWS, MAX_VIEWS));
+ }
+ }
+ }
+
+ private void createCustomViews(LinearLayout root, int number) {
+ LinearLayout horizontalLayout = null;
+ for (int i = 0; i < number; i++) {
+ final int j = i % 8;
+ if (horizontalLayout != null && j == 0) {
+ root.addView(horizontalLayout);
+ horizontalLayout = null;
+ }
+ if (horizontalLayout == null) {
+ horizontalLayout = createHorizontalLayout();
+ }
+ horizontalLayout.addView(createItem(null, i));
+ }
+ if (horizontalLayout != null) {
+ root.addView(horizontalLayout);
+ }
+ }
+
+ private LinearLayout createHorizontalLayout() {
+ final LinearLayout layout = new LinearLayout(getApplicationContext());
+ layout.setOrientation(LinearLayout.HORIZONTAL);
+ return layout;
+ }
+
+ private LinearLayout createItem(Drawable drawable, int index) {
+ final LinearLayout group = new LinearLayout(getApplicationContext());
+ group.setOrientation(LinearLayout.VERTICAL);
+ group.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT, /* weight= */ 1.0f));
+
+ final TextView text = new TextView(this);
+ text.setText("i = " + index);
+ group.addView(text);
+
+ return group;
+ }
+}
diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java
new file mode 100644
index 0000000..7257509
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.contentcapture;
+
+import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.CREATED;
+import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.DESTROYED;
+
+import android.perftests.utils.BenchmarkState;
+import android.view.View;
+
+import androidx.test.filters.LargeTest;
+
+import com.android.compatibility.common.util.ActivitiesWatcher.ActivityWatcher;
+import com.android.perftests.contentcapture.R;
+
+import org.junit.Test;
+
+@LargeTest
+public class LoginTest extends AbstractContentCapturePerfTestCase {
+
+ @Test
+ public void testLaunchActivity() throws Throwable {
+ enableService();
+
+ testActivityLaunchTime(R.layout.test_login_activity, 0);
+ }
+
+ @Test
+ public void testLaunchActivity_contain100Views() throws Throwable {
+ enableService();
+
+ testActivityLaunchTime(R.layout.test_container_activity, 100);
+ }
+
+ @Test
+ public void testLaunchActivity_contain300Views() throws Throwable {
+ enableService();
+
+ testActivityLaunchTime(R.layout.test_container_activity, 300);
+ }
+
+ @Test
+ public void testLaunchActivity_contain500Views() throws Throwable {
+ enableService();
+
+ testActivityLaunchTime(R.layout.test_container_activity, 500);
+ }
+
+ @Test
+ public void testLaunchActivity_noService() throws Throwable {
+ testActivityLaunchTime(R.layout.test_login_activity, 0);
+ }
+
+ @Test
+ public void testLaunchActivity_noService_contain100Views() throws Throwable {
+ testActivityLaunchTime(R.layout.test_container_activity, 100);
+ }
+
+ @Test
+ public void testLaunchActivity_noService_contain300Views() throws Throwable {
+ testActivityLaunchTime(R.layout.test_container_activity, 300);
+ }
+
+ @Test
+ public void testLaunchActivity_noService_contain500Views() throws Throwable {
+ testActivityLaunchTime(R.layout.test_container_activity, 500);
+ }
+
+ private void testActivityLaunchTime(int layoutId, int numViews) throws Throwable {
+ final ActivityWatcher watcher = startWatcher();
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ launchActivity(layoutId, numViews);
+
+ // Ignore the time to finish the activity
+ state.pauseTiming();
+ watcher.waitFor(CREATED);
+ finishActivity();
+ watcher.waitFor(DESTROYED);
+ state.resumeTiming();
+ }
+ }
+
+ @Test
+ public void testOnVisibilityAggregated_visibleChanged() throws Throwable {
+ enableService();
+ final CustomTestActivity activity = launchActivity();
+ final View root = activity.getWindow().getDecorView();
+ final View username = root.findViewById(R.id.username);
+
+ testOnVisibilityAggregated(username);
+ }
+
+ @Test
+ public void testOnVisibilityAggregated_visibleChanged_noService() throws Throwable {
+ final CustomTestActivity activity = launchActivity();
+ final View root = activity.getWindow().getDecorView();
+ final View username = root.findViewById(R.id.username);
+
+ testOnVisibilityAggregated(username);
+ }
+
+ @Test
+ public void testOnVisibilityAggregated_visibleChanged_noOptions() throws Throwable {
+ enableService();
+ clearOptions();
+ final CustomTestActivity activity = launchActivity();
+ final View root = activity.getWindow().getDecorView();
+ final View username = root.findViewById(R.id.username);
+
+ testOnVisibilityAggregated(username);
+ }
+
+ @Test
+ public void testOnVisibilityAggregated_visibleChanged_notImportant() throws Throwable {
+ enableService();
+ final CustomTestActivity activity = launchActivity();
+ final View root = activity.getWindow().getDecorView();
+ final View username = root.findViewById(R.id.username);
+ username.setImportantForContentCapture(View.IMPORTANT_FOR_CONTENT_CAPTURE_NO);
+
+ testOnVisibilityAggregated(username);
+ }
+
+ private void testOnVisibilityAggregated(View view) throws Throwable {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ while (state.keepRunning()) {
+ // Only count the time of onVisibilityAggregated()
+ state.pauseTiming();
+ mActivityRule.runOnUiThread(() -> {
+ state.resumeTiming();
+ view.onVisibilityAggregated(false);
+ state.pauseTiming();
+ });
+ mActivityRule.runOnUiThread(() -> {
+ state.resumeTiming();
+ view.onVisibilityAggregated(true);
+ state.pauseTiming();
+ });
+ state.resumeTiming();
+ }
+ }
+}
diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java
new file mode 100644
index 0000000..d07ed37
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.contentcapture;
+
+import android.content.ComponentName;
+import android.service.contentcapture.ActivityEvent;
+import android.service.contentcapture.ContentCaptureService;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Pair;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class MyContentCaptureService extends ContentCaptureService {
+
+ private static final String TAG = MyContentCaptureService.class.getSimpleName();
+ private static final String MY_PACKAGE = "com.android.perftests.contentcapture";
+ public static final String SERVICE_NAME = MY_PACKAGE + "/"
+ + MyContentCaptureService.class.getName();
+
+ private static ServiceWatcher sServiceWatcher;
+
+ @NonNull
+ public static ServiceWatcher setServiceWatcher() {
+ if (sServiceWatcher != null) {
+ throw new IllegalStateException("There Can Be Only One!");
+ }
+ sServiceWatcher = new ServiceWatcher();
+ return sServiceWatcher;
+ }
+
+ public static void resetStaticState() {
+ sServiceWatcher = null;
+ }
+
+ private static void clearServiceWatcher() {
+ final ServiceWatcher sw = sServiceWatcher;
+ if (sw != null) {
+ if (sw.mReadyToClear) {
+ sw.mService = null;
+ sServiceWatcher = null;
+ } else {
+ sw.mReadyToClear = true;
+ }
+ }
+ }
+
+ @Override
+ public void onConnected() {
+ Log.i(TAG, "onConnected: sServiceWatcher=" + sServiceWatcher);
+
+ if (sServiceWatcher == null) {
+ Log.e(TAG, "onConnected() without a watcher");
+ return;
+ }
+
+ if (!sServiceWatcher.mReadyToClear && sServiceWatcher.mService != null) {
+ Log.e(TAG, "onConnected(): already created: " + sServiceWatcher);
+ return;
+ }
+
+ sServiceWatcher.mService = this;
+ sServiceWatcher.mCreated.countDown();
+ sServiceWatcher.mReadyToClear = false;
+ }
+
+ @Override
+ public void onDisconnected() {
+ Log.i(TAG, "onDisconnected: sServiceWatcher=" + sServiceWatcher);
+
+ if (sServiceWatcher == null) {
+ Log.e(TAG, "onDisconnected() without a watcher");
+ return;
+ }
+ if (sServiceWatcher.mService == null) {
+ Log.e(TAG, "onDisconnected(): no service on " + sServiceWatcher);
+ return;
+ }
+
+ sServiceWatcher.mDestroyed.countDown();
+ clearServiceWatcher();
+ }
+
+ @Override
+ public void onCreateContentCaptureSession(ContentCaptureContext context,
+ ContentCaptureSessionId sessionId) {
+ Log.i(TAG, "onCreateContentCaptureSession(ctx=" + context + ", session=" + sessionId);
+ }
+
+ @Override
+ public void onDestroyContentCaptureSession(ContentCaptureSessionId sessionId) {
+ Log.i(TAG, "onDestroyContentCaptureSession(session=" + sessionId + ")");
+ }
+
+ @Override
+ public void onContentCaptureEvent(ContentCaptureSessionId sessionId,
+ ContentCaptureEvent event) {
+ Log.i(TAG, "onContentCaptureEventsRequest(session=" + sessionId + "): " + event);
+ }
+
+ @Override
+ public void onActivityEvent(ActivityEvent event) {
+ Log.i(TAG, "onActivityEvent(): " + event);
+ }
+
+ public static final class ServiceWatcher {
+
+ private static final long GENERIC_TIMEOUT_MS = 10_000;
+ private final CountDownLatch mCreated = new CountDownLatch(1);
+ private final CountDownLatch mDestroyed = new CountDownLatch(1);
+ private boolean mReadyToClear = true;
+ private Pair<Set<String>, Set<ComponentName>> mAllowList;
+
+ private MyContentCaptureService mService;
+
+ @NonNull
+ public MyContentCaptureService waitOnCreate() throws InterruptedException {
+ await(mCreated, "not created");
+
+ if (mService == null) {
+ throw new IllegalStateException("not created");
+ }
+
+ if (mAllowList != null) {
+ Log.d(TAG, "Allow after created: " + mAllowList);
+ mService.setContentCaptureWhitelist(mAllowList.first, mAllowList.second);
+ }
+
+ return mService;
+ }
+
+ public void waitOnDestroy() throws InterruptedException {
+ await(mDestroyed, "not destroyed");
+ }
+
+ /**
+ * Allow just this package.
+ */
+ public void setAllowSelf() {
+ final ArraySet<String> pkgs = new ArraySet<>(1);
+ pkgs.add(MY_PACKAGE);
+ mAllowList = new Pair<>(pkgs, null);
+ }
+
+ @Override
+ public String toString() {
+ return "mService: " + mService + " created: " + (mCreated.getCount() == 0)
+ + " destroyed: " + (mDestroyed.getCount() == 0);
+ }
+
+ /**
+ * Awaits for a latch to be counted down.
+ */
+ private static void await(@NonNull CountDownLatch latch, @NonNull String fmt,
+ @Nullable Object... args)
+ throws InterruptedException {
+ final boolean called = latch.await(GENERIC_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ if (!called) {
+ throw new IllegalStateException(String.format(fmt, args)
+ + " in " + GENERIC_TIMEOUT_MS + "ms");
+ }
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/Android.bp b/apct-tests/perftests/core/Android.bp
index 984893a..2182f0b 100644
--- a/apct-tests/perftests/core/Android.bp
+++ b/apct-tests/perftests/core/Android.bp
@@ -1,3 +1,28 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ // 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"],
+}
+
android_test {
name: "CorePerfTests",
@@ -12,23 +37,25 @@
"androidx.appcompat_appcompat",
"androidx.test.rules",
"androidx.annotation_annotation",
+ "androidx.benchmark_benchmark-common",
+ "androidx.benchmark_benchmark-junit4",
"apct-perftests-overlay-apps",
"apct-perftests-resources-manager-apps",
"apct-perftests-utils",
+ "collector-device-lib",
"guava",
],
libs: ["android.test.base"],
+ java_resources: [ ":GoogleFontDancingScript", ],
+
+ data: [":perfetto_artifacts"],
+
platform_apis: true,
jni_libs: ["libperftestscore_jni"],
- // Use google-fonts/dancing-script for the performance metrics
- // ANDROIDMK TRANSLATION ERROR: Only $(LOCAL_PATH)/.. values are allowed
- // LOCAL_ASSET_DIR := $(TOP)/external/google-fonts/dancing-script
-
test_suites: ["device-tests"],
certificate: "platform",
-
}
diff --git a/apct-tests/perftests/core/AndroidManifest.xml b/apct-tests/perftests/core/AndroidManifest.xml
index 290f178..e0c11cf 100644
--- a/apct-tests/perftests/core/AndroidManifest.xml
+++ b/apct-tests/perftests/core/AndroidManifest.xml
@@ -10,20 +10,33 @@
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.INSTALL_PACKAGES"/>
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
+ <uses-permission android:name="android.permission.VIBRATE" />
<application>
<uses-library android:name="android.test.runner" />
- <activity android:name="android.perftests.utils.PerfTestActivity">
+ <activity android:name="android.perftests.utils.PerfTestActivity"
+ android:exported="true">
<intent-filter>
<action android:name="com.android.perftests.core.PERFTEST" />
</intent-filter>
</activity>
- <service android:name="android.os.SomeService" android:exported="false" android:process=":some_service" />
+
+ <service
+ android:name="android.os.SomeService"
+ android:exported="false"
+ android:process=":some_service" />
+
+ <provider
+ android:name="android.os.SomeProvider"
+ android:authorities="android.os.SomeProvider"
+ android:exported="false"
+ android:process=":some_provider" />
<service
android:name="android.view.autofill.MyAutofillService"
android:label="PERF AutofillService"
- android:permission="android.permission.BIND_AUTOFILL_SERVICE" >
+ android:permission="android.permission.BIND_AUTOFILL_SERVICE"
+ android:exported="true">
<intent-filter>
<action android:name="android.service.autofill.AutofillService" />
</intent-filter>
@@ -31,7 +44,7 @@
</application>
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ <instrumentation android:name="androidx.benchmark.junit4.AndroidBenchmarkRunner"
android:targetPackage="com.android.perftests.core"/>
</manifest>
diff --git a/apct-tests/perftests/core/AndroidTest.xml b/apct-tests/perftests/core/AndroidTest.xml
index 1b28913..4f8ee29 100644
--- a/apct-tests/perftests/core/AndroidTest.xml
+++ b/apct-tests/perftests/core/AndroidTest.xml
@@ -16,13 +16,40 @@
<configuration description="Runs CorePerfTests metric instrumentation.">
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-metric-instrumentation" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="push-file" key="trace_config_detailed.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" />
+ </target_preparer>
+
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CorePerfTests.apk" />
</target_preparer>
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="pull-pattern-keys" value="perfetto_file_path" />
+ </metrics_collector>
+
+ <!-- Needed for storing the perfetto files in external storage-->
+ <option name="isolated-storage" value="false" />
+
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.perftests.core" />
<option name="hidden-api-checks" value="false"/>
+
+ <option name="device-listeners" value="android.device.collectors.ProcLoadListener,android.device.collectors.PerfettoListener" />
+ <!-- ProcLoadListener related arguments -->
+ <!-- Wait for device last minute threshold to reach 3 with 2 minute timeout before starting the test run -->
+ <option name="instrumentation-arg" key="procload-collector:per_run" value="true" />
+ <option name="instrumentation-arg" key="proc-loadavg-threshold" value="3" />
+ <option name="instrumentation-arg" key="proc-loadavg-timeout" value="120000" />
+ <option name="instrumentation-arg" key="proc-loadavg-interval" value="10000" />
+
+ <!-- PerfettoListener related arguments -->
+ <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" />
+ <option name="instrumentation-arg" key="perfetto_config_file" value="trace_config.textproto" />
+
+ <option name="instrumentation-arg" key="newRunListenerMode" value="true" />
</test>
</configuration>
diff --git a/apct-tests/perftests/core/OWNERS b/apct-tests/perftests/core/OWNERS
new file mode 100644
index 0000000..18486af
--- /dev/null
+++ b/apct-tests/perftests/core/OWNERS
@@ -0,0 +1 @@
+include /graphics/java/android/graphics/fonts/OWNERS
diff --git a/apct-tests/perftests/core/apps/overlay/Android.bp b/apct-tests/perftests/core/apps/overlay/Android.bp
index 7bee30e..6465307 100644
--- a/apct-tests/perftests/core/apps/overlay/Android.bp
+++ b/apct-tests/perftests/core/apps/overlay/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // 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"],
+}
+
android_test_helper_app {
name: "Overlay0",
aaptflags: [
@@ -185,4 +194,4 @@
":LargeOverlay8",
":LargeOverlay9",
],
-}
\ No newline at end of file
+}
diff --git a/apct-tests/perftests/core/apps/overlay/OWNERS b/apct-tests/perftests/core/apps/overlay/OWNERS
new file mode 100644
index 0000000..afb98d4
--- /dev/null
+++ b/apct-tests/perftests/core/apps/overlay/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/content/om/OWNERS
diff --git a/apct-tests/perftests/core/apps/reources_manager/Android.bp b/apct-tests/perftests/core/apps/reources_manager/Android.bp
index 4516132..85dd0c4 100644
--- a/apct-tests/perftests/core/apps/reources_manager/Android.bp
+++ b/apct-tests/perftests/core/apps/reources_manager/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // 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"],
+}
+
android_test_helper_app {
name: "LargeResourcesCompressed",
static_libs: [ "androidx.appcompat_appcompat" ],
@@ -31,4 +40,4 @@
":LargeResourcesCompressed",
":LargeResourcesUncompressed",
],
-}
\ No newline at end of file
+}
diff --git a/apct-tests/perftests/core/jni/Android.bp b/apct-tests/perftests/core/jni/Android.bp
index 4c0f2aa..1e4405de 100644
--- a/apct-tests/perftests/core/jni/Android.bp
+++ b/apct-tests/perftests/core/jni/Android.bp
@@ -1,3 +1,12 @@
+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"],
+}
+
cc_library_shared {
name: "libperftestscore_jni",
sdk_version: "21",
@@ -10,4 +19,5 @@
"-Wunused",
"-Wunreachable-code",
],
+ header_libs: ["jni_headers"],
}
diff --git a/apct-tests/perftests/core/src/android/accounts/OWNERS b/apct-tests/perftests/core/src/android/accounts/OWNERS
new file mode 100644
index 0000000..df1b4f4
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/accounts/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/accounts/OWNERS
diff --git a/apct-tests/perftests/core/src/android/app/OWNERS b/apct-tests/perftests/core/src/android/app/OWNERS
new file mode 100644
index 0000000..4f168ce
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/app/OWNERS
@@ -0,0 +1,2 @@
+per-file Overlay* = file:/core/java/android/app/RESOURCES_OWNERS
+per-file Resources* = file:/core/java/android/app/RESOURCES_OWNERS
diff --git a/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java b/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java
index a320514..1bb98cb 100644
--- a/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java
@@ -62,7 +62,7 @@
state.resumeTiming();
final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, mIntent,
- 0);
+ PendingIntent.FLAG_MUTABLE_UNAUDITED);
state.pauseTiming();
pendingIntent.cancel();
@@ -80,11 +80,11 @@
while (state.keepRunning()) {
state.pauseTiming();
final PendingIntent previousPendingIntent = PendingIntent.getActivity(mContext, 0,
- mIntent, 0);
+ mIntent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
state.resumeTiming();
final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, mIntent,
- PendingIntent.FLAG_CANCEL_CURRENT);
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
state.pauseTiming();
pendingIntent.cancel();
@@ -102,11 +102,11 @@
while (state.keepRunning()) {
state.pauseTiming();
final PendingIntent previousPendingIntent = PendingIntent.getActivity(mContext, 0,
- mIntent, 0);
+ mIntent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
state.resumeTiming();
final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, mIntent,
- PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
state.pauseTiming();
previousPendingIntent.cancel();
@@ -124,7 +124,7 @@
while (state.keepRunning()) {
state.pauseTiming();
final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0,
- mIntent, 0);
+ mIntent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
state.resumeTiming();
pendingIntent.cancel();
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
index 050fecd..ac63653 100644
--- a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
@@ -17,7 +17,6 @@
import android.content.Context;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.view.Display;
@@ -78,7 +77,7 @@
}
private void getResourcesForPath(String path) {
- ResourcesManager.getInstance().getResources(null, path, null, null, null,
+ ResourcesManager.getInstance().getResources(null, path, null, null, null, null,
Display.DEFAULT_DISPLAY, null, sContext.getResources().getCompatibilityInfo(),
null, null);
}
@@ -136,4 +135,22 @@
}
}
}
+
+ @Test
+ public void getDisplayMetrics() {
+ ResourcesManager resourcesManager = ResourcesManager.getInstance();
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ // Invalidate cache.
+ resourcesManager.applyConfigurationToResources(
+ resourcesManager.getConfiguration(), null);
+ state.resumeTiming();
+
+ // Invoke twice for testing cache.
+ resourcesManager.getDisplayMetrics();
+ resourcesManager.getDisplayMetrics();
+ }
+ }
}
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java
index f4c0a17..45c723b 100644
--- a/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java
@@ -95,8 +95,9 @@
? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
Resources destResources = resourcesManager.getResources(null, ai.sourceDir,
- ai.splitSourceDirs, ai.resourceDirs, ai.sharedLibraryFiles, Display.DEFAULT_DISPLAY,
- c, mContext.getResources().getCompatibilityInfo(), null, null);
+ ai.splitSourceDirs, ai.resourceDirs, ai.overlayPaths, ai.sharedLibraryFiles,
+ Display.DEFAULT_DISPLAY, c, mContext.getResources().getCompatibilityInfo(),
+ null, null);
Assert.assertNotEquals(destResources.getAssets(), mContext.getAssets());
Resources.Theme destTheme = destResources.newTheme();
diff --git a/apct-tests/perftests/core/src/android/content/pm/PackageManagerBenchmark.java b/apct-tests/perftests/core/src/android/content/pm/PackageManagerBenchmark.java
new file mode 100644
index 0000000..a82fab4
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/content/pm/PackageManagerBenchmark.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class PackageManagerBenchmark {
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Test
+ public void createUserContextBenchmark() {
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ while (state.keepRunning()) {
+ context.createContextAsUser(UserHandle.SYSTEM, /* flags */ 0);
+ }
+ }
+
+ @Test
+ public void getResourcesForApplication_byStarAsUser()
+ throws PackageManager.NameNotFoundException {
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ while (state.keepRunning()) {
+ context.getPackageManager().getResourcesForApplicationAsUser(context.getPackageName(),
+ UserHandle.USER_SYSTEM);
+ }
+ }
+
+ @Test
+ public void getResourcesApplication_byCreateContextAsUser()
+ throws PackageManager.NameNotFoundException {
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ while (state.keepRunning()) {
+ context.createContextAsUser(UserHandle.SYSTEM, /* flags */ 0).getPackageManager()
+ .getResourcesForApplication(context.getPackageName());
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/database/CrossProcessCursorPerfTest.java b/apct-tests/perftests/core/src/android/database/CrossProcessCursorPerfTest.java
new file mode 100644
index 0000000..77654df
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/database/CrossProcessCursorPerfTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.database;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.ContentProviderClient;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.net.Uri;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class CrossProcessCursorPerfTest {
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ /**
+ * Measure transporting a small {@link Cursor}, roughly 1KB in size.
+ */
+ @Test
+ public void timeSmall() throws Exception {
+ time(1);
+ }
+
+ /**
+ * Measure transporting a small {@link Cursor}, roughly 54KB in size.
+ */
+ @Test
+ public void timeMedium() throws Exception {
+ time(100);
+ }
+
+ /**
+ * Measure transporting a small {@link Cursor}, roughly 5.4MB in size.
+ */
+ @Test
+ public void timeLarge() throws Exception {
+ time(10_000);
+ }
+
+ private static final Uri TEST_URI = Uri.parse("content://android.os.SomeProvider/");
+
+ private void time(int count) throws Exception {
+ try (ContentProviderClient client = InstrumentationRegistry.getTargetContext()
+ .getContentResolver().acquireContentProviderClient(TEST_URI)) {
+ // Configure remote side once with data size to return
+ final ContentValues values = new ContentValues();
+ values.put(Intent.EXTRA_INDEX, count);
+ client.update(TEST_URI, values, null);
+
+ // Repeatedly query that data until we reach convergence
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ try (Cursor c = client.query(TEST_URI, null, null, null)) {
+ // Actually walk the returned values to ensure we pull all
+ // data from the remote side
+ while (c.moveToNext()) {
+ assertEquals(c.getPosition(), c.getInt(0));
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/database/OWNERS b/apct-tests/perftests/core/src/android/database/OWNERS
new file mode 100644
index 0000000..bb9a2ca
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/database/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/database/OWNERS
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java
index 9f09305..f84a0d0 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java
@@ -54,11 +54,11 @@
canvas = node.start(200, 200);
int save = canvas.save();
canvas.clipRect(1, 1, 199, 199);
- canvas.insertReorderBarrier();
+ canvas.enableZ();
for (int i = 0; i < 5; i++) {
canvas.drawRenderNode(child);
}
- canvas.insertInorderBarrier();
+ canvas.disableZ();
canvas.restoreToCount(save);
node.end(canvas);
}
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java
index 8847456..e83c64c 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java
@@ -27,6 +27,7 @@
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.util.Preconditions;
import com.android.perftests.core.R;
import org.junit.Rule;
@@ -73,10 +74,31 @@
final AssetManager am = context.getAssets();
while (state.keepRunning()) {
- Typeface face = Typeface.createFromAsset(am, TEST_FONT_NAME);
+ Typeface face = createFromNonAsset(am, TEST_FONT_NAME);
}
}
+ /**
+ * {@link AssetManager#openNonAsset(String)} variant of
+ * {@link Typeface#createFromAsset(AssetManager, String)}.
+ */
+ private static Typeface createFromNonAsset(AssetManager mgr, String path) {
+ Preconditions.checkNotNull(path); // for backward compatibility
+ Preconditions.checkNotNull(mgr);
+
+ Typeface typeface = new Typeface.Builder(mgr, path).build();
+ if (typeface != null) return typeface;
+ // check if the file exists, and throw an exception for backward compatibility
+ //noinspection EmptyTryBlock
+ try (InputStream inputStream = mgr.openNonAsset(path)) {
+ // Purposely empty
+ } catch (IOException e) {
+ throw new RuntimeException("Font asset not found " + path);
+ }
+
+ return Typeface.DEFAULT;
+ }
+
@Test
public void testCreate_fromFile() {
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
@@ -90,7 +112,7 @@
throw new RuntimeException(e);
}
- try (InputStream in = am.open(TEST_FONT_NAME);
+ try (InputStream in = am.openNonAsset(TEST_FONT_NAME);
OutputStream out = new FileOutputStream(outFile)) {
byte[] buf = new byte[1024];
int n = 0;
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java
new file mode 100644
index 0000000..d272507
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.perftests;
+
+import android.graphics.Typeface;
+import android.os.SharedMemory;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.util.ArrayMap;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Map;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class TypefaceSerializationPerfTest {
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Test
+ public void testSerializeFontMap() throws Exception {
+ Map<String, Typeface> systemFontMap = Typeface.getSystemFontMap();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ while (state.keepRunning()) {
+ Typeface.serializeFontMap(systemFontMap);
+ }
+ }
+
+ @Test
+ public void testDeserializeFontMap() throws Exception {
+ SharedMemory memory = Typeface.serializeFontMap(Typeface.getSystemFontMap());
+ ByteBuffer buffer = memory.mapReadOnly().order(ByteOrder.BIG_ENDIAN);
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ ArrayMap<String, Typeface> out = new ArrayMap<>();
+ while (state.keepRunning()) {
+ buffer.position(0);
+ Typeface.deserializeFontMap(buffer, out);
+ }
+ }
+
+ @Test
+ public void testSetSystemFontMap() throws Exception {
+ SharedMemory memory = null;
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ // Explicitly destroy lazy-loaded typefaces, so that we don't hit the mmap limit
+ // (max_map_count).
+ Typeface.destroySystemFontMap();
+ Typeface.loadPreinstalledSystemFontMap();
+ if (memory != null) {
+ memory.close();
+ }
+ memory = Typeface.serializeFontMap(Typeface.getSystemFontMap());
+ state.resumeTiming();
+ Typeface.setSystemFontMap(memory);
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/mtp_perf/AppFusePerfTest.java b/apct-tests/perftests/core/src/android/mtp_perf/AppFusePerfTest.java
new file mode 100644
index 0000000..fcbfc72
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/mtp_perf/AppFusePerfTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.mtp_perf;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.ProxyFileDescriptorCallback;
+import android.os.storage.StorageManager;
+import android.system.ErrnoException;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class AppFusePerfTest {
+ static final int SIZE = 10 * 1024 * 1024; // 10MB
+
+ @Test
+ public void testReadWriteFile() throws IOException {
+ final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ final StorageManager storageManager = context.getSystemService(StorageManager.class);
+
+ final byte[] bytes = new byte[SIZE];
+ final int samples = 100;
+ final double[] readTime = new double[samples];
+ final double[] writeTime = new double[samples];
+
+ for (int i = 0; i < samples; i++) {
+ final ParcelFileDescriptor fd = storageManager.openProxyFileDescriptor(
+ ParcelFileDescriptor.MODE_READ_ONLY, new TestCallback());
+ try (final ParcelFileDescriptor.AutoCloseInputStream stream =
+ new ParcelFileDescriptor.AutoCloseInputStream(fd)) {
+ final long startTime = System.nanoTime();
+ stream.read(bytes);
+ readTime[i] = (System.nanoTime() - startTime) / 1000.0 / 1000.0;
+ }
+ }
+
+ for (int i = 0; i < samples; i++) {
+ final ParcelFileDescriptor fd = storageManager.openProxyFileDescriptor(
+ ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_TRUNCATE,
+ new TestCallback());
+ try (final ParcelFileDescriptor.AutoCloseOutputStream stream =
+ new ParcelFileDescriptor.AutoCloseOutputStream(fd)) {
+ final long startTime = System.nanoTime();
+ stream.write(bytes);
+ writeTime[i] = (System.nanoTime() - startTime) / 1000.0 / 1000.0;
+ }
+ }
+
+ double readAverage = 0;
+ double writeAverage = 0;
+ double readSquaredAverage = 0;
+ double writeSquaredAverage = 0;
+ for (int i = 0; i < samples; i++) {
+ readAverage += readTime[i];
+ writeAverage += writeTime[i];
+ readSquaredAverage += readTime[i] * readTime[i];
+ writeSquaredAverage += writeTime[i] * writeTime[i];
+ }
+
+ readAverage /= samples;
+ writeAverage /= samples;
+ readSquaredAverage /= samples;
+ writeSquaredAverage /= samples;
+
+ final Bundle results = new Bundle();
+ results.putDouble("readAverage", readAverage);
+ results.putDouble("readStandardDeviation",
+ Math.sqrt(readSquaredAverage - readAverage * readAverage));
+ results.putDouble("writeAverage", writeAverage);
+ results.putDouble("writeStandardDeviation",
+ Math.sqrt(writeSquaredAverage - writeAverage * writeAverage));
+ InstrumentationRegistry.getInstrumentation().sendStatus(Activity.RESULT_OK, results);
+ }
+
+ private static class TestCallback extends ProxyFileDescriptorCallback {
+ @Override
+ public long onGetSize() throws ErrnoException {
+ return SIZE;
+ }
+
+ @Override
+ public int onRead(long offset, int size, byte[] data) throws ErrnoException {
+ return size;
+ }
+
+ @Override
+ public int onWrite(long offset, int size, byte[] data) throws ErrnoException {
+ return size;
+ }
+
+ @Override
+ public void onFsync() throws ErrnoException {}
+
+ @Override
+ public void onRelease() {}
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/os/OWNERS b/apct-tests/perftests/core/src/android/os/OWNERS
new file mode 100644
index 0000000..a1719c9
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/OWNERS
@@ -0,0 +1 @@
+per-file PackageParsingPerfTest.kt = file:/services/core/java/com/android/server/pm/OWNERS
\ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/os/PackageParsingPerfTest.kt b/apct-tests/perftests/core/src/android/os/PackageParsingPerfTest.kt
index 29721c5..90dca25 100644
--- a/apct-tests/perftests/core/src/android/os/PackageParsingPerfTest.kt
+++ b/apct-tests/perftests/core/src/android/os/PackageParsingPerfTest.kt
@@ -97,11 +97,21 @@
private val state: BenchmarkState get() = perfStatusReporter.benchmarkState
private val apks: List<File> get() = params.apks
+ private fun safeParse(parser: ParallelParser<*>, file: File) {
+ try {
+ parser.parse(file)
+ } catch (e: Exception) {
+ // ignore
+ }
+ }
+
@Test
fun sequentialNoCache() {
params.cacheDirToParser(null).use { parser ->
while (state.keepRunning()) {
- apks.forEach { parser.parse(it) }
+ apks.forEach {
+ safeParse(parser, it)
+ }
}
}
}
@@ -110,10 +120,10 @@
fun sequentialCached() {
params.cacheDirToParser(testFolder.newFolder()).use { parser ->
// Fill the cache
- apks.forEach { parser.parse(it) }
+ apks.forEach { safeParse(parser, it) }
while (state.keepRunning()) {
- apks.forEach { parser.parse(it) }
+ apks.forEach { safeParse(parser, it) }
}
}
}
@@ -132,7 +142,7 @@
fun parallelCached() {
params.cacheDirToParser(testFolder.newFolder()).use { parser ->
// Fill the cache
- apks.forEach { parser.parse(it) }
+ apks.forEach { safeParse(parser, it) }
while (state.keepRunning()) {
apks.forEach { parser.submit(it) }
@@ -149,7 +159,15 @@
PARALLEL_MAX_THREADS, "package-parsing-test",
Process.THREAD_PRIORITY_FOREGROUND)
- fun submit(file: File) = service.submit { queue.put(parse(file)) }
+ fun submit(file: File) {
+ service.submit {
+ try {
+ queue.put(parse(file))
+ } catch (e: Exception) {
+ queue.put(e)
+ }
+ }
+ }
fun take() = queue.poll(QUEUE_POLL_TIMEOUT_SECONDS, TimeUnit.SECONDS)
@@ -178,17 +196,17 @@
// For testing, just disable enforcement to avoid hooking up to compat framework
ParseTypeImpl(ParseInput.Callback { _, _, _ -> false })
}
- val parser = ParsingPackageUtils(false, null, null,
+ val parser = ParsingPackageUtils(false, null, null, emptyList(),
object : ParsingPackageUtils.Callback {
override fun hasFeature(feature: String) = true
override fun startParsingPackage(
packageName: String,
- baseCodePath: String,
- codePath: String,
+ baseApkPath: String,
+ path: String,
manifestArray: TypedArray,
isCoreApp: Boolean
- ) = ParsingPackageImpl(packageName, baseCodePath, codePath, manifestArray)
+ ) = ParsingPackageImpl(packageName, baseApkPath, path, manifestArray)
})
override fun parseImpl(file: File) =
diff --git a/apct-tests/perftests/core/src/android/os/ParcelObtainPerfTest.java b/apct-tests/perftests/core/src/android/os/ParcelObtainPerfTest.java
new file mode 100644
index 0000000..760ae12
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/ParcelObtainPerfTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.os;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ParcelObtainPerfTest {
+ private static final int ITERATIONS = 1_000_000;
+
+ @Test
+ public void timeContention_01() throws Exception {
+ timeContention(1);
+ }
+
+ @Test
+ public void timeContention_04() throws Exception {
+ timeContention(4);
+ }
+
+ @Test
+ public void timeContention_16() throws Exception {
+ timeContention(16);
+ }
+
+ private static void timeContention(int numThreads) throws Exception {
+ final long start = SystemClock.elapsedRealtime();
+ {
+ final ObtainThread[] threads = new ObtainThread[numThreads];
+ for (int i = 0; i < numThreads; i++) {
+ final ObtainThread thread = new ObtainThread(ITERATIONS / numThreads);
+ thread.start();
+ threads[i] = thread;
+ }
+ for (int i = 0; i < numThreads; i++) {
+ threads[i].join();
+ }
+ }
+ final long duration = SystemClock.elapsedRealtime() - start;
+
+ final Bundle results = new Bundle();
+ results.putLong("duration", duration);
+ InstrumentationRegistry.getInstrumentation().sendStatus(0, results);
+ }
+
+ public static class ObtainThread extends Thread {
+ public int iterations;
+
+ public ObtainThread(int iterations) {
+ this.iterations = iterations;
+ }
+
+ @Override
+ public void run() {
+ while (iterations-- > 0) {
+ final Parcel data = Parcel.obtain();
+ final Parcel reply = Parcel.obtain();
+ try {
+ data.writeInt(32);
+ reply.writeInt(32);
+ } finally {
+ reply.recycle();
+ data.recycle();
+ }
+ }
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java b/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java
index 4db9262..be2f9d7 100644
--- a/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java
@@ -159,21 +159,6 @@
}
@Test
- public void timeObtainRecycle() {
- // Use up the pooled instances.
- // A lot bigger than the actual size but in case someone increased it.
- final int POOL_SIZE = 100;
- for (int i = 0; i < POOL_SIZE; i++) {
- Parcel.obtain();
- }
-
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- while (state.keepRunning()) {
- Parcel.obtain().recycle();
- }
- }
-
- @Test
public void timeWriteException() {
timeWriteException(false);
}
diff --git a/apct-tests/perftests/core/src/android/os/ParcelStringPerfTest.java b/apct-tests/perftests/core/src/android/os/ParcelStringPerfTest.java
new file mode 100644
index 0000000..2b861cb
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/ParcelStringPerfTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+@LargeTest
+@RunWith(Parameterized.class)
+public class ParcelStringPerfTest {
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Parameterized.Parameter(0)
+ public String mName;
+ @Parameterized.Parameter(1)
+ public String mValue;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Collection<Object[]> getParameters() {
+ return Arrays.asList(new Object[][] {
+ { "simple", "com.example.typical_package_name" },
+ { "complex", "從不喜歡孤單一個 - 蘇永康/吳雨霏" },
+ });
+ }
+
+ @Test
+ public void timeWriteString8() {
+ final Parcel parcel = Parcel.obtain();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ parcel.setDataPosition(0);
+ parcel.writeString8(mValue);
+ }
+ }
+
+ @Test
+ public void timeWriteString16() {
+ final Parcel parcel = Parcel.obtain();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ parcel.setDataPosition(0);
+ parcel.writeString16(mValue);
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/os/SomeProvider.java b/apct-tests/perftests/core/src/android/os/SomeProvider.java
new file mode 100644
index 0000000..f5e247e
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/SomeProvider.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.os;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+
+import java.util.Arrays;
+
+public class SomeProvider extends ContentProvider {
+ private Cursor mCursor;
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ return mCursor;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ final char[] valueRaw = new char[512];
+ Arrays.fill(valueRaw, '!');
+ final String value = new String(valueRaw);
+
+ final int count = values.getAsInteger(Intent.EXTRA_INDEX);
+ final MatrixCursor cursor = new MatrixCursor(new String[] { "_id", "value" });
+ for (int i = 0; i < count; i++) {
+ MatrixCursor.RowBuilder row = cursor.newRow();
+ row.add(0, i);
+ row.add(1, value);
+ }
+ mCursor = cursor;
+ return 1;
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/os/VibratorPerfTest.java b/apct-tests/perftests/core/src/android/os/VibratorPerfTest.java
new file mode 100644
index 0000000..0efe8cf
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/VibratorPerfTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import android.content.Context;
+
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+@LargeTest
+public class VibratorPerfTest {
+ @Rule
+ public final BenchmarkRule mBenchmarkRule = new BenchmarkRule();
+
+ private Vibrator mVibrator;
+
+ @Before
+ public void setUp() {
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ mVibrator = context.getSystemService(Vibrator.class);
+ }
+
+ @Test
+ public void testEffectClick() {
+ final BenchmarkState state = mBenchmarkRule.getState();
+ while (state.keepRunning()) {
+ mVibrator.vibrate(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
+ }
+ }
+
+ @Test
+ public void testOneShot() {
+ final BenchmarkState state = mBenchmarkRule.getState();
+ while (state.keepRunning()) {
+ mVibrator.vibrate(VibrationEffect.createOneShot(SECONDS.toMillis(2),
+ VibrationEffect.DEFAULT_AMPLITUDE));
+ }
+ }
+
+ @Test
+ public void testWaveform() {
+ final BenchmarkState state = mBenchmarkRule.getState();
+ long[] timings = new long[]{SECONDS.toMillis(1), SECONDS.toMillis(2), SECONDS.toMillis(1)};
+ while (state.keepRunning()) {
+ mVibrator.vibrate(VibrationEffect.createWaveform(timings, -1));
+ }
+ }
+
+ @Test
+ public void testCompose() {
+ final BenchmarkState state = mBenchmarkRule.getState();
+ while (state.keepRunning()) {
+ mVibrator.vibrate(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f, 100)
+ .compose());
+ }
+ }
+
+ @Test
+ public void testAreEffectsSupported() {
+ final BenchmarkState state = mBenchmarkRule.getState();
+ int[] effects = new int[]{VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_TICK};
+ while (state.keepRunning()) {
+ mVibrator.areEffectsSupported(effects);
+ }
+ }
+
+ @Test
+ public void testArePrimitivesSupported() {
+ final BenchmarkState state = mBenchmarkRule.getState();
+ int[] primitives = new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK,
+ VibrationEffect.Composition.PRIMITIVE_TICK};
+ while (state.keepRunning()) {
+ mVibrator.arePrimitivesSupported(primitives);
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/text/CanvasDrawTextTest.java b/apct-tests/perftests/core/src/android/text/CanvasDrawTextTest.java
index bb6b691..66b2b0e 100644
--- a/apct-tests/perftests/core/src/android/text/CanvasDrawTextTest.java
+++ b/apct-tests/perftests/core/src/android/text/CanvasDrawTextTest.java
@@ -53,19 +53,19 @@
final String text = mTextUtil.nextRandomParagraph(
WORD_LENGTH, 4 * 1024 * 1024 /* 4mb text */).toString();
final RenderNode node = RenderNode.create("benchmark", null);
- final RenderNode child = RenderNode.create("child", null);
- child.setLeftTopRightBottom(50, 50, 100, 100);
-
- RecordingCanvas canvas = node.start(100, 100);
- node.end(canvas);
- canvas = child.start(50, 50);
- child.end(canvas);
final Random r = new Random(0);
-
while (state.keepRunning()) {
+ state.pauseTiming();
+ RecordingCanvas canvas = node.beginRecording();
int start = r.nextInt(text.length() - 100);
+ state.resumeTiming();
+
canvas.drawText(text, start, start + 100, 0, 0, PAINT);
+
+ state.pauseTiming();
+ node.endRecording();
+ state.resumeTiming();
}
}
}
diff --git a/apct-tests/perftests/core/src/android/text/SystemFontsPerfTest.java b/apct-tests/perftests/core/src/android/text/SystemFontsPerfTest.java
new file mode 100644
index 0000000..5d744cd
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/text/SystemFontsPerfTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import android.graphics.fonts.SystemFonts;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class SystemFontsPerfTest {
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Test
+ public void getAvailableFonts() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ SystemFonts.resetAvailableFonts();
+ System.gc();
+ state.resumeTiming();
+
+ SystemFonts.getAvailableFonts();
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/text/TextUtilsPerfTest.java b/apct-tests/perftests/core/src/android/text/TextUtilsPerfTest.java
new file mode 100644
index 0000000..c62269e
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/text/TextUtilsPerfTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.Supplier;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class TextUtilsPerfTest {
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ public static final String TEMPLATE = "Template that combines %s and %d together";
+
+ public String mVar1 = "example";
+ public int mVar2 = 42;
+
+ /**
+ * Measure overhead of formatting a string via {@link String#format}.
+ */
+ @Test
+ public void timeFormatUpstream() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ String res = String.format(TEMPLATE, mVar1, mVar2);
+ }
+ }
+
+ /**
+ * Measure overhead of formatting a string via
+ * {@link TextUtils#formatSimple}.
+ */
+ @Test
+ public void timeFormatLocal() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ String res = TextUtils.formatSimple(TEMPLATE, mVar1, mVar2);
+ }
+ }
+
+ /**
+ * Measure overhead of formatting a string inline.
+ */
+ @Test
+ public void timeFormatInline() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ String res = "Template that combines " + mVar1 + " and " + mVar2 + " together";
+ }
+ }
+
+ /**
+ * Measure overhead of a passing null-check that uses a lambda to
+ * communicate a custom error message.
+ */
+ @Test
+ public void timeFormat_Skip_Lambda() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ requireNonNull(this, () -> {
+ return String.format(TEMPLATE, mVar1, mVar2);
+ });
+ }
+ }
+
+ /**
+ * Measure overhead of a passing null-check that uses varargs to communicate
+ * a custom error message.
+ */
+ @Test
+ public void timeFormat_Skip_Varargs() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ requireNonNull(this, TEMPLATE, mVar1, mVar2);
+ }
+ }
+
+ private static <T> T requireNonNull(T obj, Supplier<String> messageSupplier) {
+ return obj;
+ }
+
+ private static <T> T requireNonNull(T obj, String format, Object... args) {
+ return obj;
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/text/VariableFontPerfTest.java b/apct-tests/perftests/core/src/android/text/VariableFontPerfTest.java
new file mode 100644
index 0000000..fbe67a4
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/text/VariableFontPerfTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import android.graphics.Paint;
+import android.graphics.RecordingCanvas;
+import android.graphics.RenderNode;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Random;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class VariableFontPerfTest {
+ private static final int WORD_LENGTH = 9; // Random word has 9 characters.
+ private static final boolean NO_STYLE_TEXT = false;
+
+ private static final TextPaint PAINT = new TextPaint();
+
+ public VariableFontPerfTest() {}
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ private final TextPerfUtils mTextUtil = new TextPerfUtils();
+
+ @Before
+ public void setUp() {
+ mTextUtil.resetRandom(0 /* seed */);
+ }
+
+ @Test
+ public void testDraw_SetVariationOnce() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+ final Paint paint = new Paint(PAINT);
+ paint.setFontVariationSettings("'wght' 700");
+ final RenderNode node = RenderNode.create("benchmark", null);
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ final RecordingCanvas c = node.beginRecording(1200, 200);
+ state.resumeTiming();
+
+ c.drawText(text, 0, text.length(), 0, 100, paint);
+
+ state.pauseTiming();
+ node.endRecording();
+ state.resumeTiming();
+
+ }
+ }
+
+ @Test
+ public void testDraw_SetVariationEachDraw() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+ final Paint paint = new Paint(PAINT);
+ final RenderNode node = RenderNode.create("benchmark", null);
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ final RecordingCanvas c = node.beginRecording(1200, 200);
+ paint.setFontVariationSettings("'wght' 700");
+ state.resumeTiming();
+
+ c.drawText(text, 0, text.length(), 0, 100, paint);
+
+ state.pauseTiming();
+ node.endRecording();
+ state.resumeTiming();
+
+ }
+ }
+
+ @Test
+ public void testDraw_SetDifferentVariationEachDraw() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+ final Paint paint = new Paint(PAINT);
+ final RenderNode node = RenderNode.create("benchmark", null);
+ final Random random = new Random(0);
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ final RecordingCanvas c = node.beginRecording(1200, 200);
+ int weight = random.nextInt(1000);
+ paint.setFontVariationSettings("'wght' " + weight);
+ state.resumeTiming();
+
+ c.drawText(text, 0, text.length(), 0, 100, paint);
+
+ state.pauseTiming();
+ node.endRecording();
+ state.resumeTiming();
+ }
+ }
+
+ @Test
+ public void testSetFontVariationSettings() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final Paint paint = new Paint(PAINT);
+ final Random random = new Random(0);
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ int weight = random.nextInt(1000);
+ state.resumeTiming();
+
+ paint.setFontVariationSettings("'wght' " + weight);
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/util/CharsetUtilsPerfTest.java b/apct-tests/perftests/core/src/android/util/CharsetUtilsPerfTest.java
new file mode 100644
index 0000000..e2c580c
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/util/CharsetUtilsPerfTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+
+import dalvik.system.VMRuntime;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Collection;
+
+@LargeTest
+@RunWith(Parameterized.class)
+public class CharsetUtilsPerfTest {
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Parameterized.Parameter(0)
+ public String mName;
+ @Parameterized.Parameter(1)
+ public String mValue;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Collection<Object[]> getParameters() {
+ return Arrays.asList(new Object[][] {
+ { "simple", "com.example.typical_package_name" },
+ { "complex", "從不喜歡孤單一個 - 蘇永康/吳雨霏" },
+ });
+ }
+
+ @Test
+ public void timeUpstream() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mValue.getBytes(StandardCharsets.UTF_8);
+ }
+ }
+
+ /**
+ * Measure performance of writing into a small buffer where bounds checking
+ * requires careful measurement of encoded size.
+ */
+ @Test
+ public void timeLocal_SmallBuffer() {
+ final byte[] dest = (byte[]) VMRuntime.getRuntime().newNonMovableArray(byte.class, 64);
+ final long destPtr = VMRuntime.getRuntime().addressOf(dest);
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ CharsetUtils.toModifiedUtf8Bytes(mValue, destPtr, 0, dest.length);
+ }
+ }
+
+ /**
+ * Measure performance of writing into a large buffer where bounds checking
+ * only needs a simple worst-case 4-bytes-per-char check.
+ */
+ @Test
+ public void timeLocal_LargeBuffer() {
+ final byte[] dest = (byte[]) VMRuntime.getRuntime().newNonMovableArray(byte.class, 1024);
+ final long destPtr = VMRuntime.getRuntime().addressOf(dest);
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ CharsetUtils.toModifiedUtf8Bytes(mValue, destPtr, 0, dest.length);
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/util/XmlPerfTest.java b/apct-tests/perftests/core/src/android/util/XmlPerfTest.java
new file mode 100644
index 0000000..e05bd2a
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/util/XmlPerfTest.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Bundle;
+import android.os.Debug;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.HexDump;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.function.Supplier;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class XmlPerfTest {
+ /**
+ * Since allocation measurement adds overhead, it's disabled by default for
+ * performance runs. It can be manually enabled to compare GC behavior.
+ */
+ private static final boolean MEASURE_ALLOC = false;
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Test
+ public void timeWrite_Fast() throws Exception {
+ doWrite(() -> Xml.newFastSerializer());
+ }
+
+ @Test
+ public void timeWrite_Binary() throws Exception {
+ doWrite(() -> Xml.newBinarySerializer());
+ }
+
+ private void doWrite(Supplier<TypedXmlSerializer> outFactory) throws Exception {
+ if (MEASURE_ALLOC) {
+ Debug.startAllocCounting();
+ }
+
+ int iterations = 0;
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ iterations++;
+ try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
+ final TypedXmlSerializer out = outFactory.get();
+ out.setOutput(os, StandardCharsets.UTF_8.name());
+ write(out);
+ }
+ }
+
+ if (MEASURE_ALLOC) {
+ Debug.stopAllocCounting();
+ final Bundle results = new Bundle();
+ results.putLong("threadAllocCount_mean", Debug.getThreadAllocCount() / iterations);
+ results.putLong("threadAllocSize_mean", Debug.getThreadAllocSize() / iterations);
+ InstrumentationRegistry.getInstrumentation().sendStatus(0, results);
+ }
+ }
+
+ @Test
+ public void timeRead_Fast() throws Exception {
+ doRead(() -> Xml.newFastSerializer(), () -> Xml.newFastPullParser());
+ }
+
+ @Test
+ public void timeRead_Binary() throws Exception {
+ doRead(() -> Xml.newBinarySerializer(), () -> Xml.newBinaryPullParser());
+ }
+
+ private void doRead(Supplier<TypedXmlSerializer> outFactory,
+ Supplier<TypedXmlPullParser> inFactory) throws Exception {
+ final byte[] raw;
+ try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
+ TypedXmlSerializer out = outFactory.get();
+ out.setOutput(os, StandardCharsets.UTF_8.name());
+ write(out);
+ raw = os.toByteArray();
+ }
+
+ if (MEASURE_ALLOC) {
+ Debug.startAllocCounting();
+ }
+
+ int iterations = 0;
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ iterations++;
+ try (ByteArrayInputStream is = new ByteArrayInputStream(raw)) {
+ TypedXmlPullParser xml = inFactory.get();
+ xml.setInput(is, StandardCharsets.UTF_8.name());
+ read(xml);
+ }
+ }
+
+ if (MEASURE_ALLOC) {
+ Debug.stopAllocCounting();
+ final Bundle results = new Bundle();
+ results.putLong("sizeBytes", raw.length);
+ results.putLong("threadAllocCount_mean", Debug.getThreadAllocCount() / iterations);
+ results.putLong("threadAllocSize_mean", Debug.getThreadAllocSize() / iterations);
+ InstrumentationRegistry.getInstrumentation().sendStatus(0, results);
+ } else {
+ final Bundle results = new Bundle();
+ results.putLong("sizeBytes", raw.length);
+ InstrumentationRegistry.getInstrumentation().sendStatus(0, results);
+ }
+ }
+
+ /**
+ * Not even joking, this is a typical public key blob stored in
+ * {@code packages.xml}.
+ */
+ private static final byte[] KEY_BLOB = HexDump.hexStringToByteArray(""
+ + "308204a830820390a003020102020900a1573d0f45bea193300d06092a864886f70d010105050030819"
+ + "4310b3009060355040613025553311330110603550408130a43616c69666f726e696131163014060355"
+ + "0407130d4d6f756e7461696e20566965773110300e060355040a1307416e64726f69643110300e06035"
+ + "5040b1307416e64726f69643110300e06035504031307416e64726f69643122302006092a864886f70d"
+ + "0109011613616e64726f696440616e64726f69642e636f6d301e170d3131303931393138343232355a1"
+ + "70d3339303230343138343232355a308194310b3009060355040613025553311330110603550408130a"
+ + "43616c69666f726e6961311630140603550407130d4d6f756e7461696e20566965773110300e0603550"
+ + "40a1307416e64726f69643110300e060355040b1307416e64726f69643110300e06035504031307416e"
+ + "64726f69643122302006092a864886f70d0109011613616e64726f696440616e64726f69642e636f6d3"
+ + "0820120300d06092a864886f70d01010105000382010d00308201080282010100de1b51336afc909d8b"
+ + "cca5920fcdc8940578ec5c253898930e985481cfdea75ba6fc54b1f7bb492a03d98db471ab4200103a8"
+ + "314e60ee25fef6c8b83bc1b2b45b084874cffef148fa2001bb25c672b6beba50b7ac026b546da762ea2"
+ + "23829a22b80ef286131f059d2c9b4ca71d54e515a8a3fd6bf5f12a2493dfc2619b337b032a7cf8bbd34"
+ + "b833f2b93aeab3d325549a93272093943bb59dfc0197ae4861ff514e019b73f5cf10023ad1a032adb4b"
+ + "9bbaeb4debecb4941d6a02381f1165e1ac884c1fca9525c5854dce2ad8ec839b8ce78442c16367efc07"
+ + "778a337d3ca2cdf9792ac722b95d67c345f1c00976ec372f02bfcbef0262cc512a6845e71cfea0d0201"
+ + "03a381fc3081f9301d0603551d0e0416041478a0fc4517fb70ff52210df33c8d32290a44b2bb3081c90"
+ + "603551d230481c13081be801478a0fc4517fb70ff52210df33c8d32290a44b2bba1819aa48197308194"
+ + "310b3009060355040613025553311330110603550408130a43616c69666f726e6961311630140603550"
+ + "407130d4d6f756e7461696e20566965773110300e060355040a1307416e64726f69643110300e060355"
+ + "040b1307416e64726f69643110300e06035504031307416e64726f69643122302006092a864886f70d0"
+ + "109011613616e64726f696440616e64726f69642e636f6d820900a1573d0f45bea193300c0603551d13"
+ + "040530030101ff300d06092a864886f70d01010505000382010100977302dfbf668d7c61841c9c78d25"
+ + "63bcda1b199e95e6275a799939981416909722713531157f3cdcfea94eea7bb79ca3ca972bd8058a36a"
+ + "d1919291df42d7190678d4ea47a4b9552c9dfb260e6d0d9129b44615cd641c1080580e8a990dd768c6a"
+ + "b500c3b964e185874e4105109d94c5bd8c405deb3cf0f7960a563bfab58169a956372167a7e2674a04c"
+ + "4f80015d8f7869a7a4139aecbbdca2abc294144ee01e4109f0e47a518363cf6e9bf41f7560e94bdd4a5"
+ + "d085234796b05c7a1389adfd489feec2a107955129d7991daa49afb3d327dc0dc4fe959789372b093a8"
+ + "9c8dbfa41554f771c18015a6cb242a17e04d19d55d3b4664eae12caf2a11cd2b836e");
+
+ /**
+ * Typical list of permissions referenced in {@code packages.xml}.
+ */
+ private static final String[] PERMS = new String[] {
+ "android.permission.ACCESS_CACHE_FILESYSTEM",
+ "android.permission.WRITE_SETTINGS",
+ "android.permission.MANAGE_EXTERNAL_STORAGE",
+ "android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS",
+ "android.permission.FOREGROUND_SERVICE",
+ "android.permission.RECEIVE_BOOT_COMPLETED",
+ "android.permission.WRITE_MEDIA_STORAGE",
+ "android.permission.INTERNET",
+ "android.permission.UPDATE_DEVICE_STATS",
+ "android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY",
+ "android.permission.MANAGE_USB",
+ "android.permission.ACCESS_ALL_DOWNLOADS",
+ "android.permission.ACCESS_DOWNLOAD_MANAGER",
+ "android.permission.MANAGE_USERS",
+ "android.permission.ACCESS_NETWORK_STATE",
+ "android.permission.ACCESS_MTP",
+ "android.permission.INTERACT_ACROSS_USERS",
+ "android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS",
+ "android.permission.CLEAR_APP_CACHE",
+ "android.permission.CONNECTIVITY_INTERNAL",
+ "android.permission.START_ACTIVITIES_FROM_BACKGROUND",
+ "android.permission.QUERY_ALL_PACKAGES",
+ "android.permission.WAKE_LOCK",
+ "android.permission.UPDATE_APP_OPS_STATS",
+ };
+
+ /**
+ * Write a typical {@code packages.xml} file containing 100 applications,
+ * each of which defines signing key and permission information.
+ */
+ private static void write(TypedXmlSerializer out) throws IOException {
+ out.startDocument(null, true);
+ out.startTag(null, "packages");
+ for (int i = 0; i < 100; i++) {
+ out.startTag(null, "package");
+ out.attribute(null, "name", "com.android.providers.media");
+ out.attribute(null, "codePath", "/system/priv-app/MediaProviderLegacy");
+ out.attribute(null, "nativeLibraryPath", "/system/priv-app/MediaProviderLegacy/lib");
+ out.attributeLong(null, "publicFlags", 944258629L);
+ out.attributeLong(null, "privateFlags", -1946152952L);
+ out.attributeLong(null, "ft", 1603899064000L);
+ out.attributeLong(null, "it", 1603899064000L);
+ out.attributeLong(null, "ut", 1603899064000L);
+ out.attributeInt(null, "version", 1024);
+ out.attributeInt(null, "sharedUserId", 10100);
+ out.attributeBoolean(null, "isOrphaned", true);
+
+ out.startTag(null, "sigs");
+ out.startTag(null, "cert");
+ out.attributeInt(null, "index", 10);
+ out.attributeBytesHex(null, "key", KEY_BLOB);
+ out.endTag(null, "cert");
+ out.endTag(null, "sigs");
+
+ out.startTag(null, "perms");
+ for (String perm : PERMS) {
+ out.startTag(null, "item");
+ out.attributeInterned(null, "name", perm);
+ out.attributeBoolean(null, "granted", true);
+ out.attributeInt(null, "flags", 0);
+ out.endTag(null, "item");
+ }
+ out.endTag(null, "perms");
+
+ out.endTag(null, "package");
+ }
+ out.endTag(null, "packages");
+ out.endDocument();
+ }
+
+ /**
+ * Read a typical {@code packages.xml} file containing 100 applications, and
+ * verify that data passes smell test.
+ */
+ private static void read(TypedXmlPullParser xml) throws Exception {
+ int type;
+ int packages = 0;
+ int certs = 0;
+ int perms = 0;
+ while ((type = xml.next()) != XmlPullParser.END_DOCUMENT) {
+ final String tag = xml.getName();
+ if (type == XmlPullParser.START_TAG) {
+ if ("package".equals(tag)) {
+ xml.getAttributeValue(null, "name");
+ xml.getAttributeValue(null, "codePath");
+ xml.getAttributeValue(null, "nativeLibraryPath");
+ xml.getAttributeLong(null, "publicFlags");
+ assertEquals(-1946152952L, xml.getAttributeLong(null, "privateFlags"));
+ xml.getAttributeLong(null, "ft");
+ xml.getAttributeLong(null, "it");
+ xml.getAttributeLong(null, "ut");
+ xml.getAttributeInt(null, "version");
+ xml.getAttributeInt(null, "sharedUserId");
+ xml.getAttributeBoolean(null, "isOrphaned");
+ packages++;
+ } else if ("cert".equals(tag)) {
+ xml.getAttributeInt(null, "index");
+ xml.getAttributeBytesHex(null, "key");
+ certs++;
+ } else if ("item".equals(tag)) {
+ xml.getAttributeValue(null, "name");
+ xml.getAttributeBoolean(null, "granted");
+ xml.getAttributeInt(null, "flags");
+ perms++;
+ }
+ } else if (type == XmlPullParser.TEXT) {
+ xml.getText();
+ }
+ }
+
+ assertEquals(100, packages);
+ assertEquals(packages * 1, certs);
+ assertEquals(packages * PERMS.length, perms);
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/view/CutoutSpecificationBenchmark.java b/apct-tests/perftests/core/src/android/view/CutoutSpecificationBenchmark.java
index 14282bf..860c134 100644
--- a/apct-tests/perftests/core/src/android/view/CutoutSpecificationBenchmark.java
+++ b/apct-tests/perftests/core/src/android/view/CutoutSpecificationBenchmark.java
@@ -16,24 +16,20 @@
package android.view;
-import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
import android.text.TextUtils;
-import android.util.DisplayMetrics;
import android.util.Log;
import android.util.PathParser;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
import androidx.test.filters.LargeTest;
-import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -41,6 +37,10 @@
@RunWith(AndroidJUnit4.class)
@LargeTest
public class CutoutSpecificationBenchmark {
+ private static final int DISPLAY_WIDTH = 1080;
+ private static final int DISPLAY_HEIGHT = 1920;
+ private static final float DISPLAY_DENSITY = 3.5f;
+
private static final String TAG = "CutoutSpecificationBenchmark";
private static final String BOTTOM_MARKER = "@bottom";
@@ -67,22 +67,7 @@
+ "Z\n"
+ "@dp";
@Rule
- public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
-
- private Context mContext;
- private DisplayMetrics mDisplayMetrics;
-
- /**
- * Setup the necessary member field used by test methods.
- */
- @Before
- public void setUp() {
- mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
-
- mDisplayMetrics = new DisplayMetrics();
- mContext.getDisplay().getRealMetrics(mDisplayMetrics);
- }
-
+ public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
private static void toRectAndAddToRegion(Path p, Region inoutRegion, Rect inoutRect) {
final RectF rectF = new RectF();
@@ -168,19 +153,18 @@
@Test
public void parseByOldMethodForDoubleCutout() {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final BenchmarkState state = mBenchmarkRule.getState();
while (state.keepRunning()) {
- oldMethodParsingSpec(DOUBLE_CUTOUT_SPEC, mDisplayMetrics.widthPixels,
- mDisplayMetrics.heightPixels, mDisplayMetrics.density);
+ oldMethodParsingSpec(DOUBLE_CUTOUT_SPEC, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_DENSITY);
}
}
@Test
public void parseByNewMethodForDoubleCutout() {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final BenchmarkState state = mBenchmarkRule.getState();
while (state.keepRunning()) {
- new CutoutSpecification.Parser(mDisplayMetrics.density,
- mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels)
+ new CutoutSpecification.Parser(DISPLAY_DENSITY, DISPLAY_WIDTH, DISPLAY_HEIGHT)
.parse(DOUBLE_CUTOUT_SPEC);
}
}
@@ -209,10 +193,10 @@
+ "@right\n"
+ "@dp";
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final BenchmarkState state = mBenchmarkRule.getState();
while (state.keepRunning()) {
- new CutoutSpecification.Parser(mDisplayMetrics.density,
- mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels).parse(spec);
+ new CutoutSpecification.Parser(DISPLAY_DENSITY, DISPLAY_WIDTH, DISPLAY_HEIGHT)
+ .parse(spec);
}
}
@@ -231,10 +215,10 @@
+ "Z\n"
+ "@dp";
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final BenchmarkState state = mBenchmarkRule.getState();
while (state.keepRunning()) {
- new CutoutSpecification.Parser(mDisplayMetrics.density,
- mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels).parse(spec);
+ new CutoutSpecification.Parser(DISPLAY_DENSITY, DISPLAY_WIDTH, DISPLAY_HEIGHT)
+ .parse(spec);
}
}
}
diff --git a/apct-tests/perftests/core/src/android/view/InputStageBenchmark.java b/apct-tests/perftests/core/src/android/view/InputStageBenchmark.java
new file mode 100644
index 0000000..b45dbcf
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/view/InputStageBenchmark.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static org.junit.Assert.assertTrue;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.SystemClock;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.perftests.utils.PerfTestActivity;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@LargeTest
+@RunWith(Parameterized.class)
+public class InputStageBenchmark {
+ @Parameterized.Parameters(name = "mShowIme({0}), mHandlePreIme({1})")
+ public static Collection cases() {
+ return Arrays.asList(new Object[][] {
+ { false /* no ime */, false /* skip preime */},
+ { true /* show ime */, false /* skip preime */},
+ { true /* show ime */, true /* handle preime */}
+ });
+ }
+
+ @Rule
+ public final ActivityTestRule<PerfTestActivity> mActivityRule =
+ new ActivityTestRule<>(PerfTestActivity.class);
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Parameterized.Parameter(0)
+ public boolean mShowIme;
+ @Parameterized.Parameter(1)
+ public boolean mHandlePreIme;
+
+ private Instrumentation mInstrumentation;
+ private Window mWindow;
+ private CountDownLatch mWaitForReceiveInput;
+ private static final long TIMEOUT_MS = 5000;
+
+ class InstrumentedView extends View {
+ InstrumentedView(Context context) {
+ super(context);
+ setFocusable(true);
+ }
+
+ @Override
+ public boolean dispatchKeyEventPreIme(KeyEvent event) {
+ if (mHandlePreIme) {
+ mWaitForReceiveInput.countDown();
+ }
+ return mHandlePreIme;
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ mWaitForReceiveInput.countDown();
+ return true;
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ mWaitForReceiveInput.countDown();
+ return true;
+ }
+ }
+
+ class InstrumentedEditText extends EditText {
+ InstrumentedEditText(Context context) {
+ super(context);
+ setFocusable(true);
+ }
+
+ @Override
+ public boolean dispatchKeyEventPreIme(KeyEvent event) {
+ if (mHandlePreIme) {
+ mWaitForReceiveInput.countDown();
+ }
+ return mHandlePreIme;
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ mWaitForReceiveInput.countDown();
+ return true;
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ mWaitForReceiveInput.countDown();
+ return true;
+ }
+ }
+
+ private CountDownLatch showSoftKeyboard(View view) {
+ final CountDownLatch waitForIme = new CountDownLatch(1);
+ view.setOnApplyWindowInsetsListener((v, insets) -> {
+ if (insets.isVisible(WindowInsets.Type.ime())) {
+ waitForIme.countDown();
+ }
+ return insets;
+ });
+
+ assertTrue("Failed to request focus.", view.requestFocus());
+ final InputMethodManager imm =
+ mActivityRule.getActivity().getSystemService(InputMethodManager.class);
+ imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
+
+ return waitForIme;
+ }
+
+ @Before
+ public void setUp() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ final Activity activity = mActivityRule.getActivity();
+
+ final CountDownLatch[] waitForIme = new CountDownLatch[1];
+ mInstrumentation.runOnMainSync(() -> {
+ mWindow = mActivityRule.getActivity().getWindow();
+
+ if (mShowIme) {
+ final EditText edit = new InstrumentedEditText(activity);
+ mWindow.setContentView(edit);
+ waitForIme[0] = showSoftKeyboard(edit);
+ } else {
+ final View v = new InstrumentedView(activity);
+ // set FLAG_LOCAL_FOCUS_MODE to prevent delivering input events to the ime
+ // in ImeInputStage.
+ mWindow.addFlags(WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE);
+ mWindow.setContentView(v);
+ assertTrue("Failed to request focus.", v.requestFocus());
+ }
+ });
+ if (waitForIme[0] != null) {
+ try {
+ assertTrue("Failed to show InputMethod.",
+ waitForIme[0].await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ mInstrumentation.waitForIdleSync();
+ }
+
+ private void injectInputEvent(InputEvent event) {
+ mWaitForReceiveInput = new CountDownLatch(1);
+ mInstrumentation.runOnMainSync(() -> mWindow.injectInputEvent(event));
+ try {
+ mWaitForReceiveInput.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Test
+ public void testKeyEvent() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ final KeyEvent eventDown =
+ new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACKSLASH);
+ injectInputEvent(eventDown);
+
+ state.pauseTiming();
+ final KeyEvent eventUp =
+ new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACKSLASH);
+ injectInputEvent(eventUp);
+ state.resumeTiming();
+ }
+ }
+
+ @Test
+ public void testMotionEvent() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final Rect contentFrame = new Rect();
+ mInstrumentation.runOnMainSync(() ->
+ mWindow.getDecorView().getBoundsOnScreen(contentFrame));
+ final int x = contentFrame.centerX();
+ final int y = contentFrame.centerY();
+ final long eventTime = SystemClock.uptimeMillis();
+
+ while (state.keepRunning()) {
+ final MotionEvent eventDown = MotionEvent.obtain(eventTime, eventTime,
+ MotionEvent.ACTION_DOWN, x, y,
+ 1.0f /* pressure */, 1.0f /* size */, 0 /* metaState */,
+ 1.0f /* xPrecision */, 1.0f /* yPrecision */,
+ 0 /* deviceId */, 0 /* edgeFlags */,
+ InputDevice.SOURCE_TOUCHSCREEN, DEFAULT_DISPLAY);
+ injectInputEvent(eventDown);
+
+ state.pauseTiming();
+ final MotionEvent eventUp = MotionEvent.obtain(eventTime, eventTime,
+ MotionEvent.ACTION_UP, x, y,
+ 1.0f /* pressure */, 1.0f /* size */, 0 /* metaState */,
+ 1.0f /* xPrecision */, 1.0f /* yPrecision */,
+ 0 /* deviceId */, 0 /* edgeFlags */,
+ InputDevice.SOURCE_TOUCHSCREEN, DEFAULT_DISPLAY);
+ injectInputEvent(eventUp);
+ state.resumeTiming();
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/view/ViewPerfTest.java b/apct-tests/perftests/core/src/android/view/ViewPerfTest.java
index a1f8608..a2aeb31 100644
--- a/apct-tests/perftests/core/src/android/view/ViewPerfTest.java
+++ b/apct-tests/perftests/core/src/android/view/ViewPerfTest.java
@@ -16,30 +16,45 @@
package android.view;
+import static junit.framework.Assert.assertTrue;
+
import android.content.Context;
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import android.perftests.utils.PerfTestActivity;
import android.widget.FrameLayout;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
import com.android.perftests.core.R;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@LargeTest
public class ViewPerfTest {
@Rule
- public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+ public final BenchmarkRule mBenchmarkRule = new BenchmarkRule();
+
+ @Rule
+ public final ActivityTestRule<PerfTestActivity> mActivityRule =
+ new ActivityTestRule<>(PerfTestActivity.class);
+
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ }
@Test
public void testSimpleViewInflate() {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
- LayoutInflater inflater = LayoutInflater.from(context);
- FrameLayout root = new FrameLayout(context);
+ final BenchmarkState state = mBenchmarkRule.getState();
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ FrameLayout root = new FrameLayout(mContext);
while (state.keepRunning()) {
inflater.inflate(R.layout.test_simple_view, root, false);
}
@@ -47,12 +62,33 @@
@Test
public void testTwelveKeyInflate() {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
- LayoutInflater inflater = LayoutInflater.from(context);
- FrameLayout root = new FrameLayout(context);
+ final BenchmarkState state = mBenchmarkRule.getState();
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ FrameLayout root = new FrameLayout(mContext);
while (state.keepRunning()) {
inflater.inflate(R.layout.twelve_key_entry, root, false);
}
}
+
+ @Test
+ public void testPerformHapticFeedback() throws Throwable {
+ final BenchmarkState state = mBenchmarkRule.getState();
+ mActivityRule.runOnUiThread(() -> {
+ state.pauseTiming();
+ View view = new View(mContext);
+ mActivityRule.getActivity().setContentView(view);
+ assertTrue("View needs to be attached to Window to perform haptic feedback",
+ view.isAttachedToWindow());
+ state.resumeTiming();
+
+ // Disable settings so perform will never be ignored.
+ int flags = HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
+ | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING;
+
+ while (state.keepRunning()) {
+ assertTrue("Call to performHapticFeedback was ignored",
+ view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_PRESS, flags));
+ }
+ });
+ }
}
diff --git a/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java b/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
index b0edb11..a69d3ff 100644
--- a/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
+++ b/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
@@ -21,14 +21,14 @@
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.PerfTestActivity;
import android.view.View.MeasureSpec;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
@@ -50,7 +50,7 @@
new ActivityTestRule<>(PerfTestActivity.class);
@Rule
- public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+ public final BenchmarkRule mBenchmarkRule = new BenchmarkRule();
public Context getContext() {
return InstrumentationRegistry.getInstrumentation().getTargetContext();
@@ -143,7 +143,7 @@
private void testParentWithChild(TestCallback callback) throws Throwable {
mActivityRule.runOnUiThread(() -> {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final BenchmarkState state = mBenchmarkRule.getState();
FrameLayout parent = new FrameLayout(getContext());
mActivityRule.getActivity().setContentView(parent);
diff --git a/apct-tests/perftests/core/src/android/widget/EditTextLongTextPerfTest.java b/apct-tests/perftests/core/src/android/widget/EditTextLongTextPerfTest.java
index f4ad5dd..c48fa4f 100644
--- a/apct-tests/perftests/core/src/android/widget/EditTextLongTextPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/EditTextLongTextPerfTest.java
@@ -43,8 +43,8 @@
@Parameters(name = "{0}")
public static Collection cases() {
return Arrays.asList(new Object[][] {
- { "10x30K", 10, 30000 },
- { "300x1K", 300, 1000 },
+ { "10x3K", 10, 3000 },
+ { "30x1K", 30, 1000 },
});
}
diff --git a/apct-tests/perftests/core/src/com/android/internal/util/FastDataPerfTest.java b/apct-tests/perftests/core/src/com/android/internal/util/FastDataPerfTest.java
new file mode 100644
index 0000000..2700fff
--- /dev/null
+++ b/apct-tests/perftests/core/src/com/android/internal/util/FastDataPerfTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.DataOutput;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class FastDataPerfTest {
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ private static final int OUTPUT_SIZE = 64000;
+ private static final int BUFFER_SIZE = 4096;
+
+ @Test
+ public void timeWrite_Upstream() throws IOException {
+ final ByteArrayOutputStream os = new ByteArrayOutputStream(OUTPUT_SIZE);
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ os.reset();
+ final BufferedOutputStream bos = new BufferedOutputStream(os, BUFFER_SIZE);
+ final DataOutput out = new DataOutputStream(bos);
+ doWrite(out);
+ bos.flush();
+ }
+ }
+
+ @Test
+ public void timeWrite_Local() throws IOException {
+ final ByteArrayOutputStream os = new ByteArrayOutputStream(OUTPUT_SIZE);
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ os.reset();
+ final FastDataOutput out = new FastDataOutput(os, BUFFER_SIZE);
+ doWrite(out);
+ out.flush();
+ }
+ }
+
+ @Test
+ public void timeRead_Upstream() throws Exception {
+ final ByteArrayInputStream is = new ByteArrayInputStream(doWrite());
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ is.reset();
+ final BufferedInputStream bis = new BufferedInputStream(is, BUFFER_SIZE);
+ final DataInput in = new DataInputStream(bis);
+ doRead(in);
+ }
+ }
+
+ @Test
+ public void timeRead_Local() throws Exception {
+ final ByteArrayInputStream is = new ByteArrayInputStream(doWrite());
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ is.reset();
+ final DataInput in = new FastDataInput(is, BUFFER_SIZE);
+ doRead(in);
+ }
+ }
+
+ /**
+ * Since each iteration is around 64 bytes, we need to iterate many times to
+ * exercise the buffer logic.
+ */
+ private static final int REPEATS = 1000;
+
+ private static byte[] doWrite() throws IOException {
+ final ByteArrayOutputStream os = new ByteArrayOutputStream(OUTPUT_SIZE);
+ final DataOutput out = new DataOutputStream(os);
+ doWrite(out);
+ return os.toByteArray();
+ }
+
+ private static void doWrite(DataOutput out) throws IOException {
+ for (int i = 0; i < REPEATS; i++) {
+ out.writeByte(Byte.MAX_VALUE);
+ out.writeShort(Short.MAX_VALUE);
+ out.writeInt(Integer.MAX_VALUE);
+ out.writeLong(Long.MAX_VALUE);
+ out.writeFloat(Float.MAX_VALUE);
+ out.writeDouble(Double.MAX_VALUE);
+ out.writeUTF("com.example.typical_package_name");
+ }
+ }
+
+ private static void doRead(DataInput in) throws IOException {
+ for (int i = 0; i < REPEATS; i++) {
+ in.readByte();
+ in.readShort();
+ in.readInt();
+ in.readLong();
+ in.readFloat();
+ in.readDouble();
+ in.readUTF();
+ }
+ }
+}
diff --git a/apct-tests/perftests/inputmethod/Android.bp b/apct-tests/perftests/inputmethod/Android.bp
new file mode 100644
index 0000000..f2f1f75
--- /dev/null
+++ b/apct-tests/perftests/inputmethod/Android.bp
@@ -0,0 +1,39 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // 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"],
+}
+
+android_test {
+ name: "ImePerfTests",
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "androidx.test.rules",
+ "androidx.annotation_annotation",
+ "apct-perftests-utils",
+ "collector-device-lib",
+ "compatibility-device-util-axt",
+ "platform-test-annotations",
+ ],
+ test_suites: ["device-tests"],
+ data: [":perfetto_artifacts"],
+ platform_apis: true,
+ certificate: "platform",
+}
diff --git a/apct-tests/perftests/inputmethod/AndroidManifest.xml b/apct-tests/perftests/inputmethod/AndroidManifest.xml
new file mode 100644
index 0000000..1fb0b88
--- /dev/null
+++ b/apct-tests/perftests/inputmethod/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.perftests.inputmethod">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.perftests.utils.PerfTestActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="com.android.perftests.core.PERFTEST" />
+ </intent-filter>
+ </activity>
+ <service android:name="android.inputmethod.ImePerfTest$BaselineIme"
+ android:process=":BaselineIME"
+ android:label="Baseline IME"
+ android:permission="android.permission.BIND_INPUT_METHOD"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.view.InputMethod"/>
+ </intent-filter>
+ <meta-data android:name="android.view.im"
+ android:resource="@xml/simple_method"/>
+ </service>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.perftests.inputmethod">
+ <meta-data android:name="listener" android:value="android.inputmethod.ImePerfRunPrecondition" />
+ </instrumentation>
+</manifest>
diff --git a/apct-tests/perftests/inputmethod/AndroidTest.xml b/apct-tests/perftests/inputmethod/AndroidTest.xml
new file mode 100644
index 0000000..1ec0cba
--- /dev/null
+++ b/apct-tests/perftests/inputmethod/AndroidTest.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs ImePerfTests metric instrumentation.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-metric-instrumentation" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="ImePerfTests.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="force-skip-system-props" value="true" />
+ <option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
+ <option name="run-command" value="cmd window dismiss-keyguard" />
+ <option name="run-command" value="cmd package compile -m speed com.android.perftests.inputmethod" />
+ </target_preparer>
+
+ <!-- Needed for pushing the trace config file -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="push-file" key="trace_config_detailed.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" />
+ <!--Install the content provider automatically when we push some file in sdcard folder.-->
+ <!--Needed to avoid the installation during the test suite.-->
+ <option name="push-file" key="trace_config_detailed.textproto" value="/sdcard/sample.textproto" />
+ </target_preparer>
+
+ <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+ <option name="isolated-storage" value="false" />
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.perftests.inputmethod" />
+ <option name="hidden-api-checks" value="false"/>
+
+ <!-- Listener related args for collecting the traces and waiting for the device to stabilize. -->
+ <option name="device-listeners" value="android.device.collectors.ProcLoadListener,android.device.collectors.PerfettoListener" />
+
+ <!-- Guarantee that user defined RunListeners will be running before any of the default listeners defined in this runner. -->
+ <option name="instrumentation-arg" key="newRunListenerMode" value="true" />
+
+ <!-- Kill background operations -->
+ <option name="instrumentation-arg" key="kill-bg" value="true" />
+
+ <!-- ProcLoadListener related arguments -->
+ <!-- Wait for device last minute threshold to reach 3 with 2 minute timeout before starting the test run -->
+ <option name="instrumentation-arg" key="procload-collector:per_run" value="true" />
+ <option name="instrumentation-arg" key="proc-loadavg-threshold" value="3" />
+ <option name="instrumentation-arg" key="proc-loadavg-timeout" value="120000" />
+ <option name="instrumentation-arg" key="proc-loadavg-interval" value="10000" />
+
+ <!-- PerfettoListener related arguments -->
+ <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" />
+ <option name="instrumentation-arg" key="perfetto_config_file" value="trace_config.textproto" />
+ </test>
+
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="directory-keys" value="/data/local/tmp/ImePerfTests" />
+ <!-- Needed for pulling the collected trace config on to the host -->
+ <option name="pull-pattern-keys" value="perfetto_file_path" />
+ </metrics_collector>
+</configuration>
diff --git a/apct-tests/perftests/inputmethod/OWNERS b/apct-tests/perftests/inputmethod/OWNERS
new file mode 100644
index 0000000..5deb2ce
--- /dev/null
+++ b/apct-tests/perftests/inputmethod/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/view/inputmethod/OWNERS
diff --git a/apct-tests/perftests/inputmethod/README.md b/apct-tests/perftests/inputmethod/README.md
new file mode 100644
index 0000000..8ba2087
--- /dev/null
+++ b/apct-tests/perftests/inputmethod/README.md
@@ -0,0 +1,40 @@
+## IMF performance tests
+
+These tests are adaptation of Window Manager perf tests (apct-tests/perftests/windowmanager).
+
+### Precondition
+To reduce the variance of the test, if `perf-setup` (platform_testing/scripts/perf-setup)
+is available, it is better to use the following instructions to lock CPU and GPU frequencies.
+```
+m perf-setup
+PERF_SETUP_PATH=/data/local/tmp/perf-setup.sh
+adb push $OUT/$PERF_SETUP_PATH $PERF_SETUP_PATH
+adb shell chmod +x $PERF_SETUP_PATH
+adb shell $PERF_SETUP_PATH
+```
+
+### Example to run
+Use `atest`
+```
+atest ImePerfTests:ImePerfTest -- \
+ --module-arg ImePerfTests:instrumentation-arg:profiling-iterations:=20
+
+```
+Note: `instrumentation-arg:kill-bg:=true` is already defined in the AndroidText.xml
+
+Use `am instrument`
+```
+adb shell am instrument -w -r -e class android.inputmethod.ImePerfTest \
+ -e listener android.inputmethod.ImePerfRunPrecondition \
+ -e kill-bg true \
+ com.android.perftests.inputmethod/androidx.test.runner.AndroidJUnitRunner
+```
+* `kill-bg` is optional.
+
+Test arguments
+ - kill-bg
+ * boolean: Kill background process before running test.
+ - profiling-iterations
+ * int: Run the extra iterations with enabling method profiling.
+ - profiling-sampling
+ * int: The interval (0=trace each method, default is 10) of sample profiling in microseconds.
diff --git a/apct-tests/perftests/inputmethod/res/xml/simple_method.xml b/apct-tests/perftests/inputmethod/res/xml/simple_method.xml
new file mode 100644
index 0000000..87cb1ad
--- /dev/null
+++ b/apct-tests/perftests/inputmethod/res/xml/simple_method.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- Configuration info for an input method -->
+<input-method xmlns:android="http://schemas.android.com/apk/res/android" />
diff --git a/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfRunPrecondition.java b/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfRunPrecondition.java
new file mode 100644
index 0000000..4bfcade
--- /dev/null
+++ b/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfRunPrecondition.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethod;
+
+import android.perftests.utils.WindowPerfRunPreconditionBase;
+
+/** Prepare the preconditions before running performance test. */
+public class ImePerfRunPrecondition extends WindowPerfRunPreconditionBase {
+}
diff --git a/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java b/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java
new file mode 100644
index 0000000..ab3c50b
--- /dev/null
+++ b/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java
@@ -0,0 +1,501 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethod;
+
+import static android.perftests.utils.ManualBenchmarkState.StatsReport;
+import static android.perftests.utils.PerfTestActivity.ID_EDITOR;
+import static android.perftests.utils.TestUtils.getOnMainSync;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertTrue;
+
+import android.annotation.UiThread;
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.inputmethodservice.InputMethodService;
+import android.os.Process;
+import android.os.SystemClock;
+import android.perftests.utils.ManualBenchmarkState;
+import android.perftests.utils.ManualBenchmarkState.ManualBenchmarkTest;
+import android.perftests.utils.PerfManualStatusReporter;
+import android.perftests.utils.TraceMarkParser;
+import android.perftests.utils.TraceMarkParser.TraceMarkSlice;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowInsets;
+import android.view.WindowInsetsAnimation;
+import android.view.WindowInsetsController;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.filters.LargeTest;
+
+import com.android.compatibility.common.util.PollingCheck;
+
+import junit.framework.Assert;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+
+/** Measure the performance of internal methods in Input Method framework by trace tag. */
+@LargeTest
+public class ImePerfTest extends ImePerfTestBase
+ implements ManualBenchmarkState.CustomizedIterationListener {
+ private static final String TAG = ImePerfTest.class.getSimpleName();
+ private static final long ANIMATION_NOT_STARTED = -1;
+
+ @Rule
+ public final PerfManualStatusReporter mPerfStatusReporter = new PerfManualStatusReporter();
+
+ @Rule
+ public final PerfTestActivityRule mActivityRule = new PerfTestActivityRule();
+
+ /**
+ * IMF common methods to log for show/hide in trace.
+ */
+ private String[] mCommonMethods = {
+ "IC.pendingAnim",
+ "IMMS.applyImeVisibility",
+ "applyPostLayoutPolicy",
+ "applyWindowSurfaceChanges",
+ "ISC.onPostLayout"
+ };
+
+ /** IMF show methods to log in trace. */
+ private String[] mShowMethods = {
+ "IC.showRequestFromIme",
+ "IC.showRequestFromApi",
+ "IC.showRequestFromApiToImeReady",
+ "IC.pendingAnim",
+ "IMMS.applyImeVisibility",
+ "IMMS.showMySoftInput",
+ "IMMS.showSoftInput",
+ "IMS.showSoftInput",
+ "IMS.startInput",
+ "WMS.showImePostLayout",
+ "IMS.updateFullscreenMode",
+ "IMS.onComputeInsets",
+ "IMS.showWindow"
+ };
+
+ /** IMF show methods to log in trace. */
+ private String[] mShowMethodsCold = {
+ "IMS.bindInput",
+ "IMS.initializeInternal",
+ "IMS.restartInput",
+ "IMS.onCreate",
+ "IMS.initSoftInputWindow",
+ "IMS.resetStateForNewConfiguration",
+ "IMMS.onServiceConnected",
+ "IMMS.sessionCreated",
+ "IMMS.startInputOrWindowGainedFocus"
+ };
+
+ /** IMF hide lifecycle methods to log in trace. */
+ private String[] mHideMethods = {
+ "IC.hideRequestFromIme",
+ "IC.hideRequestFromApi",
+ "IMMS.hideMySoftInput",
+ "IMMS.hideSoftInput",
+ "IMS.hideSoftInput",
+ "WMS.hideIme"
+ };
+
+ /**
+ * IMF methods to log in trace.
+ */
+ private TraceMarkParser mTraceMethods;
+
+ private boolean mIsTraceStarted;
+
+ /**
+ * Ime Session for {@link BaselineIme}.
+ */
+ private static class ImeSession implements AutoCloseable {
+
+ private static final long TIMEOUT = 2000;
+ private final ComponentName mImeName;
+ private Context mContext = getInstrumentation().getContext();
+
+ ImeSession(ComponentName ime) throws Exception {
+ mImeName = ime;
+ // using adb, enable and set Baseline IME.
+ executeShellCommand("ime reset");
+ executeShellCommand("ime enable " + ime.flattenToShortString());
+ executeShellCommand("ime set " + ime.flattenToShortString());
+ PollingCheck.check("Make sure that BaselineIme becomes available "
+ + getCurrentInputMethodId(), TIMEOUT,
+ () -> ime.equals(getCurrentInputMethodId()));
+ }
+
+ @Override
+ public void close() throws Exception {
+ executeShellCommand("ime reset");
+ PollingCheck.check("Make sure that Baseline IME becomes unavailable", TIMEOUT, () ->
+ mContext.getSystemService(InputMethodManager.class)
+ .getEnabledInputMethodList()
+ .stream()
+ .noneMatch(info -> mImeName.equals(info.getComponent())));
+ }
+
+ @Nullable
+ private ComponentName getCurrentInputMethodId() {
+ return ComponentName.unflattenFromString(
+ Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.DEFAULT_INPUT_METHOD));
+ }
+ }
+
+ /**
+ * A minimal baseline IME (that has a single static view) used to measure IMF latency.
+ */
+ public static class BaselineIme extends InputMethodService {
+
+ public static final int HEIGHT_DP = 100;
+ private static int sPid;
+
+ @Override
+ public View onCreateInputView() {
+ final ViewGroup view = new FrameLayout(this);
+ final View inner = new View(this);
+ final float density = getResources().getDisplayMetrics().density;
+ final int height = (int) (HEIGHT_DP * density);
+ view.setPadding(0, 0, 0, 0);
+ view.addView(inner, new FrameLayout.LayoutParams(MATCH_PARENT, height));
+ inner.setBackgroundColor(0xff01fe10); // green
+ sPid = Process.myPid();
+ return view;
+ }
+
+ static int getPid() {
+ return sPid;
+ }
+
+ static ComponentName getName(Context context) {
+ return new ComponentName(context, BaselineIme.class);
+ }
+ }
+
+ @Test
+ @ManualBenchmarkTest(
+ targetTestDurationNs = 10 * TIME_1_S_IN_NS,
+ statsReport = @StatsReport(
+ flags = StatsReport.FLAG_ITERATION | StatsReport.FLAG_MEAN
+ | StatsReport.FLAG_MIN | StatsReport.FLAG_MAX
+ | StatsReport.FLAG_COEFFICIENT_VAR))
+ public void testShowImeWarm() throws Throwable {
+ testShowOrHideImeWarm(true /* show */);
+ }
+
+ @Test
+ @ManualBenchmarkTest(
+ targetTestDurationNs = 10 * TIME_1_S_IN_NS,
+ statsReport = @StatsReport(
+ flags = StatsReport.FLAG_ITERATION | StatsReport.FLAG_MEAN
+ | StatsReport.FLAG_MIN | StatsReport.FLAG_MAX
+ | StatsReport.FLAG_COEFFICIENT_VAR))
+ public void testHideIme() throws Throwable {
+ testShowOrHideImeWarm(false /* show */);
+ }
+
+ @Test
+ @ManualBenchmarkTest(
+ targetTestDurationNs = 10 * TIME_1_S_IN_NS,
+ statsReport = @StatsReport(
+ flags = StatsReport.FLAG_ITERATION | StatsReport.FLAG_MEAN
+ | StatsReport.FLAG_MIN | StatsReport.FLAG_MAX
+ | StatsReport.FLAG_COEFFICIENT_VAR))
+ public void testShowImeCold() throws Throwable {
+ mTraceMethods = new TraceMarkParser(
+ buildArray(mCommonMethods, mShowMethods, mShowMethodsCold));
+
+ final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ state.setCustomizedIterations(getProfilingIterations(), this);
+ if (state.isWarmingUp()) {
+ // we don't need to warmup for cold start.
+ return;
+ }
+
+ long measuredTimeNs = 0;
+ while (state.keepRunning(measuredTimeNs)) {
+ killBaselineIme();
+ try (ImeSession imeSession = new ImeSession(BaselineIme.getName(
+ getInstrumentation().getContext()))) {
+ final AtomicReference<CountDownLatch> latchStart = new AtomicReference<>();
+ final Activity activity = getActivityWithFocus();
+
+ setImeListener(activity, latchStart, null /* latchEnd */);
+ latchStart.set(new CountDownLatch(1));
+
+ if (!mIsTraceStarted) {
+ startAsyncAtrace();
+ }
+
+ final WindowInsetsController controller =
+ activity.getWindow().getDecorView().getWindowInsetsController();
+ AtomicLong startTime = new AtomicLong();
+ activity.runOnUiThread(() -> {
+ startTime.set(SystemClock.elapsedRealtimeNanos());
+ controller.show(WindowInsets.Type.ime());
+ });
+
+ measuredTimeNs = waitForAnimationStart(latchStart, startTime);
+ mActivityRule.finishActivity();
+ }
+ }
+ stopAsyncAtrace();
+ addResultToState(state);
+ }
+
+ private void killBaselineIme() {
+ assertTrue("PID of test and IME can't be same",
+ Process.myPid() != BaselineIme.getPid());
+ Process.killProcess(BaselineIme.getPid());
+ }
+
+ private void testShowOrHideImeWarm(final boolean show) throws Throwable {
+ mTraceMethods = new TraceMarkParser(buildArray(
+ mCommonMethods, show ? mShowMethods : mHideMethods));
+ final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ state.setCustomizedIterations(getProfilingIterations(), this);
+ long measuredTimeNs = 0;
+ try (ImeSession imeSession = new ImeSession(BaselineIme.getName(
+ getInstrumentation().getContext()))) {
+ final AtomicReference<CountDownLatch> latchStart = new AtomicReference<>();
+ final AtomicReference<CountDownLatch> latchEnd = new AtomicReference<>();
+ final Activity activity = getActivityWithFocus();
+
+ // call IME show/hide
+ final WindowInsetsController controller =
+ activity.getWindow().getDecorView().getWindowInsetsController();
+
+ while (state.keepRunning(measuredTimeNs)) {
+ setImeListener(activity, latchStart, latchEnd);
+ // For measuring hide, lets show IME first.
+ if (!show) {
+ initLatch(latchStart, latchEnd);
+ AtomicBoolean showCalled = new AtomicBoolean();
+ getInstrumentation().runOnMainSync(() -> {
+ if (!isImeVisible(activity)) {
+ controller.show(WindowInsets.Type.ime());
+ showCalled.set(true);
+ }
+ });
+ if (showCalled.get()) {
+ PollingCheck.check("IME show animation should finish ",
+ TIMEOUT_1_S_IN_MS * 3,
+ () -> latchStart.get().getCount() == 0
+ && latchEnd.get().getCount() == 0);
+ }
+ }
+ if (!mIsTraceStarted && !state.isWarmingUp()) {
+ startAsyncAtrace();
+ mIsTraceStarted = true;
+ }
+
+ AtomicLong startTime = new AtomicLong();
+ AtomicBoolean unexpectedVisibility = new AtomicBoolean();
+ initLatch(latchStart, latchEnd);
+ getInstrumentation().runOnMainSync(() -> {
+ boolean isVisible = isImeVisible(activity);
+ startTime.set(SystemClock.elapsedRealtimeNanos());
+
+ if (show && !isVisible) {
+ controller.show(WindowInsets.Type.ime());
+ } else if (!show && isVisible) {
+ controller.hide(WindowInsets.Type.ime());
+ } else {
+ // ignore this iteration as unexpected IME visibility was encountered.
+ unexpectedVisibility.set(true);
+ }
+ });
+
+ if (!unexpectedVisibility.get()) {
+ long timeElapsed = waitForAnimationStart(latchStart, startTime);
+ if (timeElapsed != ANIMATION_NOT_STARTED) {
+ measuredTimeNs = timeElapsed;
+ // wait for animation to end or we may start two animations and timing
+ // will not be measured accurately.
+ waitForAnimationEnd(latchEnd);
+ }
+ }
+
+ // hide IME before next iteration.
+ if (show) {
+ initLatch(latchStart, latchEnd);
+ activity.runOnUiThread(() -> controller.hide(WindowInsets.Type.ime()));
+ try {
+ latchEnd.get().await(TIMEOUT_1_S_IN_MS * 5, TimeUnit.MILLISECONDS);
+ if (latchEnd.get().getCount() != 0
+ && getOnMainSync(() -> isImeVisible(activity))) {
+ Assert.fail("IME hide animation should finish.");
+ }
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ } finally {
+ if (mIsTraceStarted) {
+ stopAsyncAtrace();
+ }
+ }
+ mActivityRule.finishActivity();
+
+ addResultToState(state);
+ }
+
+ private void initLatch(AtomicReference<CountDownLatch> latchStart,
+ AtomicReference<CountDownLatch> latchEnd) {
+ latchStart.set(new CountDownLatch(1));
+ latchEnd.set(new CountDownLatch(1));
+ }
+
+ @UiThread
+ private boolean isImeVisible(@NonNull final Activity activity) {
+ return activity.getWindow().getDecorView().getRootWindowInsets().isVisible(
+ WindowInsets.Type.ime());
+ }
+
+ private long waitForAnimationStart(
+ AtomicReference<CountDownLatch> latchStart, AtomicLong startTime) {
+ try {
+ latchStart.get().await(5, TimeUnit.SECONDS);
+ if (latchStart.get().getCount() != 0) {
+ return ANIMATION_NOT_STARTED;
+ }
+ } catch (InterruptedException e) { }
+
+ return SystemClock.elapsedRealtimeNanos() - startTime.get();
+ }
+
+ private void waitForAnimationEnd(AtomicReference<CountDownLatch> latchEnd) {
+ try {
+ latchEnd.get().await(3, TimeUnit.SECONDS);
+ } catch (InterruptedException e) { }
+ }
+
+ private void addResultToState(ManualBenchmarkState state) {
+ mTraceMethods.forAllSlices((key, slices) -> {
+ for (TraceMarkSlice slice : slices) {
+ state.addExtraResult(key, (long) (slice.getDurationInSeconds() * NANOS_PER_S));
+ }
+ });
+ Log.i(TAG, String.valueOf(mTraceMethods));
+ }
+
+ private Activity getActivityWithFocus() throws Exception {
+ final Activity activity = mActivityRule.launchActivity();
+ PollingCheck.check("Activity onResume()", TIMEOUT_1_S_IN_MS,
+ () -> activity.isResumed());
+
+ View editor = activity.findViewById(ID_EDITOR);
+ editor.requestFocus();
+
+ // wait till editor is focused so we don't count activity/view latency.
+ PollingCheck.check("Editor is focused", TIMEOUT_1_S_IN_MS,
+ () -> editor.isFocused());
+ getInstrumentation().waitForIdleSync();
+
+ return activity;
+ }
+
+ private void setImeListener(Activity activity,
+ @NonNull AtomicReference<CountDownLatch> latchStart,
+ @Nullable AtomicReference<CountDownLatch> latchEnd) {
+ // set IME animation listener
+ activity.getWindow().getDecorView().setWindowInsetsAnimationCallback(
+ new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
+ @NonNull
+ @Override
+ public WindowInsetsAnimation.Bounds onStart(
+ @NonNull WindowInsetsAnimation animation,
+ @NonNull WindowInsetsAnimation.Bounds bounds) {
+ latchStart.get().countDown();
+ return super.onStart(animation, bounds);
+ }
+
+ @NonNull
+ @Override
+ public WindowInsets onProgress(@NonNull WindowInsets insets,
+ @NonNull List<WindowInsetsAnimation> runningAnimations) {
+ return insets;
+ }
+
+ @Override
+ public void onEnd(@NonNull WindowInsetsAnimation animation) {
+ super.onEnd(animation);
+ if (latchEnd != null) {
+ latchEnd.get().countDown();
+ }
+ }
+ });
+ }
+
+ private void startAsyncAtrace() {
+ mIsTraceStarted = true;
+ // IMF uses 'wm' component for trace in InputMethodService, InputMethodManagerService,
+ // WindowManagerService and 'view' for client window (InsetsController).
+ // TODO(b/167947940): Consider a separate input_method atrace
+ startAsyncAtrace("wm view");
+ }
+
+ private void stopAsyncAtrace() {
+ if (!mIsTraceStarted) {
+ return;
+ }
+ mIsTraceStarted = false;
+ final InputStream inputStream = stopAsyncAtraceWithStream();
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ mTraceMethods.visit(line);
+ }
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to read the result of stopped atrace", e);
+ }
+ }
+
+ @Override
+ public void onStart(int iteration) {
+ // Do not capture trace when profiling because the result will be much slower.
+ stopAsyncAtrace();
+ }
+
+ @Override
+ public void onFinished(int iteration) {
+ // do nothing.
+ }
+}
diff --git a/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTestBase.java b/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTestBase.java
new file mode 100644
index 0000000..f70d79c
--- /dev/null
+++ b/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTestBase.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethod;
+
+import static android.perftests.utils.PerfTestActivity.INTENT_EXTRA_ADD_EDIT_TEXT;
+
+import android.content.Intent;
+import android.perftests.utils.PerfTestActivity;
+import android.perftests.utils.WindowPerfTestBase;
+
+public class ImePerfTestBase extends WindowPerfTestBase {
+ static final long TIMEOUT_1_S_IN_MS = 1 * 1000L;
+
+ /** Provides an activity that contains an edit text view.*/
+ static class PerfTestActivityRule extends PerfTestActivityRuleBase {
+
+ @Override
+ public PerfTestActivity launchActivity(Intent intent) {
+ intent.putExtra(INTENT_EXTRA_ADD_EDIT_TEXT, true);
+ return super.launchActivity(intent);
+ }
+ }
+
+ static String[] buildArray(String[]... arrays) {
+ int length = 0;
+ for (String[] array : arrays) {
+ length += array.length;
+ }
+ String[] newArray = new String[length];
+ int offset = 0;
+ for (String[] array : arrays) {
+ System.arraycopy(array, 0, newArray, offset, array.length);
+ offset += array.length;
+ }
+ return newArray;
+ }
+}
diff --git a/apct-tests/perftests/multiuser/Android.bp b/apct-tests/perftests/multiuser/Android.bp
index 04432f2..c967e51 100644
--- a/apct-tests/perftests/multiuser/Android.bp
+++ b/apct-tests/perftests/multiuser/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // 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"],
+}
+
android_test {
name: "MultiUserPerfTests",
srcs: ["src/**/*.java"],
@@ -22,5 +31,6 @@
],
platform_apis: true,
test_suites: ["device-tests"],
+ data: ["trace_configs/*"],
certificate: "platform",
}
diff --git a/apct-tests/perftests/multiuser/AndroidManifest.xml b/apct-tests/perftests/multiuser/AndroidManifest.xml
index e4196dd..63e5983 100644
--- a/apct-tests/perftests/multiuser/AndroidManifest.xml
+++ b/apct-tests/perftests/multiuser/AndroidManifest.xml
@@ -17,16 +17,16 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.perftests.multiuser">
- <uses-sdk android:targetSdkVersion="28" />
<uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
- <uses-permission android:name="android.permission.MANAGE_USERS" />
+ <uses-permission android:name="android.permission.DEVICE_POWER" />
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
- <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.MANAGE_USERS" />
<uses-permission android:name="android.permission.REAL_GET_TASKS" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/apct-tests/perftests/multiuser/AndroidTest.xml b/apct-tests/perftests/multiuser/AndroidTest.xml
index 9117561..8e342f3 100644
--- a/apct-tests/perftests/multiuser/AndroidTest.xml
+++ b/apct-tests/perftests/multiuser/AndroidTest.xml
@@ -16,14 +16,51 @@
<configuration description="Runs MultiUserPerfTests metric instrumentation.">
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-metric-instrumentation" />
+
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="MultiUserPerfTests.apk" />
<option name="test-file-name" value="MultiUserPerfDummyApp.apk" />
</target_preparer>
+ <!-- Needed for pushing the trace config file -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="push-file" key="trace_config_multi_user.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" />
+ <!--Install the content provider automatically when we push some file in sdcard folder.-->
+ <!--Needed to avoid the installation during the test suite.-->
+ <option name="push-file" key="trace_config_detailed.textproto" value="/sdcard/sample.textproto" />
+ </target_preparer>
+
+ <!-- Needed for pulling the collected trace config on to the host -->
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="pull-pattern-keys" value="perfetto_file_path" />
+ </metrics_collector>
+
+ <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+ <option name="isolated-storage" value="false" />
+
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.perftests.multiuser" />
<option name="hidden-api-checks" value="false"/>
+
+ <!-- Listener related args for collecting the traces and waiting for the device to stabilize. -->
+ <option name="device-listeners" value="android.device.collectors.ProcLoadListener,android.device.collectors.PerfettoListener" />
+ <!-- Guarantee that user defined RunListeners will be running before any of the default listeners defined in this runner. -->
+ <option name="instrumentation-arg" key="newRunListenerMode" value="true" />
+
+ <!-- ProcLoadListener related arguments -->
+ <!-- Wait for device last minute threshold to reach 3 with 2 minute timeout before starting the test run -->
+ <option name="instrumentation-arg" key="procload-collector:per_run" value="true" />
+ <option name="instrumentation-arg" key="proc-loadavg-threshold" value="3" />
+ <option name="instrumentation-arg" key="proc-loadavg-timeout" value="120000" />
+ <option name="instrumentation-arg" key="proc-loadavg-interval" value="10000" />
+
+ <!-- PerfettoListener related arguments -->
+ <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" />
+ <option name="instrumentation-arg" key="perfetto_config_file" value="trace_config.textproto" />
+
+ <option name="instrumentation-arg" key="newRunListenerMode" value="true" />
+
</test>
</configuration>
diff --git a/apct-tests/perftests/multiuser/OWNERS b/apct-tests/perftests/multiuser/OWNERS
new file mode 100644
index 0000000..1a206cb
--- /dev/null
+++ b/apct-tests/perftests/multiuser/OWNERS
@@ -0,0 +1 @@
+include /MULTIUSER_OWNERS
\ No newline at end of file
diff --git a/apct-tests/perftests/multiuser/apps/dummyapp/Android.bp b/apct-tests/perftests/multiuser/apps/dummyapp/Android.bp
index 08c54a6..892c140 100644
--- a/apct-tests/perftests/multiuser/apps/dummyapp/Android.bp
+++ b/apct-tests/perftests/multiuser/apps/dummyapp/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // 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"],
+}
+
android_test_helper_app {
name: "MultiUserPerfDummyApp",
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResults.java b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResults.java
index c1362dc..cd3c11c 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResults.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResults.java
@@ -22,6 +22,9 @@
import java.util.concurrent.TimeUnit;
public class BenchmarkResults {
+ /** If the test fails, output this value as a signal of the failure. */
+ public static final long DECLARED_VALUE_IF_ERROR_MS = -10;
+
private final ArrayList<Long> mResults = new ArrayList<>();
public void addDuration(long duration) {
@@ -42,6 +45,28 @@
return stats;
}
+ /**
+ * Same as {@link #getStatsToReport()} but for failure,
+ * using {@link #DECLARED_VALUE_IF_ERROR_MS}.
+ */
+ public static Bundle getFailedStatsToReport() {
+ final Bundle stats = new Bundle();
+ stats.putDouble("Mean (ms)", DECLARED_VALUE_IF_ERROR_MS);
+ return stats;
+ }
+
+ /**
+ * Same as {@link #getStatsToLog()} but for failure,
+ * using {@link #DECLARED_VALUE_IF_ERROR_MS}.
+ */
+ public static Bundle getFailedStatsToLog() {
+ final Bundle stats = new Bundle();
+ stats.putDouble("Mean (ms)", DECLARED_VALUE_IF_ERROR_MS);
+ stats.putDouble("Median (ms)", DECLARED_VALUE_IF_ERROR_MS);
+ stats.putDouble("Sigma (ms)", DECLARED_VALUE_IF_ERROR_MS);
+ return stats;
+ }
+
public ArrayList<Long> getAllDurations() {
return mResults;
}
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java
index ba33e64..b6f13fd 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java
@@ -15,6 +15,8 @@
*/
package android.multiuser;
+import static android.multiuser.BenchmarkResults.DECLARED_VALUE_IF_ERROR_MS;
+
import android.app.Activity;
import android.app.Instrumentation;
import android.os.Bundle;
@@ -40,28 +42,70 @@
return new Statement() {
@Override
public void evaluate() throws Throwable {
- base.evaluate();
- final Bundle stats = mRunner.getStatsToReport();
- final String summary = getSummaryString(description.getMethodName(),
- mRunner.getStatsToLog());
- logSummary(description.getTestClass().getSimpleName(), summary,
- mRunner.getAllDurations());
- stats.putString(Instrumentation.REPORT_KEY_STREAMRESULT, summary);
- InstrumentationRegistry.getInstrumentation().sendStatus(
- Activity.RESULT_OK, stats);
+ final String tag = description.getTestClass().getSimpleName();
+ final String methodName = description.getMethodName();
+ Throwable error = null;
+
+ try {
+ base.evaluate();
+ error = mRunner.getErrorOrNull();
+ } catch (Exception e) {
+ error = e;
+ }
+
+ if (error != null) {
+ Log.e(tag, "Test " + methodName + " failed.", error);
+ Log.d(tag, "Logcat displays the results ignoring the fact that it failed;\n"
+ + "however, fake results of " + DECLARED_VALUE_IF_ERROR_MS + "ms "
+ + "will be reported to the instrumentation caller to signify failure.");
+ }
+
+ final String summary = getSummaryString(methodName, mRunner.getStatsToLog());
+ logSummary(tag, summary, mRunner.getAllDurations());
+
+ Bundle stats;
+ if (error == null) {
+ stats = mRunner.getStatsToReport();
+ stats.putString(Instrumentation.REPORT_KEY_STREAMRESULT, summary);
+ } else {
+ stats = BenchmarkResults.getFailedStatsToReport();
+ final String failSummary = getSummaryString(methodName,
+ BenchmarkResults.getFailedStatsToLog());
+ stats.putString(Instrumentation.REPORT_KEY_STREAMRESULT, failSummary);
+ }
+ InstrumentationRegistry.getInstrumentation().sendStatus(Activity.RESULT_OK, stats);
+
+ if (error != null) {
+ throw error;
+ }
}
};
}
+ /**
+ * Prints, for example:
+ * UserLifecycleTests: (summary string)
+ * UserLifecycleTests: 1->101
+ * UserLifecycleTests: 2->102
+ * UserLifecycleTests: 3->103
+ * UserLifecycleTests: 4->102
+ */
private void logSummary(String tag, String summary, ArrayList<Long> durations) {
final StringBuilder sb = new StringBuilder(summary);
final int size = durations.size();
for (int i = 0; i < size; ++i) {
- sb.append("\n").append(i).append("->").append(durations.get(i));
+ sb.append("\n").append(i+1).append("->").append(durations.get(i));
}
Log.d(tag, sb.toString());
}
+ /**
+ * For example:
+ * testName
+ * Sigma (ms): 1
+ * Mean (ms): 2
+ * Median (ms): 3
+ */
private String getSummaryString(String testName, Bundle stats) {
final StringBuilder sb = new StringBuilder();
sb.append("\n\n").append(getKey(testName));
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
index 7b65bfa..8305d3f 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
@@ -15,6 +15,7 @@
*/
package android.multiuser;
+import android.annotation.Nullable;
import android.os.Bundle;
import android.os.SystemClock;
import android.perftests.utils.ShellHelper;
@@ -35,12 +36,14 @@
private final BenchmarkResults mResults = new BenchmarkResults();
private int mState = NOT_STARTED; // Current benchmark state.
- private int mIteration;
+ private int mIteration = 1;
public long mStartTimeNs;
public long mPausedDurationNs;
public long mPausedTimeNs;
+ private Throwable mFirstFailure = null;
+
public boolean keepRunning() {
switch (mState) {
case NOT_STARTED:
@@ -61,7 +64,7 @@
private boolean startNextTestRun() {
mResults.addDuration(System.nanoTime() - mStartTimeNs - mPausedDurationNs);
- if (mIteration == NUM_ITERATIONS) {
+ if (mIteration == NUM_ITERATIONS + 1) {
mState = FINISHED;
return false;
} else {
@@ -104,4 +107,30 @@
public ArrayList<Long> getAllDurations() {
return mResults.getAllDurations();
}
+
+ /** Returns which iteration (starting at 1) the Runner is currently on. */
+ public int getIteration() {
+ return mIteration;
+ }
+
+ /**
+ * Marks the test run as failed, along with a message of why.
+ * Only the first fail message is retained.
+ */
+ public void markAsFailed(Throwable err) {
+ if (mFirstFailure == null) {
+ mFirstFailure = err;
+ }
+ }
+
+ /** Gets the failure message if the test failed; otherwise {@code null}. */
+ public @Nullable Throwable getErrorOrNull() {
+ if (mFirstFailure != null) {
+ return mFirstFailure;
+ }
+ if (mState != FINISHED) {
+ return new AssertionError("BenchmarkRunner state is not FINISHED.");
+ }
+ return null;
+ }
}
\ No newline at end of file
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index e042782..b1c42a9 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -17,6 +17,7 @@
import static org.junit.Assume.assumeTrue;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.AppGlobals;
@@ -38,6 +39,7 @@
import android.os.IBinder;
import android.os.IProgressListener;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
@@ -62,7 +64,10 @@
/**
* Perf tests for user life cycle events.
*
- * Running the tests:
+ * To run the tests: atest UserLifecycleTests
+ *
+ *
+ * Old methods for running the tests:
*
* make MultiUserPerfTests &&
* adb install -r \
@@ -85,6 +90,10 @@
private static final int TIMEOUT_IN_SECOND = 30;
private static final int CHECK_USER_REMOVED_INTERVAL_MS = 200;
+ /** Name of users/profiles in the test. Users with this name may be freely removed. */
+ private static final String TEST_USER_NAME = "UserLifecycleTests_test_user";
+
+ /** Name of dummy package used when timing how long app launches take. */
private static final String DUMMY_PACKAGE_NAME = "perftests.multiuser.apps.dummyapp";
// Copy of UserSystemPackageInstaller whitelist mode constants.
@@ -115,6 +124,11 @@
mUsersToRemove = new ArrayList<>();
mPm = context.getPackageManager();
mHasManagedUserFeature = mPm.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS);
+ removeAnyPreviousTestUsers();
+ if (mAm.getCurrentUser() != UserHandle.USER_SYSTEM) {
+ Log.w(TAG, "WARNING: Tests are being run from user " + mAm.getCurrentUser()
+ + " rather than the system user");
+ }
}
@After
@@ -140,7 +154,7 @@
}
@Test
- public void createAndStartUser() throws Exception {
+ public void createAndStartUser() throws RemoteException {
while (mRunner.keepRunning()) {
final int userId = createUserNoFlags();
@@ -149,7 +163,7 @@
// Don't use this.startUserInBackgroundAndWaitForUnlock() since only waiting until
// ACTION_USER_STARTED.
mIam.startUserInBackground(userId);
- latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
+ waitForLatch("Failed to achieve ACTION_USER_STARTED for user " + userId, latch);
mRunner.pauseTiming();
removeUser(userId);
@@ -161,7 +175,7 @@
* Measures the time until ACTION_USER_STARTED is received.
*/
@Test
- public void startUser() throws Exception {
+ public void startUser() throws RemoteException {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int userId = createUserNoFlags();
@@ -170,7 +184,7 @@
mRunner.resumeTiming();
mIam.startUserInBackground(userId);
- latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
+ waitForLatch("Failed to achieve ACTION_USER_STARTED for user " + userId, latch);
mRunner.pauseTiming();
removeUser(userId);
@@ -182,7 +196,7 @@
* Measures the time until unlock listener is triggered and user is unlocked.
*/
@Test
- public void startAndUnlockUser() throws Exception {
+ public void startAndUnlockUser() {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int userId = createUserNoFlags();
@@ -197,10 +211,8 @@
}
}
-
-
@Test
- public void switchUser() throws Exception {
+ public void switchUser() throws RemoteException {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int startUser = mAm.getCurrentUser();
@@ -210,7 +222,7 @@
switchUser(userId);
mRunner.pauseTiming();
- switchUser(startUser);
+ switchUserNoCheck(startUser);
removeUser(userId);
mRunner.resumeTiming();
}
@@ -218,7 +230,7 @@
/** Tests switching to an already-created, but no-longer-running, user. */
@Test
- public void switchUser_stopped() throws Exception {
+ public void switchUser_stopped() throws RemoteException {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int startUser = mAm.getCurrentUser();
@@ -228,11 +240,11 @@
mRunner.resumeTiming();
mAm.switchUser(testUser);
- boolean success = latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
+ waitForLatch("Failed to achieve 2nd ACTION_USER_UNLOCKED for user " + testUser, latch);
+
mRunner.pauseTiming();
- attestTrue("Failed to achieve 2nd ACTION_USER_UNLOCKED for user " + testUser, success);
- switchUser(startUser);
+ switchUserNoCheck(startUser);
removeUser(testUser);
mRunner.resumeTiming();
}
@@ -240,7 +252,7 @@
/** Tests switching to an already-created already-running non-owner user. */
@Test
- public void switchUser_running() throws Exception {
+ public void switchUser_running() throws RemoteException {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int startUser = mAm.getCurrentUser();
@@ -250,22 +262,21 @@
switchUser(testUser);
mRunner.pauseTiming();
- attestTrue("Failed to switch to user " + testUser, mAm.isUserRunning(testUser));
- switchUser(startUser);
+ switchUserNoCheck(startUser);
removeUser(testUser);
mRunner.resumeTiming();
}
}
@Test
- public void stopUser() throws Exception {
+ public void stopUser() throws RemoteException {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int userId = createUserNoFlags();
final CountDownLatch latch = new CountDownLatch(1);
registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userId);
mIam.startUserInBackground(userId);
- latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
+ waitForLatch("Failed to achieve ACTION_USER_STARTED for user " + userId, latch);
mRunner.resumeTiming();
stopUser(userId, false);
@@ -277,7 +288,7 @@
}
@Test
- public void lockedBootCompleted() throws Exception {
+ public void lockedBootCompleted() throws RemoteException {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int startUser = mAm.getCurrentUser();
@@ -287,17 +298,17 @@
mRunner.resumeTiming();
mAm.switchUser(userId);
- latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
+ waitForLatch("Failed to achieve onLockedBootComplete for user " + userId, latch);
mRunner.pauseTiming();
- switchUser(startUser);
+ switchUserNoCheck(startUser);
removeUser(userId);
mRunner.resumeTiming();
}
}
@Test
- public void ephemeralUserStopped() throws Exception {
+ public void ephemeralUserStopped() throws RemoteException {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int startUser = mAm.getCurrentUser();
@@ -318,10 +329,14 @@
mRunner.resumeTiming();
mAm.switchUser(startUser);
- latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
+ waitForLatch("Failed to achieve ACTION_USER_STOPPED for user " + userId, latch);
mRunner.pauseTiming();
- switchLatch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
+ try {
+ switchLatch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Thread interrupted unexpectedly while waiting for switch.", e);
+ }
removeUser(userId);
mRunner.resumeTiming();
}
@@ -329,7 +344,7 @@
/** Tests creating a new profile. */
@Test
- public void managedProfileCreate() throws Exception {
+ public void managedProfileCreate() {
assumeTrue(mHasManagedUserFeature);
while (mRunner.keepRunning()) {
@@ -344,7 +359,7 @@
/** Tests starting (unlocking) a newly-created profile. */
@Test
- public void managedProfileUnlock() throws Exception {
+ public void managedProfileUnlock() {
assumeTrue(mHasManagedUserFeature);
while (mRunner.keepRunning()) {
@@ -362,7 +377,7 @@
/** Tests starting (unlocking) an already-created, but no-longer-running, profile. */
@Test
- public void managedProfileUnlock_stopped() throws Exception {
+ public void managedProfileUnlock_stopped() throws RemoteException {
assumeTrue(mHasManagedUserFeature);
while (mRunner.keepRunning()) {
@@ -385,7 +400,7 @@
* Tests starting (unlocking) and launching an already-installed app in a newly-created profile.
*/
@Test
- public void managedProfileUnlockAndLaunchApp() throws Exception {
+ public void managedProfileUnlockAndLaunchApp() throws RemoteException {
assumeTrue(mHasManagedUserFeature);
while (mRunner.keepRunning()) {
@@ -411,7 +426,7 @@
* {@link #managedProfileUnlock_stopped}}.
*/
@Test
- public void managedProfileUnlockAndLaunchApp_stopped() throws Exception {
+ public void managedProfileUnlockAndLaunchApp_stopped() throws RemoteException {
assumeTrue(mHasManagedUserFeature);
while (mRunner.keepRunning()) {
@@ -422,7 +437,7 @@
startUserInBackgroundAndWaitForUnlock(userId);
startApp(userId, DUMMY_PACKAGE_NAME);
stopUser(userId, true);
- TimeUnit.SECONDS.sleep(1); // Brief cool-down before re-starting profile.
+ SystemClock.sleep(1_000); // 1 second cool-down before re-starting profile.
mRunner.resumeTiming();
startUserInBackgroundAndWaitForUnlock(userId);
@@ -436,7 +451,7 @@
/** Tests installing a pre-existing app in a newly-created profile. */
@Test
- public void managedProfileInstall() throws Exception {
+ public void managedProfileInstall() throws RemoteException {
assumeTrue(mHasManagedUserFeature);
while (mRunner.keepRunning()) {
@@ -457,7 +472,7 @@
* and launching that app in it.
*/
@Test
- public void managedProfileCreateUnlockInstallAndLaunchApp() throws Exception {
+ public void managedProfileCreateUnlockInstallAndLaunchApp() throws RemoteException {
assumeTrue(mHasManagedUserFeature);
while (mRunner.keepRunning()) {
@@ -478,7 +493,7 @@
/** Tests stopping a profile. */
@Test
- public void managedProfileStopped() throws Exception {
+ public void managedProfileStopped() throws RemoteException {
assumeTrue(mHasManagedUserFeature);
while (mRunner.keepRunning()) {
@@ -498,7 +513,7 @@
// TODO: This is just a POC. Do this properly and add more.
/** Tests starting (unlocking) a newly-created profile using the user-type-pkg-whitelist. */
@Test
- public void managedProfileUnlock_usingWhitelist() throws Exception {
+ public void managedProfileUnlock_usingWhitelist() {
assumeTrue(mHasManagedUserFeature);
final int origMode = getUserTypePackageWhitelistMode();
setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE
@@ -522,7 +537,7 @@
}
/** Tests starting (unlocking) a newly-created profile NOT using the user-type-pkg-whitelist. */
@Test
- public void managedProfileUnlock_notUsingWhitelist() throws Exception {
+ public void managedProfileUnlock_notUsingWhitelist() {
assumeTrue(mHasManagedUserFeature);
final int origMode = getUserTypePackageWhitelistMode();
setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE);
@@ -551,19 +566,17 @@
/** Creates a new user with the given flags, returning its userId. */
private int createUserWithFlags(int flags) {
- int userId = mUm.createUser("TestUser", flags).id;
+ int userId = mUm.createUser(TEST_USER_NAME, flags).id;
mUsersToRemove.add(userId);
return userId;
}
/** Creates a managed (work) profile under the current user, returning its userId. */
private int createManagedProfile() {
- final UserInfo userInfo = mUm.createProfileForUser("TestProfile",
+ final UserInfo userInfo = mUm.createProfileForUser(TEST_USER_NAME,
UserManager.USER_TYPE_PROFILE_MANAGED, /* flags */ 0, mAm.getCurrentUser());
- if (userInfo == null) {
- throw new IllegalStateException("Creating managed profile failed. Most likely there is "
- + "already a pre-existing profile on the device.");
- }
+ attestFalse("Creating managed profile failed. Most likely there is "
+ + "already a pre-existing profile on the device.", userInfo == null);
mUsersToRemove.add(userInfo.id);
return userInfo.id;
}
@@ -576,24 +589,40 @@
*/
private void startUserInBackgroundAndWaitForUnlock(int userId) {
final ProgressWaiter waiter = new ProgressWaiter();
+ boolean success = false;
try {
mIam.startUserInBackgroundWithListener(userId, waiter);
- boolean success = waiter.waitForFinish(TIMEOUT_IN_SECOND);
- attestTrue("Failed to start user " + userId + " in background.", success);
+ success = waiter.waitForFinish(TIMEOUT_IN_SECOND);
} catch (RemoteException e) {
Log.e(TAG, "startUserInBackgroundAndWaitForUnlock failed", e);
}
+ attestTrue("Failed to start user " + userId + " in background.", success);
}
/** Starts the given user in the foreground. */
- private void switchUser(int userId) throws Exception {
+ private void switchUser(int userId) throws RemoteException {
+ boolean success = switchUserNoCheck(userId);
+ attestTrue("Failed to properly switch to user " + userId, success);
+ }
+
+ /**
+ * Starts the given user in the foreground.
+ * Returns true if successful. Does not fail the test if unsuccessful.
+ * If lack of success should fail the test, use {@link #switchUser(int)} instead.
+ */
+ private boolean switchUserNoCheck(int userId) throws RemoteException {
final CountDownLatch latch = new CountDownLatch(1);
registerUserSwitchObserver(latch, null, userId);
mAm.switchUser(userId);
- latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
+ try {
+ return latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Thread interrupted unexpectedly.", e);
+ return false;
+ }
}
- private void stopUser(int userId, boolean force) throws Exception {
+ private void stopUser(int userId, boolean force) throws RemoteException {
final CountDownLatch latch = new CountDownLatch(1);
mIam.stopUser(userId, force /* force */, new IStopUserCallback.Stub() {
@Override
@@ -605,7 +634,7 @@
public void userStopAborted(int userId) throws RemoteException {
}
});
- latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
+ waitForLatch("Failed to properly stop user " + userId, latch);
}
/**
@@ -615,15 +644,14 @@
* @param stopNewUser whether to stop the new user after switching to otherUser.
* @return userId of the newly created user.
*/
- private int initializeNewUserAndSwitchBack(boolean stopNewUser) throws Exception {
+ private int initializeNewUserAndSwitchBack(boolean stopNewUser) throws RemoteException {
final int origUser = mAm.getCurrentUser();
// First, create and switch to testUser, waiting for its ACTION_USER_UNLOCKED
final int testUser = createUserNoFlags();
final CountDownLatch latch1 = new CountDownLatch(1);
registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch1, testUser);
mAm.switchUser(testUser);
- attestTrue("Failed to achieve initial ACTION_USER_UNLOCKED for user " + testUser,
- latch1.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS));
+ waitForLatch("Failed to achieve initial ACTION_USER_UNLOCKED for user " + testUser, latch1);
// Second, switch back to origUser, waiting merely for switchUser() to finish
switchUser(origUser);
@@ -656,11 +684,7 @@
PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
PackageManager.INSTALL_REASON_UNKNOWN, sender, userId, null);
- try {
- latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- Log.e(TAG, "Thread interrupted unexpectedly.", e);
- }
+ waitForLatch("Failed to install app " + packageName + " on user " + userId, latch);
}
/**
@@ -678,7 +702,7 @@
}
private void registerUserSwitchObserver(final CountDownLatch switchLatch,
- final CountDownLatch bootCompleteLatch, final int userId) throws Exception {
+ final CountDownLatch bootCompleteLatch, final int userId) throws RemoteException {
ActivityManager.getService().registerUserSwitchObserver(
new UserSwitchObserver() {
@Override
@@ -734,6 +758,17 @@
}
}
+ /** Waits TIMEOUT_IN_SECOND for the latch to complete, otherwise declares the given error. */
+ private void waitForLatch(String errMsg, CountDownLatch latch) {
+ boolean success = false;
+ try {
+ success = latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Thread interrupted unexpectedly.", e);
+ }
+ attestTrue(errMsg, success);
+ }
+
/** Gets the PACKAGE_WHITELIST_MODE_PROP System Property. */
private int getUserTypePackageWhitelistMode() {
return SystemProperties.getInt(PACKAGE_WHITELIST_MODE_PROP,
@@ -767,13 +802,30 @@
}
}
- private void attestTrue(String message, boolean assertion) {
- if (!assertion) {
- Log.w(TAG, message);
+ private void removeAnyPreviousTestUsers() {
+ for (UserInfo user : mUm.getUsers()) {
+ if (TEST_USER_NAME.equals(user.name)) {
+ Log.i(TAG, "Found previous test user " + user.id + ". Removing it.");
+ if (mAm.getCurrentUser() == user.id) {
+ try {
+ switchUserNoCheck(UserHandle.USER_SYSTEM);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to correctly switch to system user", e);
+ }
+ }
+ mUm.removeUser(user.id);
+ }
}
}
- private void attestFalse(String message, boolean assertion) {
+ private void attestTrue(@NonNull String message, boolean assertion) {
+ if (!assertion) {
+ Log.e(TAG, "Test failed on iteration #" + mRunner.getIteration() + ": " + message);
+ mRunner.markAsFailed(new AssertionError(message));
+ }
+ }
+
+ private void attestFalse(@NonNull String message, boolean assertion) {
attestTrue(message, !assertion);
}
}
diff --git a/apct-tests/perftests/multiuser/trace_configs/trace_config_multi_user.textproto b/apct-tests/perftests/multiuser/trace_configs/trace_config_multi_user.textproto
new file mode 100644
index 0000000..14a3f8f
--- /dev/null
+++ b/apct-tests/perftests/multiuser/trace_configs/trace_config_multi_user.textproto
@@ -0,0 +1,154 @@
+# Copyright (C) 2021 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.
+
+# proto-message: TraceConfig
+
+# Enable periodic flushing of the trace buffer into the output file.
+write_into_file: true
+
+# Writes the userspace buffer into the file every 1s.
+file_write_period_ms: 1000
+
+# See b/126487238 - we need to guarantee ordering of events.
+flush_period_ms: 10000
+
+# The trace buffers needs to be big enough to hold |file_write_period_ms| of
+# trace data. The trace buffer sizing depends on the number of trace categories
+# enabled and the device activity.
+
+# RSS events
+buffers {
+ size_kb: 32768
+ fill_policy: RING_BUFFER
+}
+
+# procfs polling
+buffers {
+ size_kb: 8192
+ fill_policy: RING_BUFFER
+}
+
+data_sources {
+ config {
+ name: "linux.ftrace"
+ target_buffer: 0
+ ftrace_config {
+ # These parameters affect only the kernel trace buffer size and how
+ # frequently it gets moved into the userspace buffer defined above.
+ buffer_size_kb: 16384
+ drain_period_ms: 250
+
+ # Store certain high-volume "sched" ftrace events in a denser format
+ # (falling back to the default format if not supported by the tracer).
+ compact_sched {
+ enabled: true
+ }
+
+ # Enables symbol name resolution against /proc/kallsyms
+ symbolize_ksyms: true
+
+ # We need to do process tracking to ensure kernel ftrace events targeted at short-lived
+ # threads are associated correctly
+ ftrace_events: "task/task_newtask"
+ ftrace_events: "task/task_rename"
+ ftrace_events: "sched/sched_process_exit"
+ ftrace_events: "sched/sched_process_free"
+
+ # Memory events
+ ftrace_events: "rss_stat"
+ ftrace_events: "ion_heap_shrink"
+ ftrace_events: "ion_heap_grow"
+ ftrace_events: "ion/ion_stat"
+ ftrace_events: "dmabuf_heap/dma_heap_stat"
+ ftrace_events: "oom_score_adj_update"
+ ftrace_events: "gpu_mem/gpu_mem_total"
+
+ # Old (kernel) LMK
+ ftrace_events: "lowmemorykiller/lowmemory_kill"
+
+ atrace_apps: "*"
+
+ atrace_categories: "am"
+ atrace_categories: "bionic"
+ atrace_categories: "camera"
+ atrace_categories: "wm"
+ atrace_categories: "dalvik"
+ atrace_categories: "sched"
+ atrace_categories: "freq"
+ atrace_categories: "gfx"
+ atrace_categories: "view"
+ atrace_categories: "webview"
+ atrace_categories: "input"
+ atrace_categories: "hal"
+ atrace_categories: "binder_driver"
+ atrace_categories: "sync"
+ atrace_categories: "workq"
+ atrace_categories: "res"
+
+ }
+ }
+}
+
+data_sources: {
+ config {
+ name: "android.gpu.memory"
+ target_buffer: 0
+ }
+}
+
+data_sources {
+ config {
+ name: "linux.process_stats"
+ target_buffer: 1
+ process_stats_config {
+ proc_stats_poll_ms: 10000
+ }
+ }
+}
+
+data_sources {
+ config {
+ name: "linux.sys_stats"
+ target_buffer: 1
+ sys_stats_config {
+ meminfo_period_ms: 1000
+ meminfo_counters: MEMINFO_MEM_TOTAL
+ meminfo_counters: MEMINFO_MEM_FREE
+ meminfo_counters: MEMINFO_MEM_AVAILABLE
+ meminfo_counters: MEMINFO_BUFFERS
+ meminfo_counters: MEMINFO_CACHED
+ meminfo_counters: MEMINFO_SWAP_CACHED
+ meminfo_counters: MEMINFO_ACTIVE
+ meminfo_counters: MEMINFO_INACTIVE
+ meminfo_counters: MEMINFO_ACTIVE_ANON
+ meminfo_counters: MEMINFO_INACTIVE_ANON
+ meminfo_counters: MEMINFO_ACTIVE_FILE
+ meminfo_counters: MEMINFO_INACTIVE_FILE
+ meminfo_counters: MEMINFO_UNEVICTABLE
+ meminfo_counters: MEMINFO_SWAP_TOTAL
+ meminfo_counters: MEMINFO_SWAP_FREE
+ meminfo_counters: MEMINFO_DIRTY
+ meminfo_counters: MEMINFO_WRITEBACK
+ meminfo_counters: MEMINFO_ANON_PAGES
+ meminfo_counters: MEMINFO_MAPPED
+ meminfo_counters: MEMINFO_SHMEM
+ }
+ }
+}
+
+data_sources: {
+ config: {
+ name: "android.surfaceflinger.frametimeline"
+ }
+}
diff --git a/apct-tests/perftests/packagemanager/Android.bp b/apct-tests/perftests/packagemanager/Android.bp
index 17033e0..0e76488 100644
--- a/apct-tests/perftests/packagemanager/Android.bp
+++ b/apct-tests/perftests/packagemanager/Android.bp
@@ -1,3 +1,12 @@
+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"],
+}
+
android_test {
name: "PackageManagerPerfTests",
@@ -10,6 +19,7 @@
"androidx.test.ext.junit",
"androidx.annotation_annotation",
"apct-perftests-utils",
+ "collector-device-lib-platform",
],
libs: ["android.test.base"],
@@ -18,4 +28,8 @@
test_suites: ["device-tests"],
+ data: [":perfetto_artifacts"],
+
+ certificate: "platform",
+
}
diff --git a/apct-tests/perftests/packagemanager/AndroidManifest.xml b/apct-tests/perftests/packagemanager/AndroidManifest.xml
index 520f4b5..4bcd557 100644
--- a/apct-tests/perftests/packagemanager/AndroidManifest.xml
+++ b/apct-tests/perftests/packagemanager/AndroidManifest.xml
@@ -76,7 +76,8 @@
<application>
<uses-library android:name="android.test.runner" />
- <activity android:name="android.perftests.utils.PerfTestActivity">
+ <activity android:name="android.perftests.utils.PerfTestActivity"
+ android:exported="true">
<intent-filter>
<action android:name="com.android.perftests.packagemanager.PERFTEST" />
</intent-filter>
diff --git a/apct-tests/perftests/packagemanager/AndroidTest.xml b/apct-tests/perftests/packagemanager/AndroidTest.xml
index c112d87..4903510 100644
--- a/apct-tests/perftests/packagemanager/AndroidTest.xml
+++ b/apct-tests/perftests/packagemanager/AndroidTest.xml
@@ -15,74 +15,122 @@
~ limitations under the License.
-->
<configuration description="Runs PackageManagerPerfTests metric instrumentation.">
- <option name="test-suite-tag" value="apct" />
- <option name="test-suite-tag" value="apct-metric-instrumentation" />
+ <option name="test-suite-tag" value="apct"/>
+ <option name="test-suite-tag" value="apct-metric-instrumentation"/>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
- <option name="cleanup-apks" value="true" />
- <option name="test-file-name" value="PackageManagerPerfTests.apk" />
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="PackageManagerPerfTests.apk"/>
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
- <option name="cleanup-apks" value="true" />
- <option name="force-queryable" value="false" />
- <option name="test-file-name" value="QueriesAll0.apk" />
- <option name="test-file-name" value="QueriesAll1.apk" />
- <option name="test-file-name" value="QueriesAll2.apk" />
- <option name="test-file-name" value="QueriesAll3.apk" />
- <option name="test-file-name" value="QueriesAll4.apk" />
- <option name="test-file-name" value="QueriesAll5.apk" />
- <option name="test-file-name" value="QueriesAll6.apk" />
- <option name="test-file-name" value="QueriesAll7.apk" />
- <option name="test-file-name" value="QueriesAll8.apk" />
- <option name="test-file-name" value="QueriesAll9.apk" />
- <option name="test-file-name" value="QueriesAll10.apk" />
- <option name="test-file-name" value="QueriesAll11.apk" />
- <option name="test-file-name" value="QueriesAll12.apk" />
- <option name="test-file-name" value="QueriesAll13.apk" />
- <option name="test-file-name" value="QueriesAll14.apk" />
- <option name="test-file-name" value="QueriesAll15.apk" />
- <option name="test-file-name" value="QueriesAll16.apk" />
- <option name="test-file-name" value="QueriesAll17.apk" />
- <option name="test-file-name" value="QueriesAll18.apk" />
- <option name="test-file-name" value="QueriesAll19.apk" />
- <option name="test-file-name" value="QueriesAll20.apk" />
- <option name="test-file-name" value="QueriesAll21.apk" />
- <option name="test-file-name" value="QueriesAll22.apk" />
- <option name="test-file-name" value="QueriesAll23.apk" />
- <option name="test-file-name" value="QueriesAll24.apk" />
- <option name="test-file-name" value="QueriesAll25.apk" />
- <option name="test-file-name" value="QueriesAll26.apk" />
- <option name="test-file-name" value="QueriesAll27.apk" />
- <option name="test-file-name" value="QueriesAll28.apk" />
- <option name="test-file-name" value="QueriesAll29.apk" />
- <option name="test-file-name" value="QueriesAll30.apk" />
- <option name="test-file-name" value="QueriesAll31.apk" />
- <option name="test-file-name" value="QueriesAll32.apk" />
- <option name="test-file-name" value="QueriesAll33.apk" />
- <option name="test-file-name" value="QueriesAll34.apk" />
- <option name="test-file-name" value="QueriesAll35.apk" />
- <option name="test-file-name" value="QueriesAll36.apk" />
- <option name="test-file-name" value="QueriesAll37.apk" />
- <option name="test-file-name" value="QueriesAll38.apk" />
- <option name="test-file-name" value="QueriesAll39.apk" />
- <option name="test-file-name" value="QueriesAll40.apk" />
- <option name="test-file-name" value="QueriesAll41.apk" />
- <option name="test-file-name" value="QueriesAll42.apk" />
- <option name="test-file-name" value="QueriesAll43.apk" />
- <option name="test-file-name" value="QueriesAll44.apk" />
- <option name="test-file-name" value="QueriesAll45.apk" />
- <option name="test-file-name" value="QueriesAll46.apk" />
- <option name="test-file-name" value="QueriesAll47.apk" />
- <option name="test-file-name" value="QueriesAll48.apk" />
- <option name="test-file-name" value="QueriesAll49.apk" />
+ <option name="cleanup-apks" value="true"/>
+ <option name="force-queryable" value="false"/>
+ <option name="test-file-name" value="QueriesAll0.apk"/>
+ <option name="test-file-name" value="QueriesAll1.apk"/>
+ <option name="test-file-name" value="QueriesAll2.apk"/>
+ <option name="test-file-name" value="QueriesAll3.apk"/>
+ <option name="test-file-name" value="QueriesAll4.apk"/>
+ <option name="test-file-name" value="QueriesAll5.apk"/>
+ <option name="test-file-name" value="QueriesAll6.apk"/>
+ <option name="test-file-name" value="QueriesAll7.apk"/>
+ <option name="test-file-name" value="QueriesAll8.apk"/>
+ <option name="test-file-name" value="QueriesAll9.apk"/>
+ <option name="test-file-name" value="QueriesAll10.apk"/>
+ <option name="test-file-name" value="QueriesAll11.apk"/>
+ <option name="test-file-name" value="QueriesAll12.apk"/>
+ <option name="test-file-name" value="QueriesAll13.apk"/>
+ <option name="test-file-name" value="QueriesAll14.apk"/>
+ <option name="test-file-name" value="QueriesAll15.apk"/>
+ <option name="test-file-name" value="QueriesAll16.apk"/>
+ <option name="test-file-name" value="QueriesAll17.apk"/>
+ <option name="test-file-name" value="QueriesAll18.apk"/>
+ <option name="test-file-name" value="QueriesAll19.apk"/>
+ <option name="test-file-name" value="QueriesAll20.apk"/>
+ <option name="test-file-name" value="QueriesAll21.apk"/>
+ <option name="test-file-name" value="QueriesAll22.apk"/>
+ <option name="test-file-name" value="QueriesAll23.apk"/>
+ <option name="test-file-name" value="QueriesAll24.apk"/>
+ <option name="test-file-name" value="QueriesAll25.apk"/>
+ <option name="test-file-name" value="QueriesAll26.apk"/>
+ <option name="test-file-name" value="QueriesAll27.apk"/>
+ <option name="test-file-name" value="QueriesAll28.apk"/>
+ <option name="test-file-name" value="QueriesAll29.apk"/>
+ <option name="test-file-name" value="QueriesAll30.apk"/>
+ <option name="test-file-name" value="QueriesAll31.apk"/>
+ <option name="test-file-name" value="QueriesAll32.apk"/>
+ <option name="test-file-name" value="QueriesAll33.apk"/>
+ <option name="test-file-name" value="QueriesAll34.apk"/>
+ <option name="test-file-name" value="QueriesAll35.apk"/>
+ <option name="test-file-name" value="QueriesAll36.apk"/>
+ <option name="test-file-name" value="QueriesAll37.apk"/>
+ <option name="test-file-name" value="QueriesAll38.apk"/>
+ <option name="test-file-name" value="QueriesAll39.apk"/>
+ <option name="test-file-name" value="QueriesAll40.apk"/>
+ <option name="test-file-name" value="QueriesAll41.apk"/>
+ <option name="test-file-name" value="QueriesAll42.apk"/>
+ <option name="test-file-name" value="QueriesAll43.apk"/>
+ <option name="test-file-name" value="QueriesAll44.apk"/>
+ <option name="test-file-name" value="QueriesAll45.apk"/>
+ <option name="test-file-name" value="QueriesAll46.apk"/>
+ <option name="test-file-name" value="QueriesAll47.apk"/>
+ <option name="test-file-name" value="QueriesAll48.apk"/>
+ <option name="test-file-name" value="QueriesAll49.apk"/>
</target_preparer>
- <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
- <option name="package" value="com.android.perftests.packagemanager" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.perftests.packagemanager"/>
<option name="hidden-api-checks" value="false"/>
</test>
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
- <option name="directory-keys" value="/data/local/PackageManagerPerfTests" />
- <option name="collect-on-run-ended-only" value="true" />
+ <option name="directory-keys" value="/data/local/PackageManagerPerfTests"/>
+ <option name="collect-on-run-ended-only" value="true"/>
</metrics_collector>
+
+ <!-- Needed for pushing the trace config file -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="push-file" key="trace_config_detailed.textproto"
+ value="/data/misc/perfetto-traces/trace_config.textproto"/>
+ <!--Install the content provider automatically when we push some file in sdcard folder.-->
+ <!--Needed to avoid the installation during the test suite.-->
+ <option name="push-file" key="trace_config_detailed.textproto"
+ value="/sdcard/sample.textproto"/>
+ </target_preparer>
+
+ <!-- Needed for pulling the collected trace config on to the host -->
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="pull-pattern-keys" value="perfetto_file_path"/>
+ </metrics_collector>
+
+ <!-- Needed for storing the perfetto trace files in the sdcard/test_results -->
+ <option name="isolated-storage" value="false"/>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.perftests.packagemanager"/>
+ <option name="hidden-api-checks" value="false"/>
+
+ <!-- Listener related args for collecting the traces and waiting for the device to
+ stabilize. -->
+ <option name="device-listeners"
+ value="android.device.collectors.ProcLoadListener,android.device.collectors.PerfettoListener"/>
+ <!-- Guarantee that user defined RunListeners will be running before any of the default
+ listeners defined in this runner. -->
+ <option name="instrumentation-arg" key="newRunListenerMode" value="true"/>
+
+ <!-- ProcLoadListener related arguments -->
+ <!-- Wait for device last minute threshold to reach 3 with 2 minute timeout before starting
+ the test run -->
+ <option name="instrumentation-arg" key="procload-collector:per_run" value="true"/>
+ <option name="instrumentation-arg" key="proc-loadavg-threshold" value="3"/>
+ <option name="instrumentation-arg" key="proc-loadavg-timeout" value="120000"/>
+ <option name="instrumentation-arg" key="proc-loadavg-interval" value="10000"/>
+
+ <!-- PerfettoListener related arguments -->
+ <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
+ <option name="instrumentation-arg" key="perfetto_config_file"
+ value="trace_config.textproto"/>
+
+ </test>
+
+
</configuration>
diff --git a/apct-tests/perftests/packagemanager/OWNERS b/apct-tests/perftests/packagemanager/OWNERS
new file mode 100644
index 0000000..d825dfd
--- /dev/null
+++ b/apct-tests/perftests/packagemanager/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/pm/OWNERS
diff --git a/apct-tests/perftests/packagemanager/apps/query-all/Android.bp b/apct-tests/perftests/packagemanager/apps/query-all/Android.bp
index 3cb1589..b2339d5 100644
--- a/apct-tests/perftests/packagemanager/apps/query-all/Android.bp
+++ b/apct-tests/perftests/packagemanager/apps/query-all/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // 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"],
+}
+
android_test_helper_app {
name: "QueriesAll0",
aaptflags: [
diff --git a/apct-tests/perftests/packagemanager/apps/query-all/AndroidManifest.xml b/apct-tests/perftests/packagemanager/apps/query-all/AndroidManifest.xml
index e2cfa04..977c1dc 100644
--- a/apct-tests/perftests/packagemanager/apps/query-all/AndroidManifest.xml
+++ b/apct-tests/perftests/packagemanager/apps/query-all/AndroidManifest.xml
@@ -18,7 +18,8 @@
package="com.android.perftests.appenumeration">
<application android:hasCode="false" >
- <activity android:name="android.perftests.utils.PerfTestActivity">
+ <activity android:name="android.perftests.utils.PerfTestActivity"
+ android:exported="true">
<intent-filter>
<action android:name="com.android.perftests.packagemanager.PERFTEST" />
</intent-filter>
@@ -78,4 +79,4 @@
<package android:name="com.android.perftests.appenumeration49" />
</queries>
-</manifest>
\ No newline at end of file
+</manifest>
diff --git a/apct-tests/perftests/textclassifier/Android.bp b/apct-tests/perftests/textclassifier/Android.bp
index 49952dc..1011267 100644
--- a/apct-tests/perftests/textclassifier/Android.bp
+++ b/apct-tests/perftests/textclassifier/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // 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"],
+}
+
android_test {
name: "TextClassifierPerfTests",
srcs: ["src/**/*.java"],
@@ -19,7 +28,9 @@
"androidx.test.rules",
"androidx.annotation_annotation",
"apct-perftests-utils",
+ "collector-device-lib",
],
+ data: [":perfetto_artifacts"],
platform_apis: true,
test_suites: ["device-tests"],
}
diff --git a/apct-tests/perftests/textclassifier/AndroidTest.xml b/apct-tests/perftests/textclassifier/AndroidTest.xml
index 3df51b8..3fac462 100644
--- a/apct-tests/perftests/textclassifier/AndroidTest.xml
+++ b/apct-tests/perftests/textclassifier/AndroidTest.xml
@@ -21,8 +21,40 @@
<option name="test-file-name" value="TextClassifierPerfTests.apk" />
</target_preparer>
+ <!-- Needed for pushing the trace config file -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="push-file" key="trace_config_detailed.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" />
+ </target_preparer>
+
+ <!-- Needed for pulling the collected trace config on to the host -->
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="pull-pattern-keys" value="perfetto_file_path" />
+ </metrics_collector>
+
+ <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+ <option name="isolated-storage" value="false" />
+
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.perftests.textclassifier" />
<option name="hidden-api-checks" value="false"/>
+
+ <!-- Listener related args for collecting the traces and waiting for the device to stabilize. -->
+ <option name="device-listeners" value="android.device.collectors.ProcLoadListener,android.device.collectors.PerfettoListener" />
+ <!-- Guarantee that user defined RunListeners will be running before any of the default listeners defined in this runner. -->
+ <option name="instrumentation-arg" key="newRunListenerMode" value="true" />
+
+ <!-- ProcLoadListener related arguments -->
+ <!-- Wait for device last minute threshold to reach 3 with 2 minute timeout before starting the test run -->
+ <option name="instrumentation-arg" key="procload-collector:per_run" value="true" />
+ <option name="instrumentation-arg" key="proc-loadavg-threshold" value="3" />
+ <option name="instrumentation-arg" key="proc-loadavg-timeout" value="120000" />
+ <option name="instrumentation-arg" key="proc-loadavg-interval" value="10000" />
+
+ <!-- PerfettoListener related arguments -->
+ <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" />
+ <option name="instrumentation-arg" key="perfetto_config_file" value="trace_config.textproto" />
+
+ <option name="instrumentation-arg" key="newRunListenerMode" value="true" />
</test>
</configuration>
diff --git a/apct-tests/perftests/textclassifier/OWNERS b/apct-tests/perftests/textclassifier/OWNERS
new file mode 100644
index 0000000..46b3cb8
--- /dev/null
+++ b/apct-tests/perftests/textclassifier/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/service/textclassifier/OWNERS
diff --git a/apct-tests/perftests/textclassifier/run.sh b/apct-tests/perftests/textclassifier/run.sh
index d36d190..9a0f4f9 100755
--- a/apct-tests/perftests/textclassifier/run.sh
+++ b/apct-tests/perftests/textclassifier/run.sh
@@ -1,8 +1,8 @@
set -e
-build/soong/soong_ui.bash --make-mode TextClassifierPerfTests perf-setup.sh
+build/soong/soong_ui.bash --make-mode TextClassifierPerfTests perf-setup
adb install ${OUT}/testcases/TextClassifierPerfTests/arm64/TextClassifierPerfTests.apk
adb shell cmd package compile -m speed -f com.android.perftests.textclassifier
-adb push ${OUT}/obj/EXECUTABLES/perf-setup.sh_intermediates/perf-setup.sh /data/local/tmp/
+adb push ${OUT}/obj/EXECUTABLES/perf-setup_intermediates/perf-setup.sh /data/local/tmp/
adb shell chmod +x /data/local/tmp/perf-setup.sh
adb shell /data/local/tmp/perf-setup.sh
-adb shell am instrument -w -e package android.view.textclassifier com.android.perftests.textclassifier/androidx.test.runner.AndroidJUnitRunner
\ No newline at end of file
+adb shell am instrument -w -e package android.view.textclassifier com.android.perftests.textclassifier/androidx.test.runner.AndroidJUnitRunner
diff --git a/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassifierPerfTest.java b/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassifierPerfTest.java
index 14a121d..324def8 100644
--- a/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassifierPerfTest.java
+++ b/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassifierPerfTest.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
+import android.service.textclassifier.TextClassifierService;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
@@ -25,48 +26,73 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
-import java.util.Random;
-@RunWith(Parameterized.class)
@LargeTest
public class TextClassifierPerfTest {
- /** Request contains meaning text, rather than garbled text. */
- private static final int ACTUAL_REQUEST = 0;
- private static final String RANDOM_CHAR_SET = "abcdefghijklmnopqrstuvwxyz0123456789";
+ private static final String TEXT = " Oh hi Mark, the number is (323) 654-6192.\n"
+ + "Anyway, I'll meet you at 1600 Pennsylvania Avenue NW.\n"
+ + "My flight is LX 38 and I'll arrive at 8:00pm.\n"
+ + "Also, check out www.google.com.\n";
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
- @Parameterized.Parameters(name = "size{0}")
- public static Collection<Object[]> data() {
- return Arrays.asList(new Object[][]{{ACTUAL_REQUEST}, {10}, {100}, {1000}});
- }
-
private TextClassifier mTextClassifier;
- private final int mSize;
-
- public TextClassifierPerfTest(int size) {
- mSize = size;
- }
@Before
public void setUp() {
Context context = InstrumentationRegistry.getTargetContext();
- TextClassificationManager textClassificationManager =
- context.getSystemService(TextClassificationManager.class);
- mTextClassifier = textClassificationManager.getTextClassifier(TextClassifier.LOCAL);
+ mTextClassifier = TextClassifierService.getDefaultTextClassifierImplementation(context);
+ }
+
+ @Test
+ public void testClassifyText() {
+ TextClassification.Request request =
+ new TextClassification.Request.Builder(TEXT, 0, TEXT.length()).build();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mTextClassifier.classifyText(request);
+ }
+ }
+
+ @Test
+ public void testSuggestSelection() {
+ // Trying to select the phone number.
+ TextSelection.Request request =
+ new TextSelection.Request.Builder(
+ TEXT,
+ /* startIndex= */ 28,
+ /* endIndex= */29)
+ .build();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mTextClassifier.suggestSelection(request);
+ }
+ }
+
+ @Test
+ public void testGenerateLinks() {
+ TextLinks.Request request =
+ new TextLinks.Request.Builder(TEXT).build();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mTextClassifier.generateLinks(request);
+ }
}
@Test
public void testSuggestConversationActions() {
- String text = mSize == ACTUAL_REQUEST ? "Where are you?" : generateRandomString(mSize);
- ConversationActions.Request request = createConversationActionsRequest(text);
+ ConversationActions.Message message =
+ new ConversationActions.Message.Builder(
+ ConversationActions.Message.PERSON_USER_OTHERS)
+ .setText(TEXT)
+ .build();
+ ConversationActions.Request request = new ConversationActions.Request.Builder(
+ Collections.singletonList(message))
+ .build();
+
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
mTextClassifier.suggestConversationActions(request);
@@ -75,36 +101,10 @@
@Test
public void testDetectLanguage() {
- String text = mSize == ACTUAL_REQUEST
- ? "これは日本語のテキストです" : generateRandomString(mSize);
- TextLanguage.Request request = createTextLanguageRequest(text);
+ TextLanguage.Request request = new TextLanguage.Request.Builder(TEXT).build();
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
mTextClassifier.detectLanguage(request);
}
}
-
- private static ConversationActions.Request createConversationActionsRequest(CharSequence text) {
- ConversationActions.Message message =
- new ConversationActions.Message.Builder(
- ConversationActions.Message.PERSON_USER_OTHERS)
- .setText(text)
- .build();
- return new ConversationActions.Request.Builder(Collections.singletonList(message))
- .build();
- }
-
- private static TextLanguage.Request createTextLanguageRequest(CharSequence text) {
- return new TextLanguage.Request.Builder(text).build();
- }
-
- private static String generateRandomString(int length) {
- Random random = new Random();
- StringBuilder stringBuilder = new StringBuilder(length);
- for (int i = 0; i < length; i++) {
- int index = random.nextInt(RANDOM_CHAR_SET.length());
- stringBuilder.append(RANDOM_CHAR_SET.charAt(index));
- }
- return stringBuilder.toString();
- }
}
diff --git a/apct-tests/perftests/utils/Android.bp b/apct-tests/perftests/utils/Android.bp
index be85816..6c46a9b 100644
--- a/apct-tests/perftests/utils/Android.bp
+++ b/apct-tests/perftests/utils/Android.bp
@@ -1,3 +1,12 @@
+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"],
+}
+
java_library {
name: "apct-perftests-utils",
static_libs: [
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java b/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
index 93bf541..e192861 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
@@ -53,7 +53,8 @@
private static final int NOT_STARTED = 0; // The benchmark has not started yet.
private static final int WARMUP = 1; // The benchmark is warming up.
private static final int RUNNING = 2; // The benchmark is running.
- private static final int FINISHED = 3; // The benchmark has stopped.
+ private static final int RUNNING_CUSTOMIZED = 3; // Running for customized measurement.
+ private static final int FINISHED = 4; // The benchmark has stopped.
private int mState = NOT_STARTED; // Current benchmark state.
@@ -76,6 +77,14 @@
private int mRepeatCount = 0;
+ /**
+ * Additional iteration that used to apply customized measurement. The result during these
+ * iterations won't be counted into {@link #mStats}.
+ */
+ private int mMaxCustomizedIterations;
+ private int mCustomizedIterations;
+ private CustomizedIterationListener mCustomizedIterationListener;
+
// Statistics. These values will be filled when the benchmark has finished.
// The computation needs double precision, but long int is fine for final reporting.
private Stats mStats;
@@ -110,6 +119,15 @@
mPaused = false;
}
+ /**
+ * This is used to run the benchmark with more information by enabling some debug mechanism but
+ * we don't want to account the special runs (slower) in the stats report.
+ */
+ public void setCustomizedIterations(int iterations, CustomizedIterationListener listener) {
+ mMaxCustomizedIterations = iterations;
+ mCustomizedIterationListener = listener;
+ }
+
private void beginWarmup() {
mStartTimeNs = System.nanoTime();
mIteration = 0;
@@ -141,6 +159,11 @@
Debug.stopMethodTracing();
}
mStats = new Stats(mResults);
+ if (mMaxCustomizedIterations > 0 && mCustomizedIterationListener != null) {
+ mState = RUNNING_CUSTOMIZED;
+ mCustomizedIterationListener.onStart(mCustomizedIterations);
+ return true;
+ }
mState = FINISHED;
return false;
}
@@ -180,6 +203,15 @@
"Resume the benchmark before finishing each step.");
}
return true;
+ case RUNNING_CUSTOMIZED:
+ mCustomizedIterationListener.onFinished(mCustomizedIterations);
+ mCustomizedIterations++;
+ if (mCustomizedIterations >= mMaxCustomizedIterations) {
+ mState = FINISHED;
+ return false;
+ }
+ mCustomizedIterationListener.onStart(mCustomizedIterations);
+ return true;
case FINISHED:
throw new IllegalStateException("The benchmark has finished.");
default:
@@ -240,4 +272,13 @@
status.putLong(key + "_standardDeviation", standardDeviation());
instrumentation.sendStatus(Activity.RESULT_OK, status);
}
+
+ /** The interface to receive the events of customized iteration. */
+ public interface CustomizedIterationListener {
+ /** The customized iteration starts. */
+ void onStart(int iteration);
+
+ /** The customized iteration finished. */
+ void onFinished(int iteration);
+ }
}
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/PerfTestActivity.java b/apct-tests/perftests/utils/src/android/perftests/utils/PerfTestActivity.java
index e934feb..f3bea17 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/PerfTestActivity.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/PerfTestActivity.java
@@ -21,6 +21,8 @@
import android.content.Intent;
import android.os.Bundle;
import android.view.WindowManager;
+import android.widget.EditText;
+import android.widget.LinearLayout;
/**
* A simple activity used for testing, e.g. performance of activity switching, or as a base
@@ -28,6 +30,8 @@
*/
public class PerfTestActivity extends Activity {
public static final String INTENT_EXTRA_KEEP_SCREEN_ON = "keep_screen_on";
+ public static final String INTENT_EXTRA_ADD_EDIT_TEXT = "add_edit_text";
+ public static final int ID_EDITOR = 3252356;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -35,6 +39,15 @@
if (getIntent().getBooleanExtra(INTENT_EXTRA_KEEP_SCREEN_ON, false)) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
+ if (getIntent().getBooleanExtra(INTENT_EXTRA_ADD_EDIT_TEXT, false)) {
+ final LinearLayout layout = new LinearLayout(this);
+ layout.setOrientation(LinearLayout.VERTICAL);
+
+ final EditText editText = new EditText(this);
+ editText.setId(ID_EDITOR);
+ layout.addView(editText);
+ setContentView(layout);
+ }
}
public static Intent createLaunchIntent(Context context) {
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/TestPackageInstaller.java b/apct-tests/perftests/utils/src/android/perftests/utils/TestPackageInstaller.java
index a433d80..530dc9d 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/TestPackageInstaller.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/TestPackageInstaller.java
@@ -134,7 +134,7 @@
Intent intent = new Intent(action);
PendingIntent pending = PendingIntent.getBroadcast(mContext, sessionId, intent,
- PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
return pending.getIntentSender();
}
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/TestUtils.java b/apct-tests/perftests/utils/src/android/perftests/utils/TestUtils.java
new file mode 100644
index 0000000..d8d3ee3
--- /dev/null
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/TestUtils.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.perftests.utils;
+
+import android.app.Instrumentation;
+
+import androidx.annotation.NonNull;
+import androidx.test.InstrumentationRegistry;
+
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+
+public final class TestUtils {
+
+ /**
+ * Retrieves a value that needs to be obtained on the main thread.
+ *
+ * <p>A simple utility method that helps to return an object from the UI thread.</p>
+ *
+ * @param supplier callback to be called on the UI thread to return a value
+ * @param <T> Type of the value to be returned
+ * @return Value returned from {@code supplier}
+ */
+ public static <T> T getOnMainSync(@NonNull Supplier<T> supplier) {
+ final AtomicReference<T> result = new AtomicReference<>();
+ final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ instrumentation.runOnMainSync(() -> result.set(supplier.get()));
+ return result.get();
+ }
+}
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/WindowPerfRunPreconditionBase.java b/apct-tests/perftests/utils/src/android/perftests/utils/WindowPerfRunPreconditionBase.java
new file mode 100644
index 0000000..330a19e
--- /dev/null
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/WindowPerfRunPreconditionBase.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.perftests.utils;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.perftests.utils.WindowPerfTestBase.executeShellCommand;
+import static android.perftests.utils.WindowPerfTestBase.runWithShellPermissionIdentity;
+
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningAppProcessInfo;
+import android.app.ActivityTaskManager;
+import android.content.Context;
+import android.os.BatteryManager;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.perftests.utils.WindowPerfTestBase.SettingsSession;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.WindowManagerPolicyConstants;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.policy.PhoneWindow;
+
+import org.junit.runner.Description;
+import org.junit.runner.Result;
+import org.junit.runner.notification.RunListener;
+
+import java.util.List;
+
+/** Prepare the preconditions before running performance test. */
+public class WindowPerfRunPreconditionBase extends RunListener {
+ protected final String mTag = getClass().getSimpleName();
+
+ private static final String ARGUMENT_LOG_ONLY = "log";
+ private static final String ARGUMENT_KILL_BACKGROUND = "kill-bg";
+ private static final String ARGUMENT_PROFILING_ITERATIONS = "profiling-iterations";
+ private static final String ARGUMENT_PROFILING_SAMPLING = "profiling-sampling";
+ private static final String DEFAULT_PROFILING_ITERATIONS = "0";
+ private static final String DEFAULT_PROFILING_SAMPLING_US = "10";
+ private static final long KILL_BACKGROUND_WAIT_MS = 3000;
+
+ /** The requested iterations to run with method profiling. */
+ static int sProfilingIterations;
+
+ /** The interval of sample profiling in microseconds. */
+ static int sSamplingIntervalUs;
+
+ private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ private long mWaitPreconditionDoneMs = 500;
+
+ private final SettingsSession<Integer> mStayOnWhilePluggedInSetting = new SettingsSession<>(
+ Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0),
+ value -> executeShellCommand(String.format("settings put global %s %d",
+ Settings.Global.STAY_ON_WHILE_PLUGGED_IN, value)));
+
+ private final SettingsSession<Integer> mNavigationModeSetting = new SettingsSession<>(
+ mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_navBarInteractionMode),
+ value -> {
+ final String navOverlay;
+ switch (value) {
+ case WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON:
+ navOverlay = WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY;
+ break;
+ case WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON:
+ navOverlay = WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY;
+ break;
+ case WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL:
+ default:
+ navOverlay = WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY;
+ break;
+ }
+ executeShellCommand("cmd overlay enable-exclusive --category " + navOverlay);
+ });
+
+ /** It only executes once before all tests. */
+ @Override
+ public void testRunStarted(Description description) {
+ final Bundle arguments = InstrumentationRegistry.getArguments();
+ // If true, it only logs the method names without running.
+ final boolean skip = Boolean.parseBoolean(arguments.getString(ARGUMENT_LOG_ONLY, "false"));
+ Log.i(mTag, "arguments=" + arguments);
+ if (skip) {
+ return;
+ }
+ sProfilingIterations = Integer.parseInt(
+ arguments.getString(ARGUMENT_PROFILING_ITERATIONS, DEFAULT_PROFILING_ITERATIONS));
+ sSamplingIntervalUs = Integer.parseInt(
+ arguments.getString(ARGUMENT_PROFILING_SAMPLING, DEFAULT_PROFILING_SAMPLING_US));
+
+ // Use same navigation mode (gesture navigation) across all devices and tests
+ // for consistency.
+ mNavigationModeSetting.set(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL);
+ // Keep the device awake during testing.
+ mStayOnWhilePluggedInSetting.set(BatteryManager.BATTERY_PLUGGED_ANY);
+
+ runWithShellPermissionIdentity(() -> {
+ final ActivityTaskManager atm = mContext.getSystemService(ActivityTaskManager.class);
+ atm.removeAllVisibleRecentTasks();
+ atm.removeRootTasksWithActivityTypes(new int[] { ACTIVITY_TYPE_STANDARD,
+ ACTIVITY_TYPE_ASSISTANT, ACTIVITY_TYPE_RECENTS, ACTIVITY_TYPE_UNDEFINED });
+ });
+ PhoneWindow.sendCloseSystemWindows(mContext, mTag);
+
+ if (Boolean.parseBoolean(arguments.getString(ARGUMENT_KILL_BACKGROUND))) {
+ runWithShellPermissionIdentity(this::killBackgroundProcesses);
+ mWaitPreconditionDoneMs = KILL_BACKGROUND_WAIT_MS;
+ }
+ // Wait a while for the precondition setup to complete.
+ SystemClock.sleep(mWaitPreconditionDoneMs);
+ }
+
+ private void killBackgroundProcesses() {
+ Log.i(mTag, "Killing background processes...");
+ final ActivityManager am = mContext.getSystemService(ActivityManager.class);
+ final List<RunningAppProcessInfo> processes = am.getRunningAppProcesses();
+ if (processes == null) {
+ return;
+ }
+ for (RunningAppProcessInfo processInfo : processes) {
+ if (processInfo.importanceReasonCode == RunningAppProcessInfo.REASON_UNKNOWN
+ && processInfo.importance > RunningAppProcessInfo.IMPORTANCE_SERVICE) {
+ for (String pkg : processInfo.pkgList) {
+ am.forceStopPackage(pkg);
+ }
+ }
+ }
+ }
+
+ /** It only executes once after all tests. */
+ @Override
+ public void testRunFinished(Result result) {
+ mNavigationModeSetting.close();
+ mStayOnWhilePluggedInSetting.close();
+ }
+}
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/WindowPerfTestBase.java b/apct-tests/perftests/utils/src/android/perftests/utils/WindowPerfTestBase.java
new file mode 100644
index 0000000..ca59137
--- /dev/null
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/WindowPerfTestBase.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.perftests.utils;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import android.app.KeyguardManager;
+import android.app.UiAutomation;
+import android.content.Context;
+import android.content.Intent;
+import android.os.ParcelFileDescriptor;
+import android.os.PowerManager;
+import android.os.SystemClock;
+
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.After;
+import org.junit.BeforeClass;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+/** The base class for window related performance tests. */
+public class WindowPerfTestBase {
+ public static final long NANOS_PER_S = 1000L * 1000 * 1000;
+ public static final long TIME_1_S_IN_NS = 1 * NANOS_PER_S;
+
+ static boolean sIsProfilingMethod;
+
+ @BeforeClass
+ public static void setUpOnce() {
+ final Context context = getInstrumentation().getContext();
+
+ if (!context.getSystemService(PowerManager.class).isInteractive()
+ || context.getSystemService(KeyguardManager.class).isKeyguardLocked()) {
+ executeShellCommand("input keyevent KEYCODE_WAKEUP");
+ executeShellCommand("wm dismiss-keyguard");
+ }
+ context.startActivity(new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_HOME).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ }
+
+ @After
+ public void tearDown() {
+ // Make sure that profiling is stopped if test fails.
+ if (sIsProfilingMethod) {
+ stopProfiling();
+ }
+ }
+
+ public static UiAutomation getUiAutomation() {
+ return getInstrumentation().getUiAutomation();
+ }
+
+ public static void startAsyncAtrace(String tags) {
+ getUiAutomation().executeShellCommand("atrace -b 32768 --async_start " + tags);
+ // Avoid atrace isn't ready immediately.
+ SystemClock.sleep(TimeUnit.NANOSECONDS.toMillis(TIME_1_S_IN_NS));
+ }
+
+ public static InputStream stopAsyncAtraceWithStream() {
+ return new ParcelFileDescriptor.AutoCloseInputStream(
+ getUiAutomation().executeShellCommand("atrace --async_stop"));
+ }
+
+ /** Starts method tracing on system server. */
+ public static void startProfiling(File basePath, String outFileName) {
+ if (!basePath.exists()) {
+ executeShellCommand("mkdir -p " + basePath);
+ }
+ final String samplingArg = WindowPerfRunPreconditionBase.sSamplingIntervalUs > 0
+ ? ("--sampling " + WindowPerfRunPreconditionBase.sSamplingIntervalUs)
+ : "";
+ executeShellCommand("am profile start " + samplingArg + " system "
+ + new File(basePath, outFileName));
+ sIsProfilingMethod = true;
+ }
+
+ /** Stops method tracing of system server. */
+ public static void stopProfiling() {
+ executeShellCommand("am profile stop system");
+ sIsProfilingMethod = false;
+ }
+
+ public static boolean sIsProfilingMethod() {
+ return sIsProfilingMethod;
+ }
+
+ /** Returns how many iterations should run with method tracing. */
+ public static int getProfilingIterations() {
+ return WindowPerfRunPreconditionBase.sProfilingIterations;
+ }
+
+ /**
+ * Executes shell command with reading the output. It may also used to block until the current
+ * command is completed.
+ */
+ public static ByteArrayOutputStream executeShellCommand(String command) {
+ final ParcelFileDescriptor pfd = getUiAutomation().executeShellCommand(command);
+ final byte[] buf = new byte[512];
+ final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ int bytesRead;
+ try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
+ while ((bytesRead = fis.read(buf)) != -1) {
+ bytes.write(buf, 0, bytesRead);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return bytes;
+ }
+
+ public static void runWithShellPermissionIdentity(Runnable runnable) {
+ getUiAutomation().adoptShellPermissionIdentity();
+ try {
+ runnable.run();
+ } finally {
+ getUiAutomation().dropShellPermissionIdentity();
+ }
+ }
+
+ public static class SettingsSession<T> implements AutoCloseable {
+ private final Consumer<T> mSetter;
+ private final T mOriginalValue;
+ private boolean mChanged;
+
+ public SettingsSession(T originalValue, Consumer<T> setter) {
+ mOriginalValue = originalValue;
+ mSetter = setter;
+ }
+
+ public void set(T value) {
+ if (Objects.equals(value, mOriginalValue)) {
+ mChanged = false;
+ return;
+ }
+ mSetter.accept(value);
+ mChanged = true;
+ }
+
+ @Override
+ public void close() {
+ if (mChanged) {
+ mSetter.accept(mOriginalValue);
+ }
+ }
+ }
+
+ /**
+ * Provides the {@link PerfTestActivity} with an associated customizable intent.
+ */
+ public static class PerfTestActivityRuleBase extends ActivityTestRule<PerfTestActivity> {
+ protected final Intent mStartIntent =
+ new Intent(getInstrumentation().getTargetContext(), PerfTestActivity.class);
+
+ public PerfTestActivityRuleBase() {
+ this(false /* launchActivity */);
+ }
+
+ public PerfTestActivityRuleBase(boolean launchActivity) {
+ super(PerfTestActivity.class, false /* initialTouchMode */, launchActivity);
+ }
+
+ @Override
+ public Intent getActivityIntent() {
+ return mStartIntent;
+ }
+
+ public PerfTestActivity launchActivity() {
+ return launchActivity(mStartIntent);
+ }
+ }
+}
diff --git a/apct-tests/perftests/windowmanager/Android.bp b/apct-tests/perftests/windowmanager/Android.bp
index f02cbcf..365824e 100644
--- a/apct-tests/perftests/windowmanager/Android.bp
+++ b/apct-tests/perftests/windowmanager/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // 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"],
+}
+
android_test {
name: "WmPerfTests",
srcs: ["src/**/*.java"],
@@ -19,8 +28,11 @@
"androidx.test.rules",
"androidx.annotation_annotation",
"apct-perftests-utils",
+ "collector-device-lib",
+ "platform-test-annotations",
],
test_suites: ["device-tests"],
+ data: [":perfetto_artifacts"],
platform_apis: true,
certificate: "platform",
}
diff --git a/apct-tests/perftests/windowmanager/AndroidManifest.xml b/apct-tests/perftests/windowmanager/AndroidManifest.xml
index 7198176..95ede34 100644
--- a/apct-tests/perftests/windowmanager/AndroidManifest.xml
+++ b/apct-tests/perftests/windowmanager/AndroidManifest.xml
@@ -20,7 +20,8 @@
<application>
<uses-library android:name="android.test.runner" />
- <activity android:name="android.perftests.utils.PerfTestActivity">
+ <activity android:name="android.perftests.utils.PerfTestActivity"
+ android:exported="true">
<intent-filter>
<action android:name="com.android.perftests.core.PERFTEST" />
</intent-filter>
@@ -28,5 +29,7 @@
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.perftests.wm"/>
+ android:targetPackage="com.android.perftests.wm">
+ <meta-data android:name="listener" android:value="android.wm.WmPerfRunListener" />
+ </instrumentation>
</manifest>
diff --git a/apct-tests/perftests/windowmanager/AndroidTest.xml b/apct-tests/perftests/windowmanager/AndroidTest.xml
index 69d187f..6ac9f93 100644
--- a/apct-tests/perftests/windowmanager/AndroidTest.xml
+++ b/apct-tests/perftests/windowmanager/AndroidTest.xml
@@ -21,13 +21,47 @@
<option name="test-file-name" value="WmPerfTests.apk" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="force-skip-system-props" value="true" />
+ <option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
+ <option name="run-command" value="cmd window dismiss-keyguard" />
+ <option name="run-command" value="cmd package compile -m speed com.android.perftests.wm" />
+ </target_preparer>
+
+ <!-- Needed for pushing the trace config file -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="push-file" key="trace_config_detailed.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" />
+ </target_preparer>
+
+ <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+ <option name="isolated-storage" value="false" />
+
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.perftests.wm" />
<option name="hidden-api-checks" value="false"/>
+
+ <!-- Listener related args for collecting the traces and waiting for the device to stabilize. -->
+ <option name="device-listeners" value="android.device.collectors.ProcLoadListener,android.device.collectors.PerfettoListener" />
+
+ <!-- Guarantee that user defined RunListeners will be running before any of the default listeners defined in this runner. -->
+ <option name="instrumentation-arg" key="newRunListenerMode" value="true" />
+
+ <!-- ProcLoadListener related arguments -->
+ <!-- Wait for device last minute threshold to reach 3 with 2 minute timeout before starting the test run -->
+ <option name="instrumentation-arg" key="procload-collector:per_run" value="true" />
+ <option name="instrumentation-arg" key="proc-loadavg-threshold" value="3" />
+ <option name="instrumentation-arg" key="proc-loadavg-timeout" value="120000" />
+ <option name="instrumentation-arg" key="proc-loadavg-interval" value="10000" />
+
+ <!-- PerfettoListener related arguments -->
+ <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" />
+ <option name="instrumentation-arg" key="perfetto_config_file" value="trace_config.textproto" />
</test>
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
- <option name="directory-keys" value="/data/local/WmPerfTests" />
- <option name="collect-on-run-ended-only" value="true" />
+ <option name="directory-keys" value="/data/local/tmp/WmPerfTests" />
+ <!-- Needed for pulling the collected trace config on to the host -->
+ <option name="pull-pattern-keys" value="perfetto_file_path" />
</metrics_collector>
</configuration>
diff --git a/apct-tests/perftests/windowmanager/OWNERS b/apct-tests/perftests/windowmanager/OWNERS
new file mode 100644
index 0000000..0862c05
--- /dev/null
+++ b/apct-tests/perftests/windowmanager/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/wm/OWNERS
diff --git a/apct-tests/perftests/windowmanager/README.md b/apct-tests/perftests/windowmanager/README.md
new file mode 100644
index 0000000..7a0019a
--- /dev/null
+++ b/apct-tests/perftests/windowmanager/README.md
@@ -0,0 +1,35 @@
+## Window manager performance tests
+
+### Precondition
+To reduce the variance of the test, if `perf-setup.sh` (platform_testing/scripts/perf-setup)
+is available, it is better to use the following instructions to lock CPU and GPU frequencies.
+```
+m perf-setup
+PERF_SETUP_PATH=/data/local/tmp/perf-setup.sh
+adb push $OUT/$PERF_SETUP_PATH $PERF_SETUP_PATH
+adb shell chmod +x $PERF_SETUP_PATH
+adb shell $PERF_SETUP_PATH
+```
+
+### Example to run
+Use `atest`
+```
+atest WmPerfTests:RelayoutPerfTest -- \
+ --module-arg WmPerfTests:instrumentation-arg:kill-bg:=true
+```
+Use `am instrument`
+```
+adb shell am instrument -w -r -e class android.wm.RelayoutPerfTest \
+ -e listener android.wm.WmPerfRunListener \
+ -e kill-bg true \
+ com.android.perftests.wm/androidx.test.runner.AndroidJUnitRunner
+```
+* `kill-bg` is optional.
+
+Test arguments
+ - kill-bg
+ * boolean: Kill background process before running test.
+ - profiling-iterations
+ * int: Run the extra iterations with enabling method profiling.
+ - profiling-sampling
+ * int: The interval (0=trace each method, default is 10) of sample profiling in microseconds.
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/InternalWindowOperationPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/InternalWindowOperationPerfTest.java
index 4ed3b4e..3a11417 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/InternalWindowOperationPerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/InternalWindowOperationPerfTest.java
@@ -18,7 +18,6 @@
import static android.perftests.utils.ManualBenchmarkState.StatsReport;
-import android.os.ParcelFileDescriptor;
import android.os.SystemClock;
import android.perftests.utils.ManualBenchmarkState;
import android.perftests.utils.ManualBenchmarkState.ManualBenchmarkTest;
@@ -37,11 +36,11 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
-import java.util.concurrent.TimeUnit;
/** Measure the performance of internal methods in window manager service by trace tag. */
@LargeTest
-public class InternalWindowOperationPerfTest extends WindowManagerPerfTestBase {
+public class InternalWindowOperationPerfTest extends WindowManagerPerfTestBase
+ implements ManualBenchmarkState.CustomizedIterationListener {
private static final String TAG = InternalWindowOperationPerfTest.class.getSimpleName();
@Rule
@@ -54,7 +53,7 @@
"applyPostLayoutPolicy",
"applySurfaceChanges",
"AppTransitionReady",
- "closeSurfaceTransactiom",
+ "closeSurfaceTransaction",
"openSurfaceTransaction",
"performLayout",
"performSurfacePlacement",
@@ -68,6 +67,9 @@
"finishActivity",
"startActivityInner");
+ private boolean mIsProfiling;
+ private boolean mIsTraceStarted;
+
@Test
@ManualBenchmarkTest(
targetTestDurationNs = 20 * TIME_1_S_IN_NS,
@@ -76,13 +78,13 @@
| StatsReport.FLAG_MAX | StatsReport.FLAG_COEFFICIENT_VAR))
public void testLaunchAndFinishActivity() throws Throwable {
final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ state.setCustomizedIterations(getProfilingIterations(), this);
long measuredTimeNs = 0;
- boolean isTraceStarted = false;
while (state.keepRunning(measuredTimeNs)) {
- if (!isTraceStarted && !state.isWarmingUp()) {
- startAsyncAtrace();
- isTraceStarted = true;
+ if (!mIsTraceStarted && !mIsProfiling && !state.isWarmingUp()) {
+ startAsyncAtrace("wm");
+ mIsTraceStarted = true;
}
final long startTime = SystemClock.elapsedRealtimeNanos();
mActivityRule.launchActivity();
@@ -91,9 +93,15 @@
measuredTimeNs = SystemClock.elapsedRealtimeNanos() - startTime;
}
- stopAsyncAtrace();
+ if (mIsTraceStarted) {
+ stopAsyncAtrace();
+ }
mTraceMarkParser.forAllSlices((key, slices) -> {
+ if (slices.size() < 2) {
+ Log.w(TAG, "No sufficient samples: " + key);
+ return;
+ }
for (TraceMarkSlice slice : slices) {
state.addExtraResult(key, (long) (slice.getDurationInSeconds() * NANOS_PER_S));
}
@@ -102,20 +110,35 @@
Log.i(TAG, String.valueOf(mTraceMarkParser));
}
- private void startAsyncAtrace() throws IOException {
- sUiAutomation.executeShellCommand("atrace -b 32768 --async_start wm");
- // Avoid atrace isn't ready immediately.
- SystemClock.sleep(TimeUnit.NANOSECONDS.toMillis(TIME_1_S_IN_NS));
- }
-
- private void stopAsyncAtrace() throws IOException {
- final ParcelFileDescriptor pfd = sUiAutomation.executeShellCommand("atrace --async_stop");
- final InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+ private void stopAsyncAtrace() {
+ final InputStream inputStream = stopAsyncAtraceWithStream();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
String line;
while ((line = reader.readLine()) != null) {
mTraceMarkParser.visit(line);
}
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to read the result of stopped atrace", e);
+ }
+ }
+
+ @Override
+ public void onStart(int iteration) {
+ if (mIsTraceStarted) {
+ // Do not capture trace when profiling because the result will be much slower.
+ stopAsyncAtrace();
+ mIsTraceStarted = false;
+ }
+ mIsProfiling = true;
+ startProfiling(InternalWindowOperationPerfTest.class.getSimpleName()
+ + "_MethodTracing_" + iteration + ".trace");
+ }
+
+ @Override
+ public void onFinished(int iteration) {
+ stopProfiling();
+ if (iteration >= getProfilingIterations() - 1) {
+ mIsProfiling = false;
}
}
}
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java
index 6122ef2..98b5938 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java
@@ -24,8 +24,8 @@
import static org.hamcrest.core.Is.is;
import android.app.ActivityManager;
-import android.app.ActivityManager.TaskSnapshot;
import android.app.ActivityTaskManager;
+import android.window.TaskSnapshot;
import android.app.IActivityTaskManager;
import android.content.ComponentName;
import android.content.Context;
@@ -62,7 +62,8 @@
@RunWith(Parameterized.class)
@LargeTest
-public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase {
+public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase
+ implements ManualBenchmarkState.CustomizedIterationListener {
private static Intent sRecentsIntent;
@Rule
@@ -95,7 +96,7 @@
@BeforeClass
public static void setUpClass() {
// Get the permission to invoke startRecentsActivity.
- sUiAutomation.adoptShellPermissionIdentity();
+ getUiAutomation().adoptShellPermissionIdentity();
final Context context = getInstrumentation().getContext();
final PackageManager pm = context.getPackageManager();
@@ -128,7 +129,7 @@
ActivityManager.resumeAppSwitches();
} catch (RemoteException ignored) {
}
- sUiAutomation.dropShellPermissionIdentity();
+ getUiAutomation().dropShellPermissionIdentity();
}
@Before
@@ -162,6 +163,7 @@
| StatsReport.FLAG_COEFFICIENT_VAR))
public void testRecentsAnimation() throws Throwable {
final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ state.setCustomizedIterations(getProfilingIterations(), this);
final IActivityTaskManager atm = ActivityTaskManager.getService();
final ArrayList<Pair<String, Boolean>> finishCases = new ArrayList<>();
@@ -224,13 +226,27 @@
mMeasuredTimeNs = 0;
final long startTime = SystemClock.elapsedRealtimeNanos();
- atm.startRecentsActivity(sRecentsIntent, null /* unused */, anim);
+ atm.startRecentsActivity(sRecentsIntent, 0 /* eventTime */, anim);
final long elapsedTimeNsOfStart = SystemClock.elapsedRealtimeNanos() - startTime;
mMeasuredTimeNs += elapsedTimeNsOfStart;
state.addExtraResult("start", elapsedTimeNsOfStart);
// Ensure the animation callback is done.
- Assume.assumeTrue(recentsSemaphore.tryAcquire(TIME_5_S_IN_NS, TimeUnit.NANOSECONDS));
+ Assume.assumeTrue(recentsSemaphore.tryAcquire(
+ sIsProfilingMethod() ? 10 * TIME_5_S_IN_NS : TIME_5_S_IN_NS,
+ TimeUnit.NANOSECONDS));
}
}
+
+ @Override
+ public void onStart(int iteration) {
+ startProfiling(RecentsAnimationPerfTest.class.getSimpleName()
+ + "_interval_" + intervalBetweenOperations
+ + "_MethodTracing_" + iteration + ".trace");
+ }
+
+ @Override
+ public void onFinished(int iteration) {
+ stopProfiling();
+ }
}
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
index f04e555..1be68f5 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
@@ -21,13 +21,12 @@
import android.app.Activity;
import android.content.Context;
import android.graphics.Point;
-import android.graphics.Rect;
import android.os.RemoteException;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.PerfTestActivity;
+import android.platform.test.annotations.Presubmit;
import android.util.MergedConfiguration;
-import android.view.DisplayCutout;
import android.view.IWindow;
import android.view.IWindowSession;
import android.view.InsetsSourceControl;
@@ -37,6 +36,7 @@
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.widget.LinearLayout;
+import android.window.ClientWindowFrames;
import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
@@ -52,7 +52,9 @@
@RunWith(Parameterized.class)
@LargeTest
-public class RelayoutPerfTest extends WindowManagerPerfTestBase {
+@Presubmit
+public class RelayoutPerfTest extends WindowManagerPerfTestBase
+ implements BenchmarkState.CustomizedIterationListener {
private int mIteration;
@Rule
@@ -91,9 +93,22 @@
mActivityRule.runOnUiThread(() -> activity.setContentView(contentView));
getInstrumentation().waitForIdleSync();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ state.setCustomizedIterations(getProfilingIterations(), this);
final RelayoutRunner relayoutRunner = new RelayoutRunner(activity, contentView.getWindow(),
() -> visibilities[mIteration++ % visibilities.length]);
- relayoutRunner.runBenchmark(mPerfStatusReporter.getBenchmarkState());
+ relayoutRunner.runBenchmark(state);
+ }
+
+ @Override
+ public void onStart(int iteration) {
+ startProfiling(RelayoutPerfTest.class.getSimpleName() + "_" + testName
+ + "_MethodTracing_" + iteration + ".trace");
+ }
+
+ @Override
+ public void onFinished(int iteration) {
+ stopProfiling();
}
/** A dummy view to get IWindow. */
@@ -109,13 +124,7 @@
}
private static class RelayoutRunner {
- final Rect mOutFrame = new Rect();
- final Rect mOutContentInsets = new Rect();
- final Rect mOutVisibleInsets = new Rect();
- final Rect mOutStableInsets = new Rect();
- final Rect mOutBackDropFrame = new Rect();
- final DisplayCutout.ParcelableWrapper mOutDisplayCutout =
- new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT);
+ final ClientWindowFrames mOutFrames = new ClientWindowFrames();
final MergedConfiguration mOutMergedConfiguration = new MergedConfiguration();
final InsetsState mOutInsetsState = new InsetsState();
final InsetsSourceControl[] mOutControls = new InsetsSourceControl[0];
@@ -126,11 +135,9 @@
final int mHeight;
final Point mOutSurfaceSize = new Point();
final SurfaceControl mOutSurfaceControl;
- final SurfaceControl mOutBlastSurfaceControl = new SurfaceControl();
final IntSupplier mViewVisibility;
- int mSeq;
int mFrameNumber;
int mFlags;
@@ -147,12 +154,10 @@
void runBenchmark(BenchmarkState state) throws RemoteException {
final IWindowSession session = WindowManagerGlobal.getWindowSession();
while (state.keepRunning()) {
- session.relayout(mWindow, mSeq, mParams, mWidth, mHeight,
- mViewVisibility.getAsInt(), mFlags, mFrameNumber, mOutFrame,
- mOutContentInsets, mOutVisibleInsets, mOutStableInsets,
- mOutBackDropFrame, mOutDisplayCutout, mOutMergedConfiguration,
- mOutSurfaceControl, mOutInsetsState, mOutControls, mOutSurfaceSize,
- mOutBlastSurfaceControl);
+ session.relayout(mWindow, mParams, mWidth, mHeight,
+ mViewVisibility.getAsInt(), mFlags, mFrameNumber, mOutFrames,
+ mOutMergedConfiguration, mOutSurfaceControl, mOutInsetsState, mOutControls,
+ mOutSurfaceSize);
}
}
}
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
index c72cc9d..a2dc1c2 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
@@ -19,14 +19,12 @@
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
-import android.graphics.Rect;
import android.os.RemoteException;
import android.os.SystemClock;
import android.perftests.utils.ManualBenchmarkState;
import android.perftests.utils.ManualBenchmarkState.ManualBenchmarkTest;
import android.perftests.utils.PerfManualStatusReporter;
import android.view.Display;
-import android.view.DisplayCutout;
import android.view.IWindowSession;
import android.view.InputChannel;
import android.view.InsetsSourceControl;
@@ -48,23 +46,21 @@
public class WindowAddRemovePerfTest extends WindowManagerPerfTestBase
implements ManualBenchmarkState.CustomizedIterationListener {
- private static final int PROFILED_ITERATIONS = 2;
-
@Rule
public final PerfManualStatusReporter mPerfStatusReporter = new PerfManualStatusReporter();
@BeforeClass
public static void setUpClass() {
// Get the permission to use most window types.
- sUiAutomation.adoptShellPermissionIdentity();
+ getUiAutomation().adoptShellPermissionIdentity();
}
@AfterClass
public static void tearDownClass() {
- sUiAutomation.dropShellPermissionIdentity();
+ getUiAutomation().dropShellPermissionIdentity();
}
- /** The last {@link #PROFILED_ITERATIONS} will provide the information of method profiling. */
+ /** The last customized iterations will provide the information of method profiling. */
@Override
public void onStart(int iteration) {
startProfiling(WindowAddRemovePerfTest.class.getSimpleName()
@@ -80,17 +76,13 @@
@ManualBenchmarkTest(warmupDurationNs = TIME_1_S_IN_NS, targetTestDurationNs = TIME_5_S_IN_NS)
public void testAddRemoveWindow() throws Throwable {
final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- state.setCustomizedIterations(PROFILED_ITERATIONS, this);
+ state.setCustomizedIterations(getProfilingIterations(), this);
new TestWindow().runBenchmark(state);
}
private static class TestWindow extends BaseIWindow {
final WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams();
- final Rect mOutFrame = new Rect();
- final Rect mOutContentInsets = new Rect();
- final Rect mOutStableInsets = new Rect();
- final DisplayCutout.ParcelableWrapper mOutDisplayCutout =
- new DisplayCutout.ParcelableWrapper();
+ final InsetsState mRequestedVisibility = new InsetsState();
final InsetsState mOutInsetsState = new InsetsState();
final InsetsSourceControl[] mOutControls = new InsetsSourceControl[0];
@@ -109,9 +101,9 @@
final InputChannel inputChannel = new InputChannel();
long startTime = SystemClock.elapsedRealtimeNanos();
- session.addToDisplay(this, mSeq, mLayoutParams, View.VISIBLE,
- Display.DEFAULT_DISPLAY, mOutFrame, mOutContentInsets, mOutStableInsets,
- mOutDisplayCutout, inputChannel, mOutInsetsState, mOutControls);
+ session.addToDisplay(this, mLayoutParams, View.VISIBLE,
+ Display.DEFAULT_DISPLAY, mRequestedVisibility, inputChannel,
+ mOutInsetsState, mOutControls);
final long elapsedTimeNsOfAdd = SystemClock.elapsedRealtimeNanos() - startTime;
state.addExtraResult("add", elapsedTimeNsOfAdd);
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java b/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java
index 655d2f7..4b1982f 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java
@@ -19,125 +19,45 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import android.app.Activity;
-import android.app.KeyguardManager;
-import android.app.UiAutomation;
-import android.content.Context;
import android.content.Intent;
-import android.os.BatteryManager;
-import android.os.ParcelFileDescriptor;
-import android.os.PowerManager;
import android.perftests.utils.PerfTestActivity;
-import android.provider.Settings;
+import android.perftests.utils.WindowPerfTestBase;
-import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.lifecycle.ActivityLifecycleCallback;
import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
import androidx.test.runner.lifecycle.Stage;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
-import java.io.ByteArrayOutputStream;
import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
import java.util.concurrent.TimeUnit;
-public class WindowManagerPerfTestBase {
- static final UiAutomation sUiAutomation = getInstrumentation().getUiAutomation();
- static final long NANOS_PER_S = 1000L * 1000 * 1000;
- static final long TIME_1_S_IN_NS = 1 * NANOS_PER_S;
+public class WindowManagerPerfTestBase extends WindowPerfTestBase {
static final long TIME_5_S_IN_NS = 5 * NANOS_PER_S;
/**
* The out directory matching the directory-keys of collector in AndroidTest.xml. The directory
- * is in /data because while enabling method profling of system server, it cannot write the
+ * is in /data because while enabling method profiling of system server, it cannot write the
* trace to external storage.
*/
- static final File BASE_OUT_PATH = new File("/data/local/CorePerfTests");
+ static final File BASE_OUT_PATH = new File("/data/local/tmp/WmPerfTests");
- private static int sOriginalStayOnWhilePluggedIn;
-
- @BeforeClass
- public static void setUpOnce() {
- final Context context = getInstrumentation().getContext();
- final int stayOnWhilePluggedIn = Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
- sOriginalStayOnWhilePluggedIn = -1;
- if (stayOnWhilePluggedIn != BatteryManager.BATTERY_PLUGGED_ANY) {
- sOriginalStayOnWhilePluggedIn = stayOnWhilePluggedIn;
- // Keep the device awake during testing.
- setStayOnWhilePluggedIn(BatteryManager.BATTERY_PLUGGED_ANY);
- }
-
- if (!BASE_OUT_PATH.exists()) {
- executeShellCommand("mkdir -p " + BASE_OUT_PATH);
- }
- if (!context.getSystemService(PowerManager.class).isInteractive()
- || context.getSystemService(KeyguardManager.class).isKeyguardLocked()) {
- executeShellCommand("input keyevent KEYCODE_WAKEUP");
- executeShellCommand("wm dismiss-keyguard");
- }
- context.startActivity(new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_HOME).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
- }
-
- @AfterClass
- public static void tearDownOnce() {
- if (sOriginalStayOnWhilePluggedIn != -1) {
- setStayOnWhilePluggedIn(sOriginalStayOnWhilePluggedIn);
- }
- }
-
- private static void setStayOnWhilePluggedIn(int value) {
- executeShellCommand(String.format("settings put global %s %d",
- Settings.Global.STAY_ON_WHILE_PLUGGED_IN, value));
+ static void startProfiling(String outFileName) {
+ startProfiling(BASE_OUT_PATH, outFileName);
}
/**
- * Executes shell command with reading the output. It may also used to block until the current
- * command is completed.
+ * Provides an activity that is able to wait for a stable lifecycle stage.
*/
- static ByteArrayOutputStream executeShellCommand(String command) {
- final ParcelFileDescriptor pfd = sUiAutomation.executeShellCommand(command);
- final byte[] buf = new byte[512];
- final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
- int bytesRead;
- try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
- while ((bytesRead = fis.read(buf)) != -1) {
- bytes.write(buf, 0, bytesRead);
- }
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- return bytes;
- }
-
- /** Starts method tracing on system server. */
- void startProfiling(String subPath) {
- executeShellCommand("am profile start system " + new File(BASE_OUT_PATH, subPath));
- }
-
- void stopProfiling() {
- executeShellCommand("am profile stop system");
- }
-
- /**
- * Provides an activity that keeps screen on and is able to wait for a stable lifecycle stage.
- */
- static class PerfTestActivityRule extends ActivityTestRule<PerfTestActivity> {
- private final Intent mStartIntent =
- new Intent(getInstrumentation().getTargetContext(), PerfTestActivity.class);
+ static class PerfTestActivityRule extends PerfTestActivityRuleBase {
private final LifecycleListener mLifecycleListener = new LifecycleListener();
PerfTestActivityRule() {
- this(false /* launchActivity */);
}
PerfTestActivityRule(boolean launchActivity) {
- super(PerfTestActivity.class, false /* initialTouchMode */, launchActivity);
+ super(launchActivity);
}
@Override
@@ -156,21 +76,12 @@
}
@Override
- protected Intent getActivityIntent() {
- return mStartIntent;
- }
-
- @Override
public PerfTestActivity launchActivity(Intent intent) {
final PerfTestActivity activity = super.launchActivity(intent);
mLifecycleListener.setTargetActivity(activity);
return activity;
}
- PerfTestActivity launchActivity() {
- return launchActivity(mStartIntent);
- }
-
void waitForIdleSync(Stage state) {
mLifecycleListener.waitForIdleSync(state);
}
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.java b/apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.java
new file mode 100644
index 0000000..2b0801a
--- /dev/null
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.wm;
+
+import android.perftests.utils.WindowPerfRunPreconditionBase;
+
+/** Prepare the preconditions before running performance test. */
+public class WmPerfRunListener extends WindowPerfRunPreconditionBase {
+}
diff --git a/apct-tests/perftests/windowmanager/src/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/apct-tests/perftests/windowmanager/src/com/android/server/wm/test/filters/FrameworksTestsFilter.java
new file mode 100644
index 0000000..d018287
--- /dev/null
+++ b/apct-tests/perftests/windowmanager/src/com/android/server/wm/test/filters/FrameworksTestsFilter.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.test.filters;
+
+import android.wm.RelayoutPerfTest;
+
+import org.junit.runner.Description;
+import org.junit.runner.manipulation.Filter;
+
+/**
+ * A static filter to have the same signature as the one in frameworks/base/tests/utils/testutils/.
+ * This doesn't share the existing library because it doesn't support parameterized test.
+ */
+public class FrameworksTestsFilter extends Filter {
+
+ private boolean mShouldRun;
+
+ @Override
+ public boolean shouldRun(Description description) {
+ final Class<?> testClass = description.getTestClass();
+ // Parameterized test methods don't have the original information. So keep the last status
+ // that matches the target class.
+ mShouldRun = (mShouldRun && testClass == null) || testClass == RelayoutPerfTest.class;
+ return mShouldRun;
+ }
+
+ @Override
+ public String describe() {
+ return "Default filter";
+ }
+}
diff --git a/apex/Android.bp b/apex/Android.bp
index 992648b..f3a9362 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -12,298 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-mainline_stubs_args =
- "--error UnhiddenSystemApi " +
- "--hide BroadcastBehavior " +
- "--hide DeprecationMismatch " +
- "--hide HiddenSuperclass " +
- "--hide HiddenTypedefConstant " +
- "--hide HiddenTypeParameter " +
- "--hide MissingPermission " +
- "--hide RequiresPermission " +
- "--hide SdkConstant " +
- "--hide Todo " +
- "--hide Typo " +
- "--hide UnavailableSymbol "
-
-// TODO: modularize this so not every module has the same whitelist
-framework_packages_to_document = [
- "android",
- "dalvik",
- "java",
- "javax",
- "junit",
- "org.apache.http",
- "org.json",
- "org.w3c.dom",
- "org.xml.sax",
- "org.xmlpull",
-]
-
-// TODO: remove the hiding when server classes are cleaned up.
-mainline_framework_stubs_args =
- mainline_stubs_args +
- "--hide-package com.android.server "
-
-priv_apps = " " +
- "--show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
- "\\) "
-
-module_libs = " " +
- " --show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" +
- "\\) "
-
-mainline_service_stubs_args =
- mainline_stubs_args +
- "--show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.SYSTEM_SERVER" +
- "\\) " +
- "--hide-annotation android.annotation.Hide " +
- "--hide InternalClasses " // com.android.* classes are okay in this interface
-
-// Defaults common to all mainline module java_sdk_library instances.
-java_defaults {
- name: "framework-module-common-defaults",
-
- // Additional annotations used for compiling both the implementation and the
- // stubs libraries.
- libs: ["framework-annotations-lib"],
-
- // Framework modules are not generally shared libraries, i.e. they are not
- // intended, and must not be allowed, to be used in a <uses-library> manifest
- // entry.
- shared_library: false,
-
- // Enable api lint. This will eventually become the default for java_sdk_library
- // but it cannot yet be turned on because some usages have not been cleaned up.
- // TODO(b/156126315) - Remove when no longer needed.
- api_lint: {
- enabled: true,
- },
-
- // The API scope specific properties.
- public: {
- enabled: true,
- sdk_version: "module_current",
- },
-
- // Configure framework module specific metalava options.
- droiddoc_options: [mainline_stubs_args],
-
- annotations_enabled: true,
-
- stubs_library_visibility: [
- "//visibility:public",
- ],
-
- // Set the visibility of the modules creating the stubs source.
- stubs_source_visibility: [
- // Ignore any visibility rules specified on the java_sdk_library when
- // setting the visibility of the stubs source modules.
- "//visibility:override",
-
- // Currently, the stub source is not required for anything other than building
- // the stubs library so is private to avoid misuse.
- "//visibility:private",
- ],
-
- // Collates API usages from each module for further analysis.
- plugins: ["java_api_finder"],
-
- // Mainline modules should only rely on 'module_lib' APIs provided by other modules
- // and the non updatable parts of the platform.
- sdk_version: "module_current",
-}
-
-// Defaults for mainline module provided java_sdk_library instances.
-java_defaults {
- name: "framework-module-defaults",
- defaults: ["framework-module-common-defaults"],
-
- system: {
- enabled: true,
- sdk_version: "module_current",
- },
- module_lib: {
- enabled: true,
- sdk_version: "module_current",
- },
-}
-
-// Defaults for mainline module system server provided java_sdk_library instances.
-java_defaults {
- name: "framework-system-server-module-defaults",
- defaults: ["framework-module-common-defaults"],
-
- system_server: {
- enabled: true,
- sdk_version: "module_current",
- },
-}
-
-stubs_defaults {
- name: "framework-module-stubs-defaults-publicapi",
- args: mainline_framework_stubs_args,
- installable: false,
- sdk_version: "module_current",
- annotations_enabled: true,
- merge_annotations_dirs: [
- "metalava-manual",
- ],
- filter_packages: framework_packages_to_document,
- check_api: {
- current: {
- api_file: "api/current.txt",
- removed_api_file: "api/removed.txt",
- },
- api_lint: {
- enabled: true,
- },
- },
- dist: {
- targets: ["sdk", "win_sdk"],
- dir: "apistubs/android/public/api",
- },
-}
-
-stubs_defaults {
- name: "framework-module-stubs-defaults-systemapi",
- args: mainline_framework_stubs_args + priv_apps,
- libs: ["framework-annotations-lib"],
- installable: false,
- sdk_version: "module_current",
- annotations_enabled: true,
- merge_annotations_dirs: [
- "metalava-manual",
- ],
- filter_packages: framework_packages_to_document,
- check_api: {
- current: {
- api_file: "api/system-current.txt",
- removed_api_file: "api/system-removed.txt",
- },
- api_lint: {
- enabled: true,
- },
- },
- dist: {
- targets: ["sdk", "win_sdk"],
- dir: "apistubs/android/system/api",
- },
-}
-
-java_defaults {
- name: "framework-module-stubs-lib-defaults-publicapi",
- installable: false,
- sdk_version: "module_current",
- libs: [ "stub-annotations" ],
- java_version: "1.8",
- dist: {
- targets: ["sdk", "win_sdk"],
- dir: "apistubs/android/public",
- },
-}
-
-java_defaults {
- name: "framework-module-stubs-lib-defaults-systemapi",
- installable: false,
- sdk_version: "module_current",
- libs: [ "stub-annotations" ],
- java_version: "1.8",
- dist: {
- targets: ["sdk", "win_sdk"],
- dir: "apistubs/android/system",
- },
-}
-
-java_defaults {
- name: "framework-module-stubs-lib-defaults-module_libs_api",
- installable: false,
- sdk_version: "module_current",
- libs: [ "stub-annotations" ],
- java_version: "1.8",
- dist: {
- targets: ["sdk", "win_sdk"],
- dir: "apistubs/android/module-lib",
- },
-}
-
-// The defaults for module_libs comes in two parts - defaults for API checks
-// and defaults for stub generation. This is because we want the API txt
-// files to *only* include the module_libs_api, but the stubs to include
-// module_libs_api as well as priv_apps.
-
-stubs_defaults {
- name: "framework-module-api-defaults-module_libs_api",
- args: mainline_framework_stubs_args + module_libs,
- libs: ["framework-annotations-lib"],
- installable: false,
- sdk_version: "module_current",
- annotations_enabled: true,
- merge_annotations_dirs: [
- "metalava-manual",
- ],
- filter_packages: framework_packages_to_document,
- check_api: {
- current: {
- api_file: "api/module-lib-current.txt",
- removed_api_file: "api/module-lib-removed.txt",
- },
- api_lint: {
- enabled: true,
- },
- },
- dist: {
- targets: ["sdk", "win_sdk"],
- dir: "apistubs/android/module-lib/api",
- },
-}
-
-stubs_defaults {
- name: "framework-module-stubs-defaults-module_libs_api",
- args: mainline_framework_stubs_args + module_libs + priv_apps,
- libs: ["framework-annotations-lib"],
- installable: false,
- sdk_version: "module_current",
- annotations_enabled: true,
- merge_annotations_dirs: [
- "metalava-manual",
- ],
- filter_packages: framework_packages_to_document,
-}
-
-stubs_defaults {
- name: "service-module-stubs-srcs-defaults",
- args: mainline_service_stubs_args,
- installable: false,
- annotations_enabled: true,
- merge_annotations_dirs: [
- "metalava-manual",
- ],
- filter_packages: ["com.android."],
- check_api: {
- current: {
- api_file: "api/current.txt",
- removed_api_file: "api/removed.txt",
- },
- api_lint: {
- enabled: true,
- },
- },
- dist: {
- targets: ["sdk", "win_sdk"],
- dir: "apistubs/android/system-server/api",
- },
-}
-
-// Empty for now, but a convenient place to add rules for all
-// module java_library system_server stub libs.
-java_defaults {
- name: "service-module-stubs-defaults",
- dist: {
- targets: ["sdk", "win_sdk"],
- dir: "apistubs/android/system-server",
- },
+package {
+ default_visibility: [":__subpackages__"],
+ // 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"],
}
diff --git a/apex/OWNERS b/apex/OWNERS
new file mode 100644
index 0000000..b3e81b9
--- /dev/null
+++ b/apex/OWNERS
@@ -0,0 +1 @@
+file:platform/packages/modules/common:/OWNERS
diff --git a/apex/appsearch/Android.bp b/apex/appsearch/Android.bp
new file mode 100644
index 0000000..977e610
--- /dev/null
+++ b/apex/appsearch/Android.bp
@@ -0,0 +1,75 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // 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"],
+}
+
+apex {
+ name: "com.android.appsearch",
+ manifest: "apex_manifest.json",
+ bootclasspath_fragments: ["com.android.appsearch-bootclasspath-fragment"],
+ systemserverclasspath_fragments: ["com.android.appsearch-systemserverclasspath-fragment"],
+ key: "com.android.appsearch.key",
+ certificate: ":com.android.appsearch.certificate",
+ updatable: false,
+ jni_libs: ["libicing"],
+ generate_hashtree: false,
+}
+
+apex_key {
+ name: "com.android.appsearch.key",
+ public_key: "com.android.appsearch.avbpubkey",
+ private_key: "com.android.appsearch.pem",
+}
+
+android_app_certificate {
+ name: "com.android.appsearch.certificate",
+ // This will use com.android.appsearch.x509.pem (the cert) and
+ // com.android.appsearch.pk8 (the private key)
+ certificate: "com.android.appsearch",
+}
+
+// Encapsulate the contributions made by the com.android.appsearch to the bootclasspath.
+bootclasspath_fragment {
+ name: "com.android.appsearch-bootclasspath-fragment",
+ contents: ["framework-appsearch"],
+ apex_available: ["com.android.appsearch"],
+
+ // The bootclasspath_fragments that provide APIs on which this depends.
+ fragments: [
+ {
+ apex: "com.android.art",
+ module: "art-bootclasspath-fragment",
+ },
+ ],
+
+ // Additional stubs libraries that this fragment's contents use which are
+ // not provided by another bootclasspath_fragment.
+ additional_stubs: [
+ "android-non-updatable",
+ ],
+}
+
+// Encapsulate the contributions made by the com.android.appsearch to the systemserverclasspath.
+systemserverclasspath_fragment {
+ name: "com.android.appsearch-systemserverclasspath-fragment",
+ contents: ["service-appsearch"],
+ apex_available: ["com.android.appsearch"],
+}
diff --git a/apex/appsearch/OWNERS b/apex/appsearch/OWNERS
new file mode 100644
index 0000000..1703369
--- /dev/null
+++ b/apex/appsearch/OWNERS
@@ -0,0 +1,3 @@
+adorokhine@google.com
+sudheersai@google.com
+yamasani@google.com
diff --git a/apex/appsearch/apex_manifest.json b/apex/appsearch/apex_manifest.json
new file mode 100644
index 0000000..39a2d38
--- /dev/null
+++ b/apex/appsearch/apex_manifest.json
@@ -0,0 +1,4 @@
+{
+ "name": "com.android.appsearch",
+ "version": 300000000
+}
diff --git a/apex/appsearch/com.android.appsearch.avbpubkey b/apex/appsearch/com.android.appsearch.avbpubkey
new file mode 100644
index 0000000..4e5acae
--- /dev/null
+++ b/apex/appsearch/com.android.appsearch.avbpubkey
Binary files differ
diff --git a/apex/appsearch/com.android.appsearch.pem b/apex/appsearch/com.android.appsearch.pem
new file mode 100644
index 0000000..4ed5945
--- /dev/null
+++ b/apex/appsearch/com.android.appsearch.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKQIBAAKCAgEAro9f/jvoIsj6ywuRmuUQS8UtprhohJitrovDMfm/T2R/WQvy
+AvUxgetyF4XvBPCDRqCsGxXCJMQOn1furrAeTmWbGHPhA0PI1Ys/qtfNMbh9THyn
+70I2c4X70CUQ+8/Y8BJ8CAB4iER/s9QtD28QLvM2BBUzRoKUSBGUYNMlYobjgRdK
+57V7yg48LkvUIg1fzIW3M5gCgOXa0u1xOadKX3m7tzCboHcXp5anfWX5PH1+okRu
+jzdI8OjtUq23qhoRw5Skz0Vbf4a+8t3kT3slF/Q7O8LoRPwpZsvIcvTyCGAqlra7
+2L2LN4H1p+u2ko3r/QmRbJn2eXW07elkyrggXMyn2rTxibQgk53wYfSavMyNd/E7
++de/uJ60l2aPa+5KUaR8eYwchXEELdqQ+zRgSZ2711xCaY4glEj7DT6VlEEdr26x
+akX0ra7e2sVGv1um/dvSyVO5aFKKjVvo4LqhWKWO8yvDMxmDDTNatvWhY2Bhd3RA
+0hilYpWQFb9Tv5f4E0tZmfvlddgux7sw++Y/RIimBFoSyf5AezAUIFYYoYvEzytB
+muq1/ecNHr+Z2tZMxN88sJVhzRzD9tKUyXhvxOV2Lg9TIeVTWGwQqgSnHWtIe+1p
+cw8inPfYEhP4Q+3W/RlPvNdu75x8Nj2aG7bxZnhoQDRDw5ddgma27I+a8esCAwEA
+AQKCAgBsNh9I6HRAVBz8kCBkSEnw3rwtFTZdtJQ+lw+bRHpvShqT5g7R/JQDOSTS
+JkoE4uBOgT4P0E45Inz6FLW2/yDacqxR3UwJDRVMI/WFACCJCRhLuR8V+BLvTIjN
+AJ1lrPSL5rmS8E/IEcakgQyp+6ypnkXHBCl0NXCcuKEl4N7VFE+mb/0UZPHnUSnH
+fWR085uGmwH17u7mXxdnGKDPH8DALSPMLUrcj9dPIdqUpwl5kUZWa1uqVphWF98/
+GMe5oE2Q0+3TO+i7xplKz3lAOFPHZLTvmCUK1tMHkZ6ifOwpewwLwB30/5N1BpB1
+126nrWk0xKCtFUixBOHzdnLwJHKSbi7chQU5q39oAJoTfxdmAJlaG0zQHUQZ44MQ
+gemzSA7uJbtoAOAZVF1K14xbIpnfidqTB7N3RCmiJE+/Hpkq6PxgPfu5rqocPbPC
+t0FgJ4NXNmKOAuJllSlrrHATcUOhF4g5pX7tvOc8X4y7bvfwOmtw5ez3INKMF0q6
+/y0vVCi6N1Z7CTa9eY8feZ1PImk/Fkq4NInSPyx7ZE3pLYmsvuJjliFrWo9TRVae
+Dt5vvBKBOpAfhDiHkeXbX7Raj2B6c6adF4no/3SAVlAjIq1iBVjfQWyHAGUoEW1O
+u3LdHTIb6gSTLJ4AfryEKrOE+1VMlYt92GwX692KKXMaJjytSQKCAQEA3pYbl8HD
+Y++UyEN5VzWAQedT3//GDwpDfgdERe2E4smYrkVNJ2WAG2SqY1A35DIl8be3eHvl
+soaL38j48ailfDYY9tI+IlapNh+VOLej+HiOytaPlLhcv2FpSC2qZT4EiU6IBXLo
++l6FrmD/VQXTjvoktzsDB/n1t4Dfa3Ogf+lLf1Jxr94YpEnDh18V5ofj78SplVLm
+NrzsHxAafE4Ni2a7dyWjcDYIuL7FTShT+0K4W45tRr+CGxThxu7LEe7zw4Z1IagU
+jJNtXjvDD/Zw4UTqI6RwWGZsu6UjPS6LHhOqnWqflWmFRIfMbDkuWvnGZTM9DkVg
+kk1+BNi1PECZXwKCAQEAyMOjbVo6XV3lFN0X8TpHyg/z9ar00/SE7WEJHqPSuzYT
+rSfU4vDDlaPAwkYvGi9ZKi9VM+R3CyBNxnK9Yq6NurHhhrYcAwdS/hGLT1K2o0Y8
+Pgv7gZCFb+SIwLBhlUG9otGULcBzLneqgVUqyMG6IoCjuC2LRyB71Xc2UMyg6n/f
+XpV2RTMb8f+26cgm6nj0SDAfgpr8HV6uNV80c6l1A8gq86nUWwiVAEUdmExSDe7J
+shsfWAj8RSErqDXf1BtEdPLJUSIPX5VXkzAXOXIkengwVno0vv0dBN8uraS8iQSG
+0JsJLLcw9b5kvnh6FEbE7POsIqKyCZV9VADwO6YW9QKCAQBYQsdwNqoGv6KMgozj
+8tgHyfWtVduwbQ50M+dznwpZbzz2pY5Bd/MDabhSpyVyfBwlrAa5ZM+hKc7fDu7/
+zDLKfR0LCjUPIrP4PS/LjK4dQZjFf6zxeOV2EedQcqMlgCEGXTh8iKMvXDm/+sBk
+c2n/QNs8OM8r44b2m8h78B6NefGw6/0ekn/M7V72F9M0VWAh3Cauim+09tbePmFy
+NvUR+MuPJEKZpSNyNltADCS49izqSSC1tAygNniMjHXDh6/rMS7TCLYVRARTIHlp
+o/wAp3X8aiEOPJcTFRlTElihtYSq5POgqHXqxbpek5H5CyALUvT76rCvcsDspQ3A
+dZEbAoIBAQCoLEmP5o8Rev/UdEgECB/uwWJIngYsLp3TAv/SrMRvkiL1X3JTD/+m
+L9/eXVBDjPoR/khPCcg2h77ex2qhaTrL8wnKAG6CkvYQYb3impTnPIRmLT9nDxrX
+2gY78wQrNUCXTRvlH1rcx90KLb+DH9S95ig+tdf/otRYwl27XU5GYQtJfcXuvZth
+IiWku8btjpiCh909WHpsV81yY+faI08j9d8U8WQzRYMbEMpzsyrhBO/rxBCDfDNl
+7R1W8JooYRb9KAs/bVqXZNBROW2a72RjOp6zMfdRLVHLrPC7AE32MNaFk/khfesD
+T5OwgdcxeP6oxo2hDcw5fwHXBlo2fTCpAoIBAQChgjv5AfQ50spqvHy6MNem4tV0
+L0IsxmNLsi8X2a6s4kStwUzOxDA8c/e54XabxQNZ0ERU1q+bgbG7PWC4twDMPR8i
+2DO6rgqSK4MjGOTgAoeDuy3mElFQmCLRs04Wf4jh8kPi217WFlYBynh2HmBKbh42
+JmIrLetbKEK13FXRvMkgZcX4OIDrT5TOvev4VZArU8PTRlWv3sqsKAVXjX0clGHf
+I0/2kSsr2qq1UY7JrYWZsZ9uqz2ZH0pF19a6O/Cq4uqTYoL+sYzFTSeFmChRjV1g
+ancTvTn9lcBqECDMgq5DE/p96Oxg/t8elalR6WDUlysafphVz3nTuyMTh7ka
+-----END RSA PRIVATE KEY-----
diff --git a/apex/appsearch/com.android.appsearch.pk8 b/apex/appsearch/com.android.appsearch.pk8
new file mode 100644
index 0000000..77e98b2
--- /dev/null
+++ b/apex/appsearch/com.android.appsearch.pk8
Binary files differ
diff --git a/apex/appsearch/com.android.appsearch.x509.pem b/apex/appsearch/com.android.appsearch.x509.pem
new file mode 100644
index 0000000..e37c4b9
--- /dev/null
+++ b/apex/appsearch/com.android.appsearch.x509.pem
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIGETCCA/mgAwIBAgIUGl+MIvpEi0+TWzj0ieh6WtjXmP8wDQYJKoZIhvcNAQEL
+BQAwgZYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
+DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy
+b2lkMRIwEAYDVQQDDAlhcHBzZWFyY2gxIjAgBgkqhkiG9w0BCQEWE2FuZHJvaWRA
+YW5kcm9pZC5jb20wIBcNMTkxMTIwMjMxNTM1WhgPNDc1NzEwMTYyMzE1MzVaMIGW
+MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91
+bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDES
+MBAGA1UEAwwJYXBwc2VhcmNoMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJv
+aWQuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsyPlp3q3P9Xg
+W1WhIwQiF9em9oqaGQ/3dbIxickAy591qcRbpHb4lDTZusRECfqlV215mV+lv5x4
+EhOnId3uPKBAJ/YDtL7zUW6TWL7to7zEnUqSIKTcoQzNF2EiCeGuRhrtgYvAD3HQ
+dwr4xrbSADbDArF04A49voLpsmq1fyNgl86VISiMRqoSLJnA6eghlduuOt+nf252
+6WgxDs/JrO/eK70q0+RwmWzVJ/tVr+36a65N4EHhfL4t2hdV0k0XFob7hBn7XWzC
+QrSR3jCvE3yAfAr3tq5c19/WWBA7V45nEHzXyAvBUHWubYvDi+vm/yzqU2rQwScC
+bzp4zK4CnhBHqb4gHoy0+kfFIwJ1A3GT2pl3ba/NsIYgliMtPQfkDV5PE5RTNcwH
+21ewH7vm2+spQv5Z/2TEV2lEHlp2vuAliyn2AT4u1ginr6vtBRFLmpPeziFcfB0y
+7h04GctZpX8odz+XI7aMDe47RNu9XyJX0vulntxmlDF76k8Z9DIXg02hY+yc/i7+
+2ztnj1eXL51p+HyhK5VbvJWbKkVaMQijlbuIMYNzMA6L0WHWRc2Cux9UDODMGoiC
+w09JpqudCS/95I/F1xaWJ/Kh3vKeQshHAz0hrL7v7wpjmfeXf6NGsWJGy+giCwZj
+ABtn9nFQoesgi7M1LeazD5Q/4v4AMaUCAwEAAaNTMFEwHQYDVR0OBBYEFJpHCy2Y
+3qaL6cLpE9fe53L61KEEMB8GA1UdIwQYMBaAFJpHCy2Y3qaL6cLpE9fe53L61KEE
+MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAGDYAcOhbOYcDB2K
+WDZka+FCORFFvz4nLQGE7Z9TAn1g7XusM2KbXlb2drIN6CWOFlnKQrUsNsAHrc+s
+tl+A1vC3/NfYKKBVuizPx/kHUgz3k/UIJzbzEu/uCJd86idcJoUTqC/qEJAeeQqM
+XpsNP1Yg7oyzZT8sFlUAKeDeXJ7fIDXR6nduUQ6uJXkee/5JF3VedHdgHAUsC19/
+KHhyVU3MLDUNBdAmM79+DsdVYi2Pw31jojMu95Zz1MYTRBcgQAiEw5nncr38k6ac
+Gy+JffgJR68FzI4QLBSxnDRFD2zXJ09lpP6Sjb1FVcDzk7Bi/EQDLBkrkbeLsk5F
+a0xz9VoJ3kM7Cc4R9MXN4ZWuePjdJwgasnHmllsXn45R9odgJgmfzuUwtgNw/XKQ
+QcQl7Q9QUrBCqIoHijxscUZCBSmIHVNBBDckRAmSXHeWMRlO3uBR4IA/Jfrt//4f
+uc7CNUp+LQ6EzBXJOVFrXRtau6Oj+jM1+fzxKo1uV2+T+GdVEE5jeF/6nB3qna6h
+2NmyLqbqeqp2QxgzBWSGy8Ugs6zg4wItJBqOoRLKKFxTJu5OAzJ4fUA+g7WFXNhR
+kG56SJ863LZoORKHWE72oXYeIW98Tq0qKLH3NzH5L4tfX8DeBTq+APezHetH1ljA
+D0avPy62g0i643bbpwZgezBgRIKL
+-----END CERTIFICATE-----
diff --git a/apex/appsearch/framework/Android.bp b/apex/appsearch/framework/Android.bp
new file mode 100644
index 0000000..8964668
--- /dev/null
+++ b/apex/appsearch/framework/Android.bp
@@ -0,0 +1,68 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+ name: "framework-appsearch-sources",
+ srcs: [
+ ":framework-appsearch-internal-sources",
+ ":framework-appsearch-external-sources",
+ ],
+ visibility: ["//frameworks/base"],
+}
+
+filegroup {
+ name: "framework-appsearch-internal-sources",
+ srcs: [
+ "java/**/*.java",
+ "java/**/*.aidl",
+ ],
+ exclude_srcs: [":framework-appsearch-external-sources"],
+ path: "java",
+}
+
+filegroup {
+ name: "framework-appsearch-external-sources",
+ srcs: [
+ "java/external/**/*.java",
+ "java/external/**/*.aidl",
+ ],
+ path: "java/external",
+}
+
+java_sdk_library {
+ name: "framework-appsearch",
+ srcs: [":framework-appsearch-sources"],
+ sdk_version: "module_current",
+ static_libs: [
+ // This list must be kept in sync with jarjar.txt
+ "modules-utils-preconditions",
+ ],
+ defaults: ["framework-module-defaults"],
+ permitted_packages: ["android.app.appsearch"],
+ jarjar_rules: "jarjar-rules.txt",
+ apex_available: ["com.android.appsearch"],
+ impl_library_visibility: [
+ "//frameworks/base/apex/appsearch/service",
+ ],
+ unsafe_ignore_missing_latest_api: true, // TODO(b/146218515) should be removed
+}
diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt
new file mode 100644
index 0000000..761405c
--- /dev/null
+++ b/apex/appsearch/framework/api/current.txt
@@ -0,0 +1,460 @@
+// Signature format: 2.0
+package android.app.appsearch {
+
+ public final class AppSearchBatchResult<KeyType, ValueType> {
+ method @NonNull public java.util.Map<KeyType,android.app.appsearch.AppSearchResult<ValueType>> getAll();
+ method @NonNull public java.util.Map<KeyType,android.app.appsearch.AppSearchResult<ValueType>> getFailures();
+ method @NonNull public java.util.Map<KeyType,ValueType> getSuccesses();
+ method public boolean isSuccess();
+ }
+
+ public static final class AppSearchBatchResult.Builder<KeyType, ValueType> {
+ ctor public AppSearchBatchResult.Builder();
+ method @NonNull public android.app.appsearch.AppSearchBatchResult<KeyType,ValueType> build();
+ method @NonNull public android.app.appsearch.AppSearchBatchResult.Builder<KeyType,ValueType> setFailure(@NonNull KeyType, int, @Nullable String);
+ method @NonNull public android.app.appsearch.AppSearchBatchResult.Builder<KeyType,ValueType> setResult(@NonNull KeyType, @NonNull android.app.appsearch.AppSearchResult<ValueType>);
+ method @NonNull public android.app.appsearch.AppSearchBatchResult.Builder<KeyType,ValueType> setSuccess(@NonNull KeyType, @Nullable ValueType);
+ }
+
+ public class AppSearchManager {
+ method public void createGlobalSearchSession(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<android.app.appsearch.GlobalSearchSession>>);
+ method public void createSearchSession(@NonNull android.app.appsearch.AppSearchManager.SearchContext, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<android.app.appsearch.AppSearchSession>>);
+ }
+
+ public static final class AppSearchManager.SearchContext {
+ method @NonNull public String getDatabaseName();
+ }
+
+ public static final class AppSearchManager.SearchContext.Builder {
+ ctor public AppSearchManager.SearchContext.Builder(@NonNull String);
+ method @NonNull public android.app.appsearch.AppSearchManager.SearchContext build();
+ }
+
+ public final class AppSearchResult<ValueType> {
+ method @Nullable public String getErrorMessage();
+ method public int getResultCode();
+ method @Nullable public ValueType getResultValue();
+ method public boolean isSuccess();
+ method @NonNull public static <ValueType> android.app.appsearch.AppSearchResult<ValueType> newFailedResult(int, @Nullable String);
+ method @NonNull public static <ValueType> android.app.appsearch.AppSearchResult<ValueType> newSuccessfulResult(@Nullable ValueType);
+ field public static final int RESULT_INTERNAL_ERROR = 2; // 0x2
+ field public static final int RESULT_INVALID_ARGUMENT = 3; // 0x3
+ field public static final int RESULT_INVALID_SCHEMA = 7; // 0x7
+ field public static final int RESULT_IO_ERROR = 4; // 0x4
+ field public static final int RESULT_NOT_FOUND = 6; // 0x6
+ field public static final int RESULT_OK = 0; // 0x0
+ field public static final int RESULT_OUT_OF_SPACE = 5; // 0x5
+ field public static final int RESULT_SECURITY_ERROR = 8; // 0x8
+ field public static final int RESULT_UNKNOWN_ERROR = 1; // 0x1
+ }
+
+ public final class AppSearchSchema {
+ method @NonNull public java.util.List<android.app.appsearch.AppSearchSchema.PropertyConfig> getProperties();
+ method @NonNull public String getSchemaType();
+ }
+
+ public static final class AppSearchSchema.BooleanPropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig {
+ }
+
+ public static final class AppSearchSchema.BooleanPropertyConfig.Builder {
+ ctor public AppSearchSchema.BooleanPropertyConfig.Builder(@NonNull String);
+ method @NonNull public android.app.appsearch.AppSearchSchema.BooleanPropertyConfig build();
+ method @NonNull public android.app.appsearch.AppSearchSchema.BooleanPropertyConfig.Builder setCardinality(int);
+ }
+
+ public static final class AppSearchSchema.Builder {
+ ctor public AppSearchSchema.Builder(@NonNull String);
+ method @NonNull public android.app.appsearch.AppSearchSchema.Builder addProperty(@NonNull android.app.appsearch.AppSearchSchema.PropertyConfig);
+ method @NonNull public android.app.appsearch.AppSearchSchema build();
+ }
+
+ public static final class AppSearchSchema.BytesPropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig {
+ }
+
+ public static final class AppSearchSchema.BytesPropertyConfig.Builder {
+ ctor public AppSearchSchema.BytesPropertyConfig.Builder(@NonNull String);
+ method @NonNull public android.app.appsearch.AppSearchSchema.BytesPropertyConfig build();
+ method @NonNull public android.app.appsearch.AppSearchSchema.BytesPropertyConfig.Builder setCardinality(int);
+ }
+
+ public static final class AppSearchSchema.DocumentPropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig {
+ method @NonNull public String getSchemaType();
+ method public boolean shouldIndexNestedProperties();
+ }
+
+ public static final class AppSearchSchema.DocumentPropertyConfig.Builder {
+ ctor public AppSearchSchema.DocumentPropertyConfig.Builder(@NonNull String, @NonNull String);
+ method @NonNull public android.app.appsearch.AppSearchSchema.DocumentPropertyConfig build();
+ method @NonNull public android.app.appsearch.AppSearchSchema.DocumentPropertyConfig.Builder setCardinality(int);
+ method @NonNull public android.app.appsearch.AppSearchSchema.DocumentPropertyConfig.Builder setShouldIndexNestedProperties(boolean);
+ }
+
+ public static final class AppSearchSchema.DoublePropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig {
+ }
+
+ public static final class AppSearchSchema.DoublePropertyConfig.Builder {
+ ctor public AppSearchSchema.DoublePropertyConfig.Builder(@NonNull String);
+ method @NonNull public android.app.appsearch.AppSearchSchema.DoublePropertyConfig build();
+ method @NonNull public android.app.appsearch.AppSearchSchema.DoublePropertyConfig.Builder setCardinality(int);
+ }
+
+ public static final class AppSearchSchema.LongPropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig {
+ }
+
+ public static final class AppSearchSchema.LongPropertyConfig.Builder {
+ ctor public AppSearchSchema.LongPropertyConfig.Builder(@NonNull String);
+ method @NonNull public android.app.appsearch.AppSearchSchema.LongPropertyConfig build();
+ method @NonNull public android.app.appsearch.AppSearchSchema.LongPropertyConfig.Builder setCardinality(int);
+ }
+
+ public abstract static class AppSearchSchema.PropertyConfig {
+ method public int getCardinality();
+ method @NonNull public String getName();
+ field public static final int CARDINALITY_OPTIONAL = 2; // 0x2
+ field public static final int CARDINALITY_REPEATED = 1; // 0x1
+ field public static final int CARDINALITY_REQUIRED = 3; // 0x3
+ }
+
+ public static final class AppSearchSchema.StringPropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig {
+ method public int getIndexingType();
+ method public int getTokenizerType();
+ field public static final int INDEXING_TYPE_EXACT_TERMS = 1; // 0x1
+ field public static final int INDEXING_TYPE_NONE = 0; // 0x0
+ field public static final int INDEXING_TYPE_PREFIXES = 2; // 0x2
+ field public static final int TOKENIZER_TYPE_NONE = 0; // 0x0
+ field public static final int TOKENIZER_TYPE_PLAIN = 1; // 0x1
+ }
+
+ public static final class AppSearchSchema.StringPropertyConfig.Builder {
+ ctor public AppSearchSchema.StringPropertyConfig.Builder(@NonNull String);
+ method @NonNull public android.app.appsearch.AppSearchSchema.StringPropertyConfig build();
+ method @NonNull public android.app.appsearch.AppSearchSchema.StringPropertyConfig.Builder setCardinality(int);
+ method @NonNull public android.app.appsearch.AppSearchSchema.StringPropertyConfig.Builder setIndexingType(int);
+ method @NonNull public android.app.appsearch.AppSearchSchema.StringPropertyConfig.Builder setTokenizerType(int);
+ }
+
+ public final class AppSearchSession implements java.io.Closeable {
+ method public void close();
+ method public void getByDocumentId(@NonNull android.app.appsearch.GetByDocumentIdRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.appsearch.BatchResultCallback<java.lang.String,android.app.appsearch.GenericDocument>);
+ method public void getNamespaces(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.util.Set<java.lang.String>>>);
+ method public void getSchema(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<android.app.appsearch.GetSchemaResponse>>);
+ method public void getStorageInfo(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<android.app.appsearch.StorageInfo>>);
+ method public void put(@NonNull android.app.appsearch.PutDocumentsRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.appsearch.BatchResultCallback<java.lang.String,java.lang.Void>);
+ method public void remove(@NonNull android.app.appsearch.RemoveByDocumentIdRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.appsearch.BatchResultCallback<java.lang.String,java.lang.Void>);
+ method public void remove(@NonNull String, @NonNull android.app.appsearch.SearchSpec, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.lang.Void>>);
+ method public void reportUsage(@NonNull android.app.appsearch.ReportUsageRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.lang.Void>>);
+ method @NonNull public android.app.appsearch.SearchResults search(@NonNull String, @NonNull android.app.appsearch.SearchSpec);
+ method public void setSchema(@NonNull android.app.appsearch.SetSchemaRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<android.app.appsearch.SetSchemaResponse>>);
+ }
+
+ public interface BatchResultCallback<KeyType, ValueType> {
+ method public void onResult(@NonNull android.app.appsearch.AppSearchBatchResult<KeyType,ValueType>);
+ method public default void onSystemError(@Nullable Throwable);
+ }
+
+ public class GenericDocument {
+ ctor protected GenericDocument(@NonNull android.app.appsearch.GenericDocument);
+ method public long getCreationTimestampMillis();
+ method @NonNull public String getId();
+ method public static int getMaxIndexedProperties();
+ method @NonNull public String getNamespace();
+ method @Nullable public Object getProperty(@NonNull String);
+ method public boolean getPropertyBoolean(@NonNull String);
+ method @Nullable public boolean[] getPropertyBooleanArray(@NonNull String);
+ method @Nullable public byte[] getPropertyBytes(@NonNull String);
+ method @Nullable public byte[][] getPropertyBytesArray(@NonNull String);
+ method @Nullable public android.app.appsearch.GenericDocument getPropertyDocument(@NonNull String);
+ method @Nullable public android.app.appsearch.GenericDocument[] getPropertyDocumentArray(@NonNull String);
+ method public double getPropertyDouble(@NonNull String);
+ method @Nullable public double[] getPropertyDoubleArray(@NonNull String);
+ method public long getPropertyLong(@NonNull String);
+ method @Nullable public long[] getPropertyLongArray(@NonNull String);
+ method @NonNull public java.util.Set<java.lang.String> getPropertyNames();
+ method @Nullable public String getPropertyString(@NonNull String);
+ method @Nullable public String[] getPropertyStringArray(@NonNull String);
+ method @NonNull public String getSchemaType();
+ method public int getScore();
+ method public long getTtlMillis();
+ }
+
+ public static class GenericDocument.Builder<BuilderType extends android.app.appsearch.GenericDocument.Builder> {
+ ctor public GenericDocument.Builder(@NonNull String, @NonNull String, @NonNull String);
+ method @NonNull public android.app.appsearch.GenericDocument build();
+ method @NonNull public BuilderType setCreationTimestampMillis(long);
+ method @NonNull public BuilderType setPropertyBoolean(@NonNull String, @NonNull boolean...);
+ method @NonNull public BuilderType setPropertyBytes(@NonNull String, @NonNull byte[]...);
+ method @NonNull public BuilderType setPropertyDocument(@NonNull String, @NonNull android.app.appsearch.GenericDocument...);
+ method @NonNull public BuilderType setPropertyDouble(@NonNull String, @NonNull double...);
+ method @NonNull public BuilderType setPropertyLong(@NonNull String, @NonNull long...);
+ method @NonNull public BuilderType setPropertyString(@NonNull String, @NonNull java.lang.String...);
+ method @NonNull public BuilderType setScore(@IntRange(from=0, to=java.lang.Integer.MAX_VALUE) int);
+ method @NonNull public BuilderType setTtlMillis(long);
+ }
+
+ public final class GetByDocumentIdRequest {
+ method @NonNull public java.util.Set<java.lang.String> getIds();
+ method @NonNull public String getNamespace();
+ method @NonNull public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getProjections();
+ field public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*";
+ }
+
+ public static final class GetByDocumentIdRequest.Builder {
+ ctor public GetByDocumentIdRequest.Builder(@NonNull String);
+ method @NonNull public android.app.appsearch.GetByDocumentIdRequest.Builder addIds(@NonNull java.lang.String...);
+ method @NonNull public android.app.appsearch.GetByDocumentIdRequest.Builder addIds(@NonNull java.util.Collection<java.lang.String>);
+ method @NonNull public android.app.appsearch.GetByDocumentIdRequest.Builder addProjection(@NonNull String, @NonNull java.util.Collection<java.lang.String>);
+ method @NonNull public android.app.appsearch.GetByDocumentIdRequest build();
+ }
+
+ public final class GetSchemaResponse {
+ method @NonNull public java.util.Set<android.app.appsearch.AppSearchSchema> getSchemas();
+ method @IntRange(from=0) public int getVersion();
+ }
+
+ public static final class GetSchemaResponse.Builder {
+ ctor public GetSchemaResponse.Builder();
+ method @NonNull public android.app.appsearch.GetSchemaResponse.Builder addSchema(@NonNull android.app.appsearch.AppSearchSchema);
+ method @NonNull public android.app.appsearch.GetSchemaResponse build();
+ method @NonNull public android.app.appsearch.GetSchemaResponse.Builder setVersion(@IntRange(from=0) int);
+ }
+
+ public class GlobalSearchSession implements java.io.Closeable {
+ method public void close();
+ method public void reportSystemUsage(@NonNull android.app.appsearch.ReportSystemUsageRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.lang.Void>>);
+ method @NonNull public android.app.appsearch.SearchResults search(@NonNull String, @NonNull android.app.appsearch.SearchSpec);
+ }
+
+ public abstract class Migrator {
+ ctor public Migrator();
+ method @NonNull @WorkerThread public abstract android.app.appsearch.GenericDocument onDowngrade(int, int, @NonNull android.app.appsearch.GenericDocument);
+ method @NonNull @WorkerThread public abstract android.app.appsearch.GenericDocument onUpgrade(int, int, @NonNull android.app.appsearch.GenericDocument);
+ method public abstract boolean shouldMigrate(int, int);
+ }
+
+ public class PackageIdentifier {
+ ctor public PackageIdentifier(@NonNull String, @NonNull byte[]);
+ method @NonNull public String getPackageName();
+ method @NonNull public byte[] getSha256Certificate();
+ }
+
+ public final class PutDocumentsRequest {
+ method @NonNull public java.util.List<android.app.appsearch.GenericDocument> getGenericDocuments();
+ }
+
+ public static final class PutDocumentsRequest.Builder {
+ ctor public PutDocumentsRequest.Builder();
+ method @NonNull public android.app.appsearch.PutDocumentsRequest.Builder addGenericDocuments(@NonNull android.app.appsearch.GenericDocument...);
+ method @NonNull public android.app.appsearch.PutDocumentsRequest.Builder addGenericDocuments(@NonNull java.util.Collection<? extends android.app.appsearch.GenericDocument>);
+ method @NonNull public android.app.appsearch.PutDocumentsRequest build();
+ }
+
+ public final class RemoveByDocumentIdRequest {
+ method @NonNull public java.util.Set<java.lang.String> getIds();
+ method @NonNull public String getNamespace();
+ }
+
+ public static final class RemoveByDocumentIdRequest.Builder {
+ ctor public RemoveByDocumentIdRequest.Builder(@NonNull String);
+ method @NonNull public android.app.appsearch.RemoveByDocumentIdRequest.Builder addIds(@NonNull java.lang.String...);
+ method @NonNull public android.app.appsearch.RemoveByDocumentIdRequest.Builder addIds(@NonNull java.util.Collection<java.lang.String>);
+ method @NonNull public android.app.appsearch.RemoveByDocumentIdRequest build();
+ }
+
+ public final class ReportSystemUsageRequest {
+ method @NonNull public String getDatabaseName();
+ method @NonNull public String getDocumentId();
+ method @NonNull public String getNamespace();
+ method @NonNull public String getPackageName();
+ method public long getUsageTimestampMillis();
+ }
+
+ public static final class ReportSystemUsageRequest.Builder {
+ ctor public ReportSystemUsageRequest.Builder(@NonNull String, @NonNull String, @NonNull String, @NonNull String);
+ method @NonNull public android.app.appsearch.ReportSystemUsageRequest build();
+ method @NonNull public android.app.appsearch.ReportSystemUsageRequest.Builder setUsageTimestampMillis(long);
+ }
+
+ public final class ReportUsageRequest {
+ method @NonNull public String getDocumentId();
+ method @NonNull public String getNamespace();
+ method public long getUsageTimestampMillis();
+ }
+
+ public static final class ReportUsageRequest.Builder {
+ ctor public ReportUsageRequest.Builder(@NonNull String, @NonNull String);
+ method @NonNull public android.app.appsearch.ReportUsageRequest build();
+ method @NonNull public android.app.appsearch.ReportUsageRequest.Builder setUsageTimestampMillis(long);
+ }
+
+ public final class SearchResult {
+ method @NonNull public String getDatabaseName();
+ method @NonNull public android.app.appsearch.GenericDocument getGenericDocument();
+ method @NonNull public java.util.List<android.app.appsearch.SearchResult.MatchInfo> getMatchInfos();
+ method @NonNull public String getPackageName();
+ method public double getRankingSignal();
+ }
+
+ public static final class SearchResult.Builder {
+ ctor public SearchResult.Builder(@NonNull String, @NonNull String);
+ method @NonNull public android.app.appsearch.SearchResult.Builder addMatchInfo(@NonNull android.app.appsearch.SearchResult.MatchInfo);
+ method @NonNull public android.app.appsearch.SearchResult build();
+ method @NonNull public android.app.appsearch.SearchResult.Builder setGenericDocument(@NonNull android.app.appsearch.GenericDocument);
+ method @NonNull public android.app.appsearch.SearchResult.Builder setRankingSignal(double);
+ }
+
+ public static final class SearchResult.MatchInfo {
+ method @NonNull public CharSequence getExactMatch();
+ method @NonNull public android.app.appsearch.SearchResult.MatchRange getExactMatchRange();
+ method @NonNull public String getFullText();
+ method @NonNull public String getPropertyPath();
+ method @NonNull public CharSequence getSnippet();
+ method @NonNull public android.app.appsearch.SearchResult.MatchRange getSnippetRange();
+ }
+
+ public static final class SearchResult.MatchInfo.Builder {
+ ctor public SearchResult.MatchInfo.Builder(@NonNull String);
+ method @NonNull public android.app.appsearch.SearchResult.MatchInfo build();
+ method @NonNull public android.app.appsearch.SearchResult.MatchInfo.Builder setExactMatchRange(@NonNull android.app.appsearch.SearchResult.MatchRange);
+ method @NonNull public android.app.appsearch.SearchResult.MatchInfo.Builder setSnippetRange(@NonNull android.app.appsearch.SearchResult.MatchRange);
+ }
+
+ public static final class SearchResult.MatchRange {
+ ctor public SearchResult.MatchRange(int, int);
+ method public int getEnd();
+ method public int getStart();
+ }
+
+ public class SearchResults implements java.io.Closeable {
+ method public void close();
+ method public void getNextPage(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.util.List<android.app.appsearch.SearchResult>>>);
+ }
+
+ public final class SearchSpec {
+ method @NonNull public java.util.List<java.lang.String> getFilterNamespaces();
+ method @NonNull public java.util.List<java.lang.String> getFilterPackageNames();
+ method @NonNull public java.util.List<java.lang.String> getFilterSchemas();
+ method public int getMaxSnippetSize();
+ method public int getOrder();
+ method @NonNull public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getProjections();
+ method public int getRankingStrategy();
+ method public int getResultCountPerPage();
+ method public int getResultGroupingLimit();
+ method public int getResultGroupingTypeFlags();
+ method public int getSnippetCount();
+ method public int getSnippetCountPerProperty();
+ method public int getTermMatch();
+ field public static final int GROUPING_TYPE_PER_NAMESPACE = 2; // 0x2
+ field public static final int GROUPING_TYPE_PER_PACKAGE = 1; // 0x1
+ field public static final int ORDER_ASCENDING = 1; // 0x1
+ field public static final int ORDER_DESCENDING = 0; // 0x0
+ field public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*";
+ field public static final int RANKING_STRATEGY_CREATION_TIMESTAMP = 2; // 0x2
+ field public static final int RANKING_STRATEGY_DOCUMENT_SCORE = 1; // 0x1
+ field public static final int RANKING_STRATEGY_NONE = 0; // 0x0
+ field public static final int RANKING_STRATEGY_RELEVANCE_SCORE = 3; // 0x3
+ field public static final int RANKING_STRATEGY_SYSTEM_USAGE_COUNT = 6; // 0x6
+ field public static final int RANKING_STRATEGY_SYSTEM_USAGE_LAST_USED_TIMESTAMP = 7; // 0x7
+ field public static final int RANKING_STRATEGY_USAGE_COUNT = 4; // 0x4
+ field public static final int RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP = 5; // 0x5
+ field public static final int TERM_MATCH_EXACT_ONLY = 1; // 0x1
+ field public static final int TERM_MATCH_PREFIX = 2; // 0x2
+ }
+
+ public static final class SearchSpec.Builder {
+ ctor public SearchSpec.Builder();
+ method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterNamespaces(@NonNull java.lang.String...);
+ method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterNamespaces(@NonNull java.util.Collection<java.lang.String>);
+ method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterPackageNames(@NonNull java.lang.String...);
+ method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterPackageNames(@NonNull java.util.Collection<java.lang.String>);
+ method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterSchemas(@NonNull java.lang.String...);
+ method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterSchemas(@NonNull java.util.Collection<java.lang.String>);
+ method @NonNull public android.app.appsearch.SearchSpec.Builder addProjection(@NonNull String, @NonNull java.util.Collection<java.lang.String>);
+ method @NonNull public android.app.appsearch.SearchSpec build();
+ method @NonNull public android.app.appsearch.SearchSpec.Builder setMaxSnippetSize(@IntRange(from=0, to=0x2710) int);
+ method @NonNull public android.app.appsearch.SearchSpec.Builder setOrder(int);
+ method @NonNull public android.app.appsearch.SearchSpec.Builder setRankingStrategy(int);
+ method @NonNull public android.app.appsearch.SearchSpec.Builder setResultCountPerPage(@IntRange(from=0, to=0x2710) int);
+ method @NonNull public android.app.appsearch.SearchSpec.Builder setResultGrouping(int, int);
+ method @NonNull public android.app.appsearch.SearchSpec.Builder setSnippetCount(@IntRange(from=0, to=0x2710) int);
+ method @NonNull public android.app.appsearch.SearchSpec.Builder setSnippetCountPerProperty(@IntRange(from=0, to=0x2710) int);
+ method @NonNull public android.app.appsearch.SearchSpec.Builder setTermMatch(int);
+ }
+
+ public final class SetSchemaRequest {
+ method @NonNull public java.util.Map<java.lang.String,android.app.appsearch.Migrator> getMigrators();
+ method @NonNull public java.util.Set<android.app.appsearch.AppSearchSchema> getSchemas();
+ method @NonNull public java.util.Set<java.lang.String> getSchemasNotDisplayedBySystem();
+ method @NonNull public java.util.Map<java.lang.String,java.util.Set<android.app.appsearch.PackageIdentifier>> getSchemasVisibleToPackages();
+ method @IntRange(from=1) public int getVersion();
+ method public boolean isForceOverride();
+ }
+
+ public static final class SetSchemaRequest.Builder {
+ ctor public SetSchemaRequest.Builder();
+ method @NonNull public android.app.appsearch.SetSchemaRequest.Builder addSchemas(@NonNull android.app.appsearch.AppSearchSchema...);
+ method @NonNull public android.app.appsearch.SetSchemaRequest.Builder addSchemas(@NonNull java.util.Collection<android.app.appsearch.AppSearchSchema>);
+ method @NonNull public android.app.appsearch.SetSchemaRequest build();
+ method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setForceOverride(boolean);
+ method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setMigrator(@NonNull String, @NonNull android.app.appsearch.Migrator);
+ method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setMigrators(@NonNull java.util.Map<java.lang.String,android.app.appsearch.Migrator>);
+ method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeDisplayedBySystem(@NonNull String, boolean);
+ method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeVisibilityForPackage(@NonNull String, boolean, @NonNull android.app.appsearch.PackageIdentifier);
+ method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setVersion(@IntRange(from=1) int);
+ }
+
+ public class SetSchemaResponse {
+ method @NonNull public java.util.Set<java.lang.String> getDeletedTypes();
+ method @NonNull public java.util.Set<java.lang.String> getIncompatibleTypes();
+ method @NonNull public java.util.Set<java.lang.String> getMigratedTypes();
+ method @NonNull public java.util.List<android.app.appsearch.SetSchemaResponse.MigrationFailure> getMigrationFailures();
+ }
+
+ public static final class SetSchemaResponse.Builder {
+ ctor public SetSchemaResponse.Builder();
+ method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addDeletedType(@NonNull String);
+ method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addDeletedTypes(@NonNull java.util.Collection<java.lang.String>);
+ method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addIncompatibleType(@NonNull String);
+ method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addIncompatibleTypes(@NonNull java.util.Collection<java.lang.String>);
+ method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addMigratedType(@NonNull String);
+ method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addMigratedTypes(@NonNull java.util.Collection<java.lang.String>);
+ method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addMigrationFailure(@NonNull android.app.appsearch.SetSchemaResponse.MigrationFailure);
+ method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addMigrationFailures(@NonNull java.util.Collection<android.app.appsearch.SetSchemaResponse.MigrationFailure>);
+ method @NonNull public android.app.appsearch.SetSchemaResponse build();
+ }
+
+ public static class SetSchemaResponse.MigrationFailure {
+ ctor public SetSchemaResponse.MigrationFailure(@NonNull String, @NonNull String, @NonNull String, @NonNull android.app.appsearch.AppSearchResult<?>);
+ method @NonNull public android.app.appsearch.AppSearchResult<java.lang.Void> getAppSearchResult();
+ method @NonNull public String getDocumentId();
+ method @NonNull public String getNamespace();
+ method @NonNull public String getSchemaType();
+ }
+
+ public class StorageInfo {
+ method public int getAliveDocumentsCount();
+ method public int getAliveNamespacesCount();
+ method public long getSizeBytes();
+ }
+
+ public static final class StorageInfo.Builder {
+ ctor public StorageInfo.Builder();
+ method @NonNull public android.app.appsearch.StorageInfo build();
+ method @NonNull public android.app.appsearch.StorageInfo.Builder setAliveDocumentsCount(int);
+ method @NonNull public android.app.appsearch.StorageInfo.Builder setAliveNamespacesCount(int);
+ method @NonNull public android.app.appsearch.StorageInfo.Builder setSizeBytes(long);
+ }
+
+}
+
+package android.app.appsearch.exceptions {
+
+ public class AppSearchException extends java.lang.Exception {
+ ctor public AppSearchException(int);
+ ctor public AppSearchException(int, @Nullable String);
+ ctor public AppSearchException(int, @Nullable String, @Nullable Throwable);
+ method public int getResultCode();
+ method @NonNull public <T> android.app.appsearch.AppSearchResult<T> toAppSearchResult();
+ }
+
+}
+
diff --git a/wifi/api/module-lib-current.txt b/apex/appsearch/framework/api/module-lib-current.txt
similarity index 100%
copy from wifi/api/module-lib-current.txt
copy to apex/appsearch/framework/api/module-lib-current.txt
diff --git a/apex/statsd/framework/api/module-lib-removed.txt b/apex/appsearch/framework/api/module-lib-removed.txt
similarity index 100%
rename from apex/statsd/framework/api/module-lib-removed.txt
rename to apex/appsearch/framework/api/module-lib-removed.txt
diff --git a/apex/statsd/framework/api/removed.txt b/apex/appsearch/framework/api/removed.txt
similarity index 100%
rename from apex/statsd/framework/api/removed.txt
rename to apex/appsearch/framework/api/removed.txt
diff --git a/apex/appsearch/framework/api/system-current.txt b/apex/appsearch/framework/api/system-current.txt
new file mode 100644
index 0000000..4a6194e
--- /dev/null
+++ b/apex/appsearch/framework/api/system-current.txt
@@ -0,0 +1,9 @@
+// Signature format: 2.0
+package android.app.appsearch {
+
+ public class AppSearchManagerFrameworkInitializer {
+ method public static void initialize();
+ }
+
+}
+
diff --git a/apex/statsd/framework/api/system-removed.txt b/apex/appsearch/framework/api/system-removed.txt
similarity index 100%
rename from apex/statsd/framework/api/system-removed.txt
rename to apex/appsearch/framework/api/system-removed.txt
diff --git a/apex/appsearch/framework/jarjar-rules.txt b/apex/appsearch/framework/jarjar-rules.txt
new file mode 100644
index 0000000..50c3ee4
--- /dev/null
+++ b/apex/appsearch/framework/jarjar-rules.txt
@@ -0,0 +1,6 @@
+# Rename all com.android.internal.util classes to prevent class name collisions
+# between this module and the other versions of the utility classes linked into
+# the framework.
+
+# These must be kept in sync with the sources of framework-utils-appsearch
+rule com.android.internal.util.Preconditions* android.app.appsearch.internal.util.Preconditions@1
diff --git a/apex/appsearch/framework/java/TEST_MAPPING b/apex/appsearch/framework/java/TEST_MAPPING
new file mode 100644
index 0000000..12188f8
--- /dev/null
+++ b/apex/appsearch/framework/java/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "imports": [
+ {
+ "path": "frameworks/base/apex/appsearch/service/java/com/android/server/appsearch"
+ }
+ ]
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
new file mode 100644
index 0000000..fb1cccf
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.appsearch;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.SystemService;
+import android.annotation.UserHandleAware;
+import android.app.appsearch.aidl.IAppSearchManager;
+import android.content.Context;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * Provides access to the centralized AppSearch index maintained by the system.
+ *
+ * <p>AppSearch is an offline, on-device search library for managing structured data featuring:
+ *
+ * <ul>
+ * <li>APIs to index and retrieve data via full-text search.
+ * <li>An API for applications to explicitly grant read-access permission of their data to other
+ * applications.
+ * <b>See: {@link SetSchemaRequest.Builder#setSchemaTypeVisibilityForPackage}</b>
+ * <li>An API for applications to opt into or out of having their data displayed on System UI
+ * surfaces by the System-designated global querier.
+ * <b>See: {@link SetSchemaRequest.Builder#setSchemaTypeDisplayedBySystem}</b>
+ * </ul>
+ *
+ * <p>Applications create a database by opening an {@link AppSearchSession}.
+ *
+ * <p>Example:
+ *
+ * <pre>
+ * AppSearchManager appSearchManager = context.getSystemService(AppSearchManager.class);
+ *
+ * AppSearchManager.SearchContext searchContext = new AppSearchManager.SearchContext.Builder().
+ * setDatabaseName(dbName).build());
+ * appSearchManager.createSearchSession(searchContext, mExecutor, appSearchSessionResult -> {
+ * mAppSearchSession = appSearchSessionResult.getResultValue();
+ * });</pre>
+ *
+ * <p>After opening the session, a schema must be set in order to define the organizational
+ * structure of data. The schema is set by calling {@link AppSearchSession#setSchema}. The schema is
+ * composed of a collection of {@link AppSearchSchema} objects, each of which defines a unique type
+ * of data.
+ *
+ * <p>Example:
+ *
+ * <pre>
+ * AppSearchSchema emailSchemaType = new AppSearchSchema.Builder("Email")
+ * .addProperty(new StringPropertyConfig.Builder("subject")
+ * .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ * .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
+ * .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ * .build()
+ * ).build();
+ *
+ * SetSchemaRequest request = new SetSchemaRequest.Builder().addSchema(emailSchemaType).build();
+ * mAppSearchSession.set(request, mExecutor, appSearchResult -> {
+ * if (appSearchResult.isSuccess()) {
+ * //Schema has been successfully set.
+ * }
+ * });</pre>
+ *
+ * <p>The basic unit of data in AppSearch is represented as a {@link GenericDocument} object,
+ * containing an ID, namespace, time-to-live, score, and properties. A namespace organizes a logical
+ * group of documents. For example, a namespace can be created to group documents on a per-account
+ * basis. An ID identifies a single document within a namespace. The combination of namespace and ID
+ * uniquely identifies a {@link GenericDocument} in the database.
+ *
+ * <p>Once the schema has been set, {@link GenericDocument} objects can be put into the database and
+ * indexed by calling {@link AppSearchSession#put}.
+ *
+ * <p>Example:
+ *
+ * <pre>
+ * // Although for this example we use GenericDocument directly, we recommend extending
+ * // GenericDocument to create specific types (i.e. Email) with specific setters/getters.
+ * GenericDocument email = new GenericDocument.Builder<>(NAMESPACE, ID, EMAIL_SCHEMA_TYPE)
+ * .setPropertyString(“subject”, EMAIL_SUBJECT)
+ * .setScore(EMAIL_SCORE)
+ * .build();
+ *
+ * PutDocumentsRequest request = new PutDocumentsRequest.Builder().addGenericDocuments(email)
+ * .build();
+ * mAppSearchSession.put(request, mExecutor, appSearchBatchResult -> {
+ * if (appSearchBatchResult.isSuccess()) {
+ * //All documents have been successfully indexed.
+ * }
+ * });</pre>
+ *
+ * <p>Searching within the database is done by calling {@link AppSearchSession#search} and providing
+ * the query string to search for, as well as a {@link SearchSpec}.
+ *
+ * <p>Alternatively, {@link AppSearchSession#getByDocumentId} can be called to retrieve documents by
+ * namespace and ID.
+ *
+ * <p>Document removal is done either by time-to-live expiration, or explicitly calling a remove
+ * operation. Remove operations can be done by namespace and ID via {@link
+ * AppSearchSession#remove(RemoveByDocumentIdRequest, Executor, BatchResultCallback)}, or by query
+ * via {@link AppSearchSession#remove(String, SearchSpec, Executor, Consumer)}.
+ */
+@SystemService(Context.APP_SEARCH_SERVICE)
+public class AppSearchManager {
+
+ private final IAppSearchManager mService;
+ private final Context mContext;
+
+ /** @hide */
+ public AppSearchManager(@NonNull Context context, @NonNull IAppSearchManager service) {
+ mContext = Objects.requireNonNull(context);
+ mService = Objects.requireNonNull(service);
+ }
+
+ /** Contains information about how to create the search session. */
+ public static final class SearchContext {
+ final String mDatabaseName;
+
+ SearchContext(@NonNull String databaseName) {
+ mDatabaseName = Objects.requireNonNull(databaseName);
+ }
+
+ /**
+ * Returns the name of the database to create or open.
+ *
+ * <p>Databases with different names are fully separate with distinct types, namespaces, and
+ * data.
+ */
+ @NonNull
+ public String getDatabaseName() {
+ return mDatabaseName;
+ }
+
+ /** Builder for {@link SearchContext} objects. */
+ public static final class Builder {
+ private final String mDatabaseName;
+ private boolean mBuilt = false;
+
+ /**
+ * Creates a new {@link SearchContext.Builder}.
+ *
+ * <p>{@link AppSearchSession} will create or open a database under the given name.
+ *
+ * <p>Databases with different names are fully separate with distinct types, namespaces,
+ * and data.
+ *
+ * <p>Database name cannot contain {@code '/'}.
+ *
+ * @param databaseName The name of the database.
+ * @throws IllegalArgumentException if the databaseName contains {@code '/'}.
+ */
+ public Builder(@NonNull String databaseName) {
+ Objects.requireNonNull(databaseName);
+ Preconditions.checkArgument(
+ !databaseName.contains("/"), "Database name cannot contain '/'");
+ mDatabaseName = databaseName;
+ }
+
+ /** Builds a {@link SearchContext} instance. */
+ @NonNull
+ public SearchContext build() {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ mBuilt = true;
+ return new SearchContext(mDatabaseName);
+ }
+ }
+ }
+
+ /**
+ * Creates a new {@link AppSearchSession}.
+ *
+ * <p>This process requires an AppSearch native indexing file system. If it's not created, the
+ * initialization process will create one under the user's credential encrypted directory.
+ *
+ * @param searchContext The {@link SearchContext} contains all information to create a new
+ * {@link AppSearchSession}
+ * @param executor Executor on which to invoke the callback.
+ * @param callback The {@link AppSearchResult}<{@link AppSearchSession}> of performing
+ * this operation. Or a {@link AppSearchResult} with failure reason code and error
+ * information.
+ */
+ @UserHandleAware
+ public void createSearchSession(
+ @NonNull SearchContext searchContext,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<AppSearchResult<AppSearchSession>> callback) {
+ Objects.requireNonNull(searchContext);
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+ AppSearchSession.createSearchSession(
+ searchContext,
+ mService,
+ mContext.getUser(),
+ getPackageName(),
+ executor,
+ callback);
+ }
+
+ /**
+ * Creates a new {@link GlobalSearchSession}.
+ *
+ * <p>This process requires an AppSearch native indexing file system. If it's not created, the
+ * initialization process will create one under the user's credential encrypted directory.
+ *
+ * @param executor Executor on which to invoke the callback.
+ * @param callback The {@link AppSearchResult}<{@link GlobalSearchSession}> of performing
+ * this operation. Or a {@link AppSearchResult} with failure reason code and error
+ * information.
+ */
+ @UserHandleAware
+ public void createGlobalSearchSession(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<AppSearchResult<GlobalSearchSession>> callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+ GlobalSearchSession.createGlobalSearchSession(
+ mService, mContext.getUser(), getPackageName(), executor, callback);
+ }
+
+ /** Returns the package name that should be used for uid verification. */
+ @NonNull
+ private String getPackageName() {
+ return mContext.getOpPackageName();
+ }
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManagerFrameworkInitializer.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManagerFrameworkInitializer.java
new file mode 100644
index 0000000..7dc527a
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManagerFrameworkInitializer.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.appsearch;
+
+import android.annotation.SystemApi;
+import android.app.SystemServiceRegistry;
+import android.app.appsearch.aidl.IAppSearchManager;
+import android.content.Context;
+
+/**
+ * Class holding initialization code for the AppSearch module.
+ *
+ * @hide
+ */
+@SystemApi
+public class AppSearchManagerFrameworkInitializer {
+ private AppSearchManagerFrameworkInitializer() {}
+
+ /**
+ * Called by {@link SystemServiceRegistry}'s static initializer and registers all AppSearch
+ * services to {@link Context}, so that {@link Context#getSystemService} can return them.
+ *
+ * @throws IllegalStateException if this is called from anywhere besides
+ * {@link SystemServiceRegistry}
+ */
+ public static void initialize() {
+ SystemServiceRegistry.registerContextAwareService(
+ Context.APP_SEARCH_SERVICE, AppSearchManager.class,
+ (context, service) ->
+ new AppSearchManager(context, IAppSearchManager.Stub.asInterface(service)));
+ }
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchMigrationHelper.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchMigrationHelper.java
new file mode 100644
index 0000000..c738504
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchMigrationHelper.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appsearch;
+
+import static android.app.appsearch.AppSearchResult.RESULT_INVALID_SCHEMA;
+import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
+import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
+
+import android.annotation.NonNull;
+import android.annotation.WorkerThread;
+import android.app.appsearch.aidl.AppSearchResultParcel;
+import android.app.appsearch.aidl.IAppSearchManager;
+import android.app.appsearch.aidl.IAppSearchResultCallback;
+import android.app.appsearch.exceptions.AppSearchException;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.ArraySet;
+
+import java.io.Closeable;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * The helper class for {@link AppSearchSchema} migration.
+ *
+ * <p>It will query and migrate {@link GenericDocument} in given type to a new version.
+ * @hide
+ */
+public class AppSearchMigrationHelper implements Closeable {
+ private final IAppSearchManager mService;
+ private final String mPackageName;
+ private final String mDatabaseName;
+ private final UserHandle mUserHandle;
+ private final File mMigratedFile;
+ private final Set<String> mDestinationTypes;
+ private boolean mAreDocumentsMigrated = false;
+
+ AppSearchMigrationHelper(@NonNull IAppSearchManager service,
+ @NonNull UserHandle userHandle,
+ @NonNull String packageName,
+ @NonNull String databaseName,
+ @NonNull Set<AppSearchSchema> newSchemas) throws IOException {
+ mService = Objects.requireNonNull(service);
+ mUserHandle = Objects.requireNonNull(userHandle);
+ mPackageName = Objects.requireNonNull(packageName);
+ mDatabaseName = Objects.requireNonNull(databaseName);
+ mMigratedFile = File.createTempFile(/*prefix=*/"appsearch", /*suffix=*/null);
+ mDestinationTypes = new ArraySet<>(newSchemas.size());
+ for (AppSearchSchema newSchema : newSchemas) {
+ mDestinationTypes.add(newSchema.getSchemaType());
+ }
+ }
+
+ /**
+ * Queries all documents that need to be migrated to a different version and transform
+ * documents to that version by passing them to the provided {@link Migrator}.
+ *
+ * <p>The method will be executed on the executor provided to
+ * {@link AppSearchSession#setSchema}.
+ *
+ * @param schemaType The schema type that needs to be updated and whose {@link GenericDocument}
+ * need to be migrated.
+ * @param migrator The {@link Migrator} that will upgrade or downgrade a {@link
+ * GenericDocument} to new version.
+ */
+ @WorkerThread
+ public void queryAndTransform(@NonNull String schemaType, @NonNull Migrator migrator,
+ int currentVersion, int finalVersion)
+ throws IOException, AppSearchException, InterruptedException, ExecutionException {
+ File queryFile = File.createTempFile(/*prefix=*/"appsearch", /*suffix=*/null);
+ try (ParcelFileDescriptor fileDescriptor =
+ ParcelFileDescriptor.open(queryFile, MODE_WRITE_ONLY)) {
+ CompletableFuture<AppSearchResult<Void>> future = new CompletableFuture<>();
+ mService.writeQueryResultsToFile(mPackageName, mDatabaseName,
+ fileDescriptor,
+ /*queryExpression=*/ "",
+ new SearchSpec.Builder()
+ .addFilterSchemas(schemaType)
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .build().getBundle(),
+ mUserHandle,
+ new IAppSearchResultCallback.Stub() {
+ @Override
+ public void onResult(AppSearchResultParcel resultParcel) {
+ future.complete(resultParcel.getResult());
+ }
+ });
+ AppSearchResult<Void> result = future.get();
+ if (!result.isSuccess()) {
+ throw new AppSearchException(result.getResultCode(), result.getErrorMessage());
+ }
+ readAndTransform(queryFile, migrator, currentVersion, finalVersion);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } finally {
+ queryFile.delete();
+ }
+ }
+
+ /**
+ * Puts all {@link GenericDocument} migrated from the previous call to
+ * {@link #queryAndTransform} into AppSearch.
+ *
+ * <p> This method should be only called once.
+ *
+ * @param responseBuilder a SetSchemaResponse builder whose result will be returned by this
+ * function with any
+ * {@link android.app.appsearch.SetSchemaResponse.MigrationFailure}
+ * added in.
+ * @return the {@link SetSchemaResponse} for {@link AppSearchSession#setSchema} call.
+ */
+ @NonNull
+ AppSearchResult<SetSchemaResponse> putMigratedDocuments(
+ @NonNull SetSchemaResponse.Builder responseBuilder) {
+ if (!mAreDocumentsMigrated) {
+ return AppSearchResult.newSuccessfulResult(responseBuilder.build());
+ }
+ try (ParcelFileDescriptor fileDescriptor =
+ ParcelFileDescriptor.open(mMigratedFile, MODE_READ_ONLY)) {
+ CompletableFuture<AppSearchResult<List<Bundle>>> future = new CompletableFuture<>();
+ mService.putDocumentsFromFile(mPackageName, mDatabaseName, fileDescriptor, mUserHandle,
+ new IAppSearchResultCallback.Stub() {
+ @Override
+ public void onResult(AppSearchResultParcel resultParcel) {
+ future.complete(resultParcel.getResult());
+ }
+ });
+ AppSearchResult<List<Bundle>> result = future.get();
+ if (!result.isSuccess()) {
+ return AppSearchResult.newFailedResult(result);
+ }
+ List<Bundle> migratedFailureBundles = result.getResultValue();
+ for (int i = 0; i < migratedFailureBundles.size(); i++) {
+ responseBuilder.addMigrationFailure(
+ new SetSchemaResponse.MigrationFailure(migratedFailureBundles.get(i)));
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (Throwable t) {
+ return AppSearchResult.throwableToFailedResult(t);
+ } finally {
+ mMigratedFile.delete();
+ }
+ return AppSearchResult.newSuccessfulResult(responseBuilder.build());
+ }
+
+ /**
+ * Reads all saved {@link GenericDocument}s from the given {@link File}.
+ *
+ * <p>Transforms those {@link GenericDocument}s to the final version.
+ *
+ * <p>Save migrated {@link GenericDocument}s to the {@link #mMigratedFile}.
+ */
+ private void readAndTransform(@NonNull File file, @NonNull Migrator migrator,
+ int currentVersion, int finalVersion)
+ throws IOException, AppSearchException {
+ try (DataInputStream inputStream = new DataInputStream(new FileInputStream(file));
+ DataOutputStream outputStream = new DataOutputStream(new FileOutputStream(
+ mMigratedFile, /*append=*/ true))) {
+ GenericDocument document;
+ while (true) {
+ try {
+ document = readDocumentFromInputStream(inputStream);
+ } catch (EOFException e) {
+ break;
+ // Nothing wrong. We just finished reading.
+ }
+
+ GenericDocument newDocument;
+ if (currentVersion < finalVersion) {
+ newDocument = migrator.onUpgrade(currentVersion, finalVersion, document);
+ } else {
+ // currentVersion == finalVersion case won't trigger migration and get here.
+ newDocument = migrator.onDowngrade(currentVersion, finalVersion, document);
+ }
+
+ if (!mDestinationTypes.contains(newDocument.getSchemaType())) {
+ // we exit before the new schema has been set to AppSearch. So no
+ // observable changes will be applied to stored schemas and documents.
+ // And the temp file will be deleted at close(), which will be triggered at
+ // the end of try-with-resources block of SearchSessionImpl.
+ throw new AppSearchException(
+ RESULT_INVALID_SCHEMA,
+ "Receive a migrated document with schema type: "
+ + newDocument.getSchemaType()
+ + ". But the schema types doesn't exist in the request");
+ }
+ writeBundleToOutputStream(outputStream, newDocument.getBundle());
+ }
+ mAreDocumentsMigrated = true;
+ }
+ }
+
+ /**
+ * Reads the {@link Bundle} of a {@link GenericDocument} from given {@link DataInputStream}.
+ *
+ * @param inputStream The inputStream to read from
+ *
+ * @throws IOException on read failure.
+ * @throws EOFException if {@link java.io.InputStream} reaches the end.
+ */
+ @NonNull
+ public static GenericDocument readDocumentFromInputStream(
+ @NonNull DataInputStream inputStream) throws IOException {
+ int length = inputStream.readInt();
+ if (length == 0) {
+ throw new EOFException();
+ }
+ byte[] serializedMessage = new byte[length];
+ inputStream.read(serializedMessage);
+
+ Parcel parcel = Parcel.obtain();
+ try {
+ parcel.unmarshall(serializedMessage, 0, serializedMessage.length);
+ parcel.setDataPosition(0);
+ Bundle bundle = parcel.readBundle();
+ return new GenericDocument(bundle);
+ } finally {
+ parcel.recycle();
+ }
+ }
+
+ /**
+ * Serializes a {@link Bundle} and writes into the given {@link DataOutputStream}.
+ */
+ public static void writeBundleToOutputStream(
+ @NonNull DataOutputStream outputStream, @NonNull Bundle bundle)
+ throws IOException {
+ Parcel parcel = Parcel.obtain();
+ try {
+ parcel.writeBundle(bundle);
+ byte[] serializedMessage = parcel.marshall();
+ outputStream.writeInt(serializedMessage.length);
+ outputStream.write(serializedMessage);
+ } finally {
+ parcel.recycle();
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ mMigratedFile.delete();
+ }
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
new file mode 100644
index 0000000..82b6d62
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -0,0 +1,885 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appsearch;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.app.appsearch.aidl.AppSearchBatchResultParcel;
+import android.app.appsearch.aidl.AppSearchResultParcel;
+import android.app.appsearch.aidl.IAppSearchBatchResultCallback;
+import android.app.appsearch.aidl.IAppSearchManager;
+import android.app.appsearch.aidl.IAppSearchResultCallback;
+import android.app.appsearch.exceptions.AppSearchException;
+import android.app.appsearch.util.SchemaMigrationUtil;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.internal.util.Preconditions;
+
+import java.io.Closeable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * Provides a connection to a single AppSearch database.
+ *
+ * <p>An {@link AppSearchSession} instance provides access to database operations such as
+ * setting a schema, adding documents, and searching.
+ *
+ * <p>This class is thread safe.
+ *
+ * @see GlobalSearchSession
+ */
+public final class AppSearchSession implements Closeable {
+ private static final String TAG = "AppSearchSession";
+
+ private final String mPackageName;
+ private final String mDatabaseName;
+ private final UserHandle mUserHandle;
+ private final IAppSearchManager mService;
+
+ private boolean mIsMutated = false;
+ private boolean mIsClosed = false;
+
+ /**
+ * Creates a search session for the client, defined by the {@code userHandle} and
+ * {@code packageName}.
+ */
+ static void createSearchSession(
+ @NonNull AppSearchManager.SearchContext searchContext,
+ @NonNull IAppSearchManager service,
+ @NonNull UserHandle userHandle,
+ @NonNull String packageName,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<AppSearchResult<AppSearchSession>> callback) {
+ AppSearchSession searchSession =
+ new AppSearchSession(service, userHandle, packageName, searchContext.mDatabaseName);
+ searchSession.initialize(executor, callback);
+ }
+
+ // NOTE: No instance of this class should be created or returned except via initialize().
+ // Once the callback.accept has been called here, the class is ready to use.
+ private void initialize(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<AppSearchResult<AppSearchSession>> callback) {
+ try {
+ mService.initialize(
+ mPackageName,
+ mUserHandle,
+ /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(),
+ new IAppSearchResultCallback.Stub() {
+ @Override
+ public void onResult(AppSearchResultParcel resultParcel) {
+ executor.execute(() -> {
+ AppSearchResult<Void> result = resultParcel.getResult();
+ if (result.isSuccess()) {
+ callback.accept(
+ AppSearchResult.newSuccessfulResult(
+ AppSearchSession.this));
+ } else {
+ callback.accept(AppSearchResult.newFailedResult(result));
+ }
+ });
+ }
+ });
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private AppSearchSession(@NonNull IAppSearchManager service, @NonNull UserHandle userHandle,
+ @NonNull String packageName, @NonNull String databaseName) {
+ mService = service;
+ mUserHandle = userHandle;
+ mPackageName = packageName;
+ mDatabaseName = databaseName;
+ }
+
+ /**
+ * Sets the schema that represents the organizational structure of data within the AppSearch
+ * database.
+ *
+ * <p>Upon creating an {@link AppSearchSession}, {@link #setSchema} should be called. If the
+ * schema needs to be updated, or it has not been previously set, then the provided schema will
+ * be saved and persisted to disk. Otherwise, {@link #setSchema} is handled efficiently as a
+ * no-op call.
+ *
+ * @param request the schema to set or update the AppSearch database to.
+ * @param workExecutor Executor on which to schedule heavy client-side background work such as
+ * transforming documents.
+ * @param callbackExecutor Executor on which to invoke the callback.
+ * @param callback Callback to receive errors resulting from setting the schema. If the
+ * operation succeeds, the callback will be invoked with {@code null}.
+ */
+ public void setSchema(
+ @NonNull SetSchemaRequest request,
+ @NonNull Executor workExecutor,
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull Consumer<AppSearchResult<SetSchemaResponse>> callback) {
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(workExecutor);
+ Objects.requireNonNull(callbackExecutor);
+ Objects.requireNonNull(callback);
+ Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed");
+ List<Bundle> schemaBundles = new ArrayList<>(request.getSchemas().size());
+ for (AppSearchSchema schema : request.getSchemas()) {
+ schemaBundles.add(schema.getBundle());
+ }
+ Map<String, List<Bundle>> schemasVisibleToPackagesBundles =
+ new ArrayMap<>(request.getSchemasVisibleToPackagesInternal().size());
+ for (Map.Entry<String, Set<PackageIdentifier>> entry :
+ request.getSchemasVisibleToPackagesInternal().entrySet()) {
+ List<Bundle> packageIdentifierBundles = new ArrayList<>(entry.getValue().size());
+ for (PackageIdentifier packageIdentifier : entry.getValue()) {
+ packageIdentifierBundles.add(packageIdentifier.getBundle());
+ }
+ schemasVisibleToPackagesBundles.put(entry.getKey(), packageIdentifierBundles);
+ }
+
+ // No need to trigger migration if user never set migrator
+ if (request.getMigrators().isEmpty()) {
+ setSchemaNoMigrations(
+ request,
+ schemaBundles,
+ schemasVisibleToPackagesBundles,
+ callbackExecutor,
+ callback);
+ } else {
+ setSchemaWithMigrations(
+ request,
+ schemaBundles,
+ schemasVisibleToPackagesBundles,
+ workExecutor,
+ callbackExecutor,
+ callback);
+ }
+ mIsMutated = true;
+ }
+
+ /**
+ * Retrieves the schema most recently successfully provided to {@link #setSchema}.
+ *
+ * @param executor Executor on which to invoke the callback.
+ * @param callback Callback to receive the pending results of schema.
+ */
+ public void getSchema(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<AppSearchResult<GetSchemaResponse>> callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+ Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed");
+ try {
+ mService.getSchema(
+ mPackageName,
+ mDatabaseName,
+ mUserHandle,
+ new IAppSearchResultCallback.Stub() {
+ @Override
+ public void onResult(AppSearchResultParcel resultParcel) {
+ executor.execute(() -> {
+ AppSearchResult<Bundle> result = resultParcel.getResult();
+ if (result.isSuccess()) {
+ GetSchemaResponse response =
+ new GetSchemaResponse(result.getResultValue());
+ callback.accept(AppSearchResult.newSuccessfulResult(response));
+ } else {
+ callback.accept(AppSearchResult.newFailedResult(result));
+ }
+ });
+ }
+ });
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Retrieves the set of all namespaces in the current database with at least one document.
+ *