Merge "Merge Android 12"
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -2,4 +2,6 @@
|
||||
.project
|
||||
*.iml
|
||||
*.sw*
|
||||
gen/
|
||||
gen/
|
||||
.vscode/
|
||||
*.code-workspace
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
drops {
|
||||
android_build_drop {
|
||||
build_id: "7396576"
|
||||
build_id: "7552332"
|
||||
target: "CtsShim"
|
||||
source_file: "aosp_arm64/CtsShimPriv.apk"
|
||||
}
|
||||
@@ -10,4 +10,6 @@ drops {
|
||||
git_project: "platform/frameworks/base"
|
||||
git_branch: "sc-dev"
|
||||
transform: TRANSFORM_NONE
|
||||
transform_options {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
drops {
|
||||
android_build_drop {
|
||||
build_id: "7396576"
|
||||
build_id: "7552332"
|
||||
target: "CtsShim"
|
||||
source_file: "aosp_arm64/CtsShim.apk"
|
||||
}
|
||||
@@ -10,4 +10,6 @@ drops {
|
||||
git_project: "platform/frameworks/base"
|
||||
git_branch: "sc-dev"
|
||||
transform: TRANSFORM_NONE
|
||||
transform_options {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
drops {
|
||||
android_build_drop {
|
||||
build_id: "7396576"
|
||||
build_id: "7552332"
|
||||
target: "CtsShim"
|
||||
source_file: "aosp_x86_64/CtsShimPriv.apk"
|
||||
}
|
||||
@@ -10,4 +10,6 @@ drops {
|
||||
git_project: "platform/frameworks/base"
|
||||
git_branch: "sc-dev"
|
||||
transform: TRANSFORM_NONE
|
||||
transform_options {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
drops {
|
||||
android_build_drop {
|
||||
build_id: "7396576"
|
||||
build_id: "7552332"
|
||||
target: "CtsShim"
|
||||
source_file: "aosp_x86_64/CtsShim.apk"
|
||||
}
|
||||
@@ -10,4 +10,6 @@ drops {
|
||||
git_project: "platform/frameworks/base"
|
||||
git_branch: "sc-dev"
|
||||
transform: TRANSFORM_NONE
|
||||
transform_options {
|
||||
}
|
||||
}
|
||||
|
||||
74
Android.bp
74
Android.bp
@@ -105,13 +105,17 @@ filegroup {
|
||||
":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",
|
||||
":idmap2_core_aidl",
|
||||
":incidentcompanion_aidl",
|
||||
":inputconstants_aidl",
|
||||
":installd_aidl",
|
||||
":libaudioclient_aidl",
|
||||
":libbinder_aidl",
|
||||
@@ -122,6 +126,7 @@ filegroup {
|
||||
":resourcemanager_aidl",
|
||||
":storaged_aidl",
|
||||
":vold_aidl",
|
||||
":deviceproductinfoconstants_aidl",
|
||||
|
||||
// For the generated R.java and Manifest.java
|
||||
":framework-res{.aapt.srcjar}",
|
||||
@@ -137,9 +142,14 @@ 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",
|
||||
@@ -155,8 +165,13 @@ java_library {
|
||||
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",
|
||||
@@ -218,6 +233,7 @@ java_library {
|
||||
"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",
|
||||
@@ -240,14 +256,18 @@ java_library {
|
||||
"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",
|
||||
|
||||
@@ -268,27 +288,34 @@ filegroup {
|
||||
visibility: ["//visibility:public"],
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "framework-minus-apex",
|
||||
java_defaults {
|
||||
name: "framework-minus-apex-defaults",
|
||||
defaults: ["framework-aidl-export-defaults"],
|
||||
srcs: [
|
||||
":framework-non-updatable-sources",
|
||||
":framework-connectivity-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",
|
||||
],
|
||||
},
|
||||
dxflags: [
|
||||
"--core-library",
|
||||
"--multi-dex",
|
||||
],
|
||||
installable: true,
|
||||
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",
|
||||
@@ -299,6 +326,7 @@ java_library {
|
||||
"services-platform-compat-config",
|
||||
"TeleService-platform-compat-config",
|
||||
"documents-ui-compat-config",
|
||||
"calendar-provider-compat-config",
|
||||
],
|
||||
libs: [
|
||||
"app-compat-annotations",
|
||||
@@ -313,21 +341,44 @@ java_library {
|
||||
// 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",
|
||||
],
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "framework-minus-apex",
|
||||
defaults: ["framework-minus-apex-defaults"],
|
||||
installable: true,
|
||||
// For backwards compatibility.
|
||||
stem: "framework",
|
||||
apex_available: ["//apex_available:platform"],
|
||||
visibility: [
|
||||
"//frameworks/base",
|
||||
// TODO: remove when framework-connectivity can build against API
|
||||
"//frameworks/base/packages/Connectivity/framework",
|
||||
// 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
|
||||
@@ -346,6 +397,7 @@ java_library {
|
||||
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",
|
||||
@@ -374,13 +426,14 @@ filegroup {
|
||||
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",
|
||||
@@ -408,9 +461,8 @@ java_library {
|
||||
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",
|
||||
"core/java/com/android/internal/util/AsyncChannel.java",
|
||||
"core/java/com/android/internal/util/AsyncService.java",
|
||||
"core/java/com/android/internal/util/Protocol.java",
|
||||
@@ -484,9 +536,12 @@ stubs_defaults {
|
||||
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",
|
||||
],
|
||||
},
|
||||
@@ -503,6 +558,7 @@ stubs_defaults {
|
||||
"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",
|
||||
|
||||
36
ApiDocs.bp
36
ApiDocs.bp
@@ -101,12 +101,14 @@ stubs_defaults {
|
||||
"metalava-manual",
|
||||
],
|
||||
write_sdk_values: true,
|
||||
}
|
||||
|
||||
droidstubs {
|
||||
name: "android-non-updatable-doc-stubs",
|
||||
defaults: ["android-non-updatable-doc-stubs-defaults"],
|
||||
args: metalava_framework_docs_args,
|
||||
// TODO(b/169090544): remove below aidl includes.
|
||||
aidl: {
|
||||
local_include_dirs: ["media/aidl"],
|
||||
include_dirs: [
|
||||
"frameworks/av/aidl",
|
||||
"frameworks/native/libs/permission/aidl",
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
// Defaults module for doc-stubs targets that use module source code as input.
|
||||
@@ -115,9 +117,13 @@ stubs_defaults {
|
||||
name: "framework-doc-stubs-sources-default",
|
||||
defaults: ["framework-doc-stubs-default"],
|
||||
srcs: [
|
||||
":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",
|
||||
@@ -127,6 +133,12 @@ stubs_defaults {
|
||||
],
|
||||
}
|
||||
|
||||
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"],
|
||||
@@ -157,10 +169,14 @@ droidstubs {
|
||||
args: metalava_framework_docs_args,
|
||||
srcs: [
|
||||
":android.net.ipsec.ike{.public.stubs.source}",
|
||||
":framework-appsearch{.public.stubs.source}",
|
||||
":framework-connectivity{.public.stubs.source}",
|
||||
":framework-graphics{.public.stubs.source}",
|
||||
":framework-media{.public.stubs.source}",
|
||||
":framework-mediaprovider{.public.stubs.source}",
|
||||
":framework-permission{.public.stubs.source}",
|
||||
":framework-permission-s{.public.stubs.source}",
|
||||
":framework-scheduling{.public.stubs.source}",
|
||||
":framework-sdkextensions{.public.stubs.source}",
|
||||
":framework-statsd{.public.stubs.source}",
|
||||
":framework-tethering{.public.stubs.source}",
|
||||
@@ -221,6 +237,14 @@ doc_defaults {
|
||||
":current-support-api",
|
||||
":current-androidx-api",
|
||||
],
|
||||
// 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 {
|
||||
|
||||
@@ -7,11 +7,16 @@ bpfmt = true
|
||||
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}
|
||||
|
||||
|
||||
@@ -35,9 +35,11 @@ gensrcs {
|
||||
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",
|
||||
}
|
||||
@@ -63,8 +65,10 @@ gensrcs {
|
||||
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",
|
||||
@@ -76,12 +80,14 @@ java_library_host {
|
||||
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: [
|
||||
@@ -90,8 +96,9 @@ java_library_host {
|
||||
],
|
||||
type: "full",
|
||||
},
|
||||
// Protos have lots of MissingOverride and similar.
|
||||
errorprone: {
|
||||
javacflags: ["-Xep:MissingOverride:OFF"], // b/72714520
|
||||
javacflags: ["-XepDisableAllChecks"],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -106,13 +113,16 @@ java_library {
|
||||
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",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -127,12 +137,15 @@ java_library {
|
||||
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.
|
||||
@@ -161,7 +174,9 @@ cc_defaults {
|
||||
srcs: [
|
||||
":ipconnectivity-proto-src",
|
||||
":libstats_atom_enum_protos",
|
||||
":libstats_atom_message_protos",
|
||||
"core/proto/**/*.proto",
|
||||
":service-permission-protos",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@@ -205,6 +205,7 @@ droidstubs {
|
||||
api_lint: {
|
||||
enabled: true,
|
||||
new_since: ":android.api.module-lib.latest",
|
||||
baseline_file: "core/api/module-lib-lint-baseline.txt",
|
||||
},
|
||||
},
|
||||
dists: [
|
||||
@@ -238,10 +239,14 @@ 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",
|
||||
@@ -253,10 +258,14 @@ 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",
|
||||
|
||||
29
TEST_MAPPING
29
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,6 +67,17 @@
|
||||
"exclude-annotation": "org.junit.Ignore"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "FrameworksInProcessTests",
|
||||
"options": [
|
||||
{
|
||||
"exclude-annotation": "androidx.test.filters.FlakyTest"
|
||||
},
|
||||
{
|
||||
"exclude-annotation": "org.junit.Ignore"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"postsubmit-managedprofile-stress": [
|
||||
|
||||
@@ -16,6 +16,7 @@ java_library_host {
|
||||
name: "platformtestprotos",
|
||||
srcs: [
|
||||
":libstats_atom_enum_protos",
|
||||
":libstats_atom_message_protos",
|
||||
":libstats_internal_protos",
|
||||
":statsd_internal_protos",
|
||||
],
|
||||
|
||||
@@ -29,7 +29,10 @@ android_test {
|
||||
"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"],
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 @@ public abstract class AbstractAutofillPerfTestCase {
|
||||
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 @@ public abstract class AbstractAutofillPerfTestCase {
|
||||
});
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.perftests.utils.PerfTestActivity;
|
||||
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 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
|
||||
*/
|
||||
@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 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
|
||||
@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 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
|
||||
@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 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
|
||||
|
||||
// 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 class LoginTest extends AbstractAutofillPerfTestCase {
|
||||
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 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
|
||||
|
||||
// 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 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
|
||||
*/
|
||||
@Test
|
||||
public void testChange_noService() throws Throwable {
|
||||
resetService();
|
||||
mTestWatcher.resetAutofillService();
|
||||
|
||||
changeTest(false);
|
||||
}
|
||||
@@ -205,7 +214,7 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
|
||||
@Test
|
||||
public void testChange_serviceDoesNotAutofill() throws Throwable {
|
||||
MyAutofillService.newCannedResponse().reply();
|
||||
setService();
|
||||
mTestWatcher.setAutofillService();
|
||||
|
||||
changeTest(true);
|
||||
}
|
||||
@@ -216,10 +225,10 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
|
||||
@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 class LoginTest extends AbstractAutofillPerfTestCase {
|
||||
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 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
|
||||
// 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 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
|
||||
// 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);
|
||||
|
||||
|
||||
@@ -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.util.Pair;
|
||||
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 @@ import java.util.concurrent.TimeUnit;
|
||||
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";
|
||||
@@ -55,6 +53,14 @@ public class MyAutofillService extends AutofillService {
|
||||
|
||||
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.
|
||||
*/
|
||||
@@ -92,6 +98,11 @@ public class MyAutofillService extends AutofillService {
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnected() {
|
||||
AutofillTestWatcher.ServiceWatcher.onConnected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal,
|
||||
FillCallback callback) {
|
||||
@@ -120,20 +131,27 @@ public class MyAutofillService extends AutofillService {
|
||||
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 @@ public class MyAutofillService extends AutofillService {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ public class MyAutofillService extends AutofillService {
|
||||
}
|
||||
|
||||
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 @@ public class MyAutofillService extends AutofillService {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ android_test {
|
||||
"androidx.annotation_annotation",
|
||||
"apct-perftests-utils",
|
||||
"ub-uiautomator",
|
||||
"collector-device-lib-platform",
|
||||
],
|
||||
platform_apis: true,
|
||||
test_suites: ["device-tests"],
|
||||
|
||||
@@ -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>
|
||||
38
apct-tests/perftests/contentcapture/Android.bp
Normal file
38
apct-tests/perftests/contentcapture/Android.bp
Normal file
@@ -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"],
|
||||
}
|
||||
39
apct-tests/perftests/contentcapture/AndroidManifest.xml
Normal file
39
apct-tests/perftests/contentcapture/AndroidManifest.xml
Normal file
@@ -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>
|
||||
59
apct-tests/perftests/contentcapture/AndroidTest.xml
Normal file
59
apct-tests/perftests/contentcapture/AndroidTest.xml
Normal file
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,19 @@
|
||||
//
|
||||
// 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
|
||||
@@ -21,23 +37,25 @@ android_test {
|
||||
"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",
|
||||
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -62,7 +62,7 @@ public class PendingIntentPerfTest {
|
||||
state.resumeTiming();
|
||||
|
||||
final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, mIntent,
|
||||
0);
|
||||
PendingIntent.FLAG_MUTABLE_UNAUDITED);
|
||||
|
||||
state.pauseTiming();
|
||||
pendingIntent.cancel();
|
||||
@@ -80,11 +80,11 @@ public class PendingIntentPerfTest {
|
||||
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 @@ public class PendingIntentPerfTest {
|
||||
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 @@ public class PendingIntentPerfTest {
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0,
|
||||
mIntent, 0);
|
||||
mIntent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
|
||||
state.resumeTiming();
|
||||
|
||||
pendingIntent.cancel();
|
||||
|
||||
@@ -17,7 +17,6 @@ package android.app;
|
||||
|
||||
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 @@ public class ResourcesManagerPerfTest {
|
||||
}
|
||||
|
||||
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 @@ public class ResourcesManagerPerfTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,8 +95,9 @@ public class ResourcesThemePerfTest {
|
||||
? 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();
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,11 +54,11 @@ public class CanvasPerfTest {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import androidx.test.InstrumentationRegistry;
|
||||
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 @@ public class TypefaceCreatePerfTest {
|
||||
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 @@ public class TypefaceCreatePerfTest {
|
||||
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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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() {}
|
||||
}
|
||||
}
|
||||
@@ -97,11 +97,21 @@ class PackageParsingPerfTest {
|
||||
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 @@ class PackageParsingPerfTest {
|
||||
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 @@ class PackageParsingPerfTest {
|
||||
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 @@ class PackageParsingPerfTest {
|
||||
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 @@ class PackageParsingPerfTest {
|
||||
// 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) =
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,21 +158,6 @@ public class ParcelPerfTest {
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
73
apct-tests/perftests/core/src/android/os/SomeProvider.java
Normal file
73
apct-tests/perftests/core/src/android/os/SomeProvider.java
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
101
apct-tests/perftests/core/src/android/os/VibratorPerfTest.java
Normal file
101
apct-tests/perftests/core/src/android/os/VibratorPerfTest.java
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,19 +53,19 @@ public class CanvasDrawTextTest {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
292
apct-tests/perftests/core/src/android/util/XmlPerfTest.java
Normal file
292
apct-tests/perftests/core/src/android/util/XmlPerfTest.java
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 @@ import org.junit.runner.RunWith;
|
||||
@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 @@ public class CutoutSpecificationBenchmark {
|
||||
+ "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 @@ public class CutoutSpecificationBenchmark {
|
||||
|
||||
@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 @@ public class CutoutSpecificationBenchmark {
|
||||
+ "@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 @@ public class CutoutSpecificationBenchmark {
|
||||
+ "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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 @@ public class ViewPerfTest {
|
||||
|
||||
@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));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,14 +21,14 @@ import static org.junit.Assert.assertTrue;
|
||||
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 @@ public class ViewShowHidePerfTest {
|
||||
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 @@ public class ViewShowHidePerfTest {
|
||||
|
||||
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);
|
||||
|
||||
@@ -43,8 +43,8 @@ public class EditTextLongTextPerfTest {
|
||||
@Parameters(name = "{0}")
|
||||
public static Collection cases() {
|
||||
return Arrays.asList(new Object[][] {
|
||||
{ "10x30K", 10, 30000 },
|
||||
{ "300x1K", 300, 1000 },
|
||||
{ "10x3K", 10, 3000 },
|
||||
{ "30x1K", 30, 1000 },
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
39
apct-tests/perftests/inputmethod/Android.bp
Normal file
39
apct-tests/perftests/inputmethod/Android.bp
Normal file
@@ -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",
|
||||
}
|
||||
44
apct-tests/perftests/inputmethod/AndroidManifest.xml
Normal file
44
apct-tests/perftests/inputmethod/AndroidManifest.xml
Normal file
@@ -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>
|
||||
73
apct-tests/perftests/inputmethod/AndroidTest.xml
Normal file
73
apct-tests/perftests/inputmethod/AndroidTest.xml
Normal file
@@ -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>
|
||||
40
apct-tests/perftests/inputmethod/README.md
Normal file
40
apct-tests/perftests/inputmethod/README.md
Normal file
@@ -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.
|
||||
19
apct-tests/perftests/inputmethod/res/xml/simple_method.xml
Normal file
19
apct-tests/perftests/inputmethod/res/xml/simple_method.xml
Normal file
@@ -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" />
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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.
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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" />
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<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_multi_user.textproto" value="/sdcard/sample.textproto" />
|
||||
<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 -->
|
||||
|
||||
@@ -22,6 +22,9 @@ import java.util.Collections;
|
||||
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 @@ public class BenchmarkResults {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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 @@ public class BenchmarkResultsReporter implements TestRule {
|
||||
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));
|
||||
|
||||
@@ -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 @@ public class BenchmarkRunner {
|
||||
|
||||
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 @@ public class BenchmarkRunner {
|
||||
|
||||
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 class BenchmarkRunner {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ package android.multiuser;
|
||||
|
||||
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.Bundle;
|
||||
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 @@ import java.util.concurrent.TimeUnit;
|
||||
/**
|
||||
* 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 @@ public class UserLifecycleTests {
|
||||
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 @@ public class UserLifecycleTests {
|
||||
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 @@ public class UserLifecycleTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createAndStartUser() throws Exception {
|
||||
public void createAndStartUser() throws RemoteException {
|
||||
while (mRunner.keepRunning()) {
|
||||
final int userId = createUserNoFlags();
|
||||
|
||||
@@ -149,7 +163,7 @@ public class UserLifecycleTests {
|
||||
// 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 @@ public class UserLifecycleTests {
|
||||
* 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 @@ public class UserLifecycleTests {
|
||||
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 @@ public class UserLifecycleTests {
|
||||
* 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 @@ public class UserLifecycleTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@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 @@ public class UserLifecycleTests {
|
||||
switchUser(userId);
|
||||
|
||||
mRunner.pauseTiming();
|
||||
switchUser(startUser);
|
||||
switchUserNoCheck(startUser);
|
||||
removeUser(userId);
|
||||
mRunner.resumeTiming();
|
||||
}
|
||||
@@ -218,7 +230,7 @@ public class UserLifecycleTests {
|
||||
|
||||
/** 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 @@ public class UserLifecycleTests {
|
||||
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 @@ public class UserLifecycleTests {
|
||||
|
||||
/** 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 @@ public class UserLifecycleTests {
|
||||
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 @@ public class UserLifecycleTests {
|
||||
}
|
||||
|
||||
@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 @@ public class UserLifecycleTests {
|
||||
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 @@ public class UserLifecycleTests {
|
||||
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 @@ public class UserLifecycleTests {
|
||||
|
||||
/** Tests creating a new profile. */
|
||||
@Test
|
||||
public void managedProfileCreate() throws Exception {
|
||||
public void managedProfileCreate() {
|
||||
assumeTrue(mHasManagedUserFeature);
|
||||
|
||||
while (mRunner.keepRunning()) {
|
||||
@@ -344,7 +359,7 @@ public class UserLifecycleTests {
|
||||
|
||||
/** 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 @@ public class UserLifecycleTests {
|
||||
|
||||
/** 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 @@ public class UserLifecycleTests {
|
||||
* 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 @@ public class UserLifecycleTests {
|
||||
* {@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 @@ public class UserLifecycleTests {
|
||||
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 @@ public class UserLifecycleTests {
|
||||
|
||||
/** 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 @@ public class UserLifecycleTests {
|
||||
* 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 @@ public class UserLifecycleTests {
|
||||
|
||||
/** Tests stopping a profile. */
|
||||
@Test
|
||||
public void managedProfileStopped() throws Exception {
|
||||
public void managedProfileStopped() throws RemoteException {
|
||||
assumeTrue(mHasManagedUserFeature);
|
||||
|
||||
while (mRunner.keepRunning()) {
|
||||
@@ -498,7 +513,7 @@ public class UserLifecycleTests {
|
||||
// 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 @@ public class UserLifecycleTests {
|
||||
}
|
||||
/** 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 @@ public class UserLifecycleTests {
|
||||
|
||||
/** 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 @@ public class UserLifecycleTests {
|
||||
*/
|
||||
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 class UserLifecycleTests {
|
||||
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 @@ public class UserLifecycleTests {
|
||||
* @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 @@ public class UserLifecycleTests {
|
||||
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 @@ public class UserLifecycleTests {
|
||||
}
|
||||
|
||||
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 @@ public class UserLifecycleTests {
|
||||
}
|
||||
}
|
||||
|
||||
/** 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 @@ public class UserLifecycleTests {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,21 +80,21 @@ data_sources {
|
||||
atrace_apps: "*"
|
||||
|
||||
atrace_categories: "am"
|
||||
atrace_categories: "binder_driver"
|
||||
atrace_categories: "bionic"
|
||||
atrace_categories: "dalvik"
|
||||
atrace_categories: "input"
|
||||
atrace_categories: "pm"
|
||||
atrace_categories: "res"
|
||||
atrace_categories: "rro"
|
||||
atrace_categories: "ss"
|
||||
atrace_categories: "view"
|
||||
atrace_categories: "camera"
|
||||
atrace_categories: "wm"
|
||||
|
||||
atrace_categories: "freq"
|
||||
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"
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ android_test {
|
||||
"androidx.test.ext.junit",
|
||||
"androidx.annotation_annotation",
|
||||
"apct-perftests-utils",
|
||||
"collector-device-lib-platform",
|
||||
],
|
||||
|
||||
libs: ["android.test.base"],
|
||||
@@ -27,4 +28,8 @@ android_test {
|
||||
|
||||
test_suites: ["device-tests"],
|
||||
|
||||
data: [":perfetto_artifacts"],
|
||||
|
||||
certificate: "platform",
|
||||
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
</manifest>
|
||||
|
||||
@@ -28,7 +28,9 @@ android_test {
|
||||
"androidx.test.rules",
|
||||
"androidx.annotation_annotation",
|
||||
"apct-perftests-utils",
|
||||
"collector-device-lib",
|
||||
],
|
||||
data: [":perfetto_artifacts"],
|
||||
platform_apis: true,
|
||||
test_suites: ["device-tests"],
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.view.textclassifier;
|
||||
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 androidx.test.filters.LargeTest;
|
||||
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 @@ public class TextClassifierPerfTest {
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,8 @@ public final class BenchmarkState {
|
||||
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 @@ public final class BenchmarkState {
|
||||
|
||||
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 @@ public final class BenchmarkState {
|
||||
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 @@ public final class BenchmarkState {
|
||||
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 @@ public final class BenchmarkState {
|
||||
"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 @@ public final class BenchmarkState {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ import android.content.Context;
|
||||
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 @@ import android.view.WindowManager;
|
||||
*/
|
||||
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 @@ public class PerfTestActivity extends Activity {
|
||||
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) {
|
||||
|
||||
@@ -134,7 +134,7 @@ public class TestPackageInstaller {
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,8 +28,11 @@ android_test {
|
||||
"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",
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
35
apct-tests/perftests/windowmanager/README.md
Normal file
35
apct-tests/perftests/windowmanager/README.md
Normal file
@@ -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.
|
||||
@@ -18,7 +18,6 @@ package android.wm;
|
||||
|
||||
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.BufferedReader;
|
||||
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 @@ public class InternalWindowOperationPerfTest extends WindowManagerPerfTestBase {
|
||||
"applyPostLayoutPolicy",
|
||||
"applySurfaceChanges",
|
||||
"AppTransitionReady",
|
||||
"closeSurfaceTransactiom",
|
||||
"closeSurfaceTransaction",
|
||||
"openSurfaceTransaction",
|
||||
"performLayout",
|
||||
"performSurfacePlacement",
|
||||
@@ -68,6 +67,9 @@ public class InternalWindowOperationPerfTest extends WindowManagerPerfTestBase {
|
||||
"finishActivity",
|
||||
"startActivityInner");
|
||||
|
||||
private boolean mIsProfiling;
|
||||
private boolean mIsTraceStarted;
|
||||
|
||||
@Test
|
||||
@ManualBenchmarkTest(
|
||||
targetTestDurationNs = 20 * TIME_1_S_IN_NS,
|
||||
@@ -76,13 +78,13 @@ public class InternalWindowOperationPerfTest extends WindowManagerPerfTestBase {
|
||||
| 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 @@ public class InternalWindowOperationPerfTest extends WindowManagerPerfTestBase {
|
||||
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 @@ public class InternalWindowOperationPerfTest extends WindowManagerPerfTestBase {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ import static org.hamcrest.core.AnyOf.anyOf;
|
||||
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 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
@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 @@ public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase {
|
||||
@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 @@ public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase {
|
||||
ActivityManager.resumeAppSwitches();
|
||||
} catch (RemoteException ignored) {
|
||||
}
|
||||
sUiAutomation.dropShellPermissionIdentity();
|
||||
getUiAutomation().dropShellPermissionIdentity();
|
||||
}
|
||||
|
||||
@Before
|
||||
@@ -162,6 +163,7 @@ public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase {
|
||||
| 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 @@ public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,13 +21,12 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat
|
||||
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.View;
|
||||
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 @@ import java.util.function.IntSupplier;
|
||||
|
||||
@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 @@ public class RelayoutPerfTest extends WindowManagerPerfTestBase {
|
||||
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 @@ public class RelayoutPerfTest extends WindowManagerPerfTestBase {
|
||||
}
|
||||
|
||||
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 @@ public class RelayoutPerfTest extends WindowManagerPerfTestBase {
|
||||
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 @@ public class RelayoutPerfTest extends WindowManagerPerfTestBase {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,14 +19,12 @@ package android.wm;
|
||||
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 @@ import org.junit.Test;
|
||||
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 @@ public class WindowAddRemovePerfTest extends WindowManagerPerfTestBase
|
||||
@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 @@ public class WindowAddRemovePerfTest extends WindowManagerPerfTestBase
|
||||
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);
|
||||
|
||||
|
||||
@@ -19,125 +19,45 @@ package android.wm;
|
||||
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
|
||||
@@ -155,11 +75,6 @@ public class WindowManagerPerfTestBase {
|
||||
return super.apply(wrappedStatement, description);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Intent getActivityIntent() {
|
||||
return mStartIntent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PerfTestActivity launchActivity(Intent intent) {
|
||||
final PerfTestActivity activity = super.launchActivity(intent);
|
||||
@@ -167,10 +82,6 @@ public class WindowManagerPerfTestBase {
|
||||
return activity;
|
||||
}
|
||||
|
||||
PerfTestActivity launchActivity() {
|
||||
return launchActivity(mStartIntent);
|
||||
}
|
||||
|
||||
void waitForIdleSync(Stage state) {
|
||||
mLifecycleListener.waitForIdleSync(state);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user